diff --git a/.gitignore b/.gitignore index 9154f4c..d1cebfc 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,41 @@ hs_err_pid* replay_pid* +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### IntelliJ IDEA ### +.idea/modules.xml +.idea/jarRepositories.xml +.idea/compiler.xml +.idea/libraries/ +*.iws +*.iml +*.ipr + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store \ No newline at end of file diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 0000000..b3ab2da --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..2e74ab9 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,15 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml new file mode 100644 index 0000000..9645b9d --- /dev/null +++ b/.idea/workspace.xml @@ -0,0 +1,368 @@ + + + + + + + + + + + + + + + + { + "associatedIndex": 1 +} + + + + { + "keyToString": { + "Application.CloudSendInfoUtils.executor": "Debug", + "Application.DataUtils.executor": "Debug", + "Application.Group.executor": "Run", + "Application.InstructionSendInfoUtils.executor": "Run", + "Application.TestController.executor": "Run", + "Application.XmlRpcHttpDirectCall.executor": "Run", + "Application.test111.executor": "Run", + "Maven.commoms [clean].executor": "Run", + "Maven.commoms [package].executor": "Run", + "Maven.core [clean].executor": "Run", + "Maven.core [install].executor": "Run", + "Maven.core [package].executor": "Run", + "Maven.station_control [clean,install,-U].executor": "Run", + "Maven.station_control [clean].executor": "Run", + "Maven.station_control [dependency:resolve,-U].executor": "Run", + "Maven.station_control [dependency:tree].executor": "Run", + "Maven.station_control [install].executor": "Run", + "Maven.station_control [package].executor": "Run", + "Maven.station_control [tree].executor": "Run", + "Maven.web-server [clean,install,-U].executor": "Run", + "Maven.web-server [clean].executor": "Run", + "Maven.web-server [compile].executor": "Run", + "Maven.web-server [dependency:resolve,-U].executor": "Run", + "Maven.web-server [package].executor": "Run", + "RequestMappingsPanelOrder0": "0", + "RequestMappingsPanelOrder1": "1", + "RequestMappingsPanelWidth0": "75", + "RequestMappingsPanelWidth1": "75", + "RunOnceActivity.ShowReadmeOnStart": "true", + "Spring Boot.WebServerApplication.executor": "Debug", + "kotlin-language-version-configured": "true", + "last_opened_file_path": "D:/andy/ideaWorker/my/station-control/web-server/src/main/resources", + "node.js.detected.package.eslint": "true", + "node.js.detected.package.tslint": "true", + "node.js.selected.package.eslint": "(autodetect)", + "node.js.selected.package.tslint": "(autodetect)", + "nodejs_package_manager_path": "npm", + "project.structure.last.edited": "Project", + "project.structure.proportion": "0.15", + "project.structure.side.proportion": "0.2", + "settings.editor.selected.configurable": "fileTemplates", + "vue.rearranger.settings.migration": "true" + } +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1754904370788 + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/commoms/core/pom.xml b/commoms/core/pom.xml new file mode 100644 index 0000000..13cde45 --- /dev/null +++ b/commoms/core/pom.xml @@ -0,0 +1,59 @@ + + + 4.0.0 + + com.evotech.hd + commoms + 1.0-SNAPSHOT + + com.evotech.hd + core + 1.0-SNAPSHOT + core + core + + + + org.projectlombok + lombok + 1.18.38 + + + io.swagger + swagger-annotations + 1.6.2 + + + org.slf4j + slf4j-api + + + org.mapstruct + mapstruct-processor + 1.4.2.Final + provided + + + org.mapstruct + mapstruct + 1.4.2.Final + compile + + + com.baomidou + mybatis-plus-boot-starter + 3.4.2 + + + org.apache.commons + commons-lang3 + 3.14.0 + + + com.fasterxml.jackson.core + jackson-databind + + + \ No newline at end of file diff --git a/commoms/core/src/main/java/com/evotech/hd/core/converter/ObjectToStringDeserializer.java b/commoms/core/src/main/java/com/evotech/hd/core/converter/ObjectToStringDeserializer.java new file mode 100644 index 0000000..0f8a69c --- /dev/null +++ b/commoms/core/src/main/java/com/evotech/hd/core/converter/ObjectToStringDeserializer.java @@ -0,0 +1,24 @@ +package com.evotech.hd.core.converter; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; + +import java.io.IOException; + +/** + * ObjectToStringDeserializer + * + * @author andy.shi + * @ClassName:ObjectToStringDeserializer + * @date: 2025年10月25日 8:50 + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ + +public class ObjectToStringDeserializer extends JsonDeserializer { + @Override + public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { + // 将 JSON 对象转为字符串(如 {"key":"value"} → "{\"key\":\"value\"}") + return p.readValueAsTree().toString(); + } +} \ No newline at end of file diff --git a/commoms/core/src/main/java/com/evotech/hd/core/dtos/BaseDTO.java b/commoms/core/src/main/java/com/evotech/hd/core/dtos/BaseDTO.java new file mode 100644 index 0000000..b67a86c --- /dev/null +++ b/commoms/core/src/main/java/com/evotech/hd/core/dtos/BaseDTO.java @@ -0,0 +1,88 @@ +package com.evotech.hd.core.dtos; + + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; +import com.evotech.hd.core.converter.ObjectToStringDeserializer; +import com.evotech.hd.query.Query; +import com.evotech.hd.query.QueryType; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import org.apache.commons.lang3.StringUtils; +import org.apache.ibatis.type.JdbcType; + +import java.io.Serializable; +import java.util.Date; +import java.util.LinkedHashMap; +import java.util.Map; + +@Data +@JsonInclude(JsonInclude.Include.ALWAYS) +public abstract class BaseDTO implements Serializable { + + private static final long serialVersionUID = 1L; + /** + * 实体主键 + */ + @ApiModelProperty("唯一标识") + private Integer id; + @ApiModelProperty("name") + @Query(javaField = "name", tableColumn = "a.name", type = QueryType.LIKE) + String name; + + @ApiModelProperty(hidden = true) + private Date createTime; + + @ApiModelProperty(hidden = true) + @TableField(typeHandler = JacksonTypeHandler.class, jdbcType = JdbcType.OTHER) + @JsonDeserialize(using = ObjectToStringDeserializer.class) + private String extendeds; + + + public Map getExtendeds() { + if(StringUtils.isNotEmpty(extendeds)){ + try { +// ObjectMapper 是 Jackson 库的核心类,用于处理 JSON 数据的序列化和反序列化。它提供了多种方法来将 Java 对象转换为 JSON 字符串,以及将 JSON 字符串转换为 Java 对象 + // 使用 Jackson 库将 JSON 字符串反序列化为 Map 类型的代码 + return new ObjectMapper().readValue(extendeds, Map.class); + } catch (Exception e) { + throw new RuntimeException("Failed to parse JSON", e); + } + } + return new LinkedHashMap<>(); + } + public void setParams(Map params) { + if (params == null) { + this.extendeds = null; + } else { + try { + // 将 params(一个 Map 类型的对象)转换为 JSON 格式的字符串,并将其存储在 paramsJson 字段中 + this.extendeds = new ObjectMapper().writeValueAsString(params); + } catch (Exception e) { + throw new RuntimeException("Failed to serialize JSON", e); + } + } + } + + /** + * 构造函数 + */ + public BaseDTO() { + + } + + /** + * 构造函数 + * + * @param id + */ + public BaseDTO(Integer id) { + this.id = id; + } + + +} + diff --git a/commoms/core/src/main/java/com/evotech/hd/core/dtos/LoginForm.java b/commoms/core/src/main/java/com/evotech/hd/core/dtos/LoginForm.java new file mode 100644 index 0000000..0dbb072 --- /dev/null +++ b/commoms/core/src/main/java/com/evotech/hd/core/dtos/LoginForm.java @@ -0,0 +1,22 @@ +package com.evotech.hd.core.dtos; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +@ApiModel("登录对象") +@Data +public class LoginForm { + + /** + * 用户名 + */ + @ApiModelProperty("用户名") + private String username; + + /** + * 密码 + */ + @ApiModelProperty("密码") + private String password; +} diff --git a/commoms/core/src/main/java/com/evotech/hd/core/dtos/OptionDTO.java b/commoms/core/src/main/java/com/evotech/hd/core/dtos/OptionDTO.java new file mode 100644 index 0000000..abc6431 --- /dev/null +++ b/commoms/core/src/main/java/com/evotech/hd/core/dtos/OptionDTO.java @@ -0,0 +1,62 @@ +package com.evotech.hd.core.dtos; + +import com.evotech.hd.core.entity.BaseEntity; +import com.evotech.hd.core.enums.BaseEnum; +import com.evotech.hd.utils.Collections; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.io.Serializable; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +/** + * 下拉菜单 模版 类 + * + * @ClassName:OptionDTO + * @date: 2025年09月17日 9:23 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@Data +@ApiModel("下拉菜单模版") +public class OptionDTO implements Serializable { + + @ApiModelProperty("下拉菜单name") + String label; + + @ApiModelProperty("下拉菜单value") + String value; + + List childList = Collections.emptyList(); + + public OptionDTO() { + } + + public OptionDTO(String label, String value) { + this.label = label; + this.value = value; + } + + public OptionDTO(BaseEnum baseEnum) { + this.label = String.valueOf(baseEnum.getLabel()); + this.value = String.valueOf(baseEnum.getValue()); + } + + public OptionDTO(BaseEntity baseEntity) { + this.label = baseEntity.getName(); + this.value = baseEntity.getKey(); + } + + public static List getOptionDTOList(Map params) { + List optionList = Collections.emptyList(); + Iterator> paramsIt = params.entrySet().iterator(); + while (paramsIt.hasNext()){ + Map.Entry entry = paramsIt.next(); + optionList.add(new OptionDTO(entry.getKey(), entry.getValue())); + } + return optionList; + } +} diff --git a/commoms/core/src/main/java/com/evotech/hd/core/dtos/PageDTO.java b/commoms/core/src/main/java/com/evotech/hd/core/dtos/PageDTO.java new file mode 100644 index 0000000..35f704b --- /dev/null +++ b/commoms/core/src/main/java/com/evotech/hd/core/dtos/PageDTO.java @@ -0,0 +1,23 @@ +package com.evotech.hd.core.dtos; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 类 + * + * @ClassName:PageDTO + * @date: 2025年08月20日 15:01 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@Data +@ApiModel("分页工具类") +public class PageDTO { + + @ApiModelProperty("当前页") + Integer page; + @ApiModelProperty("每页显示的条数") + Integer pageSize; +} diff --git a/commoms/core/src/main/java/com/evotech/hd/core/dtos/ParamMeta.java b/commoms/core/src/main/java/com/evotech/hd/core/dtos/ParamMeta.java new file mode 100644 index 0000000..e5c8098 --- /dev/null +++ b/commoms/core/src/main/java/com/evotech/hd/core/dtos/ParamMeta.java @@ -0,0 +1,31 @@ +package com.evotech.hd.core.dtos; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 类 + * + * @ClassName:ParamMeta + * @date: 2025年09月18日 13:38 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@Data +@ApiModel("MAP类型数据返回") +public class ParamMeta { + @ApiModelProperty("Map的key") + private String key; + + @ApiModelProperty("特殊参数对象") + private T value; + + public ParamMeta() { + } + + public ParamMeta(String key, T value) { + this.key = key; + this.value = value; + } +} diff --git a/commoms/core/src/main/java/com/evotech/hd/core/dtos/RequestParams.java b/commoms/core/src/main/java/com/evotech/hd/core/dtos/RequestParams.java new file mode 100644 index 0000000..fa85419 --- /dev/null +++ b/commoms/core/src/main/java/com/evotech/hd/core/dtos/RequestParams.java @@ -0,0 +1,22 @@ +package com.evotech.hd.core.dtos; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 类 + * + * @ClassName:Params + * @date: 2025年09月04日 15:37 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@Data +@ApiModel("请求接口参数") +public class RequestParams{ + @ApiModelProperty("请求条件") + T params; + @ApiModelProperty("分页") + PageDTO page; +} diff --git a/commoms/core/src/main/java/com/evotech/hd/core/dtos/Result.java b/commoms/core/src/main/java/com/evotech/hd/core/dtos/Result.java new file mode 100644 index 0000000..eb78d2b --- /dev/null +++ b/commoms/core/src/main/java/com/evotech/hd/core/dtos/Result.java @@ -0,0 +1,135 @@ +package com.evotech.hd.core.dtos; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.evotech.hd.core.enums.CodeMsg; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.io.Serializable; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 请求返回对象 + * @ClassName:BaseResponse + * @date: 2025年04月15日 16:37 + * @author: andy.shi + * @contact: 17330188597 + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@Data +@ApiModel +public class Result implements Serializable { + + @ApiModelProperty("请求处理状态") + private Integer status; + @ApiModelProperty("状态码") + private String code; + @ApiModelProperty("返回消息") + private String msg; + @ApiModelProperty(value = "返回数据", required = true) + private T data; + + public static Result getInstance(){ + return new Result(); + } + + public Result() { + } + + public Result build(Class cls) { + return (Result) this; + } + public Result> buildList(Class cls) { + return (Result>) this; + } + + public Result> buildIPage(Class cls) { + return (Result>) this; + } + + public Result> buildMap(Class cls) { + return (Result>) this; + } + + public Result>> buildMap(List cls) { + return (Result>>) this; + } + + public Result success() { + this.status = 1; + this.code = CodeMsg.SUCCESS.getCode(); + this.msg = CodeMsg.SUCCESS.getMsg(); + return this; + } + + public Result success(T o) { + this.status = 1; + this.code = CodeMsg.SUCCESS.getCode(); + this.msg = CodeMsg.SUCCESS.getMsg(); + this.data = o; + return this; + } + public Result success(T o, String msg) { + this.status = 1; + this.code = CodeMsg.SUCCESS.getCode(); + this.msg = msg; + this.data = o; + return this; + } + + public Result error(String errMsg) { + this.status = 1; + this.code = CodeMsg.ERROR.getCode(); + this.msg = errMsg; + return this; + } + + public Result error(String errMsg, T data) { + this.status = 1; + this.code = CodeMsg.ERROR.getCode(); + this.msg = errMsg; + this.data = data; + return this; + } + + public Result error(String code, String errMsg) { + this.status = 1; + this.code = code; + this.msg = errMsg; + this.data = data; + return this; + } + + public Result error(CodeMsg codeMsg) { + this.status = 1; + this.code = codeMsg.getCode(); + this.msg = codeMsg.getMsg(); + return this; + } + + public Result error(CodeMsg codeMsg, String errorMsg) { + this.status = 1; + this.code = codeMsg.getCode(); + this.msg = errorMsg; + return this; + } + + public Result exception(T data) { + this.status = -1; + this.code = CodeMsg.ERROR.getCode(); + this.msg = CodeMsg.ERROR.getMsg(); + this.data = data; + return this; + } + + public Result businessException(String code, String msg, T o) { + this.status = -1; + this.code = code; + this.msg = msg; + this.data = o; + return this; + } +} diff --git a/commoms/core/src/main/java/com/evotech/hd/core/dtos/ResultUtil.java b/commoms/core/src/main/java/com/evotech/hd/core/dtos/ResultUtil.java new file mode 100644 index 0000000..2928634 --- /dev/null +++ b/commoms/core/src/main/java/com/evotech/hd/core/dtos/ResultUtil.java @@ -0,0 +1,29 @@ +package com.evotech.hd.core.dtos; + +import com.evotech.hd.core.enums.CodeMsg; +import lombok.extern.slf4j.Slf4j; + +/** + * 请求相应工具类 + * @ClassName:ResponseUtil + * @date: 2025年04月22日 16:38 + * @author: andy.shi + * @contact: 17330188597 + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@Slf4j +public class ResultUtil { + + + public static T getValue(Result result){ + if(verifyCode(result)){ + return result.getData(); + } + log.error("当前请求出现异常{}", result.getMsg()); + return null; + } + + public static Boolean verifyCode(Result result){ + return CodeMsg.SUCCESS.getCode().equals(result.getCode()); + } +} diff --git a/commoms/core/src/main/java/com/evotech/hd/core/dtos/UserDTO.java b/commoms/core/src/main/java/com/evotech/hd/core/dtos/UserDTO.java new file mode 100644 index 0000000..f0cd763 --- /dev/null +++ b/commoms/core/src/main/java/com/evotech/hd/core/dtos/UserDTO.java @@ -0,0 +1,51 @@ +/** + * Copyright © 2021-2026 space All rights reserved. + */ +package com.evotech.hd.core.dtos; + + +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 用户Entity + * + * @author space + * @version 2021-09-05 + */ +@Data +@EqualsAndHashCode(callSuper = false) +public class UserDTO extends BaseDTO { + + private static final long serialVersionUID = 1L; + + private Integer gitUserId; + + private String loginName; + + private String loginFlag; + + /** + * 密码 + */ + + private String password; + /** + * 姓名 + */ + + private String name; + + + + + public UserDTO() { + super(); + } + + public UserDTO(Integer id) { + super(id); + } + + +} diff --git a/commoms/core/src/main/java/com/evotech/hd/core/dtos/business/AccessStrategyDTO.java b/commoms/core/src/main/java/com/evotech/hd/core/dtos/business/AccessStrategyDTO.java new file mode 100644 index 0000000..5b9c46e --- /dev/null +++ b/commoms/core/src/main/java/com/evotech/hd/core/dtos/business/AccessStrategyDTO.java @@ -0,0 +1,53 @@ +package com.evotech.hd.core.dtos.business; + +import com.evotech.hd.core.dtos.BaseDTO; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 存储策略类 + * + * @ClassName:AccessStrategy + * @date: 2025年08月27日 15:59 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@Data +@ApiModel("存储策略类") +public class AccessStrategyDTO extends BaseDTO { + + @ApiModelProperty("存取策略: 1.存取相同, 2 顺序存续. 3间隔存储 4 随机存储") + Integer strategy; + /*** + * 电源策略 + */ + @ApiModelProperty("电源策略") + Integer powerStrategy; +// /*** +// * 充电策略 1 即存即充, 2 按计划存储 +// */ +// @ApiModelProperty("充电策略") +// Integer chargingStrategy; +// /*** +// *即存即充功率 +// */ + @ApiModelProperty("即存即充功率") + Integer instantChargePower; + + @ApiModelProperty("最低安全库存") + Integer minimumSafetyStock; + + @ApiModelProperty("最低换电SOC") + Integer lowestBatterySwapSoc; + + @ApiModelProperty("充满界定soc") + Integer fullOfLimitSoc; + + @ApiModelProperty("生效的Rgv编号") + String effectiveRgvNo; + + + @ApiModelProperty(value = "数据是否有效: valid有效 invalid 无效", hidden = true) + String valid; +} diff --git a/commoms/core/src/main/java/com/evotech/hd/core/dtos/business/BatteryCompartmentDTO.java b/commoms/core/src/main/java/com/evotech/hd/core/dtos/business/BatteryCompartmentDTO.java new file mode 100644 index 0000000..5904ae9 --- /dev/null +++ b/commoms/core/src/main/java/com/evotech/hd/core/dtos/business/BatteryCompartmentDTO.java @@ -0,0 +1,75 @@ +package com.evotech.hd.core.dtos.business; + +import com.evotech.hd.core.dtos.BaseDTO; +import com.fasterxml.jackson.annotation.JsonInclude; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.Date; + +/** + * 类 + * + * @ClassName:Test + * @date: 2025年09月19日 17:06 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ + +@ApiModel("电池仓详情") +@Data +@JsonInclude(JsonInclude.Include.ALWAYS) +public class BatteryCompartmentDTO extends BaseDTO { + + @ApiModelProperty("电池仓编号") + String batteryCompartmentNo; + + @ApiModelProperty("电池仓状态:1-正常,2-检修, 3-坏") + Integer status; + + @ApiModelProperty("运行状态:1正常, 2-禁用") + Integer state; + + @ApiModelProperty("排序") + Integer sort; + + @ApiModelProperty("是否存在电池") + Boolean existsBattery; + + @ApiModelProperty("充电机状态, 0:空闲; 1:监控状态; 2:单功率充电; 3:双功率充电;4:辅助充电;5:调试状态;6:设备错误状态") + Integer chargerStatus; + + @ApiModelProperty("电池到位信号, 0 无电池 1电池正常 2, 电池错误") + String batteryPlaceStatus; + + @ApiModelProperty("电池通信状态") + String batteryCommunicationStatus; + + @ApiModelProperty("直流电表通信状态") + String directCurrentCommunicationStatus; + + @ApiModelProperty("电池低压电源状态") + String lowPressurePowerSupplyStatus; + + @ApiModelProperty("辅助电源状态") + String auxiliaryPowerSupplyStatus; + + @ApiModelProperty("最大充电功率") + String maximumChargingPower; + + @ApiModelProperty("直流电表压 (KW/H) 忘记是啥了, 到时候在说") + String directCurrentMeterVoltage; + + @ApiModelProperty("电池存入时间") + Date depositTime; + + @ApiModelProperty("电池信息") + BatteryDTO battery; + + @ApiModelProperty(value = "反向记录充电订单Id, 用于刷新数据",hidden = true) + Integer orderChargingId; + + @ApiModelProperty(value = "是否全功率充电",hidden = true) + Boolean allPower; +} \ No newline at end of file diff --git a/commoms/core/src/main/java/com/evotech/hd/core/dtos/business/BatteryDTO.java b/commoms/core/src/main/java/com/evotech/hd/core/dtos/business/BatteryDTO.java new file mode 100644 index 0000000..3dd2d52 --- /dev/null +++ b/commoms/core/src/main/java/com/evotech/hd/core/dtos/business/BatteryDTO.java @@ -0,0 +1,56 @@ +package com.evotech.hd.core.dtos.business; + +import com.baomidou.mybatisplus.annotation.TableName; +import com.evotech.hd.core.dtos.BaseDTO; +import com.evotech.hd.core.entity.BaseEntity; +import com.fasterxml.jackson.annotation.JsonInclude; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 电池信息类 + * + * @ClassName:Battery + * @date: 2025年08月18日 12:00 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@Data +@ApiModel("电池信息类") +@JsonInclude(JsonInclude.Include.ALWAYS) +public class BatteryDTO extends BaseDTO { + + @ApiModelProperty("电池编号") + private String batteryCode; + + @ApiModelProperty("电池类型") + String batteryType; + + @ApiModelProperty("电池模式; 0:未上电;1:监控模式;2:充电初始化;3:单功充电;4:双功充电;5:暂停充电;6:停止充电;7:充电故障;8:充电错误") + String batteryMode; + + @ApiModelProperty("电池仓Id") + Integer batteryCompartmentId; + + @ApiModelProperty("电池状态在仓状态, 1在仓 0 离仓") + Integer storageStatus; + + @ApiModelProperty("电池健康度") + String soh; + + @ApiModelProperty("电池额定容量") + String ratedCapacity; + + + @ApiModelProperty("电池单体数量") + Integer monomerQuantity; + + @ApiModelProperty("探针数量") + Integer numberOfProbes; + + @ApiModelProperty("电池电量") + String soc; + +} diff --git a/commoms/core/src/main/java/com/evotech/hd/core/dtos/business/BatteryStrategyDTO.java b/commoms/core/src/main/java/com/evotech/hd/core/dtos/business/BatteryStrategyDTO.java new file mode 100644 index 0000000..1ed261d --- /dev/null +++ b/commoms/core/src/main/java/com/evotech/hd/core/dtos/business/BatteryStrategyDTO.java @@ -0,0 +1,32 @@ +package com.evotech.hd.core.dtos.business; + +import com.evotech.hd.core.dtos.BaseDTO; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 充电策略 + * + * @ClassName:BatteryStrategyDTO + * @date: 2025年09月27日 15:59 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@Data +@ApiModel("充电策略") +public class BatteryStrategyDTO extends BaseDTO { + + @ApiModelProperty("开始时间") + private String beginTime; + + @ApiModelProperty("结束时间") + private String endTime; + + @ApiModelProperty("充电功率") + private Integer chargingPower; + + @ApiModelProperty("删除标记") + private Boolean del; +} diff --git a/commoms/core/src/main/java/com/evotech/hd/core/dtos/business/BatterySwapStepDTO.java b/commoms/core/src/main/java/com/evotech/hd/core/dtos/business/BatterySwapStepDTO.java new file mode 100644 index 0000000..aff6925 --- /dev/null +++ b/commoms/core/src/main/java/com/evotech/hd/core/dtos/business/BatterySwapStepDTO.java @@ -0,0 +1,46 @@ +package com.evotech.hd.core.dtos.business; + +import com.evotech.hd.core.dtos.BaseDTO; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.Date; + +/** + * 换电步骤表类 + * @ClassName:BatterySwapStep + * @date: 2025年08月18日 10:10 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@Data +@ApiModel("换电步骤表类") +public class BatterySwapStepDTO extends BaseDTO { + + /*** + * 订单Id + */ + @ApiModelProperty(value = "订单Id", hidden = true) + Integer orderId; + /*** + * 换电步骤 + */ + @ApiModelProperty(value = "换电步骤") + Integer step; + @ApiModelProperty(value = "换电步骤名称") + String stepName; + /*** + * 换电时间 + */ + @ApiModelProperty(value = "换电时间") + Date stepTime; + /*** + * 是否人为操作 + */ + @ApiModelProperty(value = "是否人为操作") + String humanOperation; + //是否出现错误 + @ApiModelProperty(value = "是否出现错误") + Boolean error; +} diff --git a/commoms/core/src/main/java/com/evotech/hd/core/dtos/business/HomeCompartmentDTO.java b/commoms/core/src/main/java/com/evotech/hd/core/dtos/business/HomeCompartmentDTO.java new file mode 100644 index 0000000..d4eceb3 --- /dev/null +++ b/commoms/core/src/main/java/com/evotech/hd/core/dtos/business/HomeCompartmentDTO.java @@ -0,0 +1,35 @@ +package com.evotech.hd.core.dtos.business; + +import com.evotech.hd.core.dtos.BaseDTO; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 首页电池信息类 + * + * @ClassName:HomeCompartmentDTO + * @date: 2025年08月22日 11:43 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@Data +@ApiModel("首页电池仓信息") +public class HomeCompartmentDTO extends BaseDTO { + + @ApiModelProperty("总电池仓位数") + Integer totalNum; + + @ApiModelProperty("空闲仓位数") + Integer idleNum; + + @ApiModelProperty("可用仓位数") + Integer fullyChargedNum; + + @ApiModelProperty("充电中的仓位数") + Integer chargingNum; + + @ApiModelProperty("损坏仓位数") + Integer damagedNum; + +} diff --git a/commoms/core/src/main/java/com/evotech/hd/core/dtos/business/OrderChargingDTO.java b/commoms/core/src/main/java/com/evotech/hd/core/dtos/business/OrderChargingDTO.java new file mode 100644 index 0000000..8b296b1 --- /dev/null +++ b/commoms/core/src/main/java/com/evotech/hd/core/dtos/business/OrderChargingDTO.java @@ -0,0 +1,51 @@ +package com.evotech.hd.core.dtos.business; + +import com.evotech.hd.core.dtos.BaseDTO; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.Date; + +/** + * 充电订单 + * + * @author andy.shi + * @ClassName:OrderCharging + * @date: 2025年09月30日 14:21 + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@Data +@ApiModel("充电订单") +public class OrderChargingDTO extends BaseDTO { + + @ApiModelProperty("充电单号") + String chargingNo; + + @ApiModelProperty("电池编号") + String batteryCode; + + @ApiModelProperty("电池仓Id") + Integer batteryCompartmentId; + + @ApiModelProperty("策略信息") + String strategyInfo; + + @ApiModelProperty("充电状态 1:充电结束 2 充电中") + Integer status; + + @ApiModelProperty("耗电量;如果soc的话为soc") + Integer powerConsumption; + + @ApiModelProperty("开始电量; 如果soc的话为开始soc") + Integer startElectric; + + @ApiModelProperty("结束电量; 如果soc的话为开始soc") + Integer endElectric; + + @ApiModelProperty("充电开始时间") + Date beginTime; + + @ApiModelProperty("充电结束时间") + Date endTime; +} diff --git a/commoms/core/src/main/java/com/evotech/hd/core/dtos/business/OrderReservationDTO.java b/commoms/core/src/main/java/com/evotech/hd/core/dtos/business/OrderReservationDTO.java new file mode 100644 index 0000000..0d042d0 --- /dev/null +++ b/commoms/core/src/main/java/com/evotech/hd/core/dtos/business/OrderReservationDTO.java @@ -0,0 +1,56 @@ +package com.evotech.hd.core.dtos.business; + +import com.evotech.hd.core.dtos.BaseDTO; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.Date; + +/** + * 预约订单类 + * @ClassName:OrderReservationDTO + * @date: 2025年08月18日 11:00 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@Data +@ApiModel("预约订单类") +public class OrderReservationDTO extends BaseDTO { + @ApiModelProperty("数据来源") + String source; + + @ApiModelProperty(value = "云平台预约Id", hidden = true) + Integer cloudReservationId; + + @ApiModelProperty("预约人姓名") + String reservationName; + + @ApiModelProperty("预约人电话") + String reservationPhone; + + @ApiModelProperty("车牌号") + String plateNum; + + @ApiModelProperty(value = "微信预约人Id", hidden = true) + String wechatUserId; + + @ApiModelProperty(value = "微信预约人姓名") + String wechatUserName; + + @ApiModelProperty(value = "预约时间") + Date reservationTime; + + @ApiModelProperty(value = "预约换电日期") + String swapDay; + + @ApiModelProperty(value = "预约换电时间段") + String swapDuration; + + @ApiModelProperty(value = "状态:1-预约成功,2-到店使用,3-取消,4-过期") + Integer status; + +// @ApiModelProperty(value = "推送状态:1-成功, 2-失败") +// Integer pushStatus; 预约单是特殊的订单, 需要验证云平台是否成功, 所以不存在说推送失败的情况 + +} diff --git a/commoms/core/src/main/java/com/evotech/hd/core/dtos/business/OrderSwapDTO.java b/commoms/core/src/main/java/com/evotech/hd/core/dtos/business/OrderSwapDTO.java new file mode 100644 index 0000000..91a4601 --- /dev/null +++ b/commoms/core/src/main/java/com/evotech/hd/core/dtos/business/OrderSwapDTO.java @@ -0,0 +1,123 @@ +package com.evotech.hd.core.dtos.business; + +import com.evotech.hd.core.dtos.BaseDTO; +import com.evotech.hd.utils.Collections; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.Date; +import java.util.List; + +/** + * 换电订单表 + * @ClassName:OrderSwap + * @date: 2025年08月18日 11:11 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@Data +@ApiModel("换电订单类") +public class OrderSwapDTO extends BaseDTO { + + @ApiModelProperty(value = "云平台订单id", hidden = true) + Integer cloudOrderId; + + @ApiModelProperty(value = "订单编号") + String orderNo; + + @ApiModelProperty(value = "预约人微信Id", hidden = true) + String preWechatId; + + @ApiModelProperty(value = "预约订单Id", hidden = true) + String orderPreId; + + @ApiModelProperty(value = "预约用户名称") + String orderPreName; + + @ApiModelProperty(value = "类型") + String orderType; + + @ApiModelProperty(value = "车牌号") + String plateNum; + + @ApiModelProperty(value = "订单时间") + Date orderTime; + + @ApiModelProperty(value = "订单总金额 (分)") + Integer orderAmount; + + @ApiModelProperty(value = "订单状态:1-已创建,2-换电中,3-换电完成,4-充电中,5-充电完成,6-待结算,7-已完成,9-已取消") + Integer status; + + @ApiModelProperty(value = "支付方式 1余额, 2微信, 3支付宝") + String payType; + + @ApiModelProperty(value = "支付状态") + String payStatus; + + @ApiModelProperty(value = "支付时间") + Date payTime; + + @ApiModelProperty(value = "计算费用方式:1-ODO,2-SOC,3-按电量") + String feeType; + + @ApiModelProperty(value = "订单金额 (分)") + Integer orderFee; + + @ApiModelProperty(value = "服务金额 (分)") + Integer serviceFee; + + @ApiModelProperty(value = "服务开始时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + Date serviceTimeBegin; + + @ApiModelProperty(value = "服务结束时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + Date serviceTimeEnd; + + @ApiModelProperty(value = "消耗电量;如果是soc结算 则是soc的差值") + String electricityQuantity; + + @ApiModelProperty(value = "租借电池包仓位") + String rentBatNo; + + @ApiModelProperty(value = "租用电池包编码") + String rentBatCode; + + @ApiModelProperty(value = "租用电池包SOC") + String rentBatSoc; + + @ApiModelProperty(value = "归还电池包仓位") + String returnBatNo; + + @ApiModelProperty(value = "归还电池包编码") + String returnBatCode; + + @ApiModelProperty(value = "归还电池包SOC") + String returnBatSoc; + + @ApiModelProperty(value = "换电模式:1-全自动,2-半自动,3-人工干预") + String changeMode; + + @ApiModelProperty(value = "换电车道 1-A 车道;2-B 车道") + String changeLane; + + @ApiModelProperty(value = "换电步骤") + List batterySwapStepDTOList = Collections.emptyList(); + + @ApiModelProperty(value = "推送状态:1-成功, 2-失败") + Integer pushStatus; + + + @ApiModelProperty(value = "当前站端使用的rgv数量") + Integer rgvNum; + + public OrderSwapDTO() { + } + + public OrderSwapDTO(Integer rgvNum) { + this.rgvNum = rgvNum; + } +} diff --git a/commoms/core/src/main/java/com/evotech/hd/core/dtos/business/RunningInstructionsDTO.java b/commoms/core/src/main/java/com/evotech/hd/core/dtos/business/RunningInstructionsDTO.java new file mode 100644 index 0000000..338a69a --- /dev/null +++ b/commoms/core/src/main/java/com/evotech/hd/core/dtos/business/RunningInstructionsDTO.java @@ -0,0 +1,22 @@ +package com.evotech.hd.core.dtos.business; + +import com.evotech.hd.core.dtos.BaseDTO; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 物理指令步骤类 + * + * @ClassName:DictDetailDTO + * @date: 2025年08月26日 16:42 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@ApiModel("物理指令名称") +@Data +public class RunningInstructionsDTO extends BaseDTO { + + @ApiModelProperty("指令编码") + String code; +} diff --git a/commoms/core/src/main/java/com/evotech/hd/core/dtos/business/RunningInstructionsDetailDTO.java b/commoms/core/src/main/java/com/evotech/hd/core/dtos/business/RunningInstructionsDetailDTO.java new file mode 100644 index 0000000..3993ae3 --- /dev/null +++ b/commoms/core/src/main/java/com/evotech/hd/core/dtos/business/RunningInstructionsDetailDTO.java @@ -0,0 +1,35 @@ +package com.evotech.hd.core.dtos.business; + +import com.evotech.hd.core.dtos.BaseDTO; +import com.evotech.hd.query.Query; +import com.evotech.hd.query.QueryType; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 物理指令步骤类 + * + * @ClassName:DictDetailDTO + * @date: 2025年08月26日 16:42 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@ApiModel("物理指令步骤") +@Data +public class RunningInstructionsDetailDTO extends BaseDTO { + + @ApiModelProperty("指令的paramCode") + String dictValue; + + @ApiModelProperty("指令的分类") + @Query(javaField = "typeCode", tableColumn = "a.type_code", type = QueryType.EQ) + String typeCode; + + @ApiModelProperty("指令的默认值") + Integer defaultValue; + + @ApiModelProperty("执行顺序, 数值越靠前") + Integer sort; + +} diff --git a/commoms/core/src/main/java/com/evotech/hd/core/dtos/business/Test2.java b/commoms/core/src/main/java/com/evotech/hd/core/dtos/business/Test2.java new file mode 100644 index 0000000..2592067 --- /dev/null +++ b/commoms/core/src/main/java/com/evotech/hd/core/dtos/business/Test2.java @@ -0,0 +1,22 @@ +package com.evotech.hd.core.dtos.business; + +import com.evotech.hd.core.dtos.BaseDTO; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 类 + * + * @ClassName:Test2 + * @date: 2025年09月19日 17:06 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@ApiModel("二层测试") +@Data +public class Test2 extends BaseDTO { + @ApiModelProperty("二层名称") + String name; + +} \ No newline at end of file diff --git a/commoms/core/src/main/java/com/evotech/hd/core/dtos/business/airconditioning/AirConditioningBaseDTO.java b/commoms/core/src/main/java/com/evotech/hd/core/dtos/business/airconditioning/AirConditioningBaseDTO.java new file mode 100644 index 0000000..d31fcbe --- /dev/null +++ b/commoms/core/src/main/java/com/evotech/hd/core/dtos/business/airconditioning/AirConditioningBaseDTO.java @@ -0,0 +1,30 @@ +package com.evotech.hd.core.dtos.business.airconditioning; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 空调控制:基本属性 + * + * @author andy.shi + * @ClassName:AirConditioningBaseDTO + * @date: 2025年09月23日 14:43 + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@Data +@ApiModel("空调控制:基本属性") +public class AirConditioningBaseDTO { + + @ApiModelProperty("空调连接状态") + Boolean connectDeviceStatus; + + @ApiModelProperty("运行状态") + Boolean runStatus; + + @ApiModelProperty("自检状态") + Boolean selfCheckStatus; + + @ApiModelProperty("轴流风机状态") + Boolean axialFlowFanStatus; +} diff --git a/commoms/core/src/main/java/com/evotech/hd/core/dtos/business/airconditioning/AirConditioningCompressorDTO.java b/commoms/core/src/main/java/com/evotech/hd/core/dtos/business/airconditioning/AirConditioningCompressorDTO.java new file mode 100644 index 0000000..7245e6e --- /dev/null +++ b/commoms/core/src/main/java/com/evotech/hd/core/dtos/business/airconditioning/AirConditioningCompressorDTO.java @@ -0,0 +1,36 @@ +package com.evotech.hd.core.dtos.business.airconditioning; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 空调控制:压缩机 + * + * @author andy.shi + * @ClassName:AirConditioningBaseDTO + * @date: 2025年09月23日 14:43 + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@Data +@ApiModel("空调控制:压缩机") +public class AirConditioningCompressorDTO { + + @ApiModelProperty("压缩机运行状态") + Boolean airConCmpStatus; + + @ApiModelProperty("压缩机欠流故障") + Boolean airConCmpLowCurError; + + @ApiModelProperty("压缩机过载故障") + Boolean airConCmpOverLoadError; + + @ApiModelProperty("压缩机电流") + String airConCmpCurrent; + + @ApiModelProperty("压缩机启动温度") + String airConCmpStartTemp; + + @ApiModelProperty("压缩机停止回差值") + String airConCmpStopTemp; +} diff --git a/commoms/core/src/main/java/com/evotech/hd/core/dtos/business/airconditioning/AirConditioningExtDTO.java b/commoms/core/src/main/java/com/evotech/hd/core/dtos/business/airconditioning/AirConditioningExtDTO.java new file mode 100644 index 0000000..17dda1e --- /dev/null +++ b/commoms/core/src/main/java/com/evotech/hd/core/dtos/business/airconditioning/AirConditioningExtDTO.java @@ -0,0 +1,29 @@ +package com.evotech.hd.core.dtos.business.airconditioning; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 空调控制:柜外 + * + * @author andy.shi + * @ClassName:AirConditioningExtDTO + * @date: 2025年09月23日 14:43 + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@Data +@ApiModel("空调控制:柜外") +public class AirConditioningExtDTO { + + @ApiModelProperty("柜外温度传感器故障") + Boolean airConExtTempSensorError; + + @ApiModelProperty("柜外环境温度") + String airConExtTemp; + + @ApiModelProperty("外部故障告警") + Boolean airConExtErrorAlarm; + + +} diff --git a/commoms/core/src/main/java/com/evotech/hd/core/dtos/business/airconditioning/AirConditioningExternalFanDTO.java b/commoms/core/src/main/java/com/evotech/hd/core/dtos/business/airconditioning/AirConditioningExternalFanDTO.java new file mode 100644 index 0000000..9b11e00 --- /dev/null +++ b/commoms/core/src/main/java/com/evotech/hd/core/dtos/business/airconditioning/AirConditioningExternalFanDTO.java @@ -0,0 +1,27 @@ +package com.evotech.hd.core.dtos.business.airconditioning; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 空调控制:外风机 + * + * @author andy.shi + * @ClassName:AirConditioningExternalFanDTO + * @date: 2025年09月24日 15:46 + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@Data +@ApiModel("空调控制:外风机") +public class AirConditioningExternalFanDTO { + + @ApiModelProperty("外风机运行状态") + Boolean airConExtFanStatus; + + @ApiModelProperty("外风机告警") + Boolean airConExtFanAlarm; + + @ApiModelProperty("外风机电流") + String airConExtFanCurrent; +} diff --git a/commoms/core/src/main/java/com/evotech/hd/core/dtos/business/airconditioning/AirConditioningHeaterDTO.java b/commoms/core/src/main/java/com/evotech/hd/core/dtos/business/airconditioning/AirConditioningHeaterDTO.java new file mode 100644 index 0000000..2b85f33 --- /dev/null +++ b/commoms/core/src/main/java/com/evotech/hd/core/dtos/business/airconditioning/AirConditioningHeaterDTO.java @@ -0,0 +1,36 @@ +package com.evotech.hd.core.dtos.business.airconditioning; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 空调控制:加热器 + * + * @author andy.shi + * @ClassName:AirConditioningHeaterDTO + * @date: 2025年09月23日 14:43 + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@Data +@ApiModel("空调控制:加热器") +public class AirConditioningHeaterDTO { + + @ApiModelProperty("加热器运行状态") + Boolean airConHeatStatus; + + @ApiModelProperty("加热器欠流故障") + Boolean airConHeatLowCurError; + + @ApiModelProperty("加热器过载故障") + Boolean airConHeatOverLoadError; + + @ApiModelProperty("加热器电流") + String airConHeatCurrent; + + @ApiModelProperty("加热器启动温度") + String airConHeatStartTemp; + + @ApiModelProperty("加热器停止回差值") + String airConHeatStopTemp; +} diff --git a/commoms/core/src/main/java/com/evotech/hd/core/dtos/business/airconditioning/AirConditioningIntDTO.java b/commoms/core/src/main/java/com/evotech/hd/core/dtos/business/airconditioning/AirConditioningIntDTO.java new file mode 100644 index 0000000..13acdc2 --- /dev/null +++ b/commoms/core/src/main/java/com/evotech/hd/core/dtos/business/airconditioning/AirConditioningIntDTO.java @@ -0,0 +1,48 @@ +package com.evotech.hd.core.dtos.business.airconditioning; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 空调控制:柜内 + * + * @author andy.shi + * @ClassName:AirConditioningIntDTO + * @date: 2025年09月23日 14:43 + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@Data +@ApiModel("空调控制:柜内") +public class AirConditioningIntDTO { + + @ApiModelProperty("柜内温度传感器故障") + Boolean airConIntTempSensorError; + + @ApiModelProperty("柜内湿度传感器故障") + Boolean airConIntHumSensorError; + + @ApiModelProperty("柜内高温告警") + Boolean airConIntOverTempAlarm; + + @ApiModelProperty("柜内低温告警") + Boolean airConIntLowTempAlarm; + + @ApiModelProperty("柜内回风温度") + String airConIntTemp; + + @ApiModelProperty("柜内湿度") + String airConIntHum; + + @ApiModelProperty("柜内温度变送电流值") + String airConIntTempSedCurrent; + + @ApiModelProperty("柜内湿度变送电流值") + String airConIntHumSedCurrent; + + @ApiModelProperty("柜内高温告警温度值") + String airConOverAlmTemp; + + @ApiModelProperty("柜内低温告警温度值") + String airConLowAlmTemp; +} diff --git a/commoms/core/src/main/java/com/evotech/hd/core/dtos/business/airconditioning/AirConditioningInternalFanDTO.java b/commoms/core/src/main/java/com/evotech/hd/core/dtos/business/airconditioning/AirConditioningInternalFanDTO.java new file mode 100644 index 0000000..edc0e8f --- /dev/null +++ b/commoms/core/src/main/java/com/evotech/hd/core/dtos/business/airconditioning/AirConditioningInternalFanDTO.java @@ -0,0 +1,33 @@ +package com.evotech.hd.core.dtos.business.airconditioning; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 空调控制:内风机 + * + * @author andy.shi + * @ClassName:AirConditioningBaseDTO + * @date: 2025年09月23日 14:43 + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@Data +@ApiModel("空调控制:内风机") +public class AirConditioningInternalFanDTO { + + @ApiModelProperty("内风机运行状态") + Boolean airConIntFanStatus; + + @ApiModelProperty("内风机告警") + Boolean airConIntFanAlarm; + + @ApiModelProperty("内风机电流") + String airConIntFanCurrent; + + @ApiModelProperty("内风机待机开启温度") + String airConIntFanStartTemp; + + @ApiModelProperty("内风机待机停止回差温度") + String airConIntFanStopTemp; +} diff --git a/commoms/core/src/main/java/com/evotech/hd/core/dtos/business/airconditioning/AirConditioningOtherDTO.java b/commoms/core/src/main/java/com/evotech/hd/core/dtos/business/airconditioning/AirConditioningOtherDTO.java new file mode 100644 index 0000000..5ef51c9 --- /dev/null +++ b/commoms/core/src/main/java/com/evotech/hd/core/dtos/business/airconditioning/AirConditioningOtherDTO.java @@ -0,0 +1,28 @@ +package com.evotech.hd.core.dtos.business.airconditioning; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 空调控制:其他 + * + * @author andy.shi + * @ClassName:AirConditioningOtherDTO + * @date: 2025年09月23日 14:43 + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@Data +@ApiModel("空调控制:其他") +public class AirConditioningOtherDTO { + + @ApiModelProperty("冷凝盘管温度传感器故障") + Boolean airConCondTempSensorError; + + @ApiModelProperty("系统供电电压") + String airConSrcVol; + + @ApiModelProperty("电源电压超限告警") + Boolean airConSrcVolOverAlarm; + +} diff --git a/commoms/core/src/main/java/com/evotech/hd/core/dtos/business/electricitymeter/ElectricityMeterBaseDTO.java b/commoms/core/src/main/java/com/evotech/hd/core/dtos/business/electricitymeter/ElectricityMeterBaseDTO.java new file mode 100644 index 0000000..e39ff28 --- /dev/null +++ b/commoms/core/src/main/java/com/evotech/hd/core/dtos/business/electricitymeter/ElectricityMeterBaseDTO.java @@ -0,0 +1,28 @@ +package com.evotech.hd.core.dtos.business.electricitymeter; + +import com.evotech.hd.core.dtos.BaseDTO; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * ElectricityMeterBaseDTO + * + * @author andy.shi + * @ClassName:ElectricityMeterBaseDTO + * @date: 2025年09月25日 14:49 + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@Data +@ApiModel("电表箱:基本属性") +public class ElectricityMeterBaseDTO extends BaseDTO { + + @ApiModelProperty("电箱温湿度传感器连接状态") + Boolean htSensorDevConnect; + + @ApiModelProperty("电箱内湿度值") + String htSensorHumidity; + + @ApiModelProperty("电箱内温度值") + String htSensorTemperature; +} diff --git a/commoms/core/src/main/java/com/evotech/hd/core/dtos/system/InstructionLogDTO.java b/commoms/core/src/main/java/com/evotech/hd/core/dtos/system/InstructionLogDTO.java new file mode 100644 index 0000000..738f7a3 --- /dev/null +++ b/commoms/core/src/main/java/com/evotech/hd/core/dtos/system/InstructionLogDTO.java @@ -0,0 +1,42 @@ +package com.evotech.hd.core.dtos.system; + +import com.evotech.hd.core.dtos.BaseDTO; +import com.evotech.hd.query.Query; +import com.evotech.hd.query.QueryType; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 指令日志类 + * + * @ClassName:Log + * @date: 2025年08月18日 10:06 + * @author: andy.shi + * @contact: 17330188597 + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@Data +@ApiModel("指令日志类") +public class InstructionLogDTO extends BaseDTO { + + @ApiModelProperty(value = "指令地址") + private String requestUri; + + @ApiModelProperty(value = "指令方式") + @Query(javaField = "method", tableColumn = "a.method", type = QueryType.EQ) + private String method; + + @ApiModelProperty(value = "指令类型") + @Query(javaField = "params", tableColumn = "a.params", type = QueryType.LIKE) + private String params; + /** + * 返回值 + */ + @ApiModelProperty(value = "返回结果") + private String result; + + @ApiModelProperty(value = "异常信息") + private String exception; + +} diff --git a/commoms/core/src/main/java/com/evotech/hd/core/dtos/system/LogDTO.java b/commoms/core/src/main/java/com/evotech/hd/core/dtos/system/LogDTO.java new file mode 100644 index 0000000..e891d7a --- /dev/null +++ b/commoms/core/src/main/java/com/evotech/hd/core/dtos/system/LogDTO.java @@ -0,0 +1,57 @@ +package com.evotech.hd.core.dtos.system; +import com.evotech.hd.core.dtos.BaseDTO; +import com.evotech.hd.query.Query; +import com.evotech.hd.query.QueryType; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 系统日志类 + * + * @ClassName:Log + * @date: 2025年08月18日 10:06 + * @author: andy.shi + * @contact: 17330188597 + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@Data +@ApiModel("日志类型") +public class LogDTO extends BaseDTO { + + @ApiModelProperty(value = "类型", allowableValues = "1-登录日志, 2-访问日志, 3-异常日志, 4-操作日志,5-推送云日志, 6-报警日志") + @Query(javaField = "type", tableColumn = "a.type", type = QueryType.EQ) + String type; + + @ApiModelProperty(value = "操作用户的IP地址") + private String remoteAddr; + + @ApiModelProperty(value = "请求uri") + private String requestUri; + + @ApiModelProperty(value = "请求类型") + private String requestType; + + @ApiModelProperty(value = "操作的方式") + private String method; + + @ApiModelProperty(value = "操作提交的数据") + private String params; + + @ApiModelProperty(value = "返回结果") + private String result; + + @ApiModelProperty(value = "操作用户代理信息") + private String userAgent; + + @ApiModelProperty(value = "异常信息") + private String exception; + + @ApiModelProperty(value = "耗时 (毫秒)") + private Long recordTime; + + @ApiModelProperty(value = "备注") + private String remarks; + + +} diff --git a/commoms/core/src/main/java/com/evotech/hd/core/dtos/system/ParamsDTO.java b/commoms/core/src/main/java/com/evotech/hd/core/dtos/system/ParamsDTO.java new file mode 100644 index 0000000..9d0327d --- /dev/null +++ b/commoms/core/src/main/java/com/evotech/hd/core/dtos/system/ParamsDTO.java @@ -0,0 +1,46 @@ +package com.evotech.hd.core.dtos.system; + +import com.evotech.hd.core.dtos.BaseDTO; +import com.evotech.hd.query.Query; +import com.evotech.hd.query.QueryType; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 类 + * + * @ClassName:ParamsDTO + * @date: 2025年08月18日 16:10 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@ApiModel("属性类型") +@Data +public class ParamsDTO extends BaseDTO { + + @ApiModelProperty("所属分类") + @Query(javaField = "groupId", tableColumn = "a.group_id", type = QueryType.EQ) + Integer groupId; + + @ApiModelProperty("参数code") + @Query(javaField = "paramCode", tableColumn = "a.param_code", type = QueryType.LIKE) + String paramCode; + + @ApiModelProperty("参数值") + @Query(javaField = "paramValue", tableColumn = "a.param_value", type = QueryType.LIKE) + String paramValue; + + @ApiModelProperty("预期结果, 不满足预期返回错误信息") + String expectedResults; + + @ApiModelProperty("是否需要核实结果") + Boolean verifyResult; + + @ApiModelProperty("错误信息") + String errMessage; + + /** 说明 */ + @ApiModelProperty("说明") + private String description; +} diff --git a/commoms/core/src/main/java/com/evotech/hd/core/entity/BaseEntity.java b/commoms/core/src/main/java/com/evotech/hd/core/entity/BaseEntity.java new file mode 100644 index 0000000..28969a0 --- /dev/null +++ b/commoms/core/src/main/java/com/evotech/hd/core/entity/BaseEntity.java @@ -0,0 +1,82 @@ +package com.evotech.hd.core.entity; + +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import org.apache.commons.lang3.StringUtils; +import org.apache.ibatis.type.JdbcType; + +import java.io.Serializable; +import java.util.Date; +import java.util.LinkedHashMap; +import java.util.Map; + +/* + * 数据Entity类 + * @author space + * @version 1.0 + */ +@Data +public abstract class BaseEntity implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 实体主键 + */ + @TableId(type = IdType.AUTO) + private Integer id; + + @TableField("name") + String name; + + /** + * 创建日期 + */ + + @ApiModelProperty(hidden = true) + @TableField(fill = FieldFill.INSERT) + private Date createTime; + + @ApiModelProperty(value = "扩展字段") + @TableField(typeHandler = JacksonTypeHandler.class, jdbcType = JdbcType.OTHER) + private String extendeds; + + + public Map getExtendeds() { + if(StringUtils.isNotEmpty(extendeds)){ + try { +// ObjectMapper 是 Jackson 库的核心类,用于处理 JSON 数据的序列化和反序列化。它提供了多种方法来将 Java 对象转换为 JSON 字符串,以及将 JSON 字符串转换为 Java 对象 + // 使用 Jackson 库将 JSON 字符串反序列化为 Map 类型的代码 + return new ObjectMapper().readValue(extendeds, Map.class); + } catch (Exception e) { + throw new RuntimeException("Failed to parse JSON", e); + } + } + return new LinkedHashMap<>(); + } + public void setParams(Map params) { + if (params == null) { + this.extendeds = null; + } else { + try { + // 将 params(一个 Map 类型的对象)转换为 JSON 格式的字符串,并将其存储在 paramsJson 字段中 + this.extendeds = new ObjectMapper().writeValueAsString(params); + } catch (Exception e) { + throw new RuntimeException("Failed to serialize JSON", e); + } + } + } + + + public String getKey(){ + return String.valueOf(this.id); + } + + +} diff --git a/commoms/core/src/main/java/com/evotech/hd/core/entity/business/Battery.java b/commoms/core/src/main/java/com/evotech/hd/core/entity/business/Battery.java new file mode 100644 index 0000000..42f2a78 --- /dev/null +++ b/commoms/core/src/main/java/com/evotech/hd/core/entity/business/Battery.java @@ -0,0 +1,77 @@ +package com.evotech.hd.core.entity.business; + +import com.baomidou.mybatisplus.annotation.TableName; +import com.evotech.hd.core.entity.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 电池信息类 + * + * @ClassName:Battery + * @date: 2025年08月18日 12:00 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@TableName("sc_battery") +public class Battery extends BaseEntity { + + /*** + * 电池编号 + */ + String batteryCode; + /*** + * 电池类型 + */ + String batteryType; + /*** + * 电池模式 + * 0:未上电 + * 1:监控模式 + * 2:充电初始化 + * 3:单功充电 + * 4:双功充电 + * 5:暂停充电 + * 6:停止充电 + * 7:充电故障 + * 8:充电错误 + */ + String batteryMode; + /*** + * 电池仓Id + */ + Integer batteryCompartmentId; + /*** + * 电池状态在仓状态 + * 1在仓 0 离仓 + */ + Integer storageStatus; + + /*** + * 电池健康度 + */ + String soh; + + /*** + * 电池额定容量 + */ + String ratedCapacity; + + /*** + * 电池单体数量 + */ + Integer monomerQuantity; + + /*** + * 探针数量 + */ + Integer numberOfProbes; + + /*** + * 电池电量 + */ + String soc; + +} diff --git a/commoms/core/src/main/java/com/evotech/hd/core/entity/business/BatteryCompartment.java b/commoms/core/src/main/java/com/evotech/hd/core/entity/business/BatteryCompartment.java new file mode 100644 index 0000000..ef1ccad --- /dev/null +++ b/commoms/core/src/main/java/com/evotech/hd/core/entity/business/BatteryCompartment.java @@ -0,0 +1,99 @@ +package com.evotech.hd.core.entity.business; + +import com.baomidou.mybatisplus.annotation.TableName; +import com.evotech.hd.core.entity.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.Date; + +/** + * 电池仓信息类 + * + * @ClassName:BatteryCompartment + * @date: 2025年08月18日 11:39 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@TableName("sc_battery_compartment") +public class BatteryCompartment extends BaseEntity { + + // + /*** + * 取消电池仓编号信息, 默认使用id + * 电池仓编号 + * 推送站端, 只用电池仓编号 + */ + String batteryCompartmentNo; + /*** + * 状态:1-正常,2-检修, 3-坏, + */ + Integer status; + /*** + * 运行状态:1正常, 2-禁用 + */ + Integer state; + /*** + * 排序 + */ + Integer sort; + /*** + * 是否存在电池 + */ + Boolean existsBattery; + /*** + * 充电机状态 + * 0:空闲(电池低压未上电,无法读取电池数据) + * 1:监控状态(电池低压上电后为监控状态可实时读取电池数据) + * 2:单功率充电 + * 3:双功率充电 + * 4:辅助充电(本仓位电池未充电,为同组内其它电池提供充电辅助,此状态下本仓位不能充电,上位机发送充电命令将不响应) + * 5:调试状态(不响应上位机充电命令) + * 6:设备错误状态 + */ + Integer chargerStatus; + /*** + * 电池到位信号 + * 0 无电池 1电池正常 2, 电池错误 + */ + Integer batteryPlaceStatus; + /*** + * 电池通信状态 + */ + Boolean batteryCommunicationStatus; + /*** + * 直流电表通信状态 + */ + Boolean directCurrentCommunicationStatus; + /*** + * 电池低压电源状态 + */ + String lowPressurePowerSupplyStatus; + /*** + * 辅助电源状态 + */ + String auxiliaryPowerSupplyStatus; + /*** + * 最大充电功率 + */ + Integer maximumChargingPower; + /*** + * 直流电表压 (KW/H) 忘记是啥了, 到时候在说 + */ + String directCurrentMeterVoltage; + /*** + * 电池存入时间 + */ + Date depositTime; + /*** + * 反向记录充电订单Id, 用于刷新数据 + */ + Integer orderChargingId; + /*** + * 是否全功率充电 + */ + Boolean allPower; + +} diff --git a/commoms/core/src/main/java/com/evotech/hd/core/entity/business/BatterySwapStep.java b/commoms/core/src/main/java/com/evotech/hd/core/entity/business/BatterySwapStep.java new file mode 100644 index 0000000..393e508 --- /dev/null +++ b/commoms/core/src/main/java/com/evotech/hd/core/entity/business/BatterySwapStep.java @@ -0,0 +1,42 @@ +package com.evotech.hd.core.entity.business; + +import com.baomidou.mybatisplus.annotation.TableName; +import com.evotech.hd.core.entity.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.Date; + +/** + * 换电步骤表类 + * @ClassName:BatterySwapStep + * @date: 2025年08月18日 10:10 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@TableName("sc_battery_swap_step") +public class BatterySwapStep extends BaseEntity { + + /*** + * 订单Id + */ + Integer orderId; + /*** + * 换电步骤 + */ + Integer step; + /*** + * 换电时间 + */ + Date stepTime; + /*** + * 是否人为操作 + */ + Boolean humanOperation; + //是否出现错误 + Boolean error; + + +} diff --git a/commoms/core/src/main/java/com/evotech/hd/core/entity/business/OrderCharging.java b/commoms/core/src/main/java/com/evotech/hd/core/entity/business/OrderCharging.java new file mode 100644 index 0000000..16791c9 --- /dev/null +++ b/commoms/core/src/main/java/com/evotech/hd/core/entity/business/OrderCharging.java @@ -0,0 +1,62 @@ +package com.evotech.hd.core.entity.business; + +import com.baomidou.mybatisplus.annotation.TableName; +import com.evotech.hd.core.entity.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.Date; + +/** + * 充电订单 + * + * @author andy.shi + * @ClassName:OrderCharging + * @date: 2025年09月30日 14:21 + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@TableName("sc_order_charging") +public class OrderCharging extends BaseEntity { + /*** + * 充电单号 + */ + String chargingNo; + /*** + * 电池编号 + */ + String batteryCode; + /*** + * 电池仓Id + */ + Integer batteryCompartmentId; + /*** + * 策略信息 + */ + String strategyInfo; + /*** + * 充电状态 1:充电结束 2 充电中 + */ + Integer status; + /*** + * 耗电量;如果soc的话为soc + */ + Integer powerConsumption; + /*** + * 开始电量; 如果soc的话为开始soc + */ + Integer startElectric; + /*** + * 结束电量; 如果soc的话为开始soc + */ + Integer endElectric; + /*** + * 充电开始时间 + */ + Date beginTime; + /*** + * 充电结束时间 + */ + Date endTime; +} diff --git a/commoms/core/src/main/java/com/evotech/hd/core/entity/business/OrderChargingStrategyLog.java b/commoms/core/src/main/java/com/evotech/hd/core/entity/business/OrderChargingStrategyLog.java new file mode 100644 index 0000000..6a51406 --- /dev/null +++ b/commoms/core/src/main/java/com/evotech/hd/core/entity/business/OrderChargingStrategyLog.java @@ -0,0 +1,41 @@ +package com.evotech.hd.core.entity.business; + +import com.baomidou.mybatisplus.annotation.TableName; +import com.evotech.hd.core.entity.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 充电过程中使用的策略记录 + * + * @author andy.shi + * @ClassName:OrderCharging + * @date: 2025年09月30日 14:21 + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@TableName("sc_order_charging_strategy_log") +public class OrderChargingStrategyLog extends BaseEntity { + /*** + * 充电订单Id + */ + Integer orderChargingId; + /*** + * 策略类 + */ + String className; + /*** + * 策略信息 + */ + String strategyInfo; + + public OrderChargingStrategyLog() { + } + + public OrderChargingStrategyLog(Integer orderChargingId, String className, String strategyInfo) { + this.orderChargingId = orderChargingId; + this.className = className; + this.strategyInfo = strategyInfo; + } +} diff --git a/commoms/core/src/main/java/com/evotech/hd/core/entity/business/OrderReservation.java b/commoms/core/src/main/java/com/evotech/hd/core/entity/business/OrderReservation.java new file mode 100644 index 0000000..9be398d --- /dev/null +++ b/commoms/core/src/main/java/com/evotech/hd/core/entity/business/OrderReservation.java @@ -0,0 +1,67 @@ +package com.evotech.hd.core.entity.business; + +import com.baomidou.mybatisplus.annotation.TableName; +import com.evotech.hd.core.entity.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.Date; + +/** + * 预约订单类 + * @ClassName:OrderReservation + * @date: 2025年08月18日 11:00 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@TableName("sc_order_reservation") +public class OrderReservation extends BaseEntity { + /*** + * 数据来源 + */ + String source; + /*** + * 云平台预约Id + */ + Integer cloudReservationId; + + String reservationName; + + String reservationPhone; + /*** + * 车牌号 + */ + String plateNum; + /*** + * 微信预约人Id + */ + String wechatUserId; + /*** + * 微信预约人姓名 + */ + String wechatUserName; + /*** + * 预约时间 + */ + Date reservationTime; + /*** + * 预约换电日期 + */ + String swapDay; + /*** + * 预约换电时间段 + */ + String swapDuration; + /*** + * 状态:1-预约成功,2-到店使用,3-取消,4-过期 + */ + Integer status; + + /*** + * 推送状态:1-成功, 2-失败 + */ + Integer pushStatus; + +} diff --git a/commoms/core/src/main/java/com/evotech/hd/core/entity/business/OrderSwap.java b/commoms/core/src/main/java/com/evotech/hd/core/entity/business/OrderSwap.java new file mode 100644 index 0000000..2cb8c8f --- /dev/null +++ b/commoms/core/src/main/java/com/evotech/hd/core/entity/business/OrderSwap.java @@ -0,0 +1,137 @@ +package com.evotech.hd.core.entity.business; + +import com.baomidou.mybatisplus.annotation.TableName; +import com.evotech.hd.core.entity.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.Date; + +/** + * 换电订单表 + * + * @ClassName:OrderSwap + * @date: 2025年08月18日 11:11 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@TableName("sc_order_swap") +public class OrderSwap extends BaseEntity { + /*** + * 云平台订单id + */ + Integer cloudOrderId; + /*** + * 订单编号 + */ + String orderNo; + /*** + * 预约人微信Id + */ + String preWechatId; + /*** + * 预约订单Id + */ + Integer orderPreId; + /*** + * 预约用户名称 + */ + String orderPreName; + /*** + * 类型 + */ + String orderType; + /*** + * 车牌号 + */ + String plateNum; + /*** + * 订单时间 + */ + Date orderTime; + /*** + * 订单总金额 (分) + */ + Integer orderAmount; + /*** + * 订单状态:1-已创建,2-换电中,3-换电完成,4-充电中,5-充电完成,6-待结算,7-已完成,9-已取消 + */ + Integer status; + /*** + * 支付方式 1余额, 2微信, 3支付宝 + */ + String payType; + /*** + * 支付状态 + */ + String payStatus; + /*** + * 支付时间 + */ + Date payTime; + /*** + * 计算费用方式:1-ODO,2-SOC,3-按电量 + */ + String feeType; + /*** + * 订单金额 (分) + */ + Integer orderFee; + /*** + * 服务金额 (分) + */ + Integer serviceFee; + /*** + * 服务开始时间 + */ + Date serviceTimeBegin; + /*** + * 服务结束时间 + */ + Date serviceTimeEnd; + /*** + * 消耗电量;如果是soc结算 则是soc的差值 + */ + String electricityQuantity; + + /*** + * 租借电池包仓位 + */ + String rentBatNo; + /*** + * 租用电池包编码 + */ + String rentBatCode; + /*** + *租用电池包SOC + */ + String rentBatSoc; + /*** + * 归还电池包仓位 + */ + String returnBatNo; + /*** + * 归还电池包编码 + */ + String returnBatCode; + /*** + * 归还电池包SOC + */ + String returnBatSoc; + /*** + * 换电模式:1-全自动,2-半自动,3-人工干预 + */ + String changeMode = "1"; + /*** + * 换电车道 1-A 车道;2-B 车道 + */ + String changeLane; + + /*** + * 推送状态:1-成功, 2-失败 + */ + Integer pushStatus; + +} diff --git a/commoms/core/src/main/java/com/evotech/hd/core/entity/business/RunningInstructions.java b/commoms/core/src/main/java/com/evotech/hd/core/entity/business/RunningInstructions.java new file mode 100644 index 0000000..49d128e --- /dev/null +++ b/commoms/core/src/main/java/com/evotech/hd/core/entity/business/RunningInstructions.java @@ -0,0 +1,22 @@ +package com.evotech.hd.core.entity.business; + +import com.baomidou.mybatisplus.annotation.TableName; +import com.evotech.hd.core.entity.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 运行指令 + * + * @ClassName:RunningInstructions + * @date: 2025年08月18日 8:58 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@TableName("sc_running_instructions") +public class RunningInstructions extends BaseEntity { + + String code; +} diff --git a/commoms/core/src/main/java/com/evotech/hd/core/entity/business/RunningInstructionsDetail.java b/commoms/core/src/main/java/com/evotech/hd/core/entity/business/RunningInstructionsDetail.java new file mode 100644 index 0000000..d40ad6a --- /dev/null +++ b/commoms/core/src/main/java/com/evotech/hd/core/entity/business/RunningInstructionsDetail.java @@ -0,0 +1,28 @@ +package com.evotech.hd.core.entity.business; + +import com.baomidou.mybatisplus.annotation.TableName; +import com.evotech.hd.core.entity.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 运行指令详情 + * + * @ClassName:RunningInstructionsDetail + * @date: 2025年08月18日 9:20 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@TableName("sc_running_instructions_detail") +public class RunningInstructionsDetail extends BaseEntity { + + String dictValue; + + String typeCode; + + Integer defaultValue; + + Integer sort; +} diff --git a/commoms/core/src/main/java/com/evotech/hd/core/entity/system/AccessStrategy.java b/commoms/core/src/main/java/com/evotech/hd/core/entity/system/AccessStrategy.java new file mode 100644 index 0000000..a249efb --- /dev/null +++ b/commoms/core/src/main/java/com/evotech/hd/core/entity/system/AccessStrategy.java @@ -0,0 +1,57 @@ +package com.evotech.hd.core.entity.system; + +import com.baomidou.mybatisplus.annotation.TableName; +import com.evotech.hd.core.entity.BaseEntity; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 存储策略类 + * + * @ClassName:AccessStrategy + * @date: 2025年08月27日 15:59 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@TableName("sc_sys_access_strategy") +public class AccessStrategy extends BaseEntity { + /*** + * 存取策略: 1.存取相同, 2 顺序存续. 3间隔存储 4 随机存储 + */ + Integer strategy; + /*** + * 电源策略 + */ + Integer powerStrategy; +// /*** +// * 充电策略 +// */ +// Integer chargingStrategy; + /*** + *即存即充功率 + */ + Integer instantChargePower; + /*** + *最低安全库存 + */ + Integer minimumSafetyStock; + /*** + *最低换电SOC + */ + Integer lowestBatterySwapSoc; + /*** + *充满界定soc + */ + Integer fullOfLimitSoc; + /*** + *生效的RGV编号 + */ + String effectiveRgvNo; + /*** + * 数据是否有效: valid有效 invalid 无效 + */ + String valid; +} diff --git a/commoms/core/src/main/java/com/evotech/hd/core/entity/system/BatteryStrategy.java b/commoms/core/src/main/java/com/evotech/hd/core/entity/system/BatteryStrategy.java new file mode 100644 index 0000000..f476828 --- /dev/null +++ b/commoms/core/src/main/java/com/evotech/hd/core/entity/system/BatteryStrategy.java @@ -0,0 +1,39 @@ +package com.evotech.hd.core.entity.system; + +import com.baomidou.mybatisplus.annotation.TableName; +import com.evotech.hd.core.entity.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 充电策略 + * + * @ClassName:BatteryStrategy + * @date: 2025年08月27日 15:59 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@TableName("sc_sys_battery_strategy") +public class BatteryStrategy extends BaseEntity { + + /*** + * 云数据id + */ + private Integer cloudId; + /*** + * 开始时间 + */ + private String beginTime; + /*** + * 结束时间 + */ + private String endTime; + /*** + * 充电功率 + */ + private Integer chargingPower; + + private Boolean del; +} diff --git a/commoms/core/src/main/java/com/evotech/hd/core/entity/system/Group.java b/commoms/core/src/main/java/com/evotech/hd/core/entity/system/Group.java new file mode 100644 index 0000000..3a2d6d7 --- /dev/null +++ b/commoms/core/src/main/java/com/evotech/hd/core/entity/system/Group.java @@ -0,0 +1,34 @@ +package com.evotech.hd.core.entity.system; + +import com.baomidou.mybatisplus.annotation.TableName; +import com.evotech.hd.core.entity.BaseEntity; +import com.evotech.hd.utils.Collections; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.apache.commons.lang3.StringUtils; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import java.io.File; +import java.util.List; + +/** + * 参数分组类 + * + * @ClassName:Group + * @date: 2025年08月16日 17:28 + * @author: andy.shi + * @contact: 17330188597 + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@TableName("sc_sys_groups") +public class Group extends BaseEntity { + + +} diff --git a/commoms/core/src/main/java/com/evotech/hd/core/entity/system/InstructionLog.java b/commoms/core/src/main/java/com/evotech/hd/core/entity/system/InstructionLog.java new file mode 100644 index 0000000..5b0ffcd --- /dev/null +++ b/commoms/core/src/main/java/com/evotech/hd/core/entity/system/InstructionLog.java @@ -0,0 +1,64 @@ +package com.evotech.hd.core.entity.system; + +import com.baomidou.mybatisplus.annotation.TableName; +import com.evotech.hd.core.entity.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 指令日志类 + * + * @ClassName:Log + * @date: 2025年08月18日 10:06 + * @author: andy.shi + * @contact: 17330188597 + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@TableName("sc_sys_instruction_log") +public class InstructionLog extends BaseEntity { + + /*** + * 类型, 参考字典 + */ + String type; + + /** + * 请求uri + */ + private String requestUri; + + /** + * 操作的方式 + */ + private String method; + + /** + * 操作提交的数据 + */ + private String params; + /** + * 返回值 + */ + private String result; + /** + * 异常信息 + */ + private String exception; + /** + * 耗时 + */ + private Long recordTime; + + /** + * 备注 + */ + private String remarks; + /*** + * 执行时间戳 + */ + private String executionTime; + + +} diff --git a/commoms/core/src/main/java/com/evotech/hd/core/entity/system/Log.java b/commoms/core/src/main/java/com/evotech/hd/core/entity/system/Log.java new file mode 100644 index 0000000..983b8d8 --- /dev/null +++ b/commoms/core/src/main/java/com/evotech/hd/core/entity/system/Log.java @@ -0,0 +1,73 @@ +package com.evotech.hd.core.entity.system; + +import com.baomidou.mybatisplus.annotation.TableName; +import com.evotech.hd.core.entity.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 系统日志类 + * + * @ClassName:Log + * @date: 2025年08月18日 10:06 + * @author: andy.shi + * @contact: 17330188597 + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@TableName("sc_sys_log") +public class Log extends BaseEntity { + + /*** + * 类型, 参考字典 + */ + String type; + /** + * 操作用户的IP地址 + */ + private String remoteAddr; + + /** + * 请求uri + */ + private String requestUri; + + /** + * 请求类型 + */ + private String requestType; + + /** + * 操作的方式 + */ + private String method; + + /** + * 操作提交的数据 + */ + private String params; + /** + * 返回值 + */ + private String result; + /** + * 操作用户代理信息 + */ + private String userAgent; + /** + * 异常信息 + */ + private String exception; + /** + * 耗时 + */ + private Long recordTime; + + /** + * 备注 + */ + private String remarks; + + +} diff --git a/commoms/core/src/main/java/com/evotech/hd/core/entity/system/OperationLog.java b/commoms/core/src/main/java/com/evotech/hd/core/entity/system/OperationLog.java new file mode 100644 index 0000000..22380e1 --- /dev/null +++ b/commoms/core/src/main/java/com/evotech/hd/core/entity/system/OperationLog.java @@ -0,0 +1,40 @@ +package com.evotech.hd.core.entity.system; + +import com.baomidou.mybatisplus.annotation.TableName; +import com.evotech.hd.core.entity.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.Date; + +/** + * 操作日志表, 隐藏表类 + * + * @ClassName:OperationLog + * @date: 2025年08月18日 9:26 + * @author: andy.shi + * @contact: 17330188597 + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@TableName("sc_sys_operation_log") +public class OperationLog extends BaseEntity { + + /*** + * 操作方法 + */ + String operationMethod; + /*** + * 操作参数 + */ + String operationParams; + /*** + * 操作信息 + */ + String operationContent; + /*** + * 操作时间 + */ + Date operationTime; +} diff --git a/commoms/core/src/main/java/com/evotech/hd/core/entity/system/Params.java b/commoms/core/src/main/java/com/evotech/hd/core/entity/system/Params.java new file mode 100644 index 0000000..fdec9ec --- /dev/null +++ b/commoms/core/src/main/java/com/evotech/hd/core/entity/system/Params.java @@ -0,0 +1,53 @@ +package com.evotech.hd.core.entity.system; + +import com.baomidou.mybatisplus.annotation.TableName; +import com.evotech.hd.core.entity.BaseEntity; +import com.evotech.hd.query.Query; +import com.evotech.hd.query.QueryType; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 站端参数配置类 + * + * @ClassName:params + * @date: 2025年08月16日 17:30 + * @author: andy.shi + * @contact: 17330188597 + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@TableName("sc_sys_params") +public class Params extends BaseEntity { + +// @ApiModelProperty("所属分类") + @Query(javaField = "groupId", tableColumn = "a.group_id", type = QueryType.EQ) + Integer groupId; + + String paramCode; + +// @ApiModelProperty("参数值") + String paramValue; + + String expectedResults; + + Boolean verifyResult; + + String errMessage; + + /** 说明 */ + private String description; + + public Params() { + } + + public Params(Integer groupId) { + this.groupId = groupId; + } + + @Override + public String getKey() { + return (this.paramValue.contains(":num") ? this.paramValue.substring(this.paramValue.indexOf(":num"), this.paramValue.length()-1) : this.paramValue); + } +} diff --git a/commoms/core/src/main/java/com/evotech/hd/core/entity/system/User.java b/commoms/core/src/main/java/com/evotech/hd/core/entity/system/User.java new file mode 100644 index 0000000..6067919 --- /dev/null +++ b/commoms/core/src/main/java/com/evotech/hd/core/entity/system/User.java @@ -0,0 +1,44 @@ +/** + * Copyright © 2021-2026 space All rights reserved. + */ +package com.evotech.hd.core.entity.system; + +import com.baomidou.mybatisplus.annotation.TableName; +import com.evotech.hd.core.entity.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 用户Entity + * @ClassName:User + * @date: 2025年08月16日 17:28 + * @author: andy.shi + * @contact: 17330188597 + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@TableName("sys_user") +public class User extends BaseEntity { + + private static final long serialVersionUID = 1L; + + /** + * 登录名 + */ + + private String loginName; + + /** + * 密码 + */ + private String password; + + + /** + * 是否允许登陆 + */ + private String loginFlag; + + +} diff --git a/commoms/core/src/main/java/com/evotech/hd/core/enums/BaseEnum.java b/commoms/core/src/main/java/com/evotech/hd/core/enums/BaseEnum.java new file mode 100644 index 0000000..d5f0921 --- /dev/null +++ b/commoms/core/src/main/java/com/evotech/hd/core/enums/BaseEnum.java @@ -0,0 +1,16 @@ +package com.evotech.hd.core.enums; + +/** + * 枚举 + * + * @ClassName:BaseEnum + * @date: 2025年05月19日 15:36 + * @author: andy.shi + * @contact: 17330188597 + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +public interface BaseEnum { + + K getLabel(); + V getValue(); +} diff --git a/commoms/core/src/main/java/com/evotech/hd/core/enums/CacheNames.java b/commoms/core/src/main/java/com/evotech/hd/core/enums/CacheNames.java new file mode 100644 index 0000000..a731aff --- /dev/null +++ b/commoms/core/src/main/java/com/evotech/hd/core/enums/CacheNames.java @@ -0,0 +1,20 @@ +package com.evotech.hd.core.enums; + +/** + * 缓存名称定义 + * 缓存设置的标准: + * 1 频繁读取,但是很少修改的内容,例如用户菜单,用户,字典 + * 2 读取频率不高,但是数据量很大也很少修改的内容,比如机构,区域 + */ +public interface CacheNames { + + String USER_CACHE_TOKEN = "user:cache:token:"; + + String USER_CACHE_LOGIN_NAME = "user:cache:loginName:"; //根据登录名关联用户 + + String SYSTEM_RUNNING_PARAMS = "system:running:params:"; + String SYSTEM_RUNNING_DICT_DETAIL = "system:running:dict:detail:"; //系统运行的指令参数 + + String SYSTEM_LOCK_PLATE = "system:lock_plate:"; //根据登录名关联用户 + +} diff --git a/commoms/core/src/main/java/com/evotech/hd/core/enums/ChargerStatusEnums.java b/commoms/core/src/main/java/com/evotech/hd/core/enums/ChargerStatusEnums.java new file mode 100644 index 0000000..f3a37b3 --- /dev/null +++ b/commoms/core/src/main/java/com/evotech/hd/core/enums/ChargerStatusEnums.java @@ -0,0 +1,58 @@ +package com.evotech.hd.core.enums; + +/** + * 枚举 + * + * @ClassName:ChargerStatusEnums + * @date: 2025年08月22日 14:33 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +public enum ChargerStatusEnums { + + UNKNOWN(-1, "未知"), + + IDLE(0, "空闲"), + + MONITORING(1, "监控"), + + SINGLE_POWER(2, "单功率充电"), + + DOUBLE_POWER(3, "双功率充电"), + + AUXILIARY_CHARGING(4, "辅助充电"), + + DEBUGGING_MODE(5, "调试模式"), + + ERROR(6, "设备错误状态"), + ; + + Integer code; + + String name; + + public Integer getCode() { + return code; + } + + public String getName() { + return name; + } + + ChargerStatusEnums(Integer code, String name) { + this.code = code; + this.name = name; + } + + public static ChargerStatusEnums getByCode(Integer code){ + if(code == null){ + return UNKNOWN; + } + for (ChargerStatusEnums en : ChargerStatusEnums.values()){ + if(en.getCode().equals(code)){ + return en; + } + } + return UNKNOWN; + } +} diff --git a/commoms/core/src/main/java/com/evotech/hd/core/enums/CodeMsg.java b/commoms/core/src/main/java/com/evotech/hd/core/enums/CodeMsg.java new file mode 100644 index 0000000..11df38b --- /dev/null +++ b/commoms/core/src/main/java/com/evotech/hd/core/enums/CodeMsg.java @@ -0,0 +1,113 @@ +package com.evotech.hd.core.enums; + +import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * 100 Continue 继续。客户端应继续其请求 + 101 Switching Protocols 切换协议。服务器根据客户端的请求切换协议。只能切换到更高级的协议,例如,切换到HTTP的新版本协议 + 200 OK 请求成功。一般用于GET与POST请求 + 201 Created 已创建。成功请求并创建了新的资源 + 202 Accepted 已接受。已经接受请求,但未处理完成 + 203 Non-Authoritative Information 非授权信息。请求成功。但返回的meta信息不在原始的服务器,而是一个副本 + 204 No Content 无内容。服务器成功处理,但未返回内容。在未更新网页的情况下,可确保浏览器继续显示当前文档 + 205 Reset Content 重置内容。服务器处理成功,用户终端(例如:浏览器)应重置文档视图。可通过此返回码清除浏览器的表单域 + 206 Partial Content 部分内容。服务器成功处理了部分GET请求 + 300 Multiple Choices 多种选择。请求的资源可包括多个位置,相应可返回一个资源特征与地址的列表用于用户终端(例如:浏览器)选择 + 301 Moved Permanently 永久移动。请求的资源已被永久的移动到新URI,返回信息会包括新的URI,浏览器会自动定向到新URI。今后任何新的请求都应使用新的URI代替 + 302 Found 临时移动。与301类似。但资源只是临时被移动。客户端应继续使用原有URI + 303 See Other 查看其它地址。与301类似。使用GET和POST请求查看 + 304 Not Modified 未修改。所请求的资源未修改,服务器返回此状态码时,不会返回任何资源。客户端通常会缓存访问过的资源,通过提供一个头信息指出客户端希望只返回在指定日期之后修改的资源 + 305 Use Proxy 使用代理。所请求的资源必须通过代理访问 + 306 Unused 已经被废弃的HTTP状态码 + 307 Temporary Redirect 临时重定向。与302类似。使用GET请求重定向 + 400 Bad Request 客户端请求的语法错误,服务器无法理解 + 401 Unauthorized 请求要求用户的身份认证 + 402 Payment Required 保留,将来使用 + 403 Forbidden 服务器理解请求客户端的请求,但是拒绝执行此请求 + 404 Not Found 服务器无法根据客户端的请求找到资源(网页)。通过此代码,网站设计人员可设置"您所请求的资源无法找到"的个性页面 + 405 Method Not Allowed 客户端请求中的方法被禁止 + 406 Not Acceptable 服务器无法根据客户端请求的内容特性完成请求 + 407 Proxy Authentication Required 请求要求代理的身份认证,与401类似,但请求者应当使用代理进行授权 + 408 Request Time-out 服务器等待客户端发送的请求时间过长,超时 + 409 Conflict 服务器完成客户端的 PUT 请求时可能返回此代码,服务器处理请求时发生了冲突 + 410 Gone 客户端请求的资源已经不存在。410不同于404,如果资源以前有现在被永久删除了可使用410代码,网站设计人员可通过301代码指定资源的新位置 + 411 Length Required 服务器无法处理客户端发送的不带Content-Length的请求信息 + 412 Precondition Failed 客户端请求信息的先决条件错误 + 413 Request Entity Too Large 由于请求的实体过大,服务器无法处理,因此拒绝请求。为防止客户端的连续请求,服务器可能会关闭连接。如果只是服务器暂时无法处理,则会包含一个Retry-After的响应信息 + 414 Request-URI Too Large 请求的URI过长(URI通常为网址),服务器无法处理 + 415 Unsupported Media Type 服务器无法处理请求附带的媒体格式 + 416 Requested range not satisfiable 客户端请求的范围无效 + 417 Expectation Failed 服务器无法满足Expect的请求头信息 + 500 Internal Server Error 服务器内部错误,无法完成请求 + 501 Not Implemented 服务器不支持请求的功能,无法完成请求 + 502 Bad Gateway 作为网关或者代理工作的服务器尝试执行请求时,从远程服务器接收到了一个无效的响应 + 503 Service Unavailable 由于超载或系统维护,服务器暂时的无法处理客户端的请求。延时的长度可包含在服务器的Retry-After头信息中 + 504 Gateway Time-out 充当网关或代理的服务器,未及时从远端服务器获取请求 + 505 HTTP Version not supported 服务器不支持请求的HTTP协议的版本,无法完成处理 + */ +public enum CodeMsg { + // 统一回复 + SUCCESS("1000", "OK"), + ERROR("4000", "ERROR"), + // 系统错误 S + USERNAME_OR_PASSWORD_ERROR("S0400", "用户名或密码错误!"), + PASSWORD_ENTER_EXCEED_LIMIT("S0401", "用户输入密码次数超限!"), + USER_NOT_LOGIN("S0402", "用户未登陆!"), + AUTHENTICATION_FAILED("S0403", "认证失败!"), + TOKEN_INVALID("S0404", "无效token!"), + TOKEN_EXPIRED("S0405", "token已过期!"), + ACCESS_DENY("S0406", "访问未授权!"), + ACCESS_SCOPE_ERROR("S0407", "客户端权限范围出错"), + GATEWAY_EXECUTION_TIMEOUT("S0408", "网关执行超时"), + GATEWAY_EXECUTION_ERROR("S0409", "网关执行出错"), + // 业务错误 B + BUSSINESS_ERROR("B0400", "业务出错"), + PARAM_IS_NULL("B0401", "请求参数为空"), + PARAM_ERROR("B0402", "用户请求参数错误"), + UPLOAD_FILE_ERROR("B0403", "用户上传文件异常"), + UPLOAD_FILE_TYPE_NOT_MATCH("B0404", "用户上传文件类型不匹配"), + UPLOAD_FILE_SIZE_EXCEEDS("B0405", "用户上传文件太大"), + + // 数据库错误 D + DATABASE_ERROR("D0400", "数据库服务出错"), + DATA_EXIST("D0401", "此数据已存在!"), + DATABASE_RESULT_NULL("D0402", "查询结果为空"), + DATABASE_TABLE_NOT_EXIST("D0403", "表不存在"), + DATABASE_COLUMN_NOT_EXIST("D0404", "列不存在"), + SQL_RUN_ERROR("D0405", "sql运行异常"), + REDIS_ERROR("D0410", "REDIS数据出错"), + + // 小程序 w + WECHAT_LOGIN_ERROR("W0400", "小程序登录异常"), + WECHAT_SERRION_ERROR("W0401", "小程序登录态异常"), + WECHAT_API_ERROR("W0402", "小程序接口调用异常"), + WECHAT_NOT_LOGIN("W0403", "用户未登陆!"), + WECHAT_TOKEN_INVALID("W0404", "无效token!"), + WECHAT_TOKEN_EXPIRED("W0405", "token已过期!"); + + String code; + + String msg; + + public String getCode() { + return code; + } + + public String getMsg() { + return msg; + } + + CodeMsg(String code, String msg) { + this.code = code; + this.msg = msg; + } + + public static CodeMsg getCodeMsgByCode(String code){ + Map map = Arrays.stream(CodeMsg.values()).collect(Collectors.toMap(CodeMsg::getCode, v->v)); + return map.get(code); + } + + +} diff --git a/commoms/core/src/main/java/com/evotech/hd/core/enums/ErrorConstants.java b/commoms/core/src/main/java/com/evotech/hd/core/enums/ErrorConstants.java new file mode 100644 index 0000000..0273987 --- /dev/null +++ b/commoms/core/src/main/java/com/evotech/hd/core/enums/ErrorConstants.java @@ -0,0 +1,14 @@ +package com.evotech.hd.core.enums; + + +public final class ErrorConstants { + + public static final String LOGIN_ERROR_NOT_LOGIN_IN = "您尚未登录,请登录后操作!"; + public static final String LOGIN_ERROR_FORBIDDEN = "该帐号已经被禁止登录!"; + public static final String LOGIN_ERROR_INCORRECT = "用户名或者密码错误!"; + public static final String LOGIN_ERROR_NOTFOUND = "用户名不存在!"; + public static final String LOGIN_ERROR_EXPIRED = "您的登录已过期,请重新登录!"; + public static final String LOGIN_ERROR_FORBID_LOGGED_IN_ELSEWHERE = "您的账号已在其它地方登录,您被禁止登录!"; + public static final String LOGIN_ERROR__KICK_OUT_LOGGED_IN_ELSEWHERE = "您的账号在另一台设备上登录,如非本人操作,请立即修改密码!"; + public static final String LOGIN_ERROR_ERROR_VALIDATE_CODE = "您输入的验证码不正确,请重新输入!"; +} diff --git a/commoms/core/src/main/java/com/evotech/hd/core/enums/OrderStatusEnums.java b/commoms/core/src/main/java/com/evotech/hd/core/enums/OrderStatusEnums.java new file mode 100644 index 0000000..6a6f29a --- /dev/null +++ b/commoms/core/src/main/java/com/evotech/hd/core/enums/OrderStatusEnums.java @@ -0,0 +1,47 @@ +package com.evotech.hd.core.enums; + +public enum OrderStatusEnums { + + CREATE(1, "已创建"), + + SWAP(2, "换电中"), + + SWAPOVER(3, "换电完成"), + + CHARGING(4, "充电中"), + + CHARGED(5, "充电完成"), + + TOPAY(6, "待结算"), + + FINISH(7, "已完成"), + + SETTLEMENT_IN_PROGRESS(8, "结算中"), + + CANCLE(9, "已取消"), + + REFUND(-1, "订单退款"), + + REFUND_PART(-2, "订单不分退款"), + + ; + + + + Integer code; + + String name; + + public Integer getCode() { + return code; + } + + public String getName() { + return name; + } + + OrderStatusEnums(Integer code, String name) { + this.code = code; + this.name = name; + } +} diff --git a/commoms/core/src/main/java/com/evotech/hd/core/enums/SwapBatteryStepEnum.java b/commoms/core/src/main/java/com/evotech/hd/core/enums/SwapBatteryStepEnum.java new file mode 100644 index 0000000..dd37660 --- /dev/null +++ b/commoms/core/src/main/java/com/evotech/hd/core/enums/SwapBatteryStepEnum.java @@ -0,0 +1,64 @@ +package com.evotech.hd.core.enums; + +/** + * 枚举 + * + * @ClassName:SwapBatteryStepEnum + * @date: 2025年06月23日 16:08 + * @author: andy.shi + * @contact: 17330188597 + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +public enum SwapBatteryStepEnum{ + + // @Schema(description = "步骤:1-车辆进站,2-车辆到达指定位置,3-对中机构,4-取新电,5-拆旧电,6-装新电,7-放旧电,8-完成") + VEHICLES_ENTER_STATION(1, "车辆进站"), + + VEHICLE_POSITION(2, "车辆到达指定位置"), + + INSTITUTIONS_FOR_CHINA(3, "对中机构"), + + DISMANTLE_OLD_ELECTRICITY(4, "拆旧电"), + + DISCHARGE_OLD_ELECTRICITY(5, "放旧电"), + + GET_NEW_ELECTRICITY(6, "取新电"), + + INSTALL_NEW_ELECTRICITY(7, "装新电"), + + COMPLETED(8, "完成"), + + + ; + + + + Integer code; + + String name; + + public Integer getCode() { + return code; + } + + public String getName() { + return name; + } + + SwapBatteryStepEnum(Integer code, String name) { + this.code = code; + this.name = name; + } + + public static String getName(Integer code){ + if(code == null){ + return ""; + } + for(SwapBatteryStepEnum swapBatteryStep : values()){ + if(swapBatteryStep.getCode().equals(code)){ + return swapBatteryStep.getName(); + } + } + return ""; + } +} diff --git a/commoms/core/src/main/java/com/evotech/hd/core/mapstruct/AccessStrategyWrapper.java b/commoms/core/src/main/java/com/evotech/hd/core/mapstruct/AccessStrategyWrapper.java new file mode 100644 index 0000000..dbc5cd3 --- /dev/null +++ b/commoms/core/src/main/java/com/evotech/hd/core/mapstruct/AccessStrategyWrapper.java @@ -0,0 +1,21 @@ +package com.evotech.hd.core.mapstruct; + +import com.evotech.hd.core.dtos.business.AccessStrategyDTO; +import com.evotech.hd.core.entity.system.AccessStrategy; +import org.mapstruct.Mapper; +import org.mapstruct.ReportingPolicy; +import org.mapstruct.factory.Mappers; + +/** + * 接口 + * + * @ClassName:OrderReservationWrapper + * @date: 2025年08月20日 15:17 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE, uses = {}) +public interface AccessStrategyWrapper extends EntityWrapper { + + AccessStrategyWrapper INSTANCE = Mappers.getMapper ( AccessStrategyWrapper.class ); +} \ No newline at end of file diff --git a/commoms/core/src/main/java/com/evotech/hd/core/mapstruct/BatteryCompartmentWrapper.java b/commoms/core/src/main/java/com/evotech/hd/core/mapstruct/BatteryCompartmentWrapper.java new file mode 100644 index 0000000..19e7bd2 --- /dev/null +++ b/commoms/core/src/main/java/com/evotech/hd/core/mapstruct/BatteryCompartmentWrapper.java @@ -0,0 +1,21 @@ +package com.evotech.hd.core.mapstruct; + +import com.evotech.hd.core.dtos.business.BatteryCompartmentDTO; +import com.evotech.hd.core.entity.business.BatteryCompartment; +import org.mapstruct.Mapper; +import org.mapstruct.ReportingPolicy; +import org.mapstruct.factory.Mappers; + +/** + * 接口 + * + * @ClassName:OrderReservationWrapper + * @date: 2025年08月20日 15:17 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE, uses = {}) +public interface BatteryCompartmentWrapper extends EntityWrapper { + + BatteryCompartmentWrapper INSTANCE = Mappers.getMapper ( BatteryCompartmentWrapper.class ); +} \ No newline at end of file diff --git a/commoms/core/src/main/java/com/evotech/hd/core/mapstruct/BatteryStrategyWrapper.java b/commoms/core/src/main/java/com/evotech/hd/core/mapstruct/BatteryStrategyWrapper.java new file mode 100644 index 0000000..a90988d --- /dev/null +++ b/commoms/core/src/main/java/com/evotech/hd/core/mapstruct/BatteryStrategyWrapper.java @@ -0,0 +1,21 @@ +package com.evotech.hd.core.mapstruct; + +import com.evotech.hd.core.dtos.business.BatteryStrategyDTO; +import com.evotech.hd.core.entity.system.BatteryStrategy; +import org.mapstruct.Mapper; +import org.mapstruct.ReportingPolicy; +import org.mapstruct.factory.Mappers; + +/** + * 接口 + * + * @ClassName:OrderReservationWrapper + * @date: 2025年08月20日 15:17 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE, uses = {}) +public interface BatteryStrategyWrapper extends EntityWrapper { + + BatteryStrategyWrapper INSTANCE = Mappers.getMapper ( BatteryStrategyWrapper.class ); +} \ No newline at end of file diff --git a/commoms/core/src/main/java/com/evotech/hd/core/mapstruct/BatteryWrapper.java b/commoms/core/src/main/java/com/evotech/hd/core/mapstruct/BatteryWrapper.java new file mode 100644 index 0000000..9d35342 --- /dev/null +++ b/commoms/core/src/main/java/com/evotech/hd/core/mapstruct/BatteryWrapper.java @@ -0,0 +1,21 @@ +package com.evotech.hd.core.mapstruct; + +import com.evotech.hd.core.dtos.business.BatteryDTO; +import com.evotech.hd.core.entity.business.Battery; +import org.mapstruct.Mapper; +import org.mapstruct.ReportingPolicy; +import org.mapstruct.factory.Mappers; + +/** + * 接口 + * + * @ClassName:BatteryWrapper + * @date: 2025年08月20日 15:17 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE, uses = {}) +public interface BatteryWrapper extends EntityWrapper { + + BatteryWrapper INSTANCE = Mappers.getMapper ( BatteryWrapper.class ); +} \ No newline at end of file diff --git a/commoms/core/src/main/java/com/evotech/hd/core/mapstruct/EntityWrapper.java b/commoms/core/src/main/java/com/evotech/hd/core/mapstruct/EntityWrapper.java new file mode 100644 index 0000000..1507f01 --- /dev/null +++ b/commoms/core/src/main/java/com/evotech/hd/core/mapstruct/EntityWrapper.java @@ -0,0 +1,43 @@ +package com.evotech.hd.core.mapstruct; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.mapstruct.Mapping; +import org.mapstruct.Named; + +import java.util.List; +import java.util.Map; + +public interface EntityWrapper { + + @Mapping(target = "extendeds", source = "extendeds", qualifiedByName = "mapToString") + E toEntity(D dto); + + @Mapping(target = "extendeds", source = "extendeds", qualifiedByName = "mapToString") + D toDTO(E entity); + + List toEntity(List dtoList); + + + List toDTO(List entityList); + + + Page toEntity(Page page); + + Page toDTO(Page page); + + // 自定义转换方法:Map -> String (JSON) + @Named("mapToString") + default String mapToString(Map map) { + if (map == null) { + return null; + } + ObjectMapper objectMapper = new ObjectMapper(); + try { + return objectMapper.writeValueAsString(map); + } catch (JsonProcessingException e) { + throw new RuntimeException("Failed to convert map to JSON string", e); + } + } +} diff --git a/commoms/core/src/main/java/com/evotech/hd/core/mapstruct/OrderChargingWrapper.java b/commoms/core/src/main/java/com/evotech/hd/core/mapstruct/OrderChargingWrapper.java new file mode 100644 index 0000000..be2a284 --- /dev/null +++ b/commoms/core/src/main/java/com/evotech/hd/core/mapstruct/OrderChargingWrapper.java @@ -0,0 +1,21 @@ +package com.evotech.hd.core.mapstruct; + +import com.evotech.hd.core.dtos.business.OrderChargingDTO; +import com.evotech.hd.core.entity.business.OrderCharging; +import org.mapstruct.Mapper; +import org.mapstruct.ReportingPolicy; +import org.mapstruct.factory.Mappers; + +/** + * 接口 + * + * @ClassName:OrderReservationWrapper + * @date: 2025年08月20日 15:17 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE, uses = {}) +public interface OrderChargingWrapper extends EntityWrapper { + + OrderChargingWrapper INSTANCE = Mappers.getMapper ( OrderChargingWrapper.class ); +} \ No newline at end of file diff --git a/commoms/core/src/main/java/com/evotech/hd/core/mapstruct/OrderReservationWrapper.java b/commoms/core/src/main/java/com/evotech/hd/core/mapstruct/OrderReservationWrapper.java new file mode 100644 index 0000000..708c9c9 --- /dev/null +++ b/commoms/core/src/main/java/com/evotech/hd/core/mapstruct/OrderReservationWrapper.java @@ -0,0 +1,21 @@ +package com.evotech.hd.core.mapstruct; + +import com.evotech.hd.core.dtos.business.OrderReservationDTO; +import com.evotech.hd.core.entity.business.OrderReservation; +import org.mapstruct.Mapper; +import org.mapstruct.ReportingPolicy; +import org.mapstruct.factory.Mappers; + +/** + * 接口 + * + * @ClassName:OrderReservationWrapper + * @date: 2025年08月20日 15:17 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE, uses = {}) +public interface OrderReservationWrapper extends EntityWrapper { + + OrderReservationWrapper INSTANCE = Mappers.getMapper ( OrderReservationWrapper.class ); +} \ No newline at end of file diff --git a/commoms/core/src/main/java/com/evotech/hd/core/mapstruct/OrderSwapWrapper.java b/commoms/core/src/main/java/com/evotech/hd/core/mapstruct/OrderSwapWrapper.java new file mode 100644 index 0000000..aa7f84a --- /dev/null +++ b/commoms/core/src/main/java/com/evotech/hd/core/mapstruct/OrderSwapWrapper.java @@ -0,0 +1,23 @@ +package com.evotech.hd.core.mapstruct; + +import com.evotech.hd.core.dtos.business.OrderReservationDTO; +import com.evotech.hd.core.dtos.business.OrderSwapDTO; +import com.evotech.hd.core.entity.business.OrderReservation; +import com.evotech.hd.core.entity.business.OrderSwap; +import org.mapstruct.Mapper; +import org.mapstruct.ReportingPolicy; +import org.mapstruct.factory.Mappers; + +/** + * 接口 + * + * @ClassName:OrderReservationWrapper + * @date: 2025年08月20日 15:17 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE, uses = {}) +public interface OrderSwapWrapper extends EntityWrapper { + + OrderSwapWrapper INSTANCE = Mappers.getMapper ( OrderSwapWrapper.class ); +} \ No newline at end of file diff --git a/commoms/core/src/main/java/com/evotech/hd/core/mapstruct/ParamsWrapper.java b/commoms/core/src/main/java/com/evotech/hd/core/mapstruct/ParamsWrapper.java new file mode 100644 index 0000000..50e64c1 --- /dev/null +++ b/commoms/core/src/main/java/com/evotech/hd/core/mapstruct/ParamsWrapper.java @@ -0,0 +1,21 @@ +/** + * Copyright © 2021-2025 JeePlus All rights reserved. + */ +package com.evotech.hd.core.mapstruct; + + +import com.evotech.hd.core.dtos.system.ParamsDTO; +import com.evotech.hd.core.entity.system.Params; +import org.mapstruct.Mapper; +import org.mapstruct.ReportingPolicy; +import org.mapstruct.factory.Mappers; + +/** + * ParamsWrapper + */ +@Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE, uses = {}) +public interface ParamsWrapper extends EntityWrapper { + + ParamsWrapper INSTANCE = Mappers.getMapper ( ParamsWrapper.class ); +} + diff --git a/commoms/core/src/main/java/com/evotech/hd/core/mapstruct/RunningInstructionsDetailWrapper.java b/commoms/core/src/main/java/com/evotech/hd/core/mapstruct/RunningInstructionsDetailWrapper.java new file mode 100644 index 0000000..b15beb3 --- /dev/null +++ b/commoms/core/src/main/java/com/evotech/hd/core/mapstruct/RunningInstructionsDetailWrapper.java @@ -0,0 +1,22 @@ +/** + * Copyright © 2021-2025 JeePlus All rights reserved. + */ +package com.evotech.hd.core.mapstruct; + + +import com.evotech.hd.core.dtos.business.RunningInstructionsDetailDTO; +import com.evotech.hd.core.entity.business.RunningInstructionsDetail; +import org.mapstruct.Mapper; +import org.mapstruct.ReportingPolicy; +import org.mapstruct.factory.Mappers; + +/** + * RunningInstructionsDetailWrapper + * + */ +@Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE, uses = {}) +public interface RunningInstructionsDetailWrapper extends EntityWrapper { + + RunningInstructionsDetailWrapper INSTANCE = Mappers.getMapper ( RunningInstructionsDetailWrapper.class ); +} + diff --git a/commoms/core/src/main/java/com/evotech/hd/core/mapstruct/RunningInstructionsWrapper.java b/commoms/core/src/main/java/com/evotech/hd/core/mapstruct/RunningInstructionsWrapper.java new file mode 100644 index 0000000..bd1189b --- /dev/null +++ b/commoms/core/src/main/java/com/evotech/hd/core/mapstruct/RunningInstructionsWrapper.java @@ -0,0 +1,23 @@ +/** + * Copyright © 2021-2025 JeePlus All rights reserved. + */ +package com.evotech.hd.core.mapstruct; + + +import com.evotech.hd.core.dtos.business.RunningInstructionsDTO; +import com.evotech.hd.core.dtos.system.ParamsDTO; +import com.evotech.hd.core.entity.business.RunningInstructions; +import com.evotech.hd.core.entity.system.Params; +import org.mapstruct.Mapper; +import org.mapstruct.ReportingPolicy; +import org.mapstruct.factory.Mappers; + +/** + * RunningInstructionsWrapper + */ +@Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE, uses = {}) +public interface RunningInstructionsWrapper extends EntityWrapper { + + RunningInstructionsWrapper INSTANCE = Mappers.getMapper ( RunningInstructionsWrapper.class ); +} + diff --git a/commoms/core/src/main/java/com/evotech/hd/exception/InstructionException.java b/commoms/core/src/main/java/com/evotech/hd/exception/InstructionException.java new file mode 100644 index 0000000..745c393 --- /dev/null +++ b/commoms/core/src/main/java/com/evotech/hd/exception/InstructionException.java @@ -0,0 +1,30 @@ +package com.evotech.hd.exception; + +import lombok.extern.slf4j.Slf4j; + +/** + * 指令下发异常 + * + * @ClassName:MongoDBException + * @date: 2025年04月30日 8:56 + * @author: andy.shi + * @contact: 17330188597 + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@Slf4j +public class InstructionException extends Exception{ + + public InstructionException() { + super(); + } + + public InstructionException(Exception e) { + super(e); + log.error("instruction 指令下发出现异常============{}", e.getMessage()); + } + + public InstructionException(String message) { + super(message); + log.error("instruction 指令下发出现异常============{}", message); + } +} diff --git a/commoms/core/src/main/java/com/evotech/hd/exception/UnauthorizedException.java b/commoms/core/src/main/java/com/evotech/hd/exception/UnauthorizedException.java new file mode 100644 index 0000000..8b9275f --- /dev/null +++ b/commoms/core/src/main/java/com/evotech/hd/exception/UnauthorizedException.java @@ -0,0 +1,28 @@ +package com.evotech.hd.exception; + +import lombok.extern.slf4j.Slf4j; + +/** + * 指令下发异常 + * + * @ClassName:MongoDBException + * @date: 2025年04月30日 8:56 + * @author: andy.shi + * @contact: 17330188597 + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@Slf4j +public class UnauthorizedException extends Exception{ + + public UnauthorizedException() { + super(); + } + + public UnauthorizedException(Exception e) { + super(e); + } + + public UnauthorizedException(String message) { + super(message); + } +} diff --git a/commoms/core/src/main/java/com/evotech/hd/query/Query.java b/commoms/core/src/main/java/com/evotech/hd/query/Query.java new file mode 100644 index 0000000..d59868d --- /dev/null +++ b/commoms/core/src/main/java/com/evotech/hd/query/Query.java @@ -0,0 +1,34 @@ +package com.evotech.hd.query; + +import java.lang.annotation.*; + +/** + * 查询字段注解 + */ +@Inherited +@Documented +@Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE}) +@Retention(RetentionPolicy.RUNTIME) +public @interface Query { + /** + * java属性名 + * + * @return + */ + String javaField() default ""; + + /** + * mapper中的数据库字段名 + * + * @return + */ + String tableColumn() default ""; + + /** + * 查询类型 + * + * @return + */ + QueryType type() default QueryType.LIKE; + +} diff --git a/commoms/core/src/main/java/com/evotech/hd/query/QueryType.java b/commoms/core/src/main/java/com/evotech/hd/query/QueryType.java new file mode 100644 index 0000000..26e6892 --- /dev/null +++ b/commoms/core/src/main/java/com/evotech/hd/query/QueryType.java @@ -0,0 +1,17 @@ +package com.evotech.hd.query; + +public enum QueryType { + EQ, + NE, + GT, + GE, + LT, + LE, + BETWEEN, + NOTBETWEEN, + LIKE, + NOTLIKE, + LIKELEFT, + LIKERIGHT, + IN +} diff --git a/commoms/core/src/main/java/com/evotech/hd/utils/Collections.java b/commoms/core/src/main/java/com/evotech/hd/utils/Collections.java new file mode 100644 index 0000000..dcbb643 --- /dev/null +++ b/commoms/core/src/main/java/com/evotech/hd/utils/Collections.java @@ -0,0 +1,318 @@ +package com.evotech.hd.utils; + + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.Serializable; +import java.util.*; +import java.util.stream.Collectors; + +/** + * @DES 集合类型工具类 + * @author andy.shi + * @createDate 2020年12月25日下午2:16:13 + * @typeName Collections + */ +public class Collections implements Serializable{ + + static Logger log = LoggerFactory.getLogger(Collections.class); + + /** + * + */ + private static final long serialVersionUID = -611031509912595162L; + + /** + * + * @param ts + * @return + */ + public static boolean isEmpty(T[] ts) { + return ts == null || ts.length == 0; + } + + /** + * + * @param ts + * @return + */ + public static boolean isNotEmpty(T[] ts) { + return ! isEmpty(ts); + } + +/************************ 以上为公共方法 以下为list ************************************************/ + /** + * @DES 检查集合为空 + * @author andy.shi + * @createDate 2020年12月25日下午2:14:43 + * @methodName Collections.isEmpty + * @param list + * @return + * @returnType boolean + */ + public static boolean isEmpty(Collection list) { + return list == null || list.size() == 0; + } + + /** + * @DES 检查集合不为空 + * @author andy.shi + * @createDate 2020年12月25日下午2:15:07 + * @methodName Collections.isNotEmpty + * @param collection + * @return + * @returnType boolean + */ + public static boolean isNotEmpty(Collection collection) { + return ! (isEmpty(collection)); + } + + /** + * 将list进行分页 + * @author Andy.shi + * @date 2020年9月15日上午9:37:07 + * @param 泛型, 根据传入的对对象类型,返回类型 + * @param list 需要分组的集合 + * @param pageNum 当前页数 + * @param pageSize 每页显示的条数 + * @return + */ + public static List pageList(List list, Integer pageNum, Integer pageSize){ + if(isEmpty(list)) { + log.warn(String.format("[info " + Collections.class.getName() + " 执行信息] : list info: [%s] 为空", new Object[]{list})); + return list; + } + try { + //如果当前页为0, 更改为1 + if(pageNum == 0) + pageNum = 1; + //计算formIndex + Integer fromIndex = ((pageNum-1)*pageSize); + //计算toIndex + Integer toIndex = fromIndex + pageSize; + //防止下标越界 + if(toIndex > list.size()) + toIndex = list.size(); + log.info(String.format("[info " + Collections.class.getName() + " 执行信息] : list info: [%s] and pageNum info:[%s] and pageSize info: [%s]", new Object[]{list, pageNum, pageSize})); + return list.subList(fromIndex, toIndex); + } catch (Exception e) { + // TODO Auto-generated catch block + log.error(String.format("Class {%s}, Method {%s}", new Object[]{Collections.class.getName(), "pageList"}),e); + throw new RuntimeException(String.format("EntityInfo --- " + Collections.class.getName() + " error: list info: [%s] and pageNum info:[%s] and pageSize info: [%s] ", new Object[]{list, pageNum, pageSize}), e); + } + } + + /** + * 创建对象 + * @author Andy.shi + * @date 2020年9月15日上午9:37:07 + * @param + * @return + */ + public static List emptyList() { + return new LinkedList(); + } + + /** + * @DES 数组转对象 + * @author andy.shi + * @createDate 2020年12月25日下午2:09:14 + * @methodName Collections.asList + * @param objs + * @return + * @returnType List + */ + public static List asList(T... objs) { + if (isEmpty(objs)) + return emptyList(); + + List list = emptyList(); + for (T obj : objs) + list.add(obj); + + return list; + } + + /** + * + * @param objs + * @return + */ + public static List asList(Collection objs) { + if (isEmpty(objs)) + return emptyList(); + + List list = emptyList(); + list.addAll(objs); + + return list; + } + + /** + * 去除集合的重复项 + * @author Andy.shi + * @date 2020年9月24日上午9:09:07 + * @param 泛型,字段的类型, 如果是对象, 请重写hashCode() 和 equals() + * @param list1 去重的集合1 + * @param list2 去重的集合2 + * @return list 去重后的对象 + */ + @SuppressWarnings("unchecked") + public static List removeDuplicatesList(List list1, List list2){ + return removeDuplicatesList(new List[] {list1, list2}); + } + + /** + * 去除集合的重复项 + * @author Andy.shi + * @date 2020年9月24日上午9:13:09 + * @param 泛型,字段的类型, 如果是对象, 请重写hashCode() 和 equals() + * @param listArray 去重的集合数组 + * @return list 去重后的对象 + */ + @SuppressWarnings("unchecked") + public static List removeDuplicatesList(List... listArray){ + try { + List listAll = emptyList(); + if(listArray == null || listArray.length == 0) { + log.warn("[info " + Collections.class.getName() + " 执行信息] : 数据集合为空, 返回空对象 "); + return emptyList(); + } + for (List list : listArray) { + if(isNotEmpty(list)) { + listAll.addAll(list); + } + } + return (List) listAll.stream().distinct().collect(Collectors.toList()); + } catch (Exception e) { + // TODO Auto-generated catch block + log.error(String.format("Class {%s}, Method {%s}", new Object[]{Collections.class.getName(), "removeDuplicatesList"}),e); + throw new RuntimeException(String.format("EntityInfo --- " + Collections.class.getName() + " error: listArray info: [%s] 去除重复数据异常; ", new Object[]{listArray}), e); + } + } + + + /** + * 获取重复数据 + * @author Andy.shi + * @date 2020年9月24日上午9:46:03 + * @param 泛型,字段的类型, 如果是对象, 请重写hashCode() 和 equals() + * @param list1 查重的集合1 + * @param list2 查重的集合1 + * @return 并集的数据集合 + */ + @SuppressWarnings("unchecked") + public static List findDuplicatesList(List list1, List list2){ + return findDuplicatesList(new List[] {list1, list2}); + } + /** + * 获取重复数据 + * @author Andy.shi + * @date 2020年9月24日上午9:44:30 + * @param 泛型,字段的类型, 如果是对象, 请重写hashCode() 和 equals() + * @param listArray 需要查重的集合数组 + * @return 并集的数据集合 + */ + public static List findDuplicatesList(List... listArray){ + try { + if(listArray == null || listArray.length == 0) { + log.warn("[info " + Collections.class.getName() + " 执行信息] : 数据集合为空, 返回空对象 "); + return emptyList(); + } + + if(listArray.length == 1) { + log.warn("[info " + Collections.class.getName() + " 执行信息] : 集合数组只有一个, 不存在重复数据 "); + return listArray[0]; + } + List firstList = listArray[0]; + + for (int i = 1; i < listArray.length; i++) { + firstList.retainAll(listArray[i]); + } + return firstList; + } catch (Exception e) { + // TODO Auto-generated catch block + log.error(String.format("Class {%s}, Method {%s}", new Object[]{Collections.class.getName(), "findDuplicatesList"}),e); + throw new RuntimeException(String.format("EntityInfo --- " + Collections.class.getName() + " error: listArray info: [%s] 获取重复数据异常; ", new Object[]{listArray}), e); + } + } + + /*** + * 获取随机元素 + * @param list + * @return + * @param + */ + public static T getRandomElement(List list) { + if(isEmpty(list)) { + log.warn("[info " + Collections.class.getName() + " 执行 getRandomElement 信息] : 数据集合为空, 返回null "); + return null; + } + Random random = new Random(); + int index = random.nextInt(list.size()); + return list.get(index); + } + + + +/********************** 以上操作list 以下操作map **************************************/ + /** + * + * @param map + * @return + */ + public static boolean isNotEmpty(Map map) { + return ! isEmpty(map); + } + + /** + * @param map + * @return + */ + public static boolean isEmpty(Map map) { + return map == null || map.size() == 0; + } + /** + * @DES 创建空的map对象 + * @author andy.shi + * @company 龙腾工作室 + * @createDate 2020年12月25日下午1:36:35 + * @methodName MapLists.emptyMap + * @return + * @returnType Map + */ + public static final Map emptyMap() { + return new LinkedHashMap(); + } + + + /** + * @DES 组装map数据对象 + * @author andy.shi + * @createDate 2020年12月25日下午1:37:05 + * @methodName MapLists.createMap + * @param args + * @return + * @returnType Map + */ + @SuppressWarnings("unchecked") + public static Map asMap(Object... args) { + Map map = emptyMap(); + if (args == null || args.length == 0) { + log.warn(String.format("[info " + Collections.class.getName() + " 执行信息] : params的参数不为偶数; args length: [%s]; args info: [%s] ", new Object[]{args.length, args})); + return map; + } else if (args.length % 2 != 0) { + throw new RuntimeException(String.format("EntityInfo --- " + Collections.class.getName() + " error: args must completely match: even param is key, odd param is value, please check your arguments. args length [%s]; args info: [%s]; ", new Object[]{args.length, args})); + } + for (int i=0; i 0){ + orderNoSuffix = randomString(red); + } + return orderNoPrefix + stationCode + orderNoMiddle2 + orderNoSuffix; + } + + public static String randomString(int length) { + if (length < 1) { + length = 1; + } + final StringBuilder sb = new StringBuilder(length); + final int baseLength = BASE_NUMBER.length(); + for (int i = 0; i < length; i++) { + final int number = ThreadLocalRandom.current().nextInt(baseLength); + sb.append(BASE_NUMBER.charAt(number)); + } + return sb.toString(); + } + +} diff --git a/commoms/core/src/main/java/com/evotech/hd/utils/DataUtils.java b/commoms/core/src/main/java/com/evotech/hd/utils/DataUtils.java new file mode 100644 index 0000000..281c6cd --- /dev/null +++ b/commoms/core/src/main/java/com/evotech/hd/utils/DataUtils.java @@ -0,0 +1,144 @@ +package com.evotech.hd.utils; + +import org.apache.commons.lang3.ObjectUtils; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; + +import javax.lang.model.util.Elements; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import java.io.*; +import java.math.BigDecimal; +import java.nio.charset.StandardCharsets; + +/** + * 类 + * + * @ClassName:DataUtils + * @date: 2025年06月03日 10:25 + * @author: andy.shi + * @contact: 17330188597 + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ + +public class DataUtils { + + public static final BigDecimal DEFAULT_VALUE = new BigDecimal(0); + + + public static T findDefaultValue(T v, T dv){ + if(ObjectUtils.isNotEmpty(v)){ + return v; + } + return dv; + } + + public static void main(String[] args) { + String filePath = "D:\\andy\\文档\\云平台\\站控\\RGVIO.xml"; // 包含XML内容的TXT文件路径 + + try { + // 1. 读取TXT文件内容 + String fileContent = readTxtFile(filePath); + + if (fileContent == null || fileContent.isEmpty()) { + System.out.println("文件内容为空或读取失败"); + return; + } + + // 2. 将文件内容解析为Document对象 + Document document = parseToDocument(fileContent); + + if (document != null) { + System.out.println("文件解析成功!"); + System.out.println("根节点名称: " + document.getDocumentElement().getNodeName()); + + NodeList nodeList = document.getElementsByTagName("IOITEM"); + int id = 848; + for (int i = 0; i < nodeList.getLength(); i++) { + Node node = nodeList.item(i); + if (node.getNodeType() == Node.ELEMENT_NODE) { + Element element = (Element) node; + String name = element.getAttribute("name"); + String dispname = element.getAttribute("dispname"); + String dt = element.getAttribute("dt"); // 带命名空间的属性 + String param = element.getAttribute("param"); // 带命名空间的属性 + String desc = element.getAttribute("desc"); + +// INSERT INTO `zk`.`sc_sys_params` (`id`, `group_id`, `name`, `param_value`, `extendeds`, `create_time`, `param_code`, `description`, `expected_results`, `verify_result`, `err_message`) +// +// VALUES (838, 2, '电池仓电池数据2', 'BSL:num_BatDataInfo2', '{\"dataType\":\"STRING\"}', '2025-08-22 17:30:13', 'BSL_BatDataInfo2', '软件初始安装时, 直接初始化', NULL, 0, NULL); + + System.out.println("INSERT INTO `zk`.`sc_sys_params` (`id`, `group_id`, `name`, `param_value`, `extendeds`, `create_time`, `param_code`, `description`, `expected_results`, `verify_result`, `err_message`) " + + "VALUES ("+(id+i)+", 2, '"+desc+"', '"+param.replaceAll("\\d+",":num")+"', '{\"dataType\":\""+dt+"\"}', now(), '"+dispname.replaceAll("\\d+","")+"', '软件初始安装时, 直接初始化', NULL, 0, NULL);"); + } + } + + // 这里可以添加对Document的进一步操作 + // 例如:获取节点、修改内容等 + } + + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * 读取TXT文件内容 + * @param filePath 文件路径 + * @return 文件内容字符串 + */ + private static String readTxtFile(String filePath) { + StringBuilder content = new StringBuilder(); + + try (BufferedReader br = new BufferedReader( + new InputStreamReader( + new FileInputStream(filePath), + StandardCharsets.UTF_8))) { + + String line; + while ((line = br.readLine()) != null) { + content.append(line).append("\n"); + } + + } catch (FileNotFoundException e) { + System.err.println("文件未找到: " + filePath); + e.printStackTrace(); + return null; + } catch (IOException e) { + System.err.println("读取文件时发生错误"); + e.printStackTrace(); + return null; + } + + return content.toString(); + } + + /** + * 将字符串内容解析为Document对象 + * @param content 包含XML的字符串 + * @return Document对象 + * @throws ParserConfigurationException 解析器配置异常 + * @throws IOException IO异常 + * @throws SAXException SAX解析异常 + */ + private static Document parseToDocument(String content) + throws ParserConfigurationException, IOException, SAXException { + + // 创建DocumentBuilderFactory + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + + // 创建DocumentBuilder + DocumentBuilder builder = factory.newDocumentBuilder(); + + // 将字符串内容转换为InputSource + InputSource inputSource = new InputSource(new StringReader(content)); + + // 解析为Document对象 + return builder.parse(inputSource); + } +} diff --git a/commoms/core/src/main/java/com/evotech/hd/utils/DateUtils.java b/commoms/core/src/main/java/com/evotech/hd/utils/DateUtils.java new file mode 100644 index 0000000..e075503 --- /dev/null +++ b/commoms/core/src/main/java/com/evotech/hd/utils/DateUtils.java @@ -0,0 +1,321 @@ +package com.evotech.hd.utils; + +import org.apache.commons.lang3.time.DateFormatUtils; + +import java.lang.management.ManagementFactory; +import java.math.BigDecimal; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.time.*; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; +import java.util.List; + +/** + * 时间工具类 + * + * @author evo + */ +public class DateUtils extends org.apache.commons.lang3.time.DateUtils +{ + public static String YYYY = "yyyy"; + + public static String YYYY_MM = "yyyy-MM"; + + public static String YYYY_MM_DD = "yyyy-MM-dd"; + + public static String YYYYMMDDHHMMSS = "yyyyMMddHHmmss"; + + public static String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss"; + + private static String[] parsePatterns = { + "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM", + "yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/MM", + "yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm", "yyyy.MM"}; + + /** + * 获取当前Date型日期 + * + * @return Date() 当前日期 + */ + public static Date getNowDate() + { + return new Date(); + } + + /** + * 获取当前日期, 默认格式为yyyy-MM-dd + * + * @return String + */ + public static String getDate() + { + return dateTimeNow(YYYY_MM_DD); + } + + public static final String getTime() + { + return dateTimeNow(YYYY_MM_DD_HH_MM_SS); + } + + public static final String dateTimeNow() + { + return dateTimeNow(YYYYMMDDHHMMSS); + } + + public static final String dateTimeNow(final String format) + { + return parseDateToStr(format, new Date()); + } + + public static final String dateTime(final Date date) + { + return parseDateToStr(YYYY_MM_DD, date); + } + + public static final String parseDateToStr(final String format, final Date date) + { + return new SimpleDateFormat(format).format(date); + } + + public static final Date dateTime(final String format, final String ts) + { + try + { + return new SimpleDateFormat(format).parse(ts); + } + catch (ParseException e) + { + throw new RuntimeException(e); + } + } + + /** + * 日期路径 即年/月/日 如2018/08/08 + */ + public static final String datePath() + { + Date now = new Date(); + return DateFormatUtils.format(now, "yyyy/MM/dd"); + } + + /** + * 日期路径 即年/月/日 如20180808 + */ + public static final String dateTime() + { + Date now = new Date(); + return DateFormatUtils.format(now, "yyyyMMdd"); + } + + /** + * 日期型字符串转化为日期 格式 + */ + public static Date parseDate(Object str) + { + if (str == null) + { + return null; + } + try + { + return parseDate(str.toString(), parsePatterns); + } + catch (ParseException e) + { + return null; + } + } + + /** + * 获取服务器启动时间 + */ + public static Date getServerStartDate() + { + long time = ManagementFactory.getRuntimeMXBean().getStartTime(); + return new Date(time); + } + + /** + * 计算相差天数 + */ + public static int differentDaysByMillisecond(Date date1, Date date2) + { + return Math.abs((int) ((date2.getTime() - date1.getTime()) / (1000 * 3600 * 24))); + } + + /** + * 计算时间差 + * + * @param endDate 最后时间 + * @param startTime 开始时间 + * @return 时间差(天/小时/分钟) + */ + public static String timeDistance(Date endDate, Date startTime) + { + long nd = 1000 * 24 * 60 * 60; + long nh = 1000 * 60 * 60; + long nm = 1000 * 60; + // long ns = 1000; + // 获得两个时间的毫秒时间差异 + long diff = endDate.getTime() - startTime.getTime(); + // 计算差多少天 + long day = diff / nd; + // 计算差多少小时 + long hour = diff % nd / nh; + // 计算差多少分钟 + long min = diff % nd % nh / nm; + // 计算差多少秒//输出结果 + // long sec = diff % nd % nh % nm / ns; + return day + "天" + hour + "小时" + min + "分钟"; + } + + /** + * 增加 LocalDateTime ==> Date + */ + public static Date toDate(LocalDateTime temporalAccessor) + { + ZonedDateTime zdt = temporalAccessor.atZone(ZoneId.systemDefault()); + return Date.from(zdt.toInstant()); + } + + /** + * 增加 LocalDate ==> Date + */ + public static Date toDate(LocalDate temporalAccessor) + { + LocalDateTime localDateTime = LocalDateTime.of(temporalAccessor, LocalTime.of(0, 0, 0)); + ZonedDateTime zdt = localDateTime.atZone(ZoneId.systemDefault()); + return Date.from(zdt.toInstant()); + } + + + public static Integer getBetweenYear(Date start, Date end, Integer offsetDay){ + Calendar cal1 = Calendar.getInstance(); + cal1.setTime(start); + Long days = new BigDecimal(getBetweenDays(start, end)).add(new BigDecimal(offsetDay)).longValue(); + int yearsDiff = 0; + long remainingDays = days; + while (remainingDays >= 365) { + int year = cal1.get(Calendar.YEAR) + yearsDiff; + //int daysInYear = ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) ? 366 : 365; + int daysInYear = 365; + if (remainingDays >= daysInYear) { + remainingDays -= daysInYear; + yearsDiff++; + } else { + break; + } + } + + return yearsDiff; + } + + public static Integer getBetweenYearByDays(Date start, Date end){ + return getBetweenYear(start, end, 0); + } + + public static Integer getBetweenMonth(Date start, Date end){ + Calendar currentDate = Calendar.getInstance(); + currentDate.setTime(start); + + // 将输入的Date转换为Calendar以便获取年月信息 + Calendar endCalendar = Calendar.getInstance(); + endCalendar.setTime(end); + // 计算年份差和月份差 + int yearsDiff = endCalendar.get(Calendar.YEAR) - currentDate.get(Calendar.YEAR); + int monthsDiff = endCalendar.get(Calendar.MONTH) - currentDate.get(Calendar.MONTH); + + // 调整月份差,考虑到年份差可能影响实际的月份数差异 + monthsDiff += yearsDiff * 12; + + return monthsDiff; + } + + public static Date getMonthFirst(Date date){ + Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + calendar.set(Calendar.DAY_OF_MONTH, 1); + return calendar.getTime(); + } + + public static Date getMonthEnd(Date date){ + Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + calendar.set(Calendar.DAY_OF_MONTH, 1); + calendar.add(Calendar.MONTH, 1); + calendar.add(Calendar.DAY_OF_MONTH, -1); + return calendar.getTime(); + } + + public static Integer getYear(Date date){ + Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + return calendar.get(Calendar.YEAR); + } + + public static Integer getMonth(Date date){ + Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + return calendar.get(Calendar.MONTH)+1; + } + + public static Integer getMonthDays(Date date){ + Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + calendar.set(Calendar.DAY_OF_MONTH,1); + return calendar.getActualMaximum(Calendar.DAY_OF_MONTH); + } + + public static Long getBetweenDays(Date start, Date end){ + Calendar currentDate = Calendar.getInstance(); + currentDate.setTime(start); + + // 将输入的Date转换为Calendar以便获取年月信息 + Calendar endCalendar = Calendar.getInstance(); + endCalendar.setTime(end); + return calculateDifferenceInDays(endCalendar, currentDate); + } + + public static Long getBetweenMinutes(Date start, Date end){ + Calendar currentDate = Calendar.getInstance(); + currentDate.setTime(start); + // 将输入的Date转换为Calendar以便获取年月信息 + Calendar endCalendar = Calendar.getInstance(); + endCalendar.setTime(end); + long diffInMillis = endCalendar.getTimeInMillis() - currentDate.getTimeInMillis(); + return diffInMillis / (60 * 1000); + } + + public static long calculateDifferenceInDays(Calendar date1, Calendar date2) { + long diffInMillies = Math.abs(date1.getTimeInMillis() - date2.getTimeInMillis()); + return diffInMillies / (24 * 60 * 60 * 1000); // 将毫秒转换为天 + } + + public static List getCurrentMonthAndDays(){ + return getMonthAndDays(DateUtils.getNowDate()); + } + public static List getMonthAndDays(Date date){ + List dayList = new ArrayList(); + Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + int days = calendar.getActualMaximum(Calendar.DAY_OF_MONTH); + SimpleDateFormat sdfm = new SimpleDateFormat("yyyy-MM"); + for(int p=1;p<=days;p++){ + try{ + String rq = sdfm.format(date)+"-"; + if(p < 10){ + rq += "0" + p; + }else { + rq += p; + } + dayList.add(rq); + }catch (Exception e){ + e.printStackTrace(); + } + } + return dayList; + } + +} diff --git a/commoms/core/src/main/java/com/evotech/hd/utils/EntityUtils.java b/commoms/core/src/main/java/com/evotech/hd/utils/EntityUtils.java new file mode 100644 index 0000000..b91bf9f --- /dev/null +++ b/commoms/core/src/main/java/com/evotech/hd/utils/EntityUtils.java @@ -0,0 +1,39 @@ +package com.evotech.hd.utils; + +import lombok.extern.slf4j.Slf4j; + +import java.lang.reflect.Field; +import java.util.Map; + +/** + * EntityUtils + * + * @author andy.shi + * @ClassName:EntityUtils + * @date: 2025年09月28日 8:49 + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@Slf4j +public class EntityUtils { + + public static Map converToMap(T t){ + Map map = Collections.emptyMap(); + try { + Class clazz = t.getClass(); + while (clazz != null){ + Field[] fields = clazz.getDeclaredFields(); + for (Field field : fields){ + field.setAccessible(true); + Object val = field.get(t); + map.put(field.getName(), val); + } + clazz = clazz.getSuperclass(); + } + } catch (Exception e) { + log.error("entity 转 Map 出现错误; entity信息为: {}, 错误信息为: {}", t.getClass().getName(), e.getMessage(), e); + e.printStackTrace(); + throw new RuntimeException(e); + } + return map; + } +} diff --git a/commoms/mongodb/pom.xml b/commoms/mongodb/pom.xml new file mode 100644 index 0000000..d68acf7 --- /dev/null +++ b/commoms/mongodb/pom.xml @@ -0,0 +1,30 @@ + + + 4.0.0 + + com.evotech.hd + commoms + 1.0-SNAPSHOT + + mongodb + + + + org.springframework.boot + spring-boot-starter-data-mongodb + + + org.springframework + spring-context + 5.3.10 + + + org.projectlombok + lombok + provided + + + + \ No newline at end of file diff --git a/commoms/mongodb/src/main/java/com/evotech/hd/config/AbstractMongoDbConfig.java b/commoms/mongodb/src/main/java/com/evotech/hd/config/AbstractMongoDbConfig.java new file mode 100644 index 0000000..5f9367a --- /dev/null +++ b/commoms/mongodb/src/main/java/com/evotech/hd/config/AbstractMongoDbConfig.java @@ -0,0 +1,105 @@ +package com.evotech.hd.config; + +import com.mongodb.ConnectionString; +import lombok.Data; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.mongodb.MongoDatabaseFactory; +import org.springframework.data.mongodb.core.MongoTemplate; +import org.springframework.data.mongodb.core.SimpleMongoClientDatabaseFactory; +import org.springframework.util.StringUtils; + +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; + + +/** + * mongodb基础连接类 + * + * @ClassName:AbstractMongoDbConfig + * @date: 2025年08月12日 + * @author: andy.shi + * @contact: 17330188597 + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@Data +@Configuration +public abstract class AbstractMongoDbConfig{ + + // MongoDB Properties + @Value("${yt.base_properties.uri:}") + private String uri; + @Value("${yt.base_properties.user_name:''}") + private String userName; + @Value("${yt.base_properties.password:''}") + private String password; + @Value("${yt.base_properties.host:''}") + private String host; + @Value("${yt.base_properties.authentication-database:''}") + private String authenticationDataBase; + private String dataBase; + @Value("${yt.base_properties.port:''}") + private Integer port; + + + public MongoDatabaseFactory mongoDbFactory(){ + //String connectionString = "mongodb://" + userName + ":" + password+ "@"+ host+":"+port+"/" + dataBase+"?authSource="+authenticationDataBase; + return new SimpleMongoClientDatabaseFactory(getConnectionString()); + } + + private ConnectionString getConnectionString(){ + if (StringUtils.hasText(uri)) { + return new ConnectionString(uri); + } + StringBuilder builder = new StringBuilder("mongodb://"); + if (StringUtils.hasText(userName)) { + builder.append(encode(userName)); + builder.append(":"); + if (StringUtils.hasText(password)) { + builder.append(encode(password)); + } + builder.append("@"); + } + builder.append((StringUtils.hasText(host)) ? host : "localhost"); + if (port != null) { + builder.append(":"); + builder.append(port); + } + //集群多地址配置, 具体参考 propertiesMongoConnectionDetails +// if (this.properties.getAdditionalHosts() != null) { +// builder.append(","); +// builder.append(String.join(",", this.properties.getAdditionalHosts())); +// } + builder.append("/"); + builder.append(dataBase); + List options = getOptions(); + if (!options.isEmpty()) { + builder.append("?"); + builder.append(String.join("&", options)); + } + return new ConnectionString(builder.toString()); + } + + private String encode(String input) { + return URLEncoder.encode(input, StandardCharsets.UTF_8); + } + + private List getOptions() { + List options = new ArrayList<>(); + //集群,名称. 具体参考 propertiesMongoConnectionDetails +// if (this.properties.getReplicaSetName() != null) { +// options.add("replicaSet=" + this.properties.getReplicaSetName()); +// } + if (StringUtils.hasText(userName) && StringUtils.hasText(authenticationDataBase)) { + options.add("authSource=" + authenticationDataBase); + } + return options; + } + + + abstract public MongoTemplate getMongoTemplate() throws Exception; + + +} diff --git a/commoms/mongodb/src/main/java/com/evotech/hd/config/ElectricityMeterBoxProperties.java b/commoms/mongodb/src/main/java/com/evotech/hd/config/ElectricityMeterBoxProperties.java new file mode 100644 index 0000000..d1f4973 --- /dev/null +++ b/commoms/mongodb/src/main/java/com/evotech/hd/config/ElectricityMeterBoxProperties.java @@ -0,0 +1,30 @@ +package com.evotech.hd.config; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.mongodb.core.MongoTemplate; + + +/** + * 电表箱类 + * + * @ClassName:BatteryInfoProperties + * @date: 2025年04月29日 16:32 + * @author: andy.shi + * @contact: 17330188597 + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ + +@Configuration +@ConfigurationProperties(prefix = "yt.electricity-meter-box", ignoreInvalidFields = true) +public class ElectricityMeterBoxProperties extends AbstractMongoDbConfig { + + @Bean("mongoTemplateBatteryBox") + @Override + public MongoTemplate getMongoTemplate() throws Exception { + return new MongoTemplate(mongoDbFactory()); + } + + +} diff --git a/commoms/mongodb/src/main/java/com/evotech/hd/constant/MongoConstant.java b/commoms/mongodb/src/main/java/com/evotech/hd/constant/MongoConstant.java new file mode 100644 index 0000000..1def0b6 --- /dev/null +++ b/commoms/mongodb/src/main/java/com/evotech/hd/constant/MongoConstant.java @@ -0,0 +1,48 @@ +package com.evotech.hd.constant; + +/** + * MongoDB的静态类 + * @ClassName:MongoConstant + * @date: 2025年04月29日 9:21 + * @author: andy.shi + * @contact: 17330188597 + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ + +public class MongoConstant { + + //电表箱子信息数据库 + public static final String ELECTRICITY_METER_BOX="electricity_meter_box"; + + public static final String SEPARATION = "_"; + /*** + * 或者查询, 示例 + * put(OR+"条件名称"+SEPARATION+"条件名称"...,"条件值"+SEPARATION+"条件值"....) + */ + public static final String OR = "or"+SEPARATION; + /*** + * 条件查询, 示例 + * put(查询类型+"条件名称","条件值") + */ + public static final String EQ = "and"; + public static final String IN = "in"; + public static final String NOT_IN = "not_in"; + public static final String GT = "gt"; + public static final String GE = "ge"; + public static final String LT = "lt"; + public static final String LE = "le"; + public static final String REGEX = "regex"; + /*** + * 排序, 示例 + * put(SORT+SORT_ASC,"条件名称") + */ + public static final String SORT = "Sort"; + public static final String SORT_ASC ="asc"; + public static final String SORT_DESC = "desc"; + + /*** + * 排序, 示例 + * put(PAGE,"当前页"+SEPARATION+"每页显示的条数") + */ + public static final String PAGE = "page"; +} diff --git a/commoms/mongodb/src/main/java/com/evotech/hd/documnet/BaseDocumentIdEntity.java b/commoms/mongodb/src/main/java/com/evotech/hd/documnet/BaseDocumentIdEntity.java new file mode 100644 index 0000000..d9029b4 --- /dev/null +++ b/commoms/mongodb/src/main/java/com/evotech/hd/documnet/BaseDocumentIdEntity.java @@ -0,0 +1,41 @@ +package com.evotech.hd.documnet; + +import lombok.Data; +import org.springframework.data.annotation.Id; +import org.springframework.data.mongodb.core.mapping.Document; + +/** + * mongo基础类 + * + * @ClassName:BaseDocumentIdEntity + * @date: 2025年08月18日 13:45 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@Data +@Document +public class BaseDocumentIdEntity { + /*** + * 时间戳做id + */ + @Id + String id; + /*** + * 站端编号 + */ + String stationCode; + + /*** + * 读取信息 + */ + String value; + + public BaseDocumentIdEntity() { + } + + public BaseDocumentIdEntity(String stationCode, String value) { + this.id = String.valueOf(System.currentTimeMillis()); + this.stationCode = stationCode; + this.value = value; + } +} diff --git a/commoms/mongodb/src/main/java/com/evotech/hd/documnet/BatteryInfo.java b/commoms/mongodb/src/main/java/com/evotech/hd/documnet/BatteryInfo.java new file mode 100644 index 0000000..4235d6d --- /dev/null +++ b/commoms/mongodb/src/main/java/com/evotech/hd/documnet/BatteryInfo.java @@ -0,0 +1,52 @@ +package com.evotech.hd.documnet; + +import lombok.Data; +import org.springframework.data.mongodb.core.mapping.Document; + +/** + * 电池实时信息 + * @ClassName:BatData + * @date: 2025年08月18日 9:13 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@Data +@Document +public class BatteryInfo extends BaseDocumentIdEntity{ + /*** + * 电池编号 + */ + String batteryCode; + /*** + * 电池仓编号 + */ + String batteryCompartmentNo; + + /*** + * value 内容参考 + * 电池电量 SOC + * 电池健康度 SOH + * 电池额定容量 batteryRatedCapacity + * 电池单体数量 batteryMonomerNum array[] + * 温度探针数量 temperatureProbeNum array[] + * 电池总电流 batteryTotalCurrent + * 电池总电压 batteryTotalVoltage + * 正极绝缘电阻 positivePoleInsulationResistance + * 负极绝缘电阻 negativePoleInsulationResistance + * 电池请求电压 batteryRequestVoltage + * 电池请求电流 batteryRequestCurrent + * 充电模块输出电压 chargingModuleOutputVoltage + * 充电模块输出电流 chargingModuleOutputCurrent + * 最高允许充电电压 maximumAllowableChargingVoltage + * 最高允许充电电流 maximumAllowableChargingCurrent + */ + + public BatteryInfo() { + } + + public BatteryInfo(String batteryCode, String batteryCompartmentNo, String stationCode, String value) { + super(stationCode,value); + this.batteryCode = batteryCode; + this.batteryCompartmentNo = batteryCompartmentNo; + } +} diff --git a/commoms/mongodb/src/main/java/com/evotech/hd/exception/MongoDBException.java b/commoms/mongodb/src/main/java/com/evotech/hd/exception/MongoDBException.java new file mode 100644 index 0000000..5c3c027 --- /dev/null +++ b/commoms/mongodb/src/main/java/com/evotech/hd/exception/MongoDBException.java @@ -0,0 +1,30 @@ +package com.evotech.hd.exception; + +import lombok.extern.slf4j.Slf4j; + +/** + * 类 + * + * @ClassName:MongoDBException + * @date: 2025年04月30日 8:56 + * @author: andy.shi + * @contact: 17330188597 + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@Slf4j +public class MongoDBException extends Exception{ + + public MongoDBException() { + super(); + } + + public MongoDBException(Exception e) { + super(e); + log.error("mongodb 数据库存储异常============{}", e.getMessage()); + } + + public MongoDBException(String message) { + super(message); + log.error("mongodb 数据库存储异常============{}", message); + } +} diff --git a/commoms/mongodb/src/main/java/com/evotech/hd/service/MongoDBService.java b/commoms/mongodb/src/main/java/com/evotech/hd/service/MongoDBService.java new file mode 100644 index 0000000..a2a753c --- /dev/null +++ b/commoms/mongodb/src/main/java/com/evotech/hd/service/MongoDBService.java @@ -0,0 +1,217 @@ +package com.evotech.hd.service; + +import com.evotech.hd.constant.MongoConstant; +import com.evotech.hd.exception.MongoDBException; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Sort; +import org.springframework.data.mongodb.core.MongoTemplate; +import org.springframework.data.mongodb.core.query.Criteria; +import org.springframework.data.mongodb.core.query.Query; +import org.springframework.stereotype.Service; +import org.springframework.util.Assert; +import org.springframework.util.CollectionUtils; +import org.springframework.util.StringUtils; + +import javax.annotation.PostConstruct; +import javax.annotation.Resource; +import java.util.*; +import java.util.regex.Pattern; + +/** + * 类 + * + * @ClassName:MongoDBService + * @date: 2025年04月29日 8:45 + * @author: andy.shi + * @contact: 17330188597 + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@Service +public class MongoDBService { + + @Resource + private List mongoTemplates; + + private Map mongoTemplateMap = new LinkedHashMap<>(); + @PostConstruct + private void init(){ + if(!CollectionUtils.isEmpty(mongoTemplates)){ + for (MongoTemplate mongoTemplate: mongoTemplates){ + mongoTemplateMap.put(mongoTemplate.getDb().getName(),mongoTemplate); + } + } + } + + /*** + * 单个对象保存 + * @param collectionName 保存的集合名称 + * @param document 保存的文档数据 + * @return + * @param + */ + public T save(String database, String collectionName, T document) { + + MongoTemplate mongoTemplate = null; + try { + mongoTemplate = getMongoTemplate(database, collectionName); + } catch (MongoDBException e) { + throw new RuntimeException(e); + } + if(!mongoTemplate.collectionExists(collectionName)){ + mongoTemplate.createCollection(collectionName); + } + return mongoTemplate.save(document, collectionName); + } + + private MongoTemplate getMongoTemplate(String database, String collectionName) throws MongoDBException { + Assert.hasText(database, "database name must not be null or empty"); + Assert.hasText(collectionName, "Collection name must not be null or empty"); + if(!StringUtils.hasText(database) || !StringUtils.hasText(database)){ + throw new MongoDBException("请求参数为空"); + } + return mongoTemplateMap.get(database); + } + + /*** + * 批量保存 + * @param collectionName 保存的集合名称 + * @param batchToSave 保存的文档数据集合 + * @return + * @param + */ + public Collection saveBatch(String database, String collectionName, Collection batchToSave){ + MongoTemplate mongoTemplate = null; + try { + mongoTemplate = getMongoTemplate(database, collectionName); + } catch (MongoDBException e) { + throw new RuntimeException(e); + } + if(!mongoTemplate.collectionExists(collectionName)){ + mongoTemplate.createCollection(collectionName); + } + return mongoTemplate.insert(batchToSave, collectionName); + } + + public List find(String database, String collectionName, Class entityClass, Map params) { + MongoTemplate mongoTemplate = null; + try { + mongoTemplate = getMongoTemplate(database, collectionName); + } catch (MongoDBException e) { + throw new RuntimeException(e); + } + return mongoTemplate.find(buildQuery(params), entityClass, collectionName); + } + + public Page findPage(String database, String collectionName, Class entityClass, Map params) { + MongoTemplate mongoTemplate = null; + try { + mongoTemplate = getMongoTemplate(database, collectionName); + } catch (MongoDBException e) { + throw new RuntimeException(e); + } + Query query = buildQuery(params); + //计算总数 + long total = mongoTemplate.count(query, entityClass, collectionName); + List list = mongoTemplate.find(query, entityClass, collectionName); + String pageInfo = String.valueOf(params.get(MongoConstant.PAGE)); + String[] pages = pageInfo.split(MongoConstant.SEPARATION); + return new PageImpl(list, PageRequest.of(Integer.valueOf(pages[0]), Integer.valueOf(pages[1])), total); + } + + +// /*** +// * 单个对象保存 +// * @param collectionName 保存的集合名称 +// * @param document 保存的文档数据 +// * @return +// * @param +// */ +// public T save(String collectionName, T document){ +// Assert.hasText(collectionName, "Collection name must not be null or empty"); +// if(!mongoTemplate.collectionExists(collectionName)){ +// mongoTemplate.createCollection(collectionName); +// } +// return mongoTemplate.save(document, collectionName); +// } +// +// /*** +// * 批量保存 +// * @param collectionName 保存的集合名称 +// * @param batchToSave 保存的文档数据集合 +// * @return +// * @param +// */ +// public Collection saveBatch(String collectionName, Collection batchToSave){ +// Assert.hasText(collectionName, "Collection name must not be null or empty"); +// if(!mongoTemplate.collectionExists(collectionName)){ +// mongoTemplate.createCollection(collectionName); +// } +// return mongoTemplate.insert(batchToSave, collectionName); +// } +// +// public List find(String collectionName, Class entityClass, Map params) { +// return mongoTemplate.find(buildQuery(params), entityClass, collectionName); +// } +// +// public Page findPage(String collectionName, Class entityClass, Map params) { +// Query query = buildQuery(params); +// //计算总数 +// long total = mongoTemplate.count(query, entityClass, collectionName); +// List list = mongoTemplate.find(query, entityClass, collectionName); +// String pageInfo = String.valueOf(params.get(MongoConstant.PAGE)); +// String[] pages = pageInfo.split(MongoConstant.SEPARATION); +// return new PageImpl(list, PageRequest.of(Integer.valueOf(pages[0]), Integer.valueOf(pages[1])), total); +// } + + + private static Query buildQuery(Map params){ + Query query = new Query(); + if(!CollectionUtils.isEmpty(params)){ + //解析参数中的key + Iterator itKey = params.keySet().iterator(); + while (itKey.hasNext()){ + String key = itKey.next(); + //解析查询类型 + String[] keyAndQueryType = key.split(MongoConstant.SEPARATION); + //根据类型做查询 + switch (keyAndQueryType[0]){ + case MongoConstant.EQ: + query.addCriteria(Criteria.where(keyAndQueryType[1]).is(params.get(key))); + break; + case MongoConstant.IN: + query.addCriteria(Criteria.where(keyAndQueryType[1]).in(params.get(key))); + break; + case MongoConstant.NOT_IN: + query.addCriteria(Criteria.where(keyAndQueryType[1]).nin(params.get(key))); + break; + case MongoConstant.GT: + query.addCriteria(Criteria.where(keyAndQueryType[1]).gt(params.get(key))); + break; + case MongoConstant.GE: + query.addCriteria(Criteria.where(keyAndQueryType[1]).gte(params.get(key))); + break; + case MongoConstant.LT: + query.addCriteria(Criteria.where(keyAndQueryType[1]).lt(params.get(key))); + break; + case MongoConstant.LE: + query.addCriteria(Criteria.where(keyAndQueryType[1]).lte(params.get(key))); + break; + case MongoConstant.REGEX: + Pattern pattern = Pattern.compile(String.valueOf(params.get(key))); + query.addCriteria(Criteria.where(keyAndQueryType[1]).regex(pattern)); + break; + case MongoConstant.SORT: + query = query.with(Sort.by((MongoConstant.SORT_ASC.equals(keyAndQueryType[1]) ? Sort.Direction.ASC : Sort.Direction.DESC), (String) params.get(key))); + break; + default: + break; + } + + } + } + return query; + } + +} diff --git a/commoms/mongodb/src/main/resources/application-mongo.yml b/commoms/mongodb/src/main/resources/application-mongo.yml new file mode 100644 index 0000000..5582bea --- /dev/null +++ b/commoms/mongodb/src/main/resources/application-mongo.yml @@ -0,0 +1,11 @@ +yt: + base_properties: + host: localhost + port: 27017 + #用于账号密码验证的库 + authentication-database: admin + user_name: root + password: + electricity-meter-box: + #//数据存储的库 + data_base: electricity_meter_box diff --git a/commoms/mongodb/src/main/resources/application-mongoDev.yml b/commoms/mongodb/src/main/resources/application-mongoDev.yml new file mode 100644 index 0000000..6cf4e10 --- /dev/null +++ b/commoms/mongodb/src/main/resources/application-mongoDev.yml @@ -0,0 +1,11 @@ +yt: + base_properties: + host: 192.168.16.128 + port: 27017 + #用于账号密码验证的库 + authentication-database: admin + user_name: root + password: ythd2025 + electricity-meter-box: + #//数据存储的库 + data_base: electricity_meter_box diff --git a/commoms/pom.xml b/commoms/pom.xml new file mode 100644 index 0000000..d17d509 --- /dev/null +++ b/commoms/pom.xml @@ -0,0 +1,19 @@ + + + 4.0.0 + + com.evotech.hd + station_control + 1.0-SNAPSHOT + + + commoms + pom + + core + mongodb + + + \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..2687c07 --- /dev/null +++ b/pom.xml @@ -0,0 +1,37 @@ + + + 4.0.0 + + + org.springframework.boot + spring-boot-starter-parent + 2.5.5 + + + + com.evotech.hd + station_control + 1.0-SNAPSHOT + pom + + commoms + web-server + + + + 17 + 17 + UTF-8 + 17 + 1.2.23 + 2.0.43 + + + + + + + + \ No newline at end of file diff --git a/web-server/.gitignore b/web-server/.gitignore new file mode 100644 index 0000000..667aaef --- /dev/null +++ b/web-server/.gitignore @@ -0,0 +1,33 @@ +HELP.md +target/ +.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ diff --git a/web-server/pom.xml b/web-server/pom.xml new file mode 100644 index 0000000..146f58b --- /dev/null +++ b/web-server/pom.xml @@ -0,0 +1,268 @@ + + + 4.0.0 + + + + + + + + com.evotech.hd + station_control + 1.0-SNAPSHOT + + + com.evotech.hd + web-server + 0.0.1-SNAPSHOT + web-server + web-server + + + + + + + + + + + + + + + + + + + + + com.evotech.hd + core + 1.0-SNAPSHOT + + + com.evotech.hd + mongodb + 1.0-SNAPSHOT + + + org.springframework.boot + spring-boot-starter-tomcat + + + org.springframework.boot + spring-boot-starter + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-test + test + + + org.springframework.boot + spring-boot-starter-security + + + org.mybatis.spring.boot + mybatis-spring-boot-starter + 2.1.3 + + + org.springframework.boot + spring-boot-starter-cache + + + org.springframework.boot + spring-boot-starter-data-redis + + + org.springframework.boot + spring-boot-starter-websocket + + + org.apache.xmlrpc + xmlrpc-client + 3.1.3 + + + org.apache.xmlrpc + xmlrpc-common + 3.1.3 + + + commons-httpclient + commons-httpclient + 3.1 + + + + + + + + org.springframework.boot + spring-boot-starter-quartz + + + + + + + + com.alibaba + druid-spring-boot-starter + ${druid.version} + + + + mysql + mysql-connector-java + 8.0.33 + runtime + + + org.springframework + spring-jdbc + 5.3.33 + + + + com.alibaba.fastjson2 + fastjson2 + ${fastjson.version} + + + + javax.servlet + javax.servlet-api + 3.1.0 + + + + + org.apache.commons + commons-pool2 + 2.11.1 + + + + cn.hutool + hutool-all + 5.7.13 + + + com.auth0 + java-jwt + 3.4.0 + + + + + io.springfox + springfox-swagger2 + 2.9.2 + + + io.springfox + springfox-swagger-ui + 2.9.2 + + + com.github.xiaoymin + swagger-bootstrap-ui + 1.9.3 + + + + + + org.assertj + assertj-core + + + + + com.google.guava + guava + 29.0-jre + + + + org.aspectj + aspectjweaver + 1.9.22.1 + + + + com.apache.commons + commons-beanutils2 + 2.0.0-M2 + system + ${project.basedir}/src/main/resources/lib/commons-beanutils2-2.0.0-M2.jar + + + org.apache.httpcomponents + httpclient + 4.5.14 + + + commons-httpclient + commons-httpclient + 3.1 + + + org.springframework + spring-context-support + 6.1.12 + + + org.eclipse.paho + org.eclipse.paho.client.mqttv3 + 1.2.5 + + + org.apache.commons + commons-collections4 + 4.4 + + + com.squareup.okhttp3 + okhttp + 4.12.0 + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.11.0 + + + + org.projectlombok + lombok + 1.18.38 + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + + diff --git a/web-server/src/main/java/com/evotech/hd/webserver/WebServerApplication.java b/web-server/src/main/java/com/evotech/hd/webserver/WebServerApplication.java new file mode 100644 index 0000000..af5c2f1 --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/WebServerApplication.java @@ -0,0 +1,24 @@ +package com.evotech.hd.webserver; + +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration; +import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; +import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.scheduling.annotation.EnableScheduling; + +@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class, MongoAutoConfiguration.class, MongoDataAutoConfiguration.class}) +@EnableScheduling +@ComponentScan("com.evotech.hd.**.**") +@MapperScan({"com.evotech.hd.**.**.mapper.**.**"}) +@EnableCaching +public class WebServerApplication { + + public static void main(String[] args) { + SpringApplication.run(WebServerApplication.class, args); + } + +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/cnstant/CommonConstants.java b/web-server/src/main/java/com/evotech/hd/webserver/cnstant/CommonConstants.java new file mode 100644 index 0000000..76f2ed6 --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/cnstant/CommonConstants.java @@ -0,0 +1,25 @@ +package com.evotech.hd.webserver.cnstant; + +public interface CommonConstants { + + + /** + * 已删除/正常数据 + */ + Integer DELETED = 1; + Integer NOT_DELETED = 0; + + /** + * 是/否 + */ + String YES = "1"; + String NO = "0"; + + + /** + * 显示/隐藏 + */ + String SHOW = "1"; + String HIDE = "0"; + +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/cnstant/InstructionConstants.java b/web-server/src/main/java/com/evotech/hd/webserver/cnstant/InstructionConstants.java new file mode 100644 index 0000000..f9c7a43 --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/cnstant/InstructionConstants.java @@ -0,0 +1,149 @@ +package com.evotech.hd.webserver.cnstant; + +/** + * 指令常量类 + * + * @ClassName:InstructionConstants + * @date: 2025年08月26日 17:09 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ + +public class InstructionConstants { + + /*** + * 打开卷帘门 + */ + public static final String OPEN_ROLLING = "open_rolling_door"; + /*** + * 关闭卷帘门 + */ + public static final String CLOSE_ROLLING = "close_rolling_door"; + /*** + * 启动对中机构 + */ + public static final String START_CORRECT_INSTITUTION = "start_correct_institution"; + /*** + * 关闭对中机构 + */ + public static final String CLOSE_CORRECT_INSTITUTION = "close_correct_institution"; + /*** + * 打开开合门 + */ + public static final String CEN_CMD_OPEN_DOOR = "cen_cmd_open_door"; + /*** + * 关闭开合门 + */ + public static final String CEN_CMD_CLOSE_DOOR = "cen_cmd_close_door"; + + + /*** + * 拆旧电池 + */ + public static final String SPLIT_OLD_BATTERY = "remove_old_battery"; + /*** + * 存旧电池 + */ + public static final String PUT_OLD_BATTERY = "put_old_battery"; + /*** + * 取新电池 + */ + public static final String TAKE_NEW_BATTERY = "take_new_battery"; + /*** + * 装新电池 + */ + public static final String LOADING_NEW_BATTERY = "loading_new_battery"; + /*** + * RGV归位 + */ + public static final String RGV_RESET_TO_ZERO = "rgv_reset_to_zero"; + /*** + * RGV停止移动 + */ + public static final String RGV_STOP_MOVE = "rgv_stop_move"; + /*** + * RGV重置 + */ + public static final String RGV_RESET = "rgv_reset"; + /*** + * 开始充电 + */ + public static final String START_CHARGING = "start_charging"; + /*** + * 结束充电 + */ + public static final String STOP_CHARGING = "stop_charging"; + + /*** + * BMS上电 + */ + public static final String BMS_POWER_ON = "bms_power_on"; + /*** + * BMS下电 + */ + public static final String BMS_POWER_OFF = "bms_power_off"; + /*** + * 空调打开 + */ + public static final String AIR_DEVICE_ON = "air_device_on"; + /*** + * 空调关闭 + */ + public static final String AIR_DEVICE_OFF = "air_device_off"; + /*** + * 空调自检打开 + */ + public static final String AIR_SELF_CHECK_ON = "air_self_check_on"; + /*** + * 空调自检关闭 + */ + public static final String AIR_SELF_CHECK_OFF = "air_self_check_off"; + /*** + * 轴流风机打开 + */ + public static final String AXIAL_FLOW_FAN_ON = "axial_flow_fan_on"; + /*** + * 轴流风机关闭 + */ + public static final String AXIAL_FLOW_FAN_OFF = "axial_flow_fan_off"; + /*** + * 内风机待机开启温度 + */ + public final static String AIR_CON_INT_FAN_START_TEMP = "air_con_int_fan_start_temp"; + /*** + * 内风机待机停止回差温度 + */ + public final static String AIR_CON_INT_FAN_STOP_TEMP = "air_con_int_fan_stop_temp"; + /*** + * 压缩机启动温度 + */ + public final static String AIR_CON_CMP_START_TEMP = "air_con_cmp_start_temp"; + /*** + * 压缩机停止回差值 + */ + public final static String AIR_CON_CMP_STOP_TEMP = "air_con_cmp_stop_temp"; + /*** + * 加热器启动温度 + */ + public final static String AIR_CON_HEAT_START_TEMP = "air_con_heat_start_temp"; + /*** + * 加热器停止回差值 + */ + public final static String AIR_CON_HEAT_STOP_TEMP = "air_con_heat_stop_temp"; + /*** + * 柜内高温告警温度值 + */ + public final static String AIR_CON_OVER_ALM_TEMP = "air_con_over_alm_temp"; + /*** + * 柜内低温告警温度值 + */ + public final static String AIR_CON_LOW_ALM_TEMP = "air_con_low_alm_temp"; + /*** + * 急停信号 + */ + public static final String EMERGENCY_BIN = "emergency_bin"; + /*** + * 消防动作 + */ + public static final String FIRE_FIGHTING_ACTION = "fire_fighting_action"; +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/cnstant/ParamSysConstants.java b/web-server/src/main/java/com/evotech/hd/webserver/cnstant/ParamSysConstants.java new file mode 100644 index 0000000..f13858b --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/cnstant/ParamSysConstants.java @@ -0,0 +1,194 @@ +package com.evotech.hd.webserver.cnstant; + +/** + * 参数常量类 + * + * @ClassName:ParamSysConstants + * @date: 2025年09月05日 14:59 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ + +public class ParamSysConstants { + + /*** + * 检查电池仓是否存在电池 + */ + public final static String EXISTS_BATTERY = "BSL_BatExist"; + /*** + * 设置最大功率 + */ + public final static String BSL_CHG_POW_CFG = "BSL_ChgPowCfg"; + /*** + * 检查空调是否连接 + */ + public final static String AIR_DEVICE_CONNECT = "AIR_CON_DevConnect"; + /*** + * 检查空调运行状态 + */ + public final static String AIR_DEVICE_RUNNING = "AIR_CON_RUN"; + /*** + * 空调自检状态 + */ + public final static String AIR_DEVICE_SELF_CHECK = "AIR_CON_SELFCHECK"; + /*** + * 轴流风机状态 + */ + public final static String AXIAL_FAN = "AxialFAN"; + /*** + * 内风机运行状态 + */ + public final static String AIR_CON_INT_FAN = "AIR_CON_INT_FAN"; + /*** + * 内风机告警 + */ + public final static String AIR_CON_INT_FAN_ALM = "AIR_CON_INT_FAN_ALM"; + /*** + * 内风机电流 + */ + public final static String AIR_CON_INT_FAN_CUR = "AIR_CON_INT_FAN_CUR"; + /*** + * 内风机待机开启温度 + */ + public final static String AIR_CON_INT_FAN_START_TEMP = "AIR_CON_INT_FAN_START_TEMP"; + /*** + * 内风机待机停止回差温度 + */ + public final static String AIR_CON_INT_FAN_STOP_TEMP = "AIR_CON_INT_FAN_STOP_TEMP"; + /*** + * 外风机运行状态 + */ + public final static String AIR_CON_EXT_FAN = "AIR_CON_EXT_FAN"; + /*** + * 内风机告警 + */ + public final static String AIR_CON_EXT_FAN_ALM = "AIR_CON_EXT_FAN_ALM"; + /*** + * 内风机电流 + */ + public final static String AIR_CON_EXT_FAN_CUR = "AIR_CON_EXT_FAN_CUR"; + + /*** + * 压缩机运行状态 + */ + public final static String AIR_CON_CMP_STATE = "AIR_CON_CMP_STATE"; + /*** + * 压缩机欠流故障 + */ + public final static String AIR_CON_CMP_LOW_CUR_ERROR = "AIR_CON_CMP_LOW_CUR_ERROR"; + /*** + * 压缩机欠流故障 + */ + public final static String AIR_CON_CMP_OVER_LOAD_ERROR = "AIR_CON_CMP_OVER_LOAD_ERROR"; + /*** + * 压缩机启动温度 + */ + public final static String AIR_CON_CMP_START_TEMP = "AIR_CON_CMP_START_TEMP"; + /*** + * 压缩机停止回差值 + */ + public final static String AIR_CON_CMP_STOP_TEMP = "AIR_CON_CMP_STOP_TEMP"; + /*** + * 压缩机或加热器电流A + */ + public final static String AIR_CON_CMP_HEAT_CUR = "AIR_CON_CMP_HEAT_CUR"; + + /*** + * 柜内温度传感器故障 + */ + public final static String AIR_CON_INT_TEMP_SENSOR_ERROR = "AIR_CON_INT_TEMPSENSOR_ERROR"; + /*** + * 柜内湿度传感器故障 + */ + public final static String AIR_CON_INT_HUM_SENSOR_ERROR = "AIR_CON_INT_HUMSENSOR_ERROR"; + /*** + * 柜内高温告警 + */ + public final static String AIR_CON_INT_OVER_TEMP_ALM = "AIR_CON_INT_OVER_TEMP_ALM"; + /*** + * 柜内低温告警 + */ + public final static String AIR_CON_INT_LOW_TEMP_ALM = "AIR_CON_INT_LOW_TEMP_ALM"; + /*** + * 柜内回风温度 + */ + public final static String AIR_CON_INT_TEMP = "AIR_CON_INT_TEMP"; + /*** + * 柜内湿度 + */ + public final static String AIR_CON_INT_HUM = "AIR_CON_INT_HUM"; + /*** + * 柜内温度变送电流值 + */ + public final static String AIR_CON_INT_TEMP_SND_CUR = "AIR_CON_INT_TEMP_SND_CUR"; + /*** + * 柜内湿度变送电流值 + */ + public final static String AIR_CON_INT_HUM_SND_CUR = "AIR_CON_INT_HUM_SND_CUR"; + + /*** + * 柜内高温告警温度值 + */ + public final static String AIR_CON_OVER_ALM_TEMP = "AIR_CON_OVER_ALM_TEMP"; + /*** + * 柜内低温告警温度值 + */ + public final static String AIR_CON_LOW_ALM_TEMP = "AIR_CON_LOW_ALM_TEMP"; + /*** + * 柜外温度传感器故障 + */ + public final static String AIR_CON_EXT_TEMP_SENSOR_ERROR = "AIR_CON_EXT_TEMPSENSOR_ERROR"; + /*** + * 柜外环境温度 + */ + public final static String AIR_CON_EXT_TEMP = "AIR_CON_EXT_TEMP"; + /*** + * 外部故障告警 + */ + public final static String AIR_CON_EXT_ERROR_ALM = "AIR_CON_EXT_ERROR_ALM"; + /*** + * 冷凝盘管温度传感器故障 + */ + public final static String AIR_CON_COND_TEMP_SENSOR_ERROR = "AIR_CON_COND_TEMPSENSOR_ERROR"; + /*** + * 系统供电电压 + */ + public final static String AIR_CON_SRC_VOL = "AIR_CON_SRC_VOL"; + /*** + * 电源电压超限告警 + */ + public final static String AIR_CON_SRC_VOL_OVER_ALM = "AIR_CON_SRC_VOL_OVER_ALM"; + /*** + * 电箱温湿度传感器连接状态 + */ + public final static String HT_SENSOR_DEV_CONNECT = "HTSensor_DevConnect"; + /*** + * 电箱内湿度值 + */ + public final static String HT_SENSOR_HUMIDITY = "HTSensor_Humidity"; + /*** + * 电箱内温度值 + */ + public final static String HT_SENSOR_TEMPERATURE = "HTSensor_Temperature"; + /*** + * 交流电表是否连接 + */ + public final static String ENERGY_METER_DEV_CONNECT = "EnergyMeter_DevConnect"; + /*** + * 交流电表正向有功总电能 + */ + public final static String ENERGY_METER_EP_IMP = "EnergyMeter_EP_imp"; + + /*** + * 是否存在电池 + */ + public final static String BSL_BAT_EXIST = "BSL_BatExist"; + /*** + * 开合门开启状态 1为开 0为关 + */ + public final static String CEN_RB_DOOR_OPEN_STATUS = "CEN_RB_DoorOpen"; + /*** + * 开合门关闭状态 1关 0开 + */ + public final static String CEN_RB_DOOR_CLOSE_STATUS = "CEN_RB_DoorClose"; +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/config/AppInit.java b/web-server/src/main/java/com/evotech/hd/webserver/config/AppInit.java new file mode 100644 index 0000000..5c32a01 --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/config/AppInit.java @@ -0,0 +1,31 @@ +/** + * Copyright © 2021-2026 space All rights reserved. + */ +package com.evotech.hd.webserver.config; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.CommandLineRunner; +import org.springframework.stereotype.Component; + +import java.net.InetAddress; + +@Component +public class AppInit implements CommandLineRunner { + @Value("${server.port}") + private String port; + + @Value("${server.servlet.context-path}") + private String path; + + @Override + public void run(String... args) throws Exception { + System.out.println(">>>>>>>>>>>>>>>jeeplus 启动成功<<<<<<<<<<<<<"); + String ip = InetAddress.getLocalHost().getHostAddress(); + System.out.println("Jeeplus Application running at:\n\t" + + "- Local: http://localhost:" + port + path + "/\n\t" + + "- Network: http://" + ip + ":" + port + path + "/\n\t" + + "- swagger: http://" + ip + ":" + port + path + "/doc.html\n" + + "----------------------------------------------------------"); + } +} + diff --git a/web-server/src/main/java/com/evotech/hd/webserver/config/GlobalProperties.java b/web-server/src/main/java/com/evotech/hd/webserver/config/GlobalProperties.java new file mode 100644 index 0000000..b26d985 --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/config/GlobalProperties.java @@ -0,0 +1,43 @@ +/** + * Copyright © 2021-2026 space All rights reserved. + */ +package com.evotech.hd.webserver.config; + +import cn.hutool.core.util.StrUtil; +import cn.hutool.extra.spring.SpringUtil; +import lombok.Data; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; + +import java.io.File; +import java.io.Serializable; + +/** + * 全局配置类 + * + * @author space + * @version 2021-06-25 + */ +@Data +@Configuration +public class GlobalProperties implements Serializable { + + /** + * 上传文件基础虚拟路径 + */ + private static final long serialVersionUID = 1L; + /** + * 超时时间 + */ + @Value("${jwt.accessToken.expireTime}") + public long EXPIRE_TIME; + + + public static GlobalProperties newInstance() { + return SpringUtil.getBean(GlobalProperties.class); + } + + public long getEXPIRE_TIME() { + return EXPIRE_TIME; + } +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/config/WebConfig.java b/web-server/src/main/java/com/evotech/hd/webserver/config/WebConfig.java new file mode 100644 index 0000000..e7c45d9 --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/config/WebConfig.java @@ -0,0 +1,141 @@ +/** + * Copyright © 2021-2026 space All rights reserved. + */ +package com.evotech.hd.webserver.config; + +import com.alibaba.druid.support.http.StatViewServlet; +import com.evotech.hd.webserver.converter.JsonMapper; +import com.evotech.hd.webserver.converter.StringToDateConverter; +import com.google.common.collect.Lists; +import org.springframework.boot.web.servlet.ServletRegistrationBean; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.format.FormatterRegistry; +import org.springframework.http.MediaType; +import org.springframework.http.converter.ByteArrayHttpMessageConverter; +import org.springframework.http.converter.FormHttpMessageConverter; +import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.http.converter.StringHttpMessageConverter; +import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; +import org.springframework.web.context.request.RequestContextListener; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; +import org.springframework.web.filter.CorsFilter; +import org.springframework.web.servlet.config.annotation.EnableWebMvc; +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +import java.util.Collections; +import java.util.List; + +@EnableWebMvc +@EnableCaching //开启缓存 +@Configuration +public class WebConfig implements WebMvcConfigurer { + + @Override + public void addResourceHandlers(ResourceHandlerRegistry registry) { + registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/"); + registry.addResourceHandler("/act/**").addResourceLocations("classpath:/act/"); + registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/"); + registry.addResourceHandler("doc.html").addResourceLocations("classpath:/META-INF/resources/"); + registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/"); + } + + /** + * 配置后台返回数据的类型转换 + * + * @param converters + */ + @Override + public void configureMessageConverters(List> converters) { + + List supportedMediaTypes = Lists.newArrayList(); + supportedMediaTypes.add(MediaType.APPLICATION_JSON_UTF8); + + + + FormHttpMessageConverter formHttpMessageConverter = new FormHttpMessageConverter(); + converters.add(formHttpMessageConverter); + + + StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter(); + stringHttpMessageConverter.setSupportedMediaTypes(supportedMediaTypes); + converters.add(stringHttpMessageConverter); +// JsonMapper jsonMapper = new JsonMapper(); +// // 创建自定义模块,注册 JSONObject 的反序列化器 +// SimpleModule module = new SimpleModule(); +// module.addDeserializer(String.class, new ObjectToStringDeserializer()); +// jsonMapper.registerModule(module); + + + MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter(); + mappingJackson2HttpMessageConverter.setPrettyPrint(false); + mappingJackson2HttpMessageConverter.setSupportedMediaTypes(supportedMediaTypes); + mappingJackson2HttpMessageConverter.setObjectMapper(new JsonMapper()); + converters.add(mappingJackson2HttpMessageConverter); + + ByteArrayHttpMessageConverter byteArrayHttpMessageConverter = new ByteArrayHttpMessageConverter(); + List byteSupportedMediaTypes = Lists.newArrayList(); + byteSupportedMediaTypes.add(MediaType.ALL); + byteSupportedMediaTypes.add(MediaType.IMAGE_PNG); + byteSupportedMediaTypes.add(MediaType.APPLICATION_OCTET_STREAM); + byteSupportedMediaTypes.add(MediaType.IMAGE_GIF); + byteSupportedMediaTypes.add(MediaType.IMAGE_JPEG); + byteSupportedMediaTypes.add(MediaType.valueOf("image/*")); + byteArrayHttpMessageConverter.setSupportedMediaTypes(byteSupportedMediaTypes); + converters.add(byteArrayHttpMessageConverter); + + + } + + /** + * 配置接收前台参数的类型转换 + * + * @param registry + */ + @Override + public void addFormatters(FormatterRegistry registry) { + registry.addConverter(new StringToDateConverter()); + } + + /** + * druidServlet注册 + */ + @Bean + public ServletRegistrationBean druidServletRegistration() { + ServletRegistrationBean registration = new ServletRegistrationBean(new StatViewServlet()); + registration.addUrlMappings("/druid/*"); + return registration; + } + + /** + * request 请求参数主子线程共享 + * + * @return + */ + @Bean + public RequestContextListener requestContextListener() { + return new RequestContextListener(); + } + + @Bean + public CorsFilter getCorsFilter(){ + CorsConfiguration cors = new CorsConfiguration(); + // 1,允许任何来源 + // springboot2.4后,当allowCredentials为true时, + // allowingOrigins不能包含特殊值"*",因为无法在“ Access-Control-Allow-Origin”响应标头上设置。 + // 要允许凭据具有一组来源,请明确列出它们或考虑改用"allowedOriginPatterns"。 + cors.setAllowedOriginPatterns(Collections.singletonList("*")); + //2,允许任何请求头 + cors.addAllowedHeader(CorsConfiguration.ALL); + //3,允许任何方法 + cors.addAllowedMethod(CorsConfiguration.ALL); + //4,允许凭证 + cors.setAllowCredentials(true); + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + source.registerCorsConfiguration("/**",cors); + return new CorsFilter(source); + } +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/config/handler/GlobalExceptionHandler.java b/web-server/src/main/java/com/evotech/hd/webserver/config/handler/GlobalExceptionHandler.java new file mode 100644 index 0000000..5585405 --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/config/handler/GlobalExceptionHandler.java @@ -0,0 +1,62 @@ +package com.evotech.hd.webserver.config.handler; + +import com.auth0.jwt.exceptions.TokenExpiredException; +import com.evotech.hd.core.dtos.Result; +import com.evotech.hd.exception.InstructionException; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.quartz.JobExecutionException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +import javax.servlet.ServletException; + +/** + * 全局异常处理器,将 Exception 翻译成 CommonResult + 对应的异常编号 + * + * @author 芋道源码 + */ +@RestControllerAdvice(basePackages = "com.evotech.hd") +@AllArgsConstructor +@Slf4j +public class GlobalExceptionHandler { + + + + /** + * 处理 SpringMVC 请求参数缺失 + * + * 例如说,接口上设置了 @RequestParam("xx") 参数,结果并未传递 xx 参数 + */ + @ExceptionHandler(value = InstructionException.class) + public Result instructionExceptionHandler(InstructionException ex) { + log.warn("[instructionExceptionHandler]", ex); + return Result.getInstance().error("指令下发异常"+ex.getMessage()); + } + + @ExceptionHandler(value = JobExecutionException.class) + public Result JobExecutionExceptionHandler(JobExecutionException ex) { + log.warn("[JobExecutionExceptionHandler]", ex); + return Result.getInstance().error("定时器异常"+ex.getMessage()); + } + + + public Result allExceptionHandler(Throwable ex) { + if (ex instanceof InstructionException) { + return instructionExceptionHandler((InstructionException) ex); + } + + return defaultExceptionHandler(ex); + } + + @ExceptionHandler(value = Exception.class) + public Result defaultExceptionHandler(Throwable ex) { + return Result.getInstance().error(ex.getMessage()); + } + + @ExceptionHandler(value = ServletException.class) + public Result ServletExceptionHandler(ServletException ex) { + log.warn("[JobExecutionExceptionHandler]", ex); + return Result.getInstance().error("定时器异常"+ex.getMessage()); + } +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/config/job/QuartzConfig.java b/web-server/src/main/java/com/evotech/hd/webserver/config/job/QuartzConfig.java new file mode 100644 index 0000000..3a541e7 --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/config/job/QuartzConfig.java @@ -0,0 +1,73 @@ +package com.evotech.hd.webserver.config.job; + +import com.evotech.hd.webserver.config.job.listener.CustomQuartzJobListener; +import org.quartz.Scheduler; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.quartz.SchedulerFactoryBean; + +import javax.annotation.PostConstruct; +import javax.sql.DataSource; +import java.util.Properties; + +/** + * Quartz配置类,扩展自动配置能力 + */ +@Configuration +public class QuartzConfig { + + private final DataSource dataSource; + @Autowired + private CustomQuartzJobListener customQuartzJobListener; + + // 注入数据源 + public QuartzConfig(DataSource dataSource) { + this.dataSource = dataSource; + } + + /** + * 配置调度器工厂 + */ + @Bean + public SchedulerFactoryBean schedulerFactoryBean() { + SchedulerFactoryBean factory = new SchedulerFactoryBean(); + + // 设置数据源 + factory.setDataSource(dataSource); + + // 添加额外的Quartz属性配置 + Properties properties = new Properties(); + // 配置任务数据不被序列化 + properties.setProperty("org.quartz.jobStore.useProperties", "true"); + // 配置序列化方式 + properties.setProperty("org.quartz.serializer.class", "org.quartz.simpl.SimpleClassLoadHelper"); + + factory.setQuartzProperties(properties); + + // 允许在应用启动时更新已存在的任务 + factory.setOverwriteExistingJobs(false); + + // 启动时延迟加载,等待其他服务就绪 + factory.setStartupDelay(3); + + return factory; + } + + /** + * 暴露Scheduler实例 + */ + @Bean + public Scheduler scheduler() throws Exception { + return schedulerFactoryBean().getScheduler(); + } + + @PostConstruct + public void registerJobListener() throws Exception { + // 注册监听器(对所有任务生效,也可指定单个任务) + scheduler().getListenerManager().addJobListener( + customQuartzJobListener, + org.quartz.impl.matchers.EverythingMatcher.allJobs() // 匹配所有任务 + ); + } +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/config/job/listener/CustomQuartzJobListener.java b/web-server/src/main/java/com/evotech/hd/webserver/config/job/listener/CustomQuartzJobListener.java new file mode 100644 index 0000000..26594ac --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/config/job/listener/CustomQuartzJobListener.java @@ -0,0 +1,112 @@ +package com.evotech.hd.webserver.config.job.listener; + +/** + * CustomQuartzJobListener + * + * @author andy.shi + * @ClassName:CustomQuartzJobListener + * @date: 2025年11月11日 16:28 + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ + +import com.alibaba.fastjson2.JSONObject; +import com.evotech.hd.core.dtos.Result; +import com.evotech.hd.exception.InstructionException; +import com.evotech.hd.utils.Collections; +import com.evotech.hd.webserver.config.handler.GlobalExceptionHandler; +import com.evotech.hd.webserver.websocket.controller.WebSocketUtils; +import org.quartz.JobDataMap; +import org.quartz.JobExecutionContext; +import org.quartz.JobExecutionException; +import org.quartz.JobListener; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.lang.reflect.UndeclaredThrowableException; + +/** + * Quartz 任务监听器:拦截任务异常,转发给 Spring 全局异常处理器 + */ +@Component +public class CustomQuartzJobListener implements JobListener { + + private static final Logger log = LoggerFactory.getLogger(CustomQuartzJobListener.class); + + // 注入 Spring 全局异常处理器(直接复用已有逻辑,避免重复代码) + @Autowired + private GlobalExceptionHandler globalExceptionHandler; + + // 监听器名称(自定义,唯一即可) + @Override + public String getName() { + return "customQuartzJobListener"; + } + + // 任务执行前触发(无需处理) + @Override + public void jobToBeExecuted(JobExecutionContext context) { + log.info("任务开始执行:{}", context.getJobDetail().getKey().getName()); + } + + // 任务被中断时触发(无需处理) + @Override + public void jobExecutionVetoed(JobExecutionContext context) { + log.warn("任务被中断:{}", context.getJobDetail().getKey().getName()); + } + + // 任务执行后触发(核心:捕获异常) + @Override + public void jobWasExecuted(JobExecutionContext context, JobExecutionException jobException) { + String jobId = context.getJobDetail().getKey().getName(); + JobDataMap jobDataMap = context.getJobDetail().getJobDataMap(); + String jobName = jobDataMap.getString("jobName"); + + if (jobException != null) { + log.error("任务执行失败:jobId==>{}, jobName==>{}", jobId, jobName, jobException); + + // 1. 拆包 JobExecutionException,获取原始异常(关键) + Throwable rootCause = jobException.getCause(); + if (rootCause == null) { + rootCause = jobException; // 若未设置 cause,直接用 JobExecutionException + } + + // 2. 手动调用 Spring 全局异常处理器的逻辑(统一返回格式、日志记录) + Result errorResponse = null; + if (rootCause instanceof UndeclaredThrowableException){ + Throwable ex = rootCause.getCause(); + if (ex == null) { + ex = rootCause; // 若未设置 cause,直接用 JobExecutionException + } + if (ex instanceof InstructionException) { + // 自定义异常:调用对应处理器 + errorResponse = globalExceptionHandler.instructionExceptionHandler((InstructionException) ex); + } else if (ex instanceof JobExecutionException) { + // 自定义异常:调用对应处理器 + errorResponse = globalExceptionHandler.JobExecutionExceptionHandler((JobExecutionException) ex); + } else { + // 系统异常:调用兜底处理器 + errorResponse = globalExceptionHandler.defaultExceptionHandler(ex); + } + } else { + // 系统异常:调用兜底处理器 + errorResponse = globalExceptionHandler.defaultExceptionHandler(rootCause); + } + + // 3. 可选:异常告警(如短信、钉钉通知,根据业务需求添加) + sendAlarm(jobName, errorResponse); + } else { + log.info("任务执行成功:{}", jobName); + } + } + + /** + * 异常告警(示例:实际可对接钉钉机器人、短信接口等) + */ + private void sendAlarm(String jobName, Result errorResponse) { + log.error("[告警] 定时任务 {} 执行失败:code={}, message={}", jobName, errorResponse.getCode(), errorResponse.getMsg()); + // 对接告警接口的代码... + WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method","alarm","data",JSONObject.toJSONString(Collections.asMap(jobName, errorResponse.getMsg()))))); + } +} \ No newline at end of file diff --git a/web-server/src/main/java/com/evotech/hd/webserver/config/mqtt/MessageTopic.java b/web-server/src/main/java/com/evotech/hd/webserver/config/mqtt/MessageTopic.java new file mode 100644 index 0000000..8f36f17 --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/config/mqtt/MessageTopic.java @@ -0,0 +1,36 @@ +package com.evotech.hd.webserver.config.mqtt; + +import lombok.Data; +import lombok.Getter; +import lombok.Setter; + +import java.io.Serializable; + +@Data +public class MessageTopic implements Serializable { + + private static final long serialVersionUID = 7238663818955151985L; + + private String businessType = "YTHD"; + + private String stationCode; + + private String dataDirection = "S2M"; + + private String messageType; + + public MessageTopic() { + } + + public MessageTopic(String stationCode, String messageType) { + this.stationCode = stationCode; + this.messageType = messageType; + } + + @Override + public String toString() { + return businessType + "/" + stationCode + "/" + dataDirection + "/" + messageType; + } + + +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/config/mqtt/MqttConfig.java b/web-server/src/main/java/com/evotech/hd/webserver/config/mqtt/MqttConfig.java new file mode 100644 index 0000000..10c3631 --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/config/mqtt/MqttConfig.java @@ -0,0 +1,94 @@ +package com.evotech.hd.webserver.config.mqtt; + +/** + * 类 + * + * @ClassName:MqttConfig + * @date: 2025年08月25日 15:28 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ + +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.eclipse.paho.client.mqttv3.MqttClient; +import org.eclipse.paho.client.mqttv3.MqttConnectOptions; +import org.eclipse.paho.client.mqttv3.MqttException; +import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.ApplicationArguments; +import org.springframework.boot.ApplicationRunner; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.stereotype.Component; + +import javax.annotation.PreDestroy; +import javax.annotation.Resource; +import java.util.concurrent.ConcurrentHashMap; + +@Configuration +@Component +@Slf4j +public class MqttConfig implements ApplicationRunner { + + @Resource + MqttProperties mqttProperties; + + + static ConcurrentHashMap mqttClientMap = new ConcurrentHashMap<>(); + + /** + * 配置MQTT连接选项(不含客户端ID) + */ + @Bean + public MqttConnectOptions mqttConnectOptions() { + MqttConnectOptions options = new MqttConnectOptions(); + // 设置服务器地址(支持多个,用数组) + options.setServerURIs(new String[]{mqttProperties.getBroker()}); + // 配置认证信息(如果有) + if (StringUtils.isNotEmpty(mqttProperties.getUsername())) { + options.setUserName(mqttProperties.getUsername()); + } + if (StringUtils.isNotEmpty(mqttProperties.getPassword())) { + options.setPassword(mqttProperties.getPassword().toCharArray()); + } + // 其他连接参数 + options.setKeepAliveInterval(mqttProperties.getKeepAlive()); // 心跳间隔 + options.setAutomaticReconnect(true); // 自动重连 + options.setCleanSession(true); // 断开后清除会话 + return options; + } + + /** + * 创建MQTT客户端(客户端ID在这里指定) + */ + @Bean // 初始化时自动连接 + public MqttClient mqttClient(MqttConnectOptions options) throws MqttException { + // 客户端ID在创建MqttClient时传入,而非通过MqttConnectOptions设置 + MqttClient client = new MqttClient(mqttProperties.getBroker(), mqttProperties.getClientId(), new MemoryPersistence()); + // 设置回调处理器 + client.setCallback(new MqttMessageCallback()); + client.connect(options); + mqttClientMap.put("cloudClient", client); + return client; + } + + @PreDestroy + public void PreDestroyComplete() { + log.info("===>>>程序要关闭了..."); + MqttClient cloudClient = mqttClientMap.get("cloudClient"); + try { + cloudClient.disconnect(); + cloudClient.close(); + log.info("=====>>>关闭了MQTT"); + } catch (MqttException e) { + e.printStackTrace(); + } + + } + + @Override + public void run(ApplicationArguments args) throws Exception { + + } +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/config/mqtt/MqttMessageCallback.java b/web-server/src/main/java/com/evotech/hd/webserver/config/mqtt/MqttMessageCallback.java new file mode 100644 index 0000000..f30eddb --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/config/mqtt/MqttMessageCallback.java @@ -0,0 +1,112 @@ +package com.evotech.hd.webserver.config.mqtt; + +/** + * 类 + * + * @ClassName:MqttMessageCallback + * @date: 2025年08月25日 15:29 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ + +import cn.hutool.extra.spring.SpringUtil; +import com.evotech.hd.core.dtos.system.ParamsDTO; +import com.evotech.hd.webserver.mqtt.MessageTopic; +import com.evotech.hd.webserver.mqtt.MqttMessageHandleService; +import com.evotech.hd.webserver.service.ParamsService; +import com.evotech.hd.webserver.utils.ParamUtils; +import lombok.extern.slf4j.Slf4j; +import org.eclipse.paho.client.mqttv3.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.stereotype.Component; + +@Slf4j +@Component +public class MqttMessageCallback implements MqttCallbackExtended, ApplicationContextAware { + + private static final Logger logger = LoggerFactory.getLogger(MqttMessageCallback.class); + + private ApplicationContext applicationContext; + + private static String stationCode; + + static String[] mqttMessageTypeArr = {"event","request", "state","keepalive"}; + + /** + * 建立连接后 + */ + @Override + public void connectComplete(boolean reconnect, String serverURI) { + log.info("\r\n=====>>>MQTT连接成功:{}", serverURI); + // 在回调中订阅,这样自动重连后,还会自动订阅 + try { + Thread.sleep(1000L); + } catch (InterruptedException e) { + e.printStackTrace(); + } + MqttClient cloudClient = MqttConfig.mqttClientMap.get("cloudClient"); + String topicPrefix = "YTHD/" + stationCode + "/M2S/"; + for (int j = 0; j < mqttMessageTypeArr.length; j++) { + String topic = topicPrefix + mqttMessageTypeArr[j]; + try { + cloudClient.subscribe(topic, 2); + log.info("\r\n=====>>>MQTT订阅主题 {} 成功...", topic); + } catch (MqttException e) { + e.printStackTrace(); + log.error("\r\n=====>>>MQTT订阅主题 {} 失败。。。", topic); + } + } + } + + /** + * 连接丢失时触发 + */ + @Override + public void connectionLost(Throwable cause) { + cause.printStackTrace(); + logger.error("MQTT连接已断开,原因:{}", cause.getMessage()); + // 自动重连由MqttConnectOptions的automaticReconnect控制 + } + + /** + * 接收消息时触发 + * YTHD/stationCode/dataDirection/messageType/ + */ + @Override + public void messageArrived(String topic, MqttMessage message) throws Exception { + logger.info("收到MQTT消息 - 主题:{},内容:{},QoS:{}", + topic, new String(message.getPayload()), message.getQos()); + String[] topicArr = topic.split("/"); + if ("YTHD".equals(topicArr[0]) && topicArr.length == 4) { + MessageTopic mt = new MessageTopic(); + mt.setDataDirection(topicArr[2]); + mt.setMessageType(topicArr[3]); + mt.setStationCode(topicArr[1]); + SpringUtil.getBean(MqttMessageHandleService.class).handle(mt, message); + } + } + + /** + * 消息发布完成时触发 + */ + @Override + public void deliveryComplete(IMqttDeliveryToken token) { + // 消息成功发布后的回调(可选处理) + try { + logger.info("消息已成功发布到主题:{}", token.getTopics()[0]); + } catch (Exception e) { + logger.error("消息发布确认失败", e); + } + } + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + this.applicationContext = applicationContext; + ParamsDTO param = this.applicationContext.getBean(ParamsService.class).getParamsDTOByCode(ParamUtils.ParamsConstant.STATION_CODE); + stationCode = param.getParamValue(); + } +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/config/mqtt/MqttProperties.java b/web-server/src/main/java/com/evotech/hd/webserver/config/mqtt/MqttProperties.java new file mode 100644 index 0000000..e0cd965 --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/config/mqtt/MqttProperties.java @@ -0,0 +1,39 @@ +package com.evotech.hd.webserver.config.mqtt; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +/** + * 类 + * + * @ClassName:MqttProperties + * @date: 2025年08月25日 16:30 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@ConfigurationProperties(prefix = "mqtt", ignoreUnknownFields = true) +@Data +@Component +public class MqttProperties { + + // MQTT服务器地址(如 tcp://localhost:1883) +// @Value("${mqtt.broker}") + private String broker; + + // 客户端ID(需唯一,在创建MqttClient时使用) +// @Value("${mqtt.client-id}") + private String clientId; + + // 用户名(可选) +// @Value("${mqtt.username:}") + private String username; + + // 密码(可选) +// @Value("${mqtt.password:}") + private String password; + + // 心跳间隔(秒) +// @Value("${mqtt.keep-alive:60}") + private int keepAlive; +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/config/mqtt/MqttPublishMessage.java b/web-server/src/main/java/com/evotech/hd/webserver/config/mqtt/MqttPublishMessage.java new file mode 100644 index 0000000..b0b1dfb --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/config/mqtt/MqttPublishMessage.java @@ -0,0 +1,39 @@ +package com.evotech.hd.webserver.config.mqtt; + +import org.eclipse.paho.client.mqttv3.*; +import org.springframework.stereotype.Component; + +@Component +public class MqttPublishMessage { + + public MqttMessage publish(String message, String topic) { + MqttClient cloudClient = MqttConfig.mqttClientMap.get("cloudClient"); + MqttMessage mqttMessage = new MqttMessage(); + /** + * Qos 0 : 这个消息最多发送一次,不能被持久化到磁盘,不能通过网络被传递,一般内部消息转换 + * Qos 1 : 这个消息至少发送一次,能被重传,能持久化,能通过网络传递,需要实现MqttConnectOptions中的持久化,否则,挂了以后,不能重传。 + * Qos 2 : 这个消息精准只发一次,能持久化,能通过网络传递,客户端和服务器都会收到消息确认 + */ + mqttMessage.setQos(2); + // 是否保留最后一条消息 + mqttMessage.setRetained(false); + // 消息内容 + mqttMessage.setPayload(message.getBytes()); + // 主题的目的地,用于发布/订阅信息 + MqttTopic mqttTopic = cloudClient.getTopic(topic); + // 提供一种机制来跟踪消息的传递进度 + // 用于在以非阻塞方式(在后台运行)执行发布是跟踪消息的传递进度 + MqttDeliveryToken token; + try { + // 将指定消息发布到主题,但不等待消息传递完成,返回的token可用于跟踪消息的传递状态 + // 一旦此方法干净地返回,消息就已被客户端接受发布,当连接可用,将在后台完成消息传递。 + token = mqttTopic.publish(mqttMessage); + token.waitForCompletion(); + } catch (MqttException e) { + e.printStackTrace(); + } + + return mqttMessage; + } + +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/config/mybatis/DataSourceType.java b/web-server/src/main/java/com/evotech/hd/webserver/config/mybatis/DataSourceType.java new file mode 100644 index 0000000..b25ebe6 --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/config/mybatis/DataSourceType.java @@ -0,0 +1,19 @@ +package com.evotech.hd.webserver.config.mybatis; + +/** + * 数据源 + * + * @author evo + */ +public enum DataSourceType +{ + /** + * 主库 + */ + MASTER, + + /** + * 从库 + */ + SLAVE +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/config/mybatis/DruidConfig.java b/web-server/src/main/java/com/evotech/hd/webserver/config/mybatis/DruidConfig.java new file mode 100644 index 0000000..7b90a0e --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/config/mybatis/DruidConfig.java @@ -0,0 +1,122 @@ +package com.evotech.hd.webserver.config.mybatis; + +import cn.hutool.extra.spring.SpringUtil; +import com.alibaba.druid.pool.DruidDataSource; +import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder; +import com.alibaba.druid.spring.boot.autoconfigure.properties.DruidStatProperties; +import com.alibaba.druid.util.Utils; +import com.evotech.hd.webserver.config.mybatis.datasource.DynamicDataSource; +import com.evotech.hd.webserver.config.mybatis.properties.DruidProperties; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; + +import javax.servlet.*; +import javax.sql.DataSource; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +/** + * druid 配置多数据源 + * + * @author evo + */ +@Configuration +public class DruidConfig +{ + @Bean + @ConfigurationProperties("spring.datasource.druid.master") + public DataSource masterDataSource(DruidProperties druidProperties) + { + DruidDataSource dataSource = DruidDataSourceBuilder.create().build(); + return druidProperties.dataSource(dataSource); + } + + @Bean + @ConfigurationProperties("spring.datasource.druid.slave") + @ConditionalOnProperty(prefix = "spring.datasource.druid.slave", name = "enabled", havingValue = "true") + public DataSource slaveDataSource(DruidProperties druidProperties) + { + DruidDataSource dataSource = DruidDataSourceBuilder.create().build(); + return druidProperties.dataSource(dataSource); + } + + @Bean(name = "dynamicDataSource") + @Primary + public DynamicDataSource dataSource(DataSource masterDataSource) + { + Map targetDataSources = new HashMap<>(); + targetDataSources.put(DataSourceType.MASTER.name(), masterDataSource); + setDataSource(targetDataSources, DataSourceType.SLAVE.name(), "slaveDataSource"); + return new DynamicDataSource(masterDataSource, targetDataSources); + } + + /** + * 设置数据源 + * + * @param targetDataSources 备选数据源集合 + * @param sourceName 数据源名称 + * @param beanName bean名称 + */ + public void setDataSource(Map targetDataSources, String sourceName, String beanName) + { + try + { + DataSource dataSource = SpringUtil.getBean(beanName); + targetDataSources.put(sourceName, dataSource); + } + catch (Exception e) + { + } + } + + /** + * 去除监控页面底部的广告 + */ + @SuppressWarnings({ "rawtypes", "unchecked" }) + @Bean + @ConditionalOnProperty(name = "spring.datasource.druid.statViewServlet.enabled", havingValue = "true") + public FilterRegistrationBean removeDruidFilterRegistrationBean(DruidStatProperties properties) + { + // 获取web监控页面的参数 + DruidStatProperties.StatViewServlet config = properties.getStatViewServlet(); + // 提取common.js的配置路径 + String pattern = config.getUrlPattern() != null ? config.getUrlPattern() : "/druid/*"; + String commonJsPattern = pattern.replaceAll("\\*", "js/common.js"); + final String filePath = "support/http/resources/js/common.js"; + // 创建filter进行过滤 + Filter filter = new Filter() + { + @Override + public void init(javax.servlet.FilterConfig filterConfig) throws ServletException + { + } + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException + { + chain.doFilter(request, response); + // 重置缓冲区,响应头不会被重置 + response.resetBuffer(); + // 获取common.js + String text = Utils.readFromResource(filePath); + // 正则替换banner, 除去底部的广告信息 + text = text.replaceAll("
", ""); + text = text.replaceAll("powered.*?shrek.wang", ""); + response.getWriter().write(text); + } + @Override + public void destroy() + { + } + }; + FilterRegistrationBean registrationBean = new FilterRegistrationBean(); + registrationBean.setFilter(filter); + registrationBean.addUrlPatterns(commonJsPattern); + return registrationBean; + } +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/config/mybatis/MybatisPlusConfig.java b/web-server/src/main/java/com/evotech/hd/webserver/config/mybatis/MybatisPlusConfig.java new file mode 100644 index 0000000..9d508ad --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/config/mybatis/MybatisPlusConfig.java @@ -0,0 +1,38 @@ +package com.evotech.hd.webserver.config.mybatis; + +import com.baomidou.mybatisplus.annotation.DbType; +import com.baomidou.mybatisplus.autoconfigure.ConfigurationCustomizer; +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; +import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; +import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.util.Map; + +@Configuration +public class MybatisPlusConfig { + + /** + * 添加分页插件 + */ + @Bean + MybatisPlusInterceptor mybatisPlusInterceptor() { + MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); + // 如果配置多个插件, 切记分页最后添加 + interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); + // 如果有多数据源可以不配具体类型, 否则都建议配上具体的 DbType + return interceptor; + } + + // 全局注册类型处理器 + @Bean + public ConfigurationCustomizer configurationCustomizer() { + return configuration -> { + // 对所有Map类型字段使用JacksonTypeHandler + configuration.getTypeHandlerRegistry() + .register(Map.class, JacksonTypeHandler.class); + }; + } + +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/config/mybatis/datasource/DynamicDataSource.java b/web-server/src/main/java/com/evotech/hd/webserver/config/mybatis/datasource/DynamicDataSource.java new file mode 100644 index 0000000..0c39b1b --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/config/mybatis/datasource/DynamicDataSource.java @@ -0,0 +1,27 @@ +package com.evotech.hd.webserver.config.mybatis.datasource; + +import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; + +import javax.sql.DataSource; +import java.util.Map; + +/** + * 动态数据源 + * + * @author evo + */ +public class DynamicDataSource extends AbstractRoutingDataSource +{ + public DynamicDataSource(DataSource defaultTargetDataSource, Map targetDataSources) + { + super.setDefaultTargetDataSource(defaultTargetDataSource); + super.setTargetDataSources(targetDataSources); + super.afterPropertiesSet(); + } + + @Override + protected Object determineCurrentLookupKey() + { + return DynamicDataSourceContextHolder.getDataSourceType(); + } +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/config/mybatis/datasource/DynamicDataSourceContextHolder.java b/web-server/src/main/java/com/evotech/hd/webserver/config/mybatis/datasource/DynamicDataSourceContextHolder.java new file mode 100644 index 0000000..0ddc08f --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/config/mybatis/datasource/DynamicDataSourceContextHolder.java @@ -0,0 +1,45 @@ +package com.evotech.hd.webserver.config.mybatis.datasource; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 数据源切换处理 + * + * @author evo + */ +public class DynamicDataSourceContextHolder +{ + public static final Logger log = LoggerFactory.getLogger(DynamicDataSourceContextHolder.class); + + /** + * 使用ThreadLocal维护变量,ThreadLocal为每个使用该变量的线程提供独立的变量副本, + * 所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。 + */ + private static final ThreadLocal CONTEXT_HOLDER = new ThreadLocal<>(); + + /** + * 设置数据源的变量 + */ + public static void setDataSourceType(String dsType) + { + log.info("切换到{}数据源", dsType); + CONTEXT_HOLDER.set(dsType); + } + + /** + * 获得数据源的变量 + */ + public static String getDataSourceType() + { + return CONTEXT_HOLDER.get(); + } + + /** + * 清空数据源变量 + */ + public static void clearDataSourceType() + { + CONTEXT_HOLDER.remove(); + } +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/config/mybatis/handler/InsertAndUpdateMybatisHandler.java b/web-server/src/main/java/com/evotech/hd/webserver/config/mybatis/handler/InsertAndUpdateMybatisHandler.java new file mode 100644 index 0000000..dc42f37 --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/config/mybatis/handler/InsertAndUpdateMybatisHandler.java @@ -0,0 +1,53 @@ +package com.evotech.hd.webserver.config.mybatis.handler; + +import com.alibaba.fastjson2.JSONObject; +import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; +import com.evotech.hd.utils.DateUtils; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.ObjectUtils; +import org.apache.ibatis.reflection.MetaObject; +import org.springframework.stereotype.Component; + +import java.text.SimpleDateFormat; +import java.util.Date; + +/** + * @desc: 修改和新增自定义注解 + * @ClassName:InsertAndUpdateMybatisHandler + * @date: 2025年04月17日 9:03 + * @author: andy.shi + * @contact: 17330188597 + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@Component +@Slf4j +public class InsertAndUpdateMybatisHandler implements MetaObjectHandler { + @Override + public void insertFill(MetaObject metaObject) { + try { + if(ObjectUtils.isNotEmpty(metaObject)){ + mySetFieldValByName("createTime", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").parse(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date())),metaObject); + } + } catch (Exception e) { + log.error("自动填充拦截器异常=====insert,对象信息:{}, 异常信息:{}", JSONObject.toJSONString(metaObject), e.getMessage()); + } + + } + @Override + public void updateFill(MetaObject metaObject) { + try { + if(ObjectUtils.isNotEmpty(metaObject)){ + mySetFieldValByName("updateTime",DateUtils.getNowDate(),metaObject); + } + } catch (Exception e) { + log.error("自动填充拦截器异常=====update,对象信息:{}, 异常信息:{}", JSONObject.toJSONString(metaObject),e.getMessage()); + } + } + + private void mySetFieldValByName(String filedName, Object fieldVal, MetaObject metaObject) { + Object createType = getFieldValByName(filedName,metaObject); + if(ObjectUtils.isEmpty(createType)){ + setFieldValByName(filedName,fieldVal,metaObject); + } + } +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/config/mybatis/properties/DruidProperties.java b/web-server/src/main/java/com/evotech/hd/webserver/config/mybatis/properties/DruidProperties.java new file mode 100644 index 0000000..d1466e6 --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/config/mybatis/properties/DruidProperties.java @@ -0,0 +1,89 @@ +package com.evotech.hd.webserver.config.mybatis.properties; + +import com.alibaba.druid.pool.DruidDataSource; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; + +/** + * druid 配置属性 + * + * @author evo + */ +@Configuration +public class DruidProperties +{ + @Value("${spring.datasource.druid.initialSize}") + private int initialSize; + + @Value("${spring.datasource.druid.minIdle}") + private int minIdle; + + @Value("${spring.datasource.druid.maxActive}") + private int maxActive; + + @Value("${spring.datasource.druid.maxWait}") + private int maxWait; + + @Value("${spring.datasource.druid.connectTimeout}") + private int connectTimeout; + + @Value("${spring.datasource.druid.socketTimeout}") + private int socketTimeout; + + @Value("${spring.datasource.druid.timeBetweenEvictionRunsMillis}") + private int timeBetweenEvictionRunsMillis; + + @Value("${spring.datasource.druid.minEvictableIdleTimeMillis}") + private int minEvictableIdleTimeMillis; + + @Value("${spring.datasource.druid.maxEvictableIdleTimeMillis}") + private int maxEvictableIdleTimeMillis; + + @Value("${spring.datasource.druid.validationQuery}") + private String validationQuery; + + @Value("${spring.datasource.druid.testWhileIdle}") + private boolean testWhileIdle; + + @Value("${spring.datasource.druid.testOnBorrow}") + private boolean testOnBorrow; + + @Value("${spring.datasource.druid.testOnReturn}") + private boolean testOnReturn; + + public DruidDataSource dataSource(DruidDataSource datasource) + { + /** 配置初始化大小、最小、最大 */ + datasource.setInitialSize(initialSize); + datasource.setMaxActive(maxActive); + datasource.setMinIdle(minIdle); + + /** 配置获取连接等待超时的时间 */ + datasource.setMaxWait(maxWait); + + /** 配置驱动连接超时时间,检测数据库建立连接的超时时间,单位是毫秒 */ + datasource.setConnectTimeout(connectTimeout); + + /** 配置网络超时时间,等待数据库操作完成的网络超时时间,单位是毫秒 */ + datasource.setSocketTimeout(socketTimeout); + + /** 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 */ + datasource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis); + + /** 配置一个连接在池中最小、最大生存的时间,单位是毫秒 */ + datasource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis); + datasource.setMaxEvictableIdleTimeMillis(maxEvictableIdleTimeMillis); + + /** + * 用来检测连接是否有效的sql,要求是一个查询语句,常用select 'x'。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会起作用。 + */ + datasource.setValidationQuery(validationQuery); + /** 建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。 */ + datasource.setTestWhileIdle(testWhileIdle); + /** 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 */ + datasource.setTestOnBorrow(testOnBorrow); + /** 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 */ + datasource.setTestOnReturn(testOnReturn); + return datasource; + } +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/config/redis/config/RedisConfig.java b/web-server/src/main/java/com/evotech/hd/webserver/config/redis/config/RedisConfig.java new file mode 100644 index 0000000..df6b8c8 --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/config/redis/config/RedisConfig.java @@ -0,0 +1,112 @@ +package com.evotech.hd.webserver.config.redis.config; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.PropertyAccessor; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator; +import org.springframework.cache.annotation.CachingConfigurerSupport; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.script.DefaultRedisScript; +import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; +import org.springframework.data.redis.serializer.StringRedisSerializer; + +/** + * redis配置 + * + * @author evo + */ +@Configuration +@EnableCaching +public class RedisConfig extends CachingConfigurerSupport +{ + + + @Bean + public RedisTemplate redisTemplate(RedisConnectionFactory connectionFactory) { + RedisTemplate template = new RedisTemplate<>(); + template.setConnectionFactory(connectionFactory); + + // 使用 Jackson2JsonRedisSerializer + Jackson2JsonRedisSerializer serializer = new Jackson2JsonRedisSerializer<>(Object.class); + ObjectMapper mapper = new ObjectMapper(); + mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); + mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); + serializer.setObjectMapper(mapper); + + template.setValueSerializer(serializer); + template.setKeySerializer(new StringRedisSerializer()); + template.setHashKeySerializer(new StringRedisSerializer()); + template.setHashValueSerializer(serializer); + template.afterPropertiesSet(); + return template; + } + +// @Bean +// public RedisTemplate redisTemplate(RedisConnectionFactory connectionFactory) +// { +// RedisTemplate template = new RedisTemplate<>(); +// template.setConnectionFactory(connectionFactory); +// +// Jackson2JsonRedisSerializer jsonSerializer = new Jackson2JsonRedisSerializer<>(Object.class); +// ObjectMapper objectMapper = new ObjectMapper(); +// objectMapper.activateDefaultTyping( +// LaissezFaireSubTypeValidator.instance, +// ObjectMapper.DefaultTyping.NON_FINAL // 非final类都包含类型信息 +// ); +// objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); +// +// jsonSerializer.setObjectMapper(objectMapper); +// +// // 设置序列化器 +// template.setValueSerializer(jsonSerializer); // 值用JSON序列化 +// template.setKeySerializer(new StringRedisSerializer()); // 键用字符串序列化 +// template.afterPropertiesSet(); +// +// +// +//// FastJson2JsonRedisSerializer serializer = new FastJson2JsonRedisSerializer(Object.class); +// +// // 使用StringRedisSerializer来序列化和反序列化redis的key值 +//// template.setKeySerializer(new StringRedisSerializer()); +//// template.setValueSerializer(serializer); +//// +//// // Hash的key也采用StringRedisSerializer的序列化方式 +//// template.setHashKeySerializer(new StringRedisSerializer()); +//// template.setHashValueSerializer(serializer); +//// +//// template.afterPropertiesSet(); +// return template; +// } + + @Bean + public DefaultRedisScript limitScript() + { + DefaultRedisScript redisScript = new DefaultRedisScript<>(); + redisScript.setScriptText(limitScriptText()); + redisScript.setResultType(Long.class); + return redisScript; + } + + /** + * 限流脚本 + */ + private String limitScriptText() + { + return "local key = KEYS[1]\n" + + "local count = tonumber(ARGV[1])\n" + + "local time = tonumber(ARGV[2])\n" + + "local current = redis.call('get', key);\n" + + "if current and tonumber(current) > count then\n" + + " return tonumber(current);\n" + + "end\n" + + "current = redis.call('incr', key)\n" + + "if tonumber(current) == 1 then\n" + + " redis.call('expire', key, time)\n" + + "end\n" + + "return tonumber(current);"; + } +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/config/redis/utils/RedisUtil.java b/web-server/src/main/java/com/evotech/hd/webserver/config/redis/utils/RedisUtil.java new file mode 100644 index 0000000..d870065 --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/config/redis/utils/RedisUtil.java @@ -0,0 +1,297 @@ +package com.evotech.hd.webserver.config.redis.utils; + +import org.springframework.data.redis.core.BoundSetOperations; +import org.springframework.data.redis.core.HashOperations; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.ValueOperations; +import org.springframework.stereotype.Component; + +import java.util.*; +import java.util.concurrent.TimeUnit; + +/** + * @author zrb + * @date 2024年9月3日17:15:36 + */ +@Component +public class RedisUtil { + + public static RedisTemplate redisTemplate; + + public RedisUtil(RedisTemplate redisTemplate){ + this.redisTemplate=redisTemplate; + } + +// public static RedisUtil getInstance(){ +// return SpringUtil.getBean(RedisUtil.class); +// } + /** + * 缓存基本的对象,Integer、String、实体类等 + * + * @param key 缓存的键值 + * @param value 缓存的值 + */ + public static void set(final String key, final T value) + { + redisTemplate.opsForValue().set(key, value); + } + + + /** + * 缓存基本的对象,Integer、String、实体类等 + * + * @param key 缓存的键值 + * @param value 缓存的值 + */ + public static void setCacheObject(final String key, final T value) + { + redisTemplate.opsForValue().set(key, value); + } + + /** + * 缓存基本的对象,Integer、String、实体类等 + * + * @param key 缓存的键值 + * @param value 缓存的值 + * @param timeout 时间 + * @param timeUnit 时间颗粒度 + */ + public static void setCacheObject(final String key, final T value, final Integer timeout, final TimeUnit timeUnit) + { + redisTemplate.opsForValue().set(key, value, timeout, timeUnit); + } + + /** + * 设置有效时间 + * + * @param key Redis键 + * @param timeout 超时时间 + * @return true=设置成功;false=设置失败 + */ + public static boolean expire(final String key, final long timeout) + { + return expire(key, timeout, TimeUnit.SECONDS); + } + + /** + * 设置有效时间 + * + * @param key Redis键 + * @param timeout 超时时间 + * @param unit 时间单位 + * @return true=设置成功;false=设置失败 + */ + public static boolean expire(final String key, final long timeout, final TimeUnit unit) + { + return redisTemplate.expire(key, timeout, unit); + } + + /** + * 获取有效时间 + * + * @param key Redis键 + * @return 有效时间 + */ + public static Object get(final String key) + { + return redisTemplate.opsForValue().get(key); + } + + /** + * 获取有效时间 + * + * @param key Redis键 + * @return 有效时间 + */ + public static long getExpire(final String key) + { + return redisTemplate.getExpire(key); + } + + /** + * 判断 key是否存在 + * + * @param key 键 + * @return true 存在 false不存在 + */ + public static Boolean hasKey(String key) + { + return redisTemplate.hasKey(key); + } + + /** + * 获得缓存的基本对象。 + * + * @param key 缓存键值 + * @return 缓存键值对应的数据 + */ + public static T getCacheObject(final String key) + { + ValueOperations operation = redisTemplate.opsForValue(); + return operation.get(key); + } + + public static boolean del(final String key) + { + return redisTemplate.delete(key); + } + + /** + * 删除单个对象 + * + * @param key + */ + public static boolean deleteObject(final String key) + { + return redisTemplate.delete(key); + } + + /** + * 删除集合对象 + * + * @param collection 多个对象 + * @return + */ + public static boolean deleteObject(final Collection collection) + { + return redisTemplate.delete(collection) > 0; + } + + /** + * 缓存List数据 + * + * @param key 缓存的键值 + * @param dataList 待缓存的List数据 + * @return 缓存的对象 + */ + public static long setCacheList(final String key, final List dataList) + { + Long count = redisTemplate.opsForList().rightPushAll(key, dataList); + return count == null ? 0 : count; + } + + /** + * 获得缓存的list对象 + * + * @param key 缓存的键值 + * @return 缓存键值对应的数据 + */ + public static List getCacheList(final String key) + { + return redisTemplate.opsForList().range(key, 0, -1); + } + + /** + * 缓存Set + * + * @param key 缓存键值 + * @param dataSet 缓存的数据 + * @return 缓存数据的对象 + */ + public static BoundSetOperations setCacheSet(final String key, final Set dataSet) + { + BoundSetOperations setOperation = redisTemplate.boundSetOps(key); + Iterator it = dataSet.iterator(); + while (it.hasNext()) + { + setOperation.add(it.next()); + } + return setOperation; + } + + /** + * 获得缓存的set + * + * @param key + * @return + */ + public static Set getCacheSet(final String key) + { + return redisTemplate.opsForSet().members(key); + } + + /** + * 缓存Map + * + * @param key + * @param dataMap + */ + public static void setCacheMap(final String key, final Map dataMap) + { + if (dataMap != null) { + redisTemplate.opsForHash().putAll(key, dataMap); + } + } + + /** + * 获得缓存的Map + * + * @param key + * @return + */ + public static Map getCacheMap(final String key) + { + return redisTemplate.opsForHash().entries(key); + } + + /** + * 往Hash中存入数据 + * + * @param key Redis键 + * @param hKey Hash键 + * @param value 值 + */ + public static void setCacheMapValue(final String key, final String hKey, final T value) + { + redisTemplate.opsForHash().put(key, hKey, value); + } + + /** + * 获取Hash中的数据 + * + * @param key Redis键 + * @param hKey Hash键 + * @return Hash中的对象 + */ + public static T getCacheMapValue(final String key, final String hKey) + { + HashOperations opsForHash = redisTemplate.opsForHash(); + return opsForHash.get(key, hKey); + } + + /** + * 获取多个Hash中的数据 + * + * @param key Redis键 + * @param hKeys Hash键集合 + * @return Hash对象集合 + */ + public static List getMultiCacheMapValue(final String key, final Collection hKeys) + { + return redisTemplate.opsForHash().multiGet(key, hKeys); + } + + /** + * 删除Hash中的某条数据 + * + * @param key Redis键 + * @param hKey Hash键 + * @return 是否成功 + */ + public static boolean deleteCacheMapValue(final String key, final String hKey) + { + return redisTemplate.opsForHash().delete(key, hKey) > 0; + } + + /** + * 获得缓存的基本对象列表 + * + * @param pattern 字符串前缀 + * @return 对象列表 + */ + public static Collection keys(final String pattern) + { + return redisTemplate.keys(pattern); + } + +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/config/security/config/WebSecurityConfig.java b/web-server/src/main/java/com/evotech/hd/webserver/config/security/config/WebSecurityConfig.java new file mode 100644 index 0000000..b5be219 --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/config/security/config/WebSecurityConfig.java @@ -0,0 +1,123 @@ +package com.evotech.hd.webserver.config.security.config; + + +import com.evotech.hd.webserver.config.security.exception.JwtAccessDeniedHandler; +import com.evotech.hd.webserver.config.security.exception.JwtAuthenticationEntryPoint; +import com.evotech.hd.webserver.config.security.jwt.JWTConfigurer; +import com.evotech.hd.webserver.config.security.jwt.TokenProvider; +import com.evotech.hd.webserver.config.security.service.CustomUserDetailsService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.core.annotation.Order; +import org.springframework.http.HttpMethod; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.dao.DaoAuthenticationProvider; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.builders.WebSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; + +@Order(0) +@EnableWebSecurity +@EnableGlobalMethodSecurity(prePostEnabled = true) // 开启方法级安全验证 +public class WebSecurityConfig extends WebSecurityConfigurerAdapter { + @Autowired + private CustomUserDetailsService userDetailsService; + @Autowired + private JwtAuthenticationEntryPoint unauthorizedHandler; + @Autowired + private JwtAccessDeniedHandler accessDeniedHandler; + + + /** + * 描述:设置授权处理相关的具体类以及加密方式 + */ + @Override + protected void configure(AuthenticationManagerBuilder auth) throws Exception { + DaoAuthenticationProvider provider = new DaoAuthenticationProvider(); + // 设置不隐藏 未找到用户异常 + provider.setHideUserNotFoundExceptions(true); + // 用户认证service - 查询数据库的逻辑 + //provider.setUserDetailsService(userDetailsService()); + // 设置密码加密算法 + provider.setPasswordEncoder(passwordEncoder()); + provider.setUserDetailsService(userDetailsService); + auth.authenticationProvider(provider); + } + + + @Override + protected void configure(HttpSecurity http) throws Exception { + http + .headers().frameOptions().disable().and() + .exceptionHandling().accessDeniedHandler(accessDeniedHandler).and() + // 由于使用的是JWT,我们这里不需要csrf + .csrf().disable() + .exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and() + // 基于token,所以不需要session + .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and() + .authorizeRequests() + .antMatchers(HttpMethod.OPTIONS, "/**").permitAll() // 允许请求无需认证 + .antMatchers( + "/401", + "/404", + "/swagger-ui.html", + "/swagger-resources/**", + "/test/**", + "/v2/api-docs", + "/websocket", + "/webjars/**", + "/sys/*").permitAll() // 允许请求无需认证 + .anyRequest().authenticated() // 所有请求都需要验证 + .and() + .apply(securityConfigurerAdapter()); + } + + /** + * 描述: 静态资源放行,这里的放行,是不走 Spring Security 过滤器链 + **/ + @Override + public void configure(WebSecurity web) { + // 可以直接访问的静态数据 + web.ignoring().antMatchers( + "/doc.html", + "/swagger-ui.html", + "/userfiles/**", + "/static/**", + "/swagger**/**", + "/webjars/**"); + } + + /** + * 描述: 密码加密算法 BCrypt 推荐使用 + **/ + @Bean + public BCryptPasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } + + /** + * 描述: 注入token + **/ + @Bean + public TokenProvider tokenProvider() { + return new TokenProvider(); + } + + private JWTConfigurer securityConfigurerAdapter() throws Exception { + return new JWTConfigurer(tokenProvider(), authenticationManager()); + } + + /** + * 描述: 注入AuthenticationManager管理器 + **/ + @Override + @Bean + public AuthenticationManager authenticationManager() throws Exception { + return super.authenticationManager(); + } +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/config/security/exception/JwtAccessDeniedHandler.java b/web-server/src/main/java/com/evotech/hd/webserver/config/security/exception/JwtAccessDeniedHandler.java new file mode 100644 index 0000000..667b3d4 --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/config/security/exception/JwtAccessDeniedHandler.java @@ -0,0 +1,34 @@ +package com.evotech.hd.webserver.config.security.exception; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import lombok.extern.slf4j.Slf4j; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpStatus; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.security.web.access.AccessDeniedHandler; +import org.springframework.stereotype.Component; + +import java.io.IOException; +import java.io.PrintWriter; + +/** + * 权限不足处理类,返回403 + * Author: liugaofeng + */ +@Component +public class JwtAccessDeniedHandler implements AccessDeniedHandler { + static Logger log = LoggerFactory.getLogger(JwtAccessDeniedHandler.class); + @Override + public void handle(HttpServletRequest httpServletRequest, HttpServletResponse response, AccessDeniedException e) throws IOException { + //登陆状态下,权限不足执行该方法 + log.error("权限不足:" + e.getMessage()); + response.setStatus(HttpStatus.FORBIDDEN.value()); + response.setCharacterEncoding("UTF-8"); + response.setContentType("application/json; charset=utf-8"); + PrintWriter printWriter = response.getWriter(); + printWriter.write("权限不足:" + e.getMessage()); + printWriter.flush(); + } +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/config/security/exception/JwtAuthenticationEntryPoint.java b/web-server/src/main/java/com/evotech/hd/webserver/config/security/exception/JwtAuthenticationEntryPoint.java new file mode 100644 index 0000000..af3d238 --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/config/security/exception/JwtAuthenticationEntryPoint.java @@ -0,0 +1,71 @@ +package com.evotech.hd.webserver.config.security.exception; + + +import com.evotech.hd.core.dtos.Result; +import com.evotech.hd.webserver.config.redis.utils.RedisUtil; +import com.evotech.hd.core.enums.CacheNames; +import com.evotech.hd.core.enums.ErrorConstants; +import com.evotech.hd.webserver.config.security.jwt.TokenProvider; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpStatus; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.AuthenticationEntryPoint; +import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; + +import java.io.IOException; +import java.io.PrintWriter; +import java.io.Serializable; + + +/** + * 认证失败处理类,返回401 + */ +@Component +public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint, Serializable { + static Logger log = LoggerFactory.getLogger(JwtAuthenticationEntryPoint.class); + private static final long serialVersionUID = -8970718410437077606L; + + private final ObjectMapper objectMapper = new ObjectMapper(); + + @Override + public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException { + //验证为未登陆状态会进入此方法,认证错误 + String errMsg; + String token = TokenProvider.resolveToken(request); + if (StringUtils.isEmpty(token)) { // 没有token抛出的异常 + errMsg = ErrorConstants.LOGIN_ERROR_NOT_LOGIN_IN; + response.setStatus(HttpStatus.UNAUTHORIZED.value()); + } else { // token过期抛出的异常 + Object accessTokenObj = RedisUtil.get(CacheNames.USER_CACHE_TOKEN + TokenProvider.getLoginName(token) + ":" + token); + if (accessTokenObj != null && accessTokenObj.toString().equals("kickOut")) { + RedisUtil.del(CacheNames.USER_CACHE_TOKEN + TokenProvider.getLoginName(token) + ":" + token); + errMsg = ErrorConstants.LOGIN_ERROR__KICK_OUT_LOGGED_IN_ELSEWHERE; + } else { + errMsg = ErrorConstants.LOGIN_ERROR_EXPIRED; + //throw new ServletException(ErrorConstants.LOGIN_ERROR_EXPIRED); + } + response.setStatus(HttpStatus.REQUEST_TIMEOUT.value()); + } + log.error(errMsg); + + response.setStatus(HttpStatus.OK.value()); + response.setHeader("Access-Control-Allow-Origin", "*"); // 前端域名 + response.setHeader("Access-Control-Allow-Methods", "GET,POST,PUT,DELETE,OPTIONS"); + response.setHeader("Access-Control-Allow-Headers", "Authorization,Content-Type"); + response.setHeader("Access-Control-Allow-Credentials", "true"); + response.setCharacterEncoding("UTF-8"); + response.setContentType("application/json; charset=utf-8"); + PrintWriter printWriter = response.getWriter(); + printWriter.write(objectMapper.writeValueAsString(Result.getInstance().error("S0405", errMsg))); + printWriter.flush(); + printWriter.close(); + } +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/config/security/jwt/JWTConfigurer.java b/web-server/src/main/java/com/evotech/hd/webserver/config/security/jwt/JWTConfigurer.java new file mode 100644 index 0000000..d2c64fd --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/config/security/jwt/JWTConfigurer.java @@ -0,0 +1,25 @@ +package com.evotech.hd.webserver.config.security.jwt; + +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.config.annotation.SecurityConfigurerAdapter; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.web.DefaultSecurityFilterChain; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; + +public class JWTConfigurer extends SecurityConfigurerAdapter { + + private final TokenProvider tokenProvider; + + private final AuthenticationManager authenticationManager; + + public JWTConfigurer(TokenProvider tokenProvider, AuthenticationManager authenticationManager) { + this.tokenProvider = tokenProvider; + this.authenticationManager = authenticationManager; + } + + @Override + public void configure(HttpSecurity http) { + JWTFilter customFilter = new JWTFilter(tokenProvider, authenticationManager); + http.addFilterBefore(customFilter, UsernamePasswordAuthenticationFilter.class); + } +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/config/security/jwt/JWTFilter.java b/web-server/src/main/java/com/evotech/hd/webserver/config/security/jwt/JWTFilter.java new file mode 100644 index 0000000..939399d --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/config/security/jwt/JWTFilter.java @@ -0,0 +1,77 @@ +package com.evotech.hd.webserver.config.security.jwt; + +import cn.hutool.core.util.StrUtil; +import com.evotech.hd.webserver.config.GlobalProperties; +import com.evotech.hd.webserver.config.redis.utils.RedisUtil; +import com.evotech.hd.core.enums.CacheNames; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.web.filter.GenericFilterBean; + +import java.io.IOException; + +/** + * JWT过滤器 + */ +public class JWTFilter extends GenericFilterBean { + + + private final TokenProvider tokenProvider; + + AuthenticationManager authenticationManager; + + + public JWTFilter(TokenProvider tokenProvider, AuthenticationManager authenticationManager) { + this.tokenProvider = tokenProvider; + this.authenticationManager = authenticationManager; + } + + + + @Override + public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { + HttpServletRequest request = (HttpServletRequest) servletRequest; + String token = TokenProvider.resolveToken(request); + String accessToken = null; + if (StrUtil.isNotEmpty(token)) { + String loginName = TokenProvider.getLoginName(token); + if (StrUtil.isNotEmpty(loginName)) { + Object accessTokenObj = RedisUtil.get(CacheNames.USER_CACHE_TOKEN + loginName + ":" + token); + if (accessTokenObj != null) { + accessToken = accessTokenObj.toString(); + // 刷新token有效时长 + RedisUtil.expire(CacheNames.USER_CACHE_TOKEN + loginName + ":" + token, GlobalProperties.newInstance().getEXPIRE_TIME()); + } + } + } + + boolean isLogin = StrUtil.isNotBlank(accessToken) && !accessToken.equals("kickOut"); + + /** + * 过滤器逻辑:如果请求中含有token,先验证token是否合法有效。 + * 如果验证token失败,直接放行,由spring security 负责安全拦截,包括未登录访问拦截和 未授权方法拦截。 + * 如果验证token有效,判断是否登录了spring security, 如果未登录,则执行登录security。 + * + * 你肯定有疑问,为啥token验证失败是放行而不是拒绝访问呢? + * 原因很简单,因为url是否可以匿名访问是在spring security配置的,比如登录url(/sys/login)可以匿名访问,如果我们拒绝了将不能执行登录操作。 + * 我们集成了spring security,由它负责登录和方法鉴权, 而token仅仅是用来携带用户信息的。所以我们jwt过滤器仅仅是用来获取token信息,判断token是否合法, + * 如果合法,剩下的登录鉴权和方法鉴权将移交给spring security进行。 + * + */ + if (isLogin && this.tokenProvider.validateToken(token, accessToken)) { // 验证token是合法token + if (SecurityContextHolder.getContext().getAuthentication() == null) { // 需要登录 + Authentication authentication = this.tokenProvider.getAuthentication(accessToken); +// authenticationManager.authenticate(authentication); 验证不需要 + SecurityContextHolder.getContext().setAuthentication(authentication); + } + } + filterChain.doFilter(servletRequest, servletResponse); + } +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/config/security/jwt/TokenProvider.java b/web-server/src/main/java/com/evotech/hd/webserver/config/security/jwt/TokenProvider.java new file mode 100644 index 0000000..6a259be --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/config/security/jwt/TokenProvider.java @@ -0,0 +1,153 @@ +package com.evotech.hd.webserver.config.security.jwt; + +import cn.hutool.core.collection.ArrayIter; +import cn.hutool.core.collection.IterUtil; +import cn.hutool.core.map.CaseInsensitiveMap; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.StrUtil; +import com.auth0.jwt.JWT; +import com.auth0.jwt.JWTVerifier; +import com.auth0.jwt.algorithms.Algorithm; +import com.auth0.jwt.exceptions.TokenExpiredException; +import com.auth0.jwt.interfaces.DecodedJWT; +import com.evotech.hd.webserver.config.GlobalProperties; +import com.evotech.hd.webserver.config.redis.utils.RedisUtil; +import com.evotech.hd.core.enums.CacheNames; +import com.evotech.hd.core.enums.ErrorConstants; +import com.evotech.hd.webserver.config.security.util.UserUtils; +import com.evotech.hd.webserver.serializer.StringToNullDeserializer; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.userdetails.User; + +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Map; + +@Slf4j +public class TokenProvider { + public static final String TOKEN = "token"; + + + /** + * 校验token是否正确 + * + * @param accessToken 密钥 + * @return 是否正确 + */ + public static boolean validateToken(String token, String accessToken) { + String userName = TokenProvider.getLoginName(accessToken); + String password = UserUtils.getByLoginName(userName).getPassword(); + try { + + Algorithm algorithm = Algorithm.HMAC256(password); + JWTVerifier verifier = JWT.require(algorithm) + .withClaim("username", userName) + .build(); + verifier.verify(accessToken); + return true; + } catch (TokenExpiredException e) { + // token失效,执行刷新操作 + String newAccessToken = createAccessToken(userName, password); + RedisUtil.set(CacheNames.USER_CACHE_TOKEN + userName + ":" + token, newAccessToken); + RedisUtil.expire(CacheNames.USER_CACHE_TOKEN + userName + ":" + token, GlobalProperties.newInstance().getEXPIRE_TIME()); + return true; + } catch (Exception e) { + e.printStackTrace(); + log.info(ErrorConstants.LOGIN_ERROR_INCORRECT); + } + return false; + + } + + /** + * 获得token中的信息无需secret解密也能获得 + * + * @return token中包含的用户名 + */ + public static String getLoginName(String token) { + try { + DecodedJWT jwt = JWT.decode(token); + return jwt.getClaim("username").asString(); + } catch (Exception e) { + return null; + } + } + + + /** + * 生成签名 + * + * @param username 用户名 + * @param password 用户的密码 + * @return 加密的token + */ + public static String createAccessToken(String username, String password) { + Date date = new Date(System.currentTimeMillis() + GlobalProperties.newInstance().getEXPIRE_TIME() * 1000); + Algorithm algorithm = Algorithm.HMAC256(password); + // 附带username信息 + return JWT.create() + .withClaim("username", username) + .withExpiresAt(date) + .sign(algorithm); + } + + + public static Authentication getAuthentication(String token) { + DecodedJWT jwt = JWT.decode(token); + String username = jwt.getClaim("username").asString(); + + // 权限集合 + List authorities = new ArrayList<>(); + authorities.add(new SimpleGrantedAuthority("**:**:**")); +// Set permissions = UserUtils.getPermissionsByLoginName(username); +// // 添加基于Permission的权限信息 +// for (String permission : permissions) { +// authorities.add(new SimpleGrantedAuthority(permission)); +// } + User principal = new User(username, "", authorities); + return new UsernamePasswordAuthenticationToken(principal, token, authorities); + } + + + /** + * 获取token,支持三种方式, 请求参数、header、cookie, 优先级依次降低,以请求参数中的优先级最高。 + * + * @param httpServletRequest + * @RETURN + */ + public static String resolveToken(HttpServletRequest httpServletRequest) { + if (httpServletRequest == null) { + return null; + } + String token0 = httpServletRequest.getParameter(TokenProvider.TOKEN); + String token1 = httpServletRequest.getHeader(TokenProvider.TOKEN); + Cookie token2 = getCookie(httpServletRequest, TokenProvider.TOKEN); + if (StrUtil.isNotBlank(token0) && !StringToNullDeserializer.checkStr(token0)) { + return token0; + } + if (StrUtil.isNotBlank(token1) && !StringToNullDeserializer.checkStr(token1)) { + return token1; + } + if (token2 != null && StrUtil.isNotBlank(token2.getValue()) && !StringToNullDeserializer.checkStr(token2.getValue())) { + return token2.getValue(); + } + return null; + } + + public static Cookie getCookie(HttpServletRequest httpServletRequest, String name) { + return readCookieMap(httpServletRequest).get(name); + } + + public static Map readCookieMap(HttpServletRequest httpServletRequest) { + Cookie[] cookies = httpServletRequest.getCookies(); + return ArrayUtil.isEmpty(cookies) ? MapUtil.empty() : IterUtil.toMap(new ArrayIter(httpServletRequest.getCookies()), new CaseInsensitiveMap(), Cookie::getName); + } +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/config/security/service/CustomUserDetailsService.java b/web-server/src/main/java/com/evotech/hd/webserver/config/security/service/CustomUserDetailsService.java new file mode 100644 index 0000000..7a43c64 --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/config/security/service/CustomUserDetailsService.java @@ -0,0 +1,48 @@ +package com.evotech.hd.webserver.config.security.service; + +import com.evotech.hd.core.dtos.UserDTO; +import com.evotech.hd.webserver.cnstant.CommonConstants; +import com.evotech.hd.core.enums.ErrorConstants; +import com.evotech.hd.webserver.service.UserService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.AccountExpiredException; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.userdetails.User; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; + +@Component +public class CustomUserDetailsService implements UserDetailsService { + @Autowired + private UserService userService; + + @Override + public User loadUserByUsername(String username) throws UsernameNotFoundException { + // 通过用户名从数据库获取用户信息 + UserDTO userInfo = userService.getUserByLoginName(username); + + // 用户不存在 + if (userInfo == null) { + throw new AccountExpiredException(ErrorConstants.LOGIN_ERROR_NOTFOUND); + } + + // 用户禁止登录 + if (CommonConstants.YES.equals(userInfo.getLoginFlag())) { + throw new AccountExpiredException(ErrorConstants.LOGIN_ERROR_FORBIDDEN); + } + + // 权限集合 + List authorities = new ArrayList<>(); + + return new User( + userInfo.getLoginName(), + userInfo.getPassword(), + authorities + ); + } +} + diff --git a/web-server/src/main/java/com/evotech/hd/webserver/config/security/util/SecurityUtils.java b/web-server/src/main/java/com/evotech/hd/webserver/config/security/util/SecurityUtils.java new file mode 100644 index 0000000..b94c3c7 --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/config/security/util/SecurityUtils.java @@ -0,0 +1,97 @@ +package com.evotech.hd.webserver.config.security.util; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.authentication.AnonymousAuthenticationToken; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; + +@Slf4j +public class SecurityUtils { + + /** + * 描述根据账号密码进行调用security进行认证授权 主动调 + * 用AuthenticationManager的authenticate方法实现 + * 授权成功后将用户信息存入SecurityContext当中 + * + * @param username 用户名 + * @param password 密码 + * @param authenticationManager 认证授权管理器, + * @return UserInfo 用户信息 + * @see AuthenticationManager + */ + public static Authentication login(String username, String password, AuthenticationManager authenticationManager) throws AuthenticationException { + //使用security框架自带的验证token生成器 也可以自定义。 + UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, password); + //该方法会去调用userDetailsService.loadUserByUsername()去验证用户名和密码,如果正确,则存储该用户名密码到“security 的 context中” + Authentication authentication = authenticationManager.authenticate(token); + SecurityContextHolder.getContext().setAuthentication(authentication); +// User userInfo = (User) authentication.getPrincipal(); + return authentication; + } + + + /** + * 获取当前登录的所有认证信息 + * + * @return + */ + public static Authentication getAuthentication() { + + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + if (!(authentication instanceof AnonymousAuthenticationToken)) { + return authentication; + } + + return null; + } + + /** + * 获取当前登录的token + * + * @return + */ + public static String getToken() { + + Authentication authentication = getAuthentication(); + return authentication != null ? authentication.getCredentials().toString() : null; + } + + + /** + * 获取当前登录用户名 + * + * @return + */ + public static String getLoginName() { + Authentication authentication = getAuthentication(); + return authentication != null ? authentication.getName() : null; + } + + /** + * 生成BCryptPasswordEncoder密码 + * + * @param password 密码 + * @return 加密字符串 + */ + public static String encryptPassword(String password) { + BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); + return passwordEncoder.encode(password); + } + + /** + * 验证密码是否正确 + * + * @param plainPassword + * @param password + * @return + */ + public static boolean validatePassword(String plainPassword, String password) { + BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); + return passwordEncoder.matches(plainPassword, password); + } + +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/config/security/util/UserUtils.java b/web-server/src/main/java/com/evotech/hd/webserver/config/security/util/UserUtils.java new file mode 100644 index 0000000..7171420 --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/config/security/util/UserUtils.java @@ -0,0 +1,180 @@ +/** + * Copyright © 2021-2026 space All rights reserved. + */ +package com.evotech.hd.webserver.config.security.util; + +import cn.hutool.extra.spring.SpringUtil; +import com.baomidou.mybatisplus.annotation.InterceptorIgnore; +import com.evotech.hd.core.dtos.UserDTO; +import com.evotech.hd.webserver.service.UserService; + +/** + * 用户工具类 + * + * @author space + * @version 2021-06-05 + */ +public class UserUtils { + + + /** + * 根据ID获取用户 + * + * @param id + * @return 取不到返回null + */ +// public static UserDTO get(String id) { +// UserDTO userDTO = (UserDTO) RedisUtils.getInstance().get(CacheNames.USER_CACHE_USER_ID, id); +// +// if (userDTO == null) { +// userDTO = SpringUtil.getBean(UserService.class).get(id); +// } +// return userDTO; +// } + + /** + * 根据登录名获取用户 + * + * @param loginName + * @return 取不到返回null + */ + @InterceptorIgnore + public static UserDTO getByLoginName(String loginName) { + UserDTO userDTO = SpringUtil.getBean(UserService.class).getUserByLoginName(loginName); + return userDTO; + } + + /** + * 清除当前用户缓存 + */ + public static void deleteCache(UserDTO userDTO) { +// RedisUtil.getInstance().del(CacheNames.USER_CACHE_USER_ID, userDTO.getId()); +// RedisUtil.getInstance().del(CacheNames.USER_CACHE_DATA_RULE_LIST, userDTO.getId()); +// RedisUtil.getInstance().del(CacheNames.USER_CACHE_MENU_LIST, userDTO.getId()); +// RedisUtil.getInstance().del(CacheNames.USER_CACHE_ROLE_LIST, userDTO.getId()); +// RedisUtil.getInstance().del(CacheNames.USER_CACHE_TOP_MENU, userDTO.getId()); +// RedisUtil.getInstance().del(CacheNames.USER_CACHE_LOGIN_NAME, userDTO.getLoginName()); +// RedisUtil.getInstance().del(CacheNames.USER_CACHE_LOGIN_NAME, userDTO.getOldLoginName()); + } + + + /** + * 获取当前用户 + * + * @return 取不到返回 new User() + */ +// public static UserDTO getCurrentUserDTO() { +// String username = SecurityUtils.getLoginName(); +// if (StrUtil.isNotEmpty(username)) { +// return getByLoginName(username); +// } +// return new UserDTO(); +// } + + /** + * 获取当前用户角色列表 + * + * @return + */ +// public static List getRoleDTOList() { +// return getCurrentUserDTO().getRoleDTOList(); +// } + + /** + * 获取当前用户授权菜单, admin用户不受角色权限控制可以获取全部菜单 + * + * @return + */ +// public static List getMenuDTOList() { +// String loginName = SecurityUtils.getLoginName(); +// return getMenuDTOListByLoginName(loginName); +// } + + +// public static List getMenuDTOListByLoginName(String loginName) { +// UserDTO user = UserUtils.getByLoginName(loginName); +// List menuDTOList = (List) RedisUtils.getInstance().get(CacheNames.USER_CACHE_MENU_LIST, user.getId()); +// if (menuDTOList == null) { +// +// if (user.isAdmin()) { +// menuDTOList = SpringUtil.getBean(MenuService.class).findList(); +// } else { +// menuDTOList = SpringUtil.getBean(MenuService.class).findByUserId(user.getId()); +// } +// RedisUtils.getInstance().set(CacheNames.USER_CACHE_MENU_LIST, user.getId(), menuDTOList); +// } +// return menuDTOList; +// } +// +// /** +// * 获取当前用户授权数据权限, admin用户不受数据权限限制可以访问全部数据 +// * +// * @return +// */ +// public static List getDataRuleList() { +// UserDTO userDTO = getCurrentUserDTO(); +// if (userDTO.isAdmin()) { +// return Lists.newArrayList(); +// } else { +// return SpringUtil.getBean(DataRuleService.class).findByUserId(userDTO); +// } +// } +// +// /** +// * 获取当前用户授权菜单 +// * +// * @return +// */ +// public static MenuDTO getTopMenuDTO() { +// MenuDTO topMenuDTO = (MenuDTO) RedisUtils.getInstance().get(CacheNames.USER_CACHE_TOP_MENU, getCurrentUserDTO().getId()); +// if (topMenuDTO == null) { +// topMenuDTO = MenuWrapper.INSTANCE.toDTO(SpringUtil.getBean(MenuService.class).getById("1")); +// RedisUtils.getInstance().set(CacheNames.USER_CACHE_TOP_MENU, getCurrentUserDTO().getId(), topMenuDTO); +// } +// return topMenuDTO; +// } +// +// +// /** +// * 获取用户权限 +// * +// * @return +// */ +// public static Set getPermissionsByLoginName(String loginName) { +// Set info = new HashSet<>(); +// List list = UserUtils.getMenuDTOListByLoginName(loginName); +// for (MenuDTO menuDTO : list) { +// if (StrUtil.isNotBlank(menuDTO.getPermission())) { +// for (String permission : StrUtil.split(menuDTO.getPermission(), ",")) { +// info.add(permission); +// } +// } +// } +// return info; +// } +// +// public static Set getPermissions() { +// String loginName = SecurityUtils.getLoginName(); +// return getPermissionsByLoginName(loginName); +// } +// +// +// /** +// * 检测登录名是否合法 +// * +// * @param oldLoginName +// * @param loginName +// * @return +// */ +// public static boolean isCheckLoginName(String oldLoginName, String loginName) { +// if (loginName != null && loginName.equals(oldLoginName)) { +// return true; +// } else if (loginName != null && SpringUtil.getBean(UserService.class).getUserByLoginName(loginName) == null) { +// return true; +// } +// return false; +// } +// + + +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/config/swagger/SwaggerConfig.java b/web-server/src/main/java/com/evotech/hd/webserver/config/swagger/SwaggerConfig.java new file mode 100644 index 0000000..47c0f7b --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/config/swagger/SwaggerConfig.java @@ -0,0 +1,77 @@ +package com.evotech.hd.webserver.config.swagger; + +import com.evotech.hd.webserver.config.security.jwt.TokenProvider; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import springfox.documentation.builders.ApiInfoBuilder; +import springfox.documentation.builders.PathSelectors; +import springfox.documentation.builders.RequestHandlerSelectors; +import springfox.documentation.service.ApiInfo; +import springfox.documentation.service.ApiKey; +import springfox.documentation.service.Contact; +import springfox.documentation.service.SecurityScheme; +import springfox.documentation.spi.DocumentationType; +import springfox.documentation.spring.web.plugins.Docket; +import springfox.documentation.swagger2.annotations.EnableSwagger2; + +import java.util.Collections; + +@Configuration +@EnableSwagger2 +@ConditionalOnProperty(name = "swagger.enable", havingValue = "true") +public class SwaggerConfig { + + + /** + * swagger2的配置文件 + * + * @return Docket + */ + @Bean + public Docket createRestApi() { + return new Docket(DocumentationType.SWAGGER_2) + .groupName("系统管理") + .apiInfo(apiInfo()) + .select() + //此包路径下的类,才生成接口文档 + .apis(RequestHandlerSelectors.basePackage("com.evotech")) + //加了ApiOperation注解的类,才生成接口文档 + .apis(RequestHandlerSelectors.withClassAnnotation(Api.class)) + .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class)) + .paths(PathSelectors.any()) + .build() + .securitySchemes(Collections.singletonList(securityScheme())); + } + + + /*** + * swagger token 配置。 + */ + @Bean + SecurityScheme securityScheme() { + return new ApiKey(TokenProvider.TOKEN, TokenProvider.TOKEN, "header"); + } + + + /** + * api文档的详细信息 + * + * @return + */ + private ApiInfo apiInfo() { + return new ApiInfoBuilder() + // //大标题 + .title("站端接口文档") + // 版本号 + .version("1.0") + // 描述 + .description("API接口") + // 作者 + .contact(new Contact("andy", "https://www.xx.com", "1042025947@qq.com")) + .build(); + } + +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/controller/AccessStrategyController.java b/web-server/src/main/java/com/evotech/hd/webserver/controller/AccessStrategyController.java new file mode 100644 index 0000000..a8bc441 --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/controller/AccessStrategyController.java @@ -0,0 +1,79 @@ +package com.evotech.hd.webserver.controller; + +import com.evotech.hd.core.dtos.OptionDTO; +import com.evotech.hd.core.dtos.RequestParams; +import com.evotech.hd.core.dtos.Result; +import com.evotech.hd.core.dtos.business.AccessStrategyDTO; +import com.evotech.hd.core.dtos.business.airconditioning.AirConditioningBaseDTO; +import com.evotech.hd.core.dtos.system.LogDTO; +import com.evotech.hd.utils.Collections; +import com.evotech.hd.webserver.logging.annotation.ApiLog; +import com.evotech.hd.webserver.logging.enums.LogTypeEnum; +import com.evotech.hd.webserver.service.AccessStrategyService; +import com.evotech.hd.webserver.utils.AccessStrategyUtil; +import com.evotech.hd.webserver.utils.ParamUtils; +import com.evotech.hd.webserver.utils.instruction.InstructionReadUtils; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; +import java.util.List; +import java.util.Map; + +/** + * 基础策略控制 Controller + * + * @author andy.shi + * @ClassName:AccessStrategyController + * @date: 2025年09月26日 15:10 + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@RestController +@RequestMapping("/access/strategy") +@Api(tags = "基础策略控制") +public class AccessStrategyController { + + @Resource + AccessStrategyService accessStrategyService; + + @ApiOperation("页面初始化") + @PostMapping("/init") + @ApiLog(value = "页面初始化") + public Result> init(){ + Map> map = ParamUtils.getBatteryCompartmentPosition(); + List result = Collections.emptyList(); + map.keySet().forEach(key ->{ + result.add(new OptionDTO("机构"+key, key)); + }); + return Result.getInstance().buildList(OptionDTO.class).success(result); + } + + @ApiOperation("查询基础策略") + @PostMapping("/base/info") + @ApiLog(value = "查询基础策略") + public Result info(){ + return Result.getInstance().build(AccessStrategyDTO.class).success(AccessStrategyUtil.getValidAccessStrategyDTO()); + } + + @ApiOperation("基础策略: 修改") + @PostMapping("/update") + @ApiLog(value = "基础策略: 修改", type = LogTypeEnum.OPERATION) + public Result update(@RequestBody AccessStrategyDTO accessStrategyDTO){ + return accessStrategyService.update(accessStrategyDTO); + } + + + + + + + + + + + +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/controller/AirConditioningController.java b/web-server/src/main/java/com/evotech/hd/webserver/controller/AirConditioningController.java new file mode 100644 index 0000000..0443736 --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/controller/AirConditioningController.java @@ -0,0 +1,237 @@ +package com.evotech.hd.webserver.controller; + +import com.evotech.hd.core.dtos.Result; +import com.evotech.hd.core.dtos.business.airconditioning.*; +import com.evotech.hd.exception.InstructionException; +import com.evotech.hd.webserver.logging.annotation.ApiLog; +import com.evotech.hd.webserver.logging.enums.LogTypeEnum; +import com.evotech.hd.webserver.utils.instruction.InstructionReadUtils; +import com.evotech.hd.webserver.utils.instruction.InstructionWriteUtils; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +/** + * 空调控制 Controller + * @author andy.shi + * @ClassName: AirConditioningController + * @date 2025年09月23日 10:28 + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@RestController +@RequestMapping("/air/conditioning") +@Api(tags = "空调控制") +public class AirConditioningController { + + + @ApiOperation("空调控制:基本属性") + @PostMapping("/base/info") + @ApiLog(value = "空调控制:基本属性") + public Result info() throws InstructionException { + AirConditioningBaseDTO airConditioningBaseDTO = new AirConditioningBaseDTO(); + airConditioningBaseDTO.setConnectDeviceStatus(InstructionReadUtils.airConnectDevConnect()); + airConditioningBaseDTO.setRunStatus(InstructionReadUtils.airRunning()); + airConditioningBaseDTO.setSelfCheckStatus(InstructionReadUtils.airSelfCheck()); + // airConditioningBaseDTO.setAxialFlowFanStatus(InstructionReadUtils.axialFlowFan()); + return Result.getInstance().build(AirConditioningBaseDTO.class).success(airConditioningBaseDTO); + } + + @ApiOperation("空调控制:基本属性") + @PostMapping("/base/operation") + @ApiLog(value = "空调控制:基本属性", type = LogTypeEnum.OPERATION) + public Result operation(@ApiParam(value = "操作设备类型: 1空调开关, 2自检开关", name = "deviceType", required = true) @RequestParam("deviceType") Integer deviceType, + @ApiParam(value = "操作状态: true打开, false 关闭", name = "status", required = true) @RequestParam("status") Boolean status) throws Exception { + switch (deviceType){ + case 1: + if (status) { + InstructionWriteUtils.airDeviceOn(); + } else { + InstructionWriteUtils.airDeviceOff(); + } + return Result.getInstance().build(Boolean.class).success(true, status ? "空调已打开" : "空调已关闭"); + case 2: + if (status) { + InstructionWriteUtils.airSelfCheckOn(); + } else { + InstructionWriteUtils.airSelfCheckOff(); + } + return Result.getInstance().build(Boolean.class).success(true, status ? "空调自检已打开" : "空调自检已关闭"); + default: + return Result.getInstance().build(Boolean.class).error("未知的操作类型",false); + } + } + + + @ApiOperation("空调控制:内风机") + @PostMapping("/internal/fan") + @ApiLog(value = "空调控制:内风机") + public Result InternalFan() throws InstructionException { + AirConditioningInternalFanDTO airConditioningInternalFanDTO = new AirConditioningInternalFanDTO(); + airConditioningInternalFanDTO.setAirConIntFanStatus(InstructionReadUtils.airConIntFanRun()); + airConditioningInternalFanDTO.setAirConIntFanAlarm(InstructionReadUtils.airConIntFanAlarm()); + airConditioningInternalFanDTO.setAirConIntFanCurrent(String.valueOf(InstructionReadUtils.airConIntFanCurrent())); + airConditioningInternalFanDTO.setAirConIntFanStartTemp(String.valueOf(InstructionReadUtils.airConIntFanStartTemp())); + airConditioningInternalFanDTO.setAirConIntFanStopTemp(String.valueOf(InstructionReadUtils.airConIntFanStopTemp())); + return Result.getInstance().build(AirConditioningInternalFanDTO.class).success(airConditioningInternalFanDTO); + } + + @ApiOperation("空调控制:内风机操作") + @PostMapping("/internal/fan/operation") + @ApiLog(value = "空调控制:内风机操作", type = LogTypeEnum.OPERATION) + public Result internalFanOperation(@ApiParam(value = "操作类型: 1.内风机待机开启温度, 2内风机待机停止回差温度", name = "deviceType", required = true) @RequestParam("deviceType") Integer deviceType, + @ApiParam(value = "温度值", name = "temperature", required = true) @RequestParam("temperature") Integer temperature) throws Exception { + switch (deviceType){ + case 1: + InstructionWriteUtils.airConIntFanStartTemp(temperature); + return Result.getInstance().build(Boolean.class).success(true, "已调整"); + case 2: + InstructionWriteUtils.airConIntFanStopTemp(temperature); + return Result.getInstance().build(Boolean.class).success(true, "已调整"); + default: + return Result.getInstance().build(Boolean.class).error("未知的操作类型",false); + } + } + + + @ApiOperation("空调控制:外风机") + @PostMapping("/external/fan") + @ApiLog(value = "空调控制:外风机") + public Result externalFan() throws InstructionException { + AirConditioningExternalFanDTO airConditioningExternalFanDTO = new AirConditioningExternalFanDTO(); + airConditioningExternalFanDTO.setAirConExtFanStatus(InstructionReadUtils.airConExtFanRun()); + airConditioningExternalFanDTO.setAirConExtFanAlarm(InstructionReadUtils.airConExtFanAlarm()); + airConditioningExternalFanDTO.setAirConExtFanCurrent(String.valueOf(InstructionReadUtils.airConExtFanCurrent())); + return Result.getInstance().build(AirConditioningExternalFanDTO.class).success(airConditioningExternalFanDTO); + } + + + @ApiOperation("空调控制:压缩机") + @PostMapping("/compressor") + @ApiLog(value = "空调控制:压缩机") + public Result compressor() throws InstructionException { + AirConditioningCompressorDTO airConditioningCompressorDTO = new AirConditioningCompressorDTO(); + airConditioningCompressorDTO.setAirConCmpStatus(InstructionReadUtils.airConCmpRun()); + airConditioningCompressorDTO.setAirConCmpLowCurError(InstructionReadUtils.airConCmpLowCurAlarm()); + airConditioningCompressorDTO.setAirConCmpOverLoadError(InstructionReadUtils.airConCmpOverLoadAlarm()); + airConditioningCompressorDTO.setAirConCmpCurrent(String.valueOf(InstructionReadUtils.airConCmpHeatCurrent())); + airConditioningCompressorDTO.setAirConCmpStartTemp(String.valueOf(InstructionReadUtils.airConCmpStartTemp())); + airConditioningCompressorDTO.setAirConCmpStopTemp(String.valueOf(InstructionReadUtils.airConCmpStopTemp())); + return Result.getInstance().build(AirConditioningCompressorDTO.class).success(airConditioningCompressorDTO); + } + + @ApiOperation("空调控制:压缩机") + @PostMapping("/compressor/operation") + @ApiLog(value = "空调控制:压缩机", type = LogTypeEnum.OPERATION) + public Result compressorOperation(@ApiParam(value = "操作类型: 1.压缩机启动温度, 2.压缩机停止回差值", name = "deviceType", required = true) @RequestParam("deviceType") Integer deviceType, + @ApiParam(value = "温度值", name = "temperature", required = true) @RequestParam("temperature") Integer temperature) throws Exception { + switch (deviceType){ + case 1: + InstructionWriteUtils.airConCmpStartTemp(temperature); + return Result.getInstance().build(Boolean.class).success(true, "已调整"); + case 2: + InstructionWriteUtils.airConCmpStopTemp(temperature); + return Result.getInstance().build(Boolean.class).success(true, "已调整"); + default: + return Result.getInstance().build(Boolean.class).error("未知的操作类型",false); + } + } + + + @ApiOperation("空调控制:加热器") + @PostMapping("/heater") + @ApiLog(value = "空调控制:加热器") + public Result heater() throws InstructionException { + AirConditioningHeaterDTO airConditioningHeaterDTO = new AirConditioningHeaterDTO(); + airConditioningHeaterDTO.setAirConHeatStatus(InstructionReadUtils.airConCmpRun()); + airConditioningHeaterDTO.setAirConHeatLowCurError(InstructionReadUtils.airConCmpLowCurAlarm()); + airConditioningHeaterDTO.setAirConHeatOverLoadError(InstructionReadUtils.airConCmpOverLoadAlarm()); + airConditioningHeaterDTO.setAirConHeatCurrent(String.valueOf(InstructionReadUtils.airConCmpHeatCurrent())); + airConditioningHeaterDTO.setAirConHeatStartTemp(String.valueOf(InstructionReadUtils.airConCmpStartTemp())); + airConditioningHeaterDTO.setAirConHeatStopTemp(String.valueOf(InstructionReadUtils.airConCmpStopTemp())); + return Result.getInstance().build(AirConditioningHeaterDTO.class).success(airConditioningHeaterDTO); + } + + @ApiOperation("空调控制:加热器") + @PostMapping("/heater/operation") + @ApiLog(value = "空调控制:加热器", type = LogTypeEnum.OPERATION) + public Result heaterOperation(@ApiParam(value = "操作类型: 1.加热器启动温度, 2.加热器停止回差值", name = "deviceType", required = true) @RequestParam("deviceType") Integer deviceType, + @ApiParam(value = "温度值", name = "temperature", required = true) @RequestParam("temperature") Integer temperature) throws Exception { + switch (deviceType){ + case 1: + InstructionWriteUtils.airConHeatStartTemp(temperature); + return Result.getInstance().build(Boolean.class).success(true, "已调整"); + case 2: + InstructionWriteUtils.airConHeatStopTemp(temperature); + return Result.getInstance().build(Boolean.class).success(true, "已调整"); + default: + return Result.getInstance().build(Boolean.class).error("未知的操作类型",false); + } + } + + + @ApiOperation("空调控制:柜内") + @PostMapping("/inside") + @ApiLog(value = "空调控制:柜内") + public Result inside() throws InstructionException { + AirConditioningIntDTO airConditioningIntDTO = new AirConditioningIntDTO(); + airConditioningIntDTO.setAirConIntTempSensorError(InstructionReadUtils.airConIntTempSensorAlarm()); + airConditioningIntDTO.setAirConIntHumSensorError(InstructionReadUtils.airConIntHumSensorAlarm()); + airConditioningIntDTO.setAirConIntOverTempAlarm(InstructionReadUtils.airConIntOverTempAlarm()); + airConditioningIntDTO.setAirConIntLowTempAlarm(InstructionReadUtils.airConIntLowTempAlarm()); + airConditioningIntDTO.setAirConIntTemp(String.valueOf(InstructionReadUtils.airConIntTemp())); + airConditioningIntDTO.setAirConIntHum(String.valueOf(InstructionReadUtils.airConIntHum())); + airConditioningIntDTO.setAirConIntTempSedCurrent(String.valueOf(InstructionReadUtils.airConIntTempSndCurrent())); + airConditioningIntDTO.setAirConIntHumSedCurrent(String.valueOf(InstructionReadUtils.airConIntHumSndCurrent())); + airConditioningIntDTO.setAirConOverAlmTemp(String.valueOf(InstructionReadUtils.airConOverAlmTemp())); + airConditioningIntDTO.setAirConLowAlmTemp(String.valueOf(InstructionReadUtils.airConLowAlmTemp())); + return Result.getInstance().build(AirConditioningIntDTO.class).success(airConditioningIntDTO); + } + + @ApiOperation("空调控制:柜内") + @PostMapping("/inside/operation") + @ApiLog(value = "空调控制:柜内", type = LogTypeEnum.OPERATION) + public Result insideOperation(@ApiParam(value = "操作类型: 1.柜内高温告警温度值, 2.柜内低温告警温度值", name = "deviceType", required = true) @RequestParam("deviceType") Integer deviceType, + @ApiParam(value = "温度值", name = "temperature", required = true) @RequestParam("temperature") Integer temperature) throws Exception { + switch (deviceType){ + case 1: + InstructionWriteUtils.airConOverAlmTemp(temperature); + return Result.getInstance().build(Boolean.class).success(true, "已调整"); + case 2: + InstructionWriteUtils.airConLowAlmTemp(temperature); + return Result.getInstance().build(Boolean.class).success(true, "已调整"); + default: + return Result.getInstance().build(Boolean.class).error("未知的操作类型",false); + } + } + + + @ApiOperation("空调控制:柜外") + @PostMapping("/external") + @ApiLog(value = "空调控制:柜外") + public Result external() throws InstructionException { + AirConditioningExtDTO airConditioningExtDTO = new AirConditioningExtDTO(); + airConditioningExtDTO.setAirConExtTempSensorError(InstructionReadUtils.airConExtTempSensorAlarm()); + airConditioningExtDTO.setAirConExtTemp(String.valueOf(InstructionReadUtils.airConExtTemp())); + airConditioningExtDTO.setAirConExtErrorAlarm(InstructionReadUtils.airConExtErrorAlarm()); + return Result.getInstance().build(AirConditioningExtDTO.class).success(airConditioningExtDTO); + } + + @ApiOperation("空调控制:其他") + @PostMapping("/other") + @ApiLog(value = "空调控制:柜外") + public Result other() throws InstructionException { + AirConditioningOtherDTO airConditioningOtherDTO = new AirConditioningOtherDTO(); + airConditioningOtherDTO.setAirConCondTempSensorError(InstructionReadUtils.airConCondTempSensorError()); + airConditioningOtherDTO.setAirConSrcVol(String.valueOf(InstructionReadUtils.airConSrcVol())); + airConditioningOtherDTO.setAirConSrcVolOverAlarm(InstructionReadUtils.airConSrcVolOverAlarm()); + return Result.getInstance().build(AirConditioningOtherDTO.class).success(airConditioningOtherDTO); + } + + + + +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/controller/BatteryStrategyController.java b/web-server/src/main/java/com/evotech/hd/webserver/controller/BatteryStrategyController.java new file mode 100644 index 0000000..1a2db93 --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/controller/BatteryStrategyController.java @@ -0,0 +1,59 @@ +package com.evotech.hd.webserver.controller; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.evotech.hd.core.dtos.RequestParams; +import com.evotech.hd.core.dtos.Result; +import com.evotech.hd.core.dtos.business.BatteryStrategyDTO; +import com.evotech.hd.core.dtos.business.OrderReservationDTO; +import com.evotech.hd.webserver.logging.annotation.ApiLog; +import com.evotech.hd.webserver.logging.enums.LogTypeEnum; +import com.evotech.hd.webserver.service.BatteryStrategyService; +import com.evotech.hd.webserver.utils.query.QueryWrapperGenerator; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; + +/** + * 充电策略控制 Controller + * + * @author andy.shi + * @ClassName:AccessStrategyController + * @date: 2025年09月26日 15:10 + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@RestController +@RequestMapping("/battery/strategy") +@Api(tags = "充电策略控制") +public class BatteryStrategyController { + + @Resource + BatteryStrategyService batteryStrategyService; + + @ApiOperation("充电策略列表") + @PostMapping("/page/list") + @ApiLog(value = "充电策略列表") + public Result> pageList(@RequestBody RequestParams params) throws Exception { + QueryWrapper queryWrapper = QueryWrapperGenerator.buildQueryCondition(params.getParams(), BatteryStrategyDTO.class); + return Result.getInstance().buildIPage(BatteryStrategyDTO.class).success(batteryStrategyService.findPageList(new Page(params.getPage().getPage(), params.getPage().getPageSize()), queryWrapper)); + } + + @ApiOperation("创建充电策略") + @PostMapping("/save") + @ApiLog(value = "创建充电策略", type = LogTypeEnum.OPERATION) + public Result save(@RequestBody BatteryStrategyDTO batteryStrategyDTO){ + return batteryStrategyService.saveOrUpdate(batteryStrategyDTO); + } + + + @ApiOperation("删除充电策略") + @PostMapping("/del/{id}") + @ApiLog(value = "删除充电策略", type = LogTypeEnum.OPERATION) + public Result save(@PathVariable("id")Integer id){ + return batteryStrategyService.del(id); + } + +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/controller/CompartmentController.java b/web-server/src/main/java/com/evotech/hd/webserver/controller/CompartmentController.java new file mode 100644 index 0000000..f5d48d8 --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/controller/CompartmentController.java @@ -0,0 +1,135 @@ +package com.evotech.hd.webserver.controller; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.evotech.hd.core.dtos.RequestParams; +import com.evotech.hd.core.dtos.Result; +import com.evotech.hd.core.dtos.business.BatteryCompartmentDTO; +import com.evotech.hd.webserver.logging.annotation.ApiLog; +import com.evotech.hd.webserver.logging.enums.LogTypeEnum; +import com.evotech.hd.webserver.service.BatteryCompartmentService; +import com.evotech.hd.webserver.utils.query.QueryWrapperGenerator; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +/** + * 电池仓 Controller + * + * @ClassName:CompartmentController + * @date: 2025年09月04日 13:57 + * @author andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ + +@RestController +@RequestMapping("/compartment") +@Api(tags = "电池仓管理") +public class CompartmentController { + @Autowired + BatteryCompartmentService batteryCompartmentService; + + @ApiOperation("电池仓列表") + @PostMapping("/page/list") + @ApiLog(value = "电池仓列表") + public Result> pageList(@RequestBody RequestParams params) throws Exception { + QueryWrapper queryWrapper = QueryWrapperGenerator.buildQueryCondition(params.getParams(), BatteryCompartmentDTO.class); + return Result.getInstance().buildIPage(BatteryCompartmentDTO.class).success(batteryCompartmentService.findPageList(new Page(params.getPage().getPage(), params.getPage().getPageSize()), queryWrapper)); + } + + @ApiOperation("初始化电池仓信息") + @PostMapping("/init") + @ApiLog(value = "初始化电池仓信息", type = LogTypeEnum.OPERATION) + public Result init(@ApiParam(value = "最大仓位号", name = "maxNo", required = true) @RequestParam("maxNo") Integer maxNo){ + return batteryCompartmentService.init(maxNo); + } + + @ApiOperation("电池仓详情") + @PostMapping("/detail") + @ApiLog(value = "电池仓详情") + public Result detail(@ApiParam(value = "仓位号; 主键Id", name = "id", required = true) @RequestParam("id") Integer id) throws Exception { + return Result.getInstance().build(BatteryCompartmentDTO.class).success(batteryCompartmentService.getInfo(id)); + } + + + @ApiOperation("设置最大充电功率") + @PostMapping("/setting/max_power") + @ApiLog(value = "设置最大充电功率", type = LogTypeEnum.OPERATION) + public Result settingMaxPower(@ApiParam(value = "最大功率", name = "maxPower", required = true) @RequestParam("maxPower") Integer maxPower, + @ApiParam(value = "仓位号; 主键Id", name = "id", required = true) @RequestParam("id") Integer id + ) throws Exception { + return batteryCompartmentService.settingMaxPower(id, maxPower); + } + + @ApiOperation("充电仓禁用/启用") + @PostMapping("/setting/state") + @ApiLog(value = "充电仓禁用/启用", type = LogTypeEnum.OPERATION) + public Result settingState(@ApiParam(value = "状态, 1正常, 2禁用", name = "state", required = true) @RequestParam("state") Integer state, + @ApiParam(value = "仓位号; 主键Id", name = "id", required = true) @RequestParam("id") Integer id + ) throws Exception { + return batteryCompartmentService.settingState(id, state); + } + + @ApiOperation("开始充电") + @PostMapping("/startChanging") + @ApiLog(value = "开始充电", type = LogTypeEnum.OPERATION) + public Result startChanging(@ApiParam(value = "仓位号; 主键Id", name = "id", required = true) @RequestParam("id") Integer id) throws Exception { + return batteryCompartmentService.startChanging(id); + } + + @ApiOperation("一键开始充电") + @PostMapping("/startChanging/all") + @ApiLog(value = "一键开始充电", type = LogTypeEnum.OPERATION) + public Result startChangingAll() throws Exception { + return batteryCompartmentService.startChangingAll(); + } + + + @ApiOperation("BMS上电") + @PostMapping("/BMSPowerOn") + @ApiLog(value = "BMS上电", type = LogTypeEnum.OPERATION) + public Result BMSPowerOn(@ApiParam(value = "仓位号; 主键Id", name = "id", required = true) @RequestParam("id") Integer id) throws Exception { + return batteryCompartmentService.BMSPowerOn(id); + } + + @ApiOperation("一键BMS上电") + @PostMapping("/BMSPowerOn/all") + @ApiLog(value = "一键BMS上电", type = LogTypeEnum.OPERATION) + public Result BMSPowerOnAll() throws Exception { + return batteryCompartmentService.BMSPowerOnAll(); + } + + + + @ApiOperation("停止充电") + @PostMapping("/stopChanging") + @ApiLog(value = "停止充电", type = LogTypeEnum.OPERATION) + public Result stopChanging(@ApiParam(value = "仓位号; 主键Id", name = "id", required = true) @RequestParam("id") Integer id) throws Exception { + return batteryCompartmentService.stopChanging(id); + } + + @ApiOperation("一键停止充电") + @PostMapping("/stopChanging/all") + @ApiLog(value = "一键停止充电", type = LogTypeEnum.OPERATION) + public Result stopChangingAll() throws Exception { + return batteryCompartmentService.stopChangingAll(); + } + + @ApiOperation("BMS下电") + @PostMapping("/BMSPowerOff") + @ApiLog(value = "BMS下电", type = LogTypeEnum.OPERATION) + public Result BMSPowerOff(@ApiParam(value = "仓位号; 主键Id", name = "id", required = true) @RequestParam("id") Integer id) throws Exception { + return batteryCompartmentService.BMSPowerOff(id); + } + + @ApiOperation("一键BMS下电") + @PostMapping("/BMSPowerOff/all") + @ApiLog(value = "一键BMS下电", type = LogTypeEnum.OPERATION) + public Result BMSPowerOffAll() throws Exception { + return batteryCompartmentService.BMSPowerOffAll(); + } + +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/controller/ElectricityMeterController.java b/web-server/src/main/java/com/evotech/hd/webserver/controller/ElectricityMeterController.java new file mode 100644 index 0000000..7d27291 --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/controller/ElectricityMeterController.java @@ -0,0 +1,38 @@ +package com.evotech.hd.webserver.controller; + +import com.evotech.hd.core.dtos.Result; +import com.evotech.hd.core.dtos.business.electricitymeter.ElectricityMeterBaseDTO; +import com.evotech.hd.exception.InstructionException; +import com.evotech.hd.webserver.logging.annotation.ApiLog; +import com.evotech.hd.webserver.utils.instruction.InstructionReadUtils; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * 电表Controller + * + * @author andy.shi + * @ClassName:ElectricityMeterController + * @date: 2025年09月25日 14:41 + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@RestController +@RequestMapping("/electricity/meter") +@Api(tags = "电表信息") +public class ElectricityMeterController { + + @ApiOperation("电表信息:基本属性") + @PostMapping("/base/info") + @ApiLog(value = "电表信息:基本属性") + public Result info() throws InstructionException { + ElectricityMeterBaseDTO electricityMeterBaseDTO = new ElectricityMeterBaseDTO(); + electricityMeterBaseDTO.setHtSensorDevConnect(InstructionReadUtils.htSensorDevConnect()); + electricityMeterBaseDTO.setHtSensorHumidity(String.valueOf(InstructionReadUtils.htSensorHumidity())); + electricityMeterBaseDTO.setHtSensorTemperature(String.valueOf(InstructionReadUtils.htSensorTemperature())); + return Result.getInstance().build(ElectricityMeterBaseDTO.class).success(electricityMeterBaseDTO); + } + +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/controller/HomeController.java b/web-server/src/main/java/com/evotech/hd/webserver/controller/HomeController.java new file mode 100644 index 0000000..6f5fa0f --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/controller/HomeController.java @@ -0,0 +1,143 @@ +package com.evotech.hd.webserver.controller; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.evotech.hd.core.dtos.RequestParams; +import com.evotech.hd.core.dtos.Result; +import com.evotech.hd.core.dtos.business.HomeCompartmentDTO; +import com.evotech.hd.core.dtos.business.OrderReservationDTO; +import com.evotech.hd.core.dtos.business.OrderSwapDTO; +import com.evotech.hd.core.dtos.system.LogDTO; +import com.evotech.hd.core.dtos.system.ParamsDTO; +import com.evotech.hd.webserver.job.service.ExecutionBatterySwapService; +import com.evotech.hd.webserver.logging.annotation.ApiLog; +import com.evotech.hd.webserver.service.*; +import com.evotech.hd.webserver.utils.sendCloud.CloudSendInfoUtils; +import com.evotech.hd.webserver.utils.ParamUtils; +import com.evotech.hd.webserver.utils.query.QueryWrapperGenerator; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +/** + * 首页 Controller + * + * @ClassName:HomeController + * @date: 2025年08月15日 16:45 + * @author andy.shi + * @contact: 17330188597 + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@RestController +@RequestMapping("/home") +@Api(tags = "首页管理") +public class HomeController { + + @Autowired + OrderSwapService orderSwapService; + @Autowired + OrderReservationService orderReservationService; + @Autowired + LogService logService; + @Autowired + BatteryCompartmentService batteryCompartmentService; + @Autowired + ExecutionBatterySwapService executionBatterySwapService; + @Autowired + private ParamsService paramsService; + + /** + * 查询站端运行状态 + * @return + */ + @ApiOperation("查询站端运行状态") + @GetMapping("/station/running") + @ApiLog(value = "查询站端运行状态") + public Result checkRunning() { + return Result.getInstance().build(Boolean.class).success(ParamUtils.findStationRunningStatus()); + } + /** + * 修改站端运行状态 + * @return + */ + @ApiOperation("修改站端运行状态") + @GetMapping("/station/running/edit") + @ApiLog(value = "修改站端运行状态") + public Result editRunning(@ApiParam(value = "站端状态: true-开始营运; false-停止营运", required = true) Boolean status) { + ParamsDTO paramsDTO = ParamUtils.getParamsDTO(ParamUtils.ParamsConstant.STATION_RUNNING_STATUS); + paramsDTO.setParamValue(String.valueOf(status)); + paramsService.saveParamsDTO(paramsDTO); + return CloudSendInfoUtils.sendStationStatus(status); + } + + /** + * 查询当前执行的订单 + * @return + */ + @ApiOperation("查询当前执行的订单") + @GetMapping("/station/running/order") + @ApiLog(value = "查询当前执行的订单") + public Result runningOrder() { + return orderSwapService.runningOrder(); + } + + /** + * 查询最新的预约单 + * @return + */ + @ApiOperation("查询最新的预约单") + @PostMapping("/station/latest/reservation") + @ApiLog(value = "查询最新的预约单") + public Result> reservation(@RequestBody RequestParams params) throws Exception { + QueryWrapper queryWrapper = QueryWrapperGenerator.buildQueryCondition(params.getParams(), OrderReservationDTO.class); + return Result.getInstance().buildIPage(OrderReservationDTO.class).success(orderReservationService.findPageList(new Page(params.getPage().getPage(), params.getPage().getPageSize()), queryWrapper)); + } + + /** + * 查询最新的日志信息 + * @return + */ + @ApiOperation("查询最新的日志信息") + @PostMapping("/station/latest/log") + @ApiLog(value = "查询最新的日志信息") + public Result> log(@RequestBody RequestParams params) throws Exception { + QueryWrapper queryWrapper = QueryWrapperGenerator.buildQueryCondition(params.getParams(), LogDTO.class); + //只要异常日志和报警日志 + queryWrapper.orderByDesc("a.type"); + return Result.getInstance().buildIPage(LogDTO.class).success(logService.findPageList(new Page(params.getPage().getPage(), params.getPage().getPageSize()), queryWrapper)); + } + + /** + * 查询电池仓信息 + * @return + */ + @ApiOperation("查询电池仓信息") + @GetMapping("/station/compartment") + @ApiLog(value = "查询电池仓信息") + public Result compartment() { + return Result.getInstance().build(HomeCompartmentDTO.class).success(batteryCompartmentService.findHomeCompartmentDTO()); + } + + /** + * 开始换电 + * @return + */ + @ApiOperation("开始换电") + @GetMapping("/station/start/battery") + @ApiLog(value = "开始换电") + public Result startBattery() throws Exception { + executionBatterySwapService.activateAlignmentMechanism(); + return Result.getInstance().build(Boolean.class).success(true); + } + + + + + + + + +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/controller/LogController.java b/web-server/src/main/java/com/evotech/hd/webserver/controller/LogController.java new file mode 100644 index 0000000..a6485dc --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/controller/LogController.java @@ -0,0 +1,56 @@ +package com.evotech.hd.webserver.controller; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.evotech.hd.core.dtos.OptionDTO; +import com.evotech.hd.core.dtos.RequestParams; +import com.evotech.hd.core.dtos.Result; +import com.evotech.hd.core.dtos.system.LogDTO; +import com.evotech.hd.webserver.logging.annotation.ApiLog; +import com.evotech.hd.webserver.logging.enums.LogTypeEnum; +import com.evotech.hd.webserver.service.LogService; +import com.evotech.hd.webserver.utils.query.QueryWrapperGenerator; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +/** + * 日志管理 Controller + * @author andy.shi + * @ClassName LogController + * @date 2025年09月04日 9:57 + * @remark 开发人员联系方式 1042025947@qq.com/微信同步 + */ + +@RestController +@RequestMapping("/log") +@Api(tags = "日志管理") +public class LogController { + + @Autowired + LogService logService; + + @ApiOperation("页面初始化") + @PostMapping("/init") + @ApiLog(value = "页面初始化") + public Result> init(){ + return Result.getInstance().buildList(OptionDTO.class).success(Arrays.stream(LogTypeEnum.values()).map(data ->new OptionDTO(data)).collect(Collectors.toList())); + } + + @ApiOperation("日志列表") + @PostMapping("/page/list") + @ApiLog(value = "日志列表") + public Result> pageList(@RequestBody RequestParams params) throws Exception { + QueryWrapper queryWrapper = QueryWrapperGenerator.buildQueryCondition(params.getParams(), LogDTO.class); + return Result.getInstance().buildIPage(LogDTO.class).success(logService.findPageList(new Page(params.getPage().getPage(), params.getPage().getPageSize()), queryWrapper)); + } +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/controller/LogInstructionController.java b/web-server/src/main/java/com/evotech/hd/webserver/controller/LogInstructionController.java new file mode 100644 index 0000000..097cb8c --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/controller/LogInstructionController.java @@ -0,0 +1,64 @@ +package com.evotech.hd.webserver.controller; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.evotech.hd.core.dtos.OptionDTO; +import com.evotech.hd.core.dtos.RequestParams; +import com.evotech.hd.core.dtos.Result; +import com.evotech.hd.core.dtos.system.InstructionLogDTO; +import com.evotech.hd.core.entity.system.Params; +import com.evotech.hd.utils.Collections; +import com.evotech.hd.webserver.logging.annotation.ApiLog; +import com.evotech.hd.webserver.service.InstructionLogService; +import com.evotech.hd.webserver.service.ParamsService; +import com.evotech.hd.webserver.utils.query.QueryWrapperGenerator; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * 日志管理 Controller + * @author andy.shi + * @ClassName LogController + * @date 2025年09月04日 9:57 + * @remark 开发人员联系方式 1042025947@qq.com/微信同步 + */ + +@RestController +@RequestMapping("/instruction/log") +@Api(tags = "指令日志管理") +public class LogInstructionController { + + @Autowired + InstructionLogService instructionLogService; + @Autowired + ParamsService paramsService; + + @ApiOperation("页面初始化") + @PostMapping("/init") + @ApiLog(value = "页面初始化") + public Result>> init() throws Exception { + List typeOptions = paramsService.findList(QueryWrapperGenerator.buildQueryCondition(new Params(2), Params.class)).stream().map(data->new OptionDTO(data)).collect(Collectors.toList()); + List methodOptions = OptionDTO.getOptionDTOList(Collections.asMap("写入数值","WriteInteger", "读取数值","ReadInteger", "读取字符","ReadString")); + return Result.getInstance().buildMap(new ArrayList()).success(Collections.asMap("typeOptions", typeOptions, "methodOptions", methodOptions)); + } + + @ApiOperation("日志列表") + @PostMapping("/page/list") + @ApiLog(value = "日志列表") + public Result> pageList(@RequestBody RequestParams params) throws Exception { + QueryWrapper queryWrapper = QueryWrapperGenerator.buildQueryCondition(params.getParams(), InstructionLogDTO.class); + return Result.getInstance().buildIPage(InstructionLogDTO.class).success(instructionLogService.findPageList(new Page(params.getPage().getPage(), params.getPage().getPageSize()), queryWrapper)); + } +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/controller/LoginController.java b/web-server/src/main/java/com/evotech/hd/webserver/controller/LoginController.java new file mode 100644 index 0000000..c516176 --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/controller/LoginController.java @@ -0,0 +1,101 @@ +/** + * Copyright © 2021-2026 space All rights reserved. + */ +package com.evotech.hd.webserver.controller; + +import com.evotech.hd.core.dtos.LoginForm; +import com.evotech.hd.core.dtos.Result; +import com.evotech.hd.core.dtos.UserDTO; +import com.evotech.hd.core.enums.CacheNames; +import com.evotech.hd.utils.Collections; +import com.evotech.hd.webserver.config.GlobalProperties; +import com.evotech.hd.webserver.config.redis.utils.RedisUtil; +import com.evotech.hd.webserver.config.security.jwt.TokenProvider; +import com.evotech.hd.webserver.config.security.util.SecurityUtils; +import com.evotech.hd.webserver.config.security.util.UserUtils; +import com.evotech.hd.webserver.logging.annotation.ApiLog; +import com.evotech.hd.webserver.logging.enums.LogTypeEnum; +import com.evotech.hd.webserver.utils.ParamUtils; +import com.evotech.hd.webserver.utils.ResponseUtil; +import io.swagger.annotations.*; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.core.Authentication; +import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.util.Map; + +/** + * 登录Controller + * + * @author andy + * @version 2025-8-1 + */ +@Slf4j +@RestController +@RequestMapping("/sys") +@Api(tags = "登录管理") +public class LoginController { + + @Autowired + AuthenticationManager authenticationManager; + + @Autowired + private RedisUtil redisUtils; + + /** + * 用户登录 + * + * @return + */ + @ApiOperation("登录接口") + @PostMapping("/login") + @ApiLog(value = "用户登录", type = LogTypeEnum.LOGIN) +/* @ApiImplicitParams({ + @ApiImplicitParam(name = TokenProvider.TOKEN, value = "Token", dataType = "String"), + @ApiImplicitParam(name = "user", value = "当前登录用户信息", dataTypeClass = UserDTO.class) + })*/ + public Result login(@RequestBody LoginForm loginForm) { + ResponseUtil responseUtil = new ResponseUtil(); + SecurityUtils.login(loginForm.getUsername(), loginForm.getPassword(), authenticationManager); //登录操作spring security + //登录成功,生成token + UserDTO userDTO = UserUtils.getByLoginName(loginForm.getUsername()); + String token = TokenProvider.createAccessToken(loginForm.getUsername(), userDTO.getPassword()); + responseUtil.add(TokenProvider.TOKEN, token); + //更新登录信息 + updateUserLoginInfo(responseUtil, userDTO, token); + return Result.getInstance().build(Map.class).success(Collections.asMap(TokenProvider.TOKEN, token, "user",userDTO,"stationName", ParamUtils.findStationName(),"stationCode", ParamUtils.findStationCode())); + } + private void updateUserLoginInfo(ResponseUtil responseUtil, UserDTO userDTO, String token) { + String username = userDTO.getLoginName(); + redisUtils.set(CacheNames.USER_CACHE_TOKEN + username + ":" + token, token); + redisUtils.expire(CacheNames.USER_CACHE_TOKEN + username + ":" + token, GlobalProperties.newInstance().getEXPIRE_TIME()); + } + + /** + * 退出登录 + * + * @param request + * @param response + * @return + */ + @ApiOperation("退出登录") + @ApiLog(value = "退出登录", type = LogTypeEnum.LOGIN) + @GetMapping("/logout") + public Result logout(HttpServletRequest request, HttpServletResponse response) { + Authentication auth = SecurityUtils.getAuthentication(); + if (auth != null) { + String token = TokenProvider.resolveToken(request); + redisUtils.del(CacheNames.USER_CACHE_TOKEN + TokenProvider.getLoginName(token) + ":" + token); + new SecurityContextLogoutHandler().logout(request, response, auth); + } + return Result.getInstance().build(Boolean.class).success(true,"退出成功"); + } + +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/controller/ManualOperationController.java b/web-server/src/main/java/com/evotech/hd/webserver/controller/ManualOperationController.java new file mode 100644 index 0000000..6bb0f11 --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/controller/ManualOperationController.java @@ -0,0 +1,373 @@ +package com.evotech.hd.webserver.controller; + +import com.evotech.hd.core.dtos.OptionDTO; +import com.evotech.hd.core.dtos.Result; +import com.evotech.hd.utils.Collections; +import com.evotech.hd.webserver.job.service.ExecutionBatterySwapService; +import com.evotech.hd.webserver.logging.annotation.ApiLog; +import com.evotech.hd.webserver.logging.enums.LogTypeEnum; +import com.evotech.hd.webserver.service.BatteryCompartmentService; +import com.evotech.hd.webserver.utils.ParamUtils; +import com.evotech.hd.webserver.utils.instruction.InstructionWriteUtils; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; +import java.util.List; +import java.util.Map; + +/** + * 手动操作 Controller + * + * @ClassName:ManualOperationController + * @date: 2025年09月16日 14:24 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@Slf4j +@RestController +@RequestMapping("/manual/operation") +@Api(tags = "手动操作") +public class ManualOperationController { + + @Resource + BatteryCompartmentService batteryCompartmentService; + @Resource + ExecutionBatterySwapService executionBatterySwapService; + + + @ApiOperation("页面初始化") + @PostMapping("/init") + @ApiLog(value = "页面初始化") + public Result> init(){ + Map> map = ParamUtils.getBatteryCompartmentPosition(); + List result = Collections.emptyList(); + map.keySet().forEach(key ->{ + OptionDTO parent = new OptionDTO("RGV"+key, key); + List child = Collections.emptyList(); + map.get(key).stream().forEach(val ->{ + child.add(new OptionDTO("电池仓"+val, String.valueOf(val))); + }); + parent.setChildList(child); + result.add(parent); + }); + return Result.getInstance().buildList(OptionDTO.class).success(result); + } + + @ApiOperation("换电机构:归零") + @PostMapping("/rgv/reset/to/zero") + @ApiLog(value = "换电机构:归零", type = LogTypeEnum.OPERATION) + public Result rvgResetToZero(@ApiParam(value = "RGV编号, 指定那个RGV移动", name = "rgvNo", required = true) @RequestParam("rgvNo") String rgvNo){ + try { + Map errorMeg = executionBatterySwapService.controllerExecutionResult(rgvNo); + if(Collections.isNotEmpty(errorMeg)){ + return Result.getInstance().build(Boolean.class).error(String.valueOf(errorMeg.get("errorMsg")), false); + } + InstructionWriteUtils.rgvResetToZero(rgvNo); + return Result.getInstance().build(Boolean.class).success(true); + } catch (Exception e) { + log.error("手动RGV归零出现错误", e.getMessage(), e); + return Result.getInstance().build(Boolean.class).error(e.getMessage(), false); + } + } + + @ApiOperation("换电机构:停止") + @PostMapping("/rgv/stop/move") + @ApiLog(value = "换电机构:停止", type = LogTypeEnum.OPERATION) + public Result rgvStopMove(){ + try { + Map> map = ParamUtils.getBatteryCompartmentPosition(); + map.keySet().forEach(rgvNo ->{ + try { + InstructionWriteUtils.rgvStopMove(rgvNo); + } catch (Exception e) { + throw new RuntimeException(e); + } + }); + return Result.getInstance().build(Boolean.class).success(true); + } catch (Exception e) { + log.error("手动RGV停止移动出现错误", e.getMessage(), e); + return Result.getInstance().build(Boolean.class).error(e.getMessage(), false); + } + } + + @ApiOperation("换电机构:紧急停止") + @PostMapping("/rgv/urgent/stop/move") + @ApiLog(value = "换电机构:停止", type = LogTypeEnum.OPERATION) + public Result rgvUrgentStopMove(){ + try { + Map> map = ParamUtils.getBatteryCompartmentPosition(); + map.keySet().forEach(rgvNo ->{ + try { + InstructionWriteUtils.emergencyStop(rgvNo); + } catch (Exception e) { + throw new RuntimeException(e); + } + }); + + return Result.getInstance().build(Boolean.class).success(true); + } catch (Exception e) { + log.error("紧急停止命令出现错误", e.getMessage(), e); + return Result.getInstance().build(Boolean.class).error(e.getMessage(), false); + } + } + + @ApiOperation("换电机构:重置") + @PostMapping("/rgv/reset") + @ApiLog(value = "换电机构:重置", type = LogTypeEnum.OPERATION) + public Result rgvReset(){ + try { + Map> map = ParamUtils.getBatteryCompartmentPosition(); + map.keySet().forEach(rgvNo ->{ + try { + InstructionWriteUtils.rgvReset(rgvNo); + } catch (Exception e) { + throw new RuntimeException(e); + } + }); + return Result.getInstance().build(Boolean.class).success(true); + } catch (Exception e) { + log.error("手动RGV重置出现错误", e.getMessage(), e); + return Result.getInstance().build(Boolean.class).error(e.getMessage(), false); + } + } + + @ApiOperation("对中机构:移动") + @PostMapping("/cen/move") + @ApiLog(value = "对中机构:移动", type = LogTypeEnum.OPERATION) + public Result cenMove(){ + try { + InstructionWriteUtils.startCorrectInstitution(); + return Result.getInstance().build(Boolean.class).success(true); + } catch (Exception e) { + log.error("手动对中机构移动出现错误", e.getMessage(), e); + return Result.getInstance().build(Boolean.class).error(e.getMessage(), false); + } + } + + + @ApiOperation("对中机构:归零") + @PostMapping("/cen/reset/to/zero") + @ApiLog(value = "对中机构:归零", type = LogTypeEnum.OPERATION) + public Result cenResetToZero(){ + try { + InstructionWriteUtils.closeCorrectInstitution(); + return Result.getInstance().build(Boolean.class).success(true); + } catch (Exception e) { + log.error("手动对中机构归零出现错误", e.getMessage(), e); + return Result.getInstance().build(Boolean.class).error(e.getMessage(), false); + } + } + + + @ApiOperation("电池移仓操作") + @PostMapping("/battery/move") + @ApiLog(value = "电池移仓操作", type = LogTypeEnum.OPERATION) + public Result batteryMove(@ApiParam(value = "指定rgv", name = "rgvNo", required = true) @RequestParam("rgvNo") String rgvNo, + @ApiParam(value = "移动仓号; 主键Id", name = "sourceId", required = true) @RequestParam("sourceId") String sourceId, + @ApiParam(value = "目标仓号; 主键Id", name = "targetId", required = true) @RequestParam("targetId") String targetId){ + try { + + Map errorMeg = executionBatterySwapService.controllerExecutionResult(rgvNo); + if(Collections.isNotEmpty(errorMeg)){ + return Result.getInstance().build(Boolean.class).error(String.valueOf(errorMeg.get("errorMsg")), false); + } + + Integer sourceIdInt = Integer.valueOf(sourceId); + Integer targetIdInt = Integer.valueOf(targetId); + + Map> map = ParamUtils.getBatteryCompartmentPosition(); + Boolean sameWarehouse = false; + List li = map.get(rgvNo); + if(Collections.isNotEmpty(li) && li.contains(sourceIdInt) && li.contains(targetIdInt)){ + sameWarehouse = true; + } + if(sameWarehouse){ + InstructionWriteUtils.takeNewBattery(rgvNo, sourceIdInt); + InstructionWriteUtils.putOldBattery(rgvNo, targetIdInt); + return Result.getInstance().build(Boolean.class).success(true); + }else{ + return Result.getInstance().build(Boolean.class).error("当前两个仓位不在同一机构仓体, 无法移动", false); + } + } catch (Exception e) { + log.error("手动电池移仓操作出现错误", e.getMessage(), e); + return Result.getInstance().build(Boolean.class).error(e.getMessage(), false); + } + } + + + @ApiOperation("消防动作") + @PostMapping("/fire/fighting/action") + @ApiLog(value = "消防动作", type = LogTypeEnum.OPERATION) + public Result fireFightingAction(@ApiParam(value = "指定rgv", name = "rgvNo", required = true) @RequestParam("rgvNo") String rgvNo, + @ApiParam(value = "移动仓号; 主键Id", name = "fireId", required = true) @RequestParam("fireId") String fireId){ + try { + Map errorMeg = executionBatterySwapService.controllerExecutionResult(rgvNo); + if(Collections.isNotEmpty(errorMeg)){ + return Result.getInstance().build(Boolean.class).error(String.valueOf(errorMeg.get("errorMsg")), false); + } + InstructionWriteUtils.fireFightingAction(rgvNo, Integer.valueOf(fireId)); + return Result.getInstance().build(Boolean.class).success(true); + } catch (Exception e) { + log.error("消防动作出现错误", e.getMessage(), e); + return Result.getInstance().build(Boolean.class).error(e.getMessage(), false); + } + } + + + + + + + @ApiOperation("车辆取下电池") + @PostMapping("/car/take/battery") + @ApiLog(value = "车辆取下电池", type = LogTypeEnum.OPERATION) + public Result carTakeBattery(@ApiParam(value = "RGV编号, 指定那个RGV移动", name = "rgvNo", required = true) @RequestParam("rgvNo") String rgvNo){ + try { + Map errorMeg = executionBatterySwapService.controllerExecutionResult(rgvNo); + if(Collections.isNotEmpty(errorMeg)){ + return Result.getInstance().build(Boolean.class).error(String.valueOf(errorMeg.get("errorMsg")), false); + } + //TODO 目测应该得需要指定那个操作平台, 但是目前没找到对应的接口 + InstructionWriteUtils.splitOldBattery(rgvNo); + return Result.getInstance().build(Boolean.class).success(true); + } catch (Exception e) { + log.error("车辆取下电池操作出现错误", e.getMessage(), e); + return Result.getInstance().build(Boolean.class).error(e.getMessage(), false); + } + } + + @ApiOperation("存放旧电池") + @PostMapping("/car/put/battery") + @ApiLog(value = "存放旧电池", type = LogTypeEnum.OPERATION) + public Result carPutBattery(@ApiParam(value = "RGV编号, 指定那个RGV移动", name = "rgvNo", required = true) @RequestParam("rgvNo") String rgvNo, + @ApiParam(value = "目标仓号; 主键Id", name = "targetId", required = true) @RequestParam("targetId") String targetId){ + try { + Map errorMeg = executionBatterySwapService.controllerExecutionResult(rgvNo); + if(Collections.isNotEmpty(errorMeg)){ + return Result.getInstance().build(Boolean.class).error(String.valueOf(errorMeg.get("errorMsg")), false); + } + //存旧电池 + InstructionWriteUtils.putOldBattery(rgvNo, Integer.valueOf(targetId)); + batteryCompartmentService.charging(Integer.valueOf(targetId)); + return Result.getInstance().build(Boolean.class).success(true); + } catch (Exception e) { + log.error("存放旧电池操作出现错误", e.getMessage(), e); + return Result.getInstance().build(Boolean.class).error(e.getMessage(), false); + } + } + + + + @ApiOperation("取指定位置电池") + @PostMapping("/battery/obtain") + @ApiLog(value = "取指定位置电池", type = LogTypeEnum.OPERATION) + public Result batteryObtain(@ApiParam(value = "RGV编号, 指定那个RGV移动", name = "rgvNo", required = true) @RequestParam("rgvNo") String rgvNo, + @ApiParam(value = "目标仓号; 主键Id", name = "sourceId", required = true) @RequestParam("sourceId") String targetId){ + try { + Map errorMeg = executionBatterySwapService.controllerExecutionResult(rgvNo); + if(Collections.isNotEmpty(errorMeg)){ + return Result.getInstance().build(Boolean.class).error(String.valueOf(errorMeg.get("errorMsg")), false); + } + //TODO 目测应该得需要指定那个操作平台, 但是目前没找到对应的接口 + InstructionWriteUtils.takeNewBattery(rgvNo, Integer.valueOf(targetId)); + return Result.getInstance().build(Boolean.class).success(true); + } catch (Exception e) { + log.error("取指定位置电池操作出现错误", e.getMessage(), e); + return Result.getInstance().build(Boolean.class).error(e.getMessage(), false); + } + } + + @ApiOperation("安装新电池") + @PostMapping("/battery/loading") + @ApiLog(value = "安装新电池", type = LogTypeEnum.OPERATION) + public Result batteryObtain(@ApiParam(value = "RGV编号, 指定那个RGV移动", name = "rgvNo", required = true) @RequestParam("rgvNo") String rgvNo){ + try { + Map errorMeg = executionBatterySwapService.controllerExecutionResult(rgvNo); + if(Collections.isNotEmpty(errorMeg)){ + return Result.getInstance().build(Boolean.class).error(String.valueOf(errorMeg.get("errorMsg")), false); + } + //TODO 目测应该得需要指定那个操作平台, 但是目前没找到对应的接口 + InstructionWriteUtils.loadingNewBattery(rgvNo); + return Result.getInstance().build(Boolean.class).success(true); + } catch (Exception e) { + log.error("安装新电池操作出现错误", e.getMessage(), e); + return Result.getInstance().build(Boolean.class).error(e.getMessage(), false); + } + } + + + @ApiOperation("卷帘门开关") + @PostMapping("/door/enterAndExit") + @ApiLog(value = "卷帘门开关操作", type = LogTypeEnum.OPERATION) + public Result enterAndExit(@ApiParam(value = "进出门选项 入口 or 出口", name = "doorNo", required = true) @RequestParam("doorNo") String doorNo, + @ApiParam(value = "开门 1 or 关门 2", name = "type", required = true) @RequestParam("type") String type){ + try { + if(!Collections.asList("入口","出口").contains(doorNo)){ + return Result.getInstance().build(Boolean.class).error("进出门选项", false); + } + if("1".equals(type)){ + //执行开门操作 + InstructionWriteUtils.openRolling(ParamUtils.getDoorParams(doorNo)); + }else if("2".equals(type)){ + //执行开门操作 + InstructionWriteUtils.closeRolling(ParamUtils.getDoorParams(doorNo)); + }else{ + return Result.getInstance().build(Boolean.class).error("未知操作命令", false); + } + return Result.getInstance().build(Boolean.class).success(true); + } catch (Exception e) { + log.error("取指定位置电池操作出现错误", e.getMessage(), e); + return Result.getInstance().build(Boolean.class).error(e.getMessage(), false); + } + } + + + + @ApiOperation("轴流风机操作") + @PostMapping("/axialFlow") + @ApiLog(value = "轴流风机操作", type = LogTypeEnum.OPERATION) + public Result axialFlow(@ApiParam(value = "打开 1 or 关闭 2", name = "type", required = true) @RequestParam("type") String type){ + try { + if("1".equals(type)){ + //执行开门操作 + InstructionWriteUtils.axialFlowFanOn(); + }else if("2".equals(type)){ + //执行开门操作 + InstructionWriteUtils.axialFlowFanOff(); + }else{ + return Result.getInstance().build(Boolean.class).error("未知操作命令", false); + } + return Result.getInstance().build(Boolean.class).success(true); + } catch (Exception e) { + log.error("轴流风机操作出现错误", e.getMessage(), e); + return Result.getInstance().build(Boolean.class).error(e.getMessage(), false); + } + } + + + + + +// @ApiOperation("安装新电池") +// @PostMapping("/battery/loading") +// @ApiLog(value = "安装新电池", type = LogTypeEnum.OPERATION) +// public Result batteryObtain(@ApiParam(value = "RGV编号, 指定那个RGV移动", name = "rgvNo", required = true) @RequestParam("rgvNo") String rgvNo){ +// try { +// //TODO 目测应该得需要指定那个操作平台, 但是目前没找到对应的接口 +// InstructionWriteUtils.loadingNewBattery(rgvNo); +// return Result.getInstance().build(Boolean.class).success(true); +// } catch (Exception e) { +// log.error("取指定位置电池操作出现错误", e.getMessage(), e); +// return Result.getInstance().build(Boolean.class).error(e.getMessage(), false); +// } +// } + + +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/controller/OrderChargingController.java b/web-server/src/main/java/com/evotech/hd/webserver/controller/OrderChargingController.java new file mode 100644 index 0000000..552cc30 --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/controller/OrderChargingController.java @@ -0,0 +1,57 @@ +package com.evotech.hd.webserver.controller; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.evotech.hd.core.dtos.RequestParams; +import com.evotech.hd.core.dtos.Result; +import com.evotech.hd.core.dtos.business.OrderChargingDTO; +import com.evotech.hd.core.dtos.business.OrderSwapDTO; +import com.evotech.hd.core.mapstruct.OrderChargingWrapper; +import com.evotech.hd.core.mapstruct.OrderSwapWrapper; +import com.evotech.hd.webserver.logging.annotation.ApiLog; +import com.evotech.hd.webserver.mapper.OrderChargingMapper; +import com.evotech.hd.webserver.service.OrderChargingService; +import com.evotech.hd.webserver.utils.query.QueryWrapperGenerator; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +/** + * ChargingOrderController + * + * @author andy.shi + * @ClassName:ChargingOrderController + * @date: 2025年10月27日 16:08 + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@RestController +@RequestMapping("/order/charging") +@Api(tags = "充电订单管理") +public class OrderChargingController { + + @Autowired + OrderChargingService orderChargingService; + @Autowired + OrderChargingWrapper orderChargingWrapper; + + @ApiOperation("订单列表") + @PostMapping("/page/list") + @ApiLog(value = "订单列表") + public Result> pageList(@RequestBody RequestParams params) throws Exception { + QueryWrapper queryWrapper = QueryWrapperGenerator.buildQueryCondition(params.getParams(), OrderChargingDTO.class); + return Result.getInstance().buildIPage(OrderChargingDTO.class).success(orderChargingService.findPageList(new Page(params.getPage().getPage(), params.getPage().getPageSize()), queryWrapper)); + } + + /** + * 订单详情 + * @return + */ + @ApiOperation("订单详情") + @PostMapping("/detail") + @ApiLog(value = "订单详情") + public Result detail(@RequestParam("orderId") String orderId){ + return Result.getInstance().build(OrderChargingDTO.class).success(orderChargingWrapper.toDTO(orderChargingService.getById(orderId))); + } +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/controller/OrderReservationController.java b/web-server/src/main/java/com/evotech/hd/webserver/controller/OrderReservationController.java new file mode 100644 index 0000000..0b3396f --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/controller/OrderReservationController.java @@ -0,0 +1,75 @@ +package com.evotech.hd.webserver.controller; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.evotech.hd.core.dtos.RequestParams; +import com.evotech.hd.core.dtos.Result; +import com.evotech.hd.core.dtos.business.OrderReservationDTO; +import com.evotech.hd.webserver.logging.annotation.ApiLog; +import com.evotech.hd.webserver.service.OrderReservationService; +import com.evotech.hd.webserver.utils.query.QueryWrapperGenerator; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * 预约订单类 + * @ClassName:OrderReservationController + * @date: 2025年08月20日 11:18 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ + +@RestController +@RequestMapping("/order/reservation") +@Api(tags = "预约订单管理") +public class OrderReservationController { + + @Autowired + OrderReservationService orderReservationService; + + + /** + * 查询预约订单 + * @return + */ + @ApiOperation("查询预约订单") + @PostMapping("/page/list") + @ApiLog(value = "查询预约订单") + public Result> list(@RequestBody RequestParams params) throws Exception { + QueryWrapper queryWrapper = QueryWrapperGenerator.buildQueryCondition(params.getParams(), OrderReservationDTO.class); + return Result.getInstance().buildIPage(OrderReservationDTO.class).success(orderReservationService.findPageList(new Page(params.getPage().getPage(), params.getPage().getPageSize()), queryWrapper)); + } + + @ApiOperation("创建预约订单") + @PostMapping("/save") + @ApiLog(value = "创建预约订单") + public Result save(@RequestBody OrderReservationDTO orderReservationTO) throws Exception { + return orderReservationService.save(orderReservationTO); + } + + @ApiOperation("取消预约订单") + @PostMapping("/cancel") + @ApiLog(value = "修改预约订单") + public Result cancel(@ApiParam(value = "预约单id", required = true) Integer id){ + return orderReservationService.cancel(id); + } + +// @ApiOperation("取消预约订单") +// @PostMapping("/checkPlateNum") +// @ApiLog(value = "修改预约订单") +// public Result checkPlateNum(@ApiParam(value = "预约单id", required = true) String plateNum) throws Exception { +// return orderReservationService.checkPlateNum(plateNum); +// } + + + + + +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/controller/OrderSwapController.java b/web-server/src/main/java/com/evotech/hd/webserver/controller/OrderSwapController.java new file mode 100644 index 0000000..a8e6dbb --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/controller/OrderSwapController.java @@ -0,0 +1,84 @@ +package com.evotech.hd.webserver.controller; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.evotech.hd.core.dtos.RequestParams; +import com.evotech.hd.core.dtos.Result; +import com.evotech.hd.core.dtos.business.OrderSwapDTO; +import com.evotech.hd.core.mapstruct.OrderSwapWrapper; +import com.evotech.hd.webserver.logging.annotation.ApiLog; +import com.evotech.hd.webserver.logging.enums.LogTypeEnum; +import com.evotech.hd.webserver.service.BatterySwapStepService; +import com.evotech.hd.webserver.service.OrderSwapService; +import com.evotech.hd.webserver.utils.query.QueryWrapperGenerator; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +/** + * 换电订单 + * + * @ClassName:OrderSwapController + * @date: 2025年09月04日 9:57 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ + +@RestController +@RequestMapping("/order/swap") +@Api(tags = "订单管理") +public class OrderSwapController { + + @Autowired + OrderSwapService orderSwapService; + @Autowired + OrderSwapWrapper orderSwapWrapper; + @Autowired + BatterySwapStepService batterySwapStepService; + + @ApiOperation("订单列表") + @PostMapping("/page/list") + @ApiLog(value = "订单列表") + public Result> pageList(@RequestBody RequestParams params) throws Exception { + QueryWrapper queryWrapper = QueryWrapperGenerator.buildQueryCondition(params.getParams(), OrderSwapDTO.class); + return Result.getInstance().buildIPage(OrderSwapDTO.class).success(orderSwapService.findPageList(new Page(params.getPage().getPage(), params.getPage().getPageSize()), queryWrapper)); + } + + /** + * 推送订单那到云端 + * @return + */ + @ApiOperation("手动推送订单到云端") + @PostMapping("/push/cloud") + @ApiLog(value = "手动推送订单到云端", type = LogTypeEnum.OPERATION) + public Result pushCloud(@RequestParam("orderId") String orderId){ + return orderSwapService.sendOrderCompleted(orderId); + } + /** + * 订单详情 + * @return + */ + @ApiOperation("订单详情") + @PostMapping("/detail") + @ApiLog(value = "订单详情") + public Result detail(@RequestParam("orderId") String orderId){ + OrderSwapDTO orderSwapDTO = orderSwapWrapper.toDTO(orderSwapService.getById(orderId)); + orderSwapDTO.setBatterySwapStepDTOList(batterySwapStepService.findListByOrderId(orderSwapDTO.getId())); + return Result.getInstance().build(OrderSwapDTO.class).success(orderSwapDTO); + } + + /** + * 手动换电完成 + * @return + */ + @ApiOperation("手动换电完成") + @PostMapping("/manualBatterySwappingCompleted") + @ApiLog(value = "手动换电完成", type=LogTypeEnum.OPERATION) + public Result ManualBatterySwappingCompleted(@RequestBody OrderSwapDTO orderSwapDTO) throws Exception { + return orderSwapService.updateOrderSwapDTO(orderSwapDTO); + } + + +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/controller/ParamController.java b/web-server/src/main/java/com/evotech/hd/webserver/controller/ParamController.java new file mode 100644 index 0000000..58a7031 --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/controller/ParamController.java @@ -0,0 +1,74 @@ +package com.evotech.hd.webserver.controller; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.evotech.hd.core.dtos.RequestParams; +import com.evotech.hd.core.dtos.Result; +import com.evotech.hd.core.dtos.system.ParamsDTO; +import com.evotech.hd.core.entity.system.Group; +import com.evotech.hd.core.entity.system.Params; +import com.evotech.hd.webserver.logging.annotation.ApiLog; +import com.evotech.hd.webserver.logging.enums.LogTypeEnum; +import com.evotech.hd.webserver.service.GroupService; +import com.evotech.hd.webserver.service.ParamsService; +import com.evotech.hd.webserver.utils.query.QueryWrapperGenerator; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.apache.commons.lang3.ObjectUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +/** + * ParamController + * + * @author andy.shi + * @ClassName:ParamController + * @date: 2025年10月17日 11:57 + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@RestController +@RequestMapping("/basic/param") +@Api(tags = "基础参数管理") +public class ParamController { + + @Autowired + ParamsService paramsService; + @Autowired + GroupService groupService; + @ApiOperation("类型树") + @PostMapping("/group/tree") + @ApiLog(value = "类型树") + public Result> treeList(){ + return Result.getInstance().buildList(Group.class).success(groupService.list()); + } + + @ApiOperation("参数列表") + @PostMapping("/list") + @ApiLog(value = "参数列表") + public Result> pageList(@RequestBody RequestParams params) throws Exception { + QueryWrapper queryWrapper = QueryWrapperGenerator.buildQueryCondition(params.getParams(), ParamsDTO.class); + return Result.getInstance().buildIPage(ParamsDTO.class).success(paramsService.findPageList(new Page(params.getPage().getPage(), params.getPage().getPageSize()), queryWrapper)); + } + + @ApiOperation("新增或修改") + @PostMapping("/save") + @ApiLog(value = "新增或修改", type = LogTypeEnum.OPERATION) + public Result save(@RequestBody ParamsDTO param) throws Exception { + Params params = paramsService.checkParamsByCode(param.getParamCode()); + if(ObjectUtils.isNotEmpty(params)){ + if(param.getId() == null){ + return Result.getInstance().build(Boolean.class).error("当前编码已经存在", false); + } + if(param.getId() != null && !param.getId().equals(params.getId())){ + return Result.getInstance().build(Boolean.class).error("当前编码已经存在", false); + } + } + return Result.getInstance().build(Boolean.class).success(ObjectUtils.isNotEmpty(paramsService.saveParamsDTO(param))); + } +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/controller/RunningInstructionsController.java b/web-server/src/main/java/com/evotech/hd/webserver/controller/RunningInstructionsController.java new file mode 100644 index 0000000..65345d8 --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/controller/RunningInstructionsController.java @@ -0,0 +1,59 @@ +package com.evotech.hd.webserver.controller; + +import com.evotech.hd.core.dtos.Result; +import com.evotech.hd.core.dtos.business.RunningInstructionsDTO; +import com.evotech.hd.core.entity.business.RunningInstructions; +import com.evotech.hd.webserver.logging.annotation.ApiLog; +import com.evotech.hd.webserver.logging.enums.LogTypeEnum; +import com.evotech.hd.webserver.service.RunningInstructionsService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.ObjectUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +/** + * RunningInstructionsController + * + * @author andy.shi + * @ClassName:RunningInstructionsController + * @date: 2025年10月18日 10:45 + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@Slf4j +@RestController +@RequestMapping("/running/instructions") +@Api(tags = "运行指令控制") +public class RunningInstructionsController { + + @Autowired + RunningInstructionsService runningInstructionsService; + @ApiOperation("类型树") + @PostMapping("/group/tree") + @ApiLog(value = "类型树") + public Result> treeList(){ + return Result.getInstance().buildList(RunningInstructionsDTO.class).success(runningInstructionsService.findList()); + } + + @ApiOperation("新增") + @PostMapping("/save") + @ApiLog(value = "新增", type = LogTypeEnum.OPERATION) + public Result save(@RequestBody RunningInstructionsDTO runningInstructionsDTO){ + RunningInstructions runningInstructions = runningInstructionsService.getOneByCode(runningInstructionsDTO.getCode()); + if(ObjectUtils.isNotEmpty(runningInstructions)){ + if(runningInstructionsDTO.getId() == null){ + return Result.getInstance().build(Boolean.class).error("当前指令编码已经存在", false); + } + if(runningInstructionsDTO.getId() != null && !runningInstructionsDTO.getId().equals(runningInstructions.getId())){ + return Result.getInstance().build(Boolean.class).error("当前指令编码已经存在", false); + } + } + return Result.getInstance().build(Boolean.class).success(runningInstructionsService.saveDTO(runningInstructionsDTO)); + } +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/controller/RunningInstructionsDetailController.java b/web-server/src/main/java/com/evotech/hd/webserver/controller/RunningInstructionsDetailController.java new file mode 100644 index 0000000..e4241ea --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/controller/RunningInstructionsDetailController.java @@ -0,0 +1,58 @@ +package com.evotech.hd.webserver.controller; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.evotech.hd.core.dtos.RequestParams; +import com.evotech.hd.core.dtos.Result; +import com.evotech.hd.core.dtos.business.RunningInstructionsDetailDTO; +import com.evotech.hd.webserver.logging.annotation.ApiLog; +import com.evotech.hd.webserver.logging.enums.LogTypeEnum; +import com.evotech.hd.webserver.service.RunningInstructionsDetailService; +import com.evotech.hd.webserver.utils.query.QueryWrapperGenerator; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +/** + * RunningInstructionsController + * + * @author andy.shi + * @ClassName:RunningInstructionsController + * @date: 2025年10月18日 10:45 + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@Slf4j +@RestController +@RequestMapping("/running/instructions/detail") +@Api(tags = "运行指令详情") +public class RunningInstructionsDetailController { + + @Autowired + RunningInstructionsDetailService runningInstructionsDetailService; + @ApiOperation("参数列表") + @PostMapping("/list") + @ApiLog(value = "参数列表") + public Result> pageList(@RequestBody RequestParams params) throws Exception { + QueryWrapper queryWrapper = QueryWrapperGenerator.buildQueryCondition(params.getParams(), RunningInstructionsDetailDTO.class); + return Result.getInstance().buildIPage(RunningInstructionsDetailDTO.class).success(runningInstructionsDetailService.findPageList(new Page(params.getPage().getPage(), params.getPage().getPageSize()), queryWrapper)); + } + + + @ApiOperation("新增或修改") + @PostMapping("/save") + @ApiLog(value = "新增或修改", type = LogTypeEnum.OPERATION) + public Result save(@RequestBody RunningInstructionsDetailDTO param) throws Exception { + return Result.getInstance().build(Boolean.class).success(runningInstructionsDetailService.saveDTO(param)); + } + + @ApiOperation("删除") + @PostMapping("/delete/{id}") + @ApiLog(value = "删除指令", type = LogTypeEnum.OPERATION) + public Result delete(@PathVariable("id")String id) throws Exception { + return Result.getInstance().build(Boolean.class).success(runningInstructionsDetailService.removeById(id)); + } + +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/controller/TestController.java b/web-server/src/main/java/com/evotech/hd/webserver/controller/TestController.java new file mode 100644 index 0000000..1ccca1d --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/controller/TestController.java @@ -0,0 +1,1385 @@ +package com.evotech.hd.webserver.controller; + +import cn.hutool.extra.spring.SpringUtil; +import com.alibaba.fastjson2.JSONObject; +import com.evotech.hd.core.dtos.Result; +import com.evotech.hd.exception.InstructionException; +import com.evotech.hd.utils.Collections; +import com.evotech.hd.webserver.job.service.ExecutionBatterySwapService; +import com.evotech.hd.webserver.logging.annotation.ApiLog; +import com.evotech.hd.webserver.logging.enums.LogTypeEnum; +import com.evotech.hd.webserver.utils.AccessStrategyUtil; +import com.evotech.hd.webserver.utils.ParamUtils; +import com.evotech.hd.webserver.utils.instruction.InstructionReadUtils; +import com.evotech.hd.webserver.utils.instruction.InstructionWriteUtils; +import com.evotech.hd.webserver.utils.sendCloud.CloudSendInfoUtils; +import com.evotech.hd.webserver.websocket.controller.WebSocketUtils; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +import springfox.documentation.spring.web.scanners.ApiDescriptionLookup; + +import javax.annotation.Resource; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * TestController + * + * @author andy.shi + * @ClassName:TestController + * @date: 2025年11月13日 14:03 + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@RestController +@RequestMapping("/test/rgv") +@Api(tags = "循环测试") +public class TestController { + + private static final Map> taskRgvMap = new ConcurrentHashMap<>(); + private static final Map closeRunMap = new ConcurrentHashMap<>(); + private static final Map categoryRunMap = new ConcurrentHashMap<>(); + + public static TestController getInstance(){ + return new TestController(); + } + + @Resource + ExecutionBatterySwapService executionBatterySwapService; + + + @PostMapping("/test") + public void test() throws Exception { + InstructionWriteUtils.openRolling(ParamUtils.getDoorParams("入口")); + } + + @ApiOperation("开始") + @PostMapping("/start/{category}") + @ApiLog(value = "开始", type = LogTypeEnum.OPERATION) + public Result start(@PathVariable("category") final String category, + @RequestParam("rgvNo") final String rgvNo, + @RequestParam("maxNum") final Integer maxNum, + @RequestParam("type") final String type, + @RequestParam(value = "sourcePositionNo", required = false) final String sourcePositionNo, + @RequestParam(value = "targetPositionNo", required = false) final String targetPositionNo) throws InstructionException, InterruptedException { + + // type 1, 取存 2 拆装 + //检查RGV是否运作 + if(executionBatterySwapService.executionResult(rgvNo)){ + //睡了1秒再次检查, 防止空闲冲突 + Thread.sleep(1000); + if(executionBatterySwapService.executionResult(rgvNo)){ + //进入循环之前, 只查询一次, 防止数据错乱 + List rgvTotal = AccessStrategyUtil.getEffectiveRgvNoList(); + if(Collections.isEmpty(rgvTotal)){ + return Result.getInstance().error("没有生效的RGV"); + } + + closeRunMap.put(rgvNo, new AtomicBoolean(false)); + categoryRunMap.put(rgvNo,category); + CompletableFuture future = CompletableFuture.runAsync( + () -> { + //如果是拆装 + if(type.equals("2")){ + //首先执行开门操作 + try { + WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method","testRgv"+category,"data",String.format("%s 下发打开开合门命令", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()))))); + InstructionWriteUtils.openCenDoor(); + Boolean result = false; + do{ + Thread.sleep(ParamUtils.getInstructionIntervalRequestMilliseconds()); + if(result = InstructionReadUtils.getCenRbDoorOpenStatus()){ + WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method","testRgv"+category,"data",String.format("%s 开合门已打开", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()))))); + } + }while (!result); + } catch (Exception e) { + throw new RuntimeException("打开开合门出现异常:",e); + } + } + + + + + for (int runNun=0 ; runNun < maxNum; runNun++){ + if(checkJt(rgvNo, category, runNun)){ + return; + } + if(type.equals("1")){ + //取出 + try { + WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method","testRgv"+category,"data", String.format("第[%s]次执行: 执行时间:[%s]; RRG:[%s], 电池仓位[%s]>>>>>下发取出命令", (runNun+1), new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()), rgvNo, sourcePositionNo)))); + InstructionWriteUtils.takeNewBattery(rgvNo, Integer.valueOf(sourcePositionNo)); + Boolean openResult = false; + do{ + errorCheck(rgvNo, category, runNun); + if(checkJt(rgvNo, category, runNun)){ + return; + } + Thread.sleep(ParamUtils.getInstructionIntervalRequestMilliseconds()); + //跳开查询 + if(openResult = executionBatterySwapService.checkOpenExecutionResult(rgvNo)){ + WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method","testRgv"+category,"data", String.format("第[%s]次执行: 执行时间:[%s]; RRG:[%s], 电池仓位[%s]>>>>>执行取出操作", (runNun+1), new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()), rgvNo, sourcePositionNo)))); + Boolean endResult = false; + do{ + errorCheck(rgvNo, category, runNun); + if(checkJt(rgvNo, category, runNun)){ + return; + } + Thread.sleep(ParamUtils.getInstructionIntervalRequestMilliseconds()); + //执行结束查询 + if(endResult = executionBatterySwapService.executionResult(rgvNo)){ + errorCheck(rgvNo, category, runNun); + if(checkJt(rgvNo, category, runNun)){ + return; + } + WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method","testRgv"+category,"data", String.format("第[%s]次执行: 执行时间:[%s]; RRG:[%s], 电池仓位[%s]>>>>>执行取出完成, 电池仓位[%s]>>>>>下发存入命令", (runNun+1), new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()), rgvNo, sourcePositionNo, targetPositionNo)))); + InstructionWriteUtils.putOldBattery(rgvNo, Integer.valueOf(sourcePositionNo)); + openResult = false; + do{ + errorCheck(rgvNo, category, runNun); + if(checkJt(rgvNo, category, runNun)){ + return; + } + Thread.sleep(ParamUtils.getInstructionIntervalRequestMilliseconds()); + //跳开查询 + if(openResult = executionBatterySwapService.checkOpenExecutionResult(rgvNo)){ + WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method","testRgv"+category,"data", String.format("第[%s]次执行: 执行时间:[%s]; RRG:[%s], 电池仓位[%s]>>>>>执行存入操作", (runNun+1), new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()),rgvNo, targetPositionNo)))); + endResult = false; + do{ + errorCheck(rgvNo, category, runNun); + if(checkJt(rgvNo, category, runNun)){ + return; + } + Thread.sleep(ParamUtils.getInstructionIntervalRequestMilliseconds()); + //执行结束查询 + if(endResult = executionBatterySwapService.executionResult(rgvNo)){ + errorCheck(rgvNo, category, runNun); + if(checkJt(rgvNo, category, runNun)){ + return; + } + WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method","testRgv"+category,"data",String.format("第[%s]次执行: 执行时间:[%s]; RRG:[%s], 电池仓位[%s]>>>>>存入操作完成", (runNun+1), new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()), rgvNo, targetPositionNo)))); +// InstructionWriteUtils.rgvResetToZero(rgvNo); +// openResult = false; +// do{ +// errorCheck(rgvNo, category, runNun); +// if(checkJt(rgvNo, category, runNun)){ +// return; +// } +// Thread.sleep(ParamUtils.getInstructionIntervalRequestMilliseconds()); +// //跳开查询 +// if(openResult = executionBatterySwapService.executionOpenResult(rgvNo)){ +// WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method","testRgv"+category,"data",String.format("第[%s]次执行: 执行时间:[%s]; RRG:[%s]>>>>>执行归位操作", (runNun+1), new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()),rgvNo)))); +// endResult = false; +// do{ +// errorCheck(rgvNo, category, runNun); +// if(checkJt(rgvNo, category, runNun)){ +// return; +// } +// Thread.sleep(ParamUtils.getInstructionIntervalRequestMilliseconds()); +// //执行结束查询 +// if(endResult = executionBatterySwapService.executionResult(rgvNo)){ +// //输出 +// WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method","testRgv"+category,"data",String.format("第[%s]次执行: 执行时间:[%s]; RRG:[%s]>>>>>归位操作完成",(runNun+1), new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()), rgvNo)))); +// errorCheck(rgvNo, category, runNun); +// } +// }while(!endResult); +// } +// }while (!openResult); + } + }while(!endResult); + } + }while (!openResult); + } + }while(!endResult); + } + }while (!openResult); + } catch (Exception e) { + try { + String message = e.getMessage(); + Map error = executionBatterySwapService.executionErrorResult(rgvNo); + if(Collections.isNotEmpty(error)){ + message = JSONObject.toJSONString(error); + } + closeRunMap.put(rgvNo, new AtomicBoolean(true)); + throw new RuntimeException(String.format(" 执行时间:[%s]; RRG:[%s] 出现异常; 机构区间为:{%s}; 执行类型为:{%s}; 执行次数为:{%s}; 异常信息为:{%s}",new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()), rgvNo, category, "取存", (runNun+1), message), e); + } catch (InstructionException ex) { + throw new RuntimeException(ex); + } + } + }else if(type.equals("2")){ + try { + WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method","testRgv"+category,"data", String.format("第[%s]次执行: 执行时间:[%s]; RRG:[%s]>>>>>下发拆电池命令", (runNun+1), new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()), rgvNo)))); + InstructionWriteUtils.splitOldBattery(rgvNo); + Boolean openResult = false; + do { + errorCheck(rgvNo, category, runNun); + if(checkJt(rgvNo, category, runNun)){ + return; + } + Thread.sleep(ParamUtils.getInstructionIntervalRequestMilliseconds()); + //跳开查询 + if (openResult = executionBatterySwapService.checkOpenExecutionResult(rgvNo)) { + WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method","testRgv"+category,"data", String.format("第[%s]次执行: 执行时间:[%s]; RRG:[%s]>>>>>执行拆电池操作", (runNun+1), new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()), rgvNo)))); + Boolean result = false; + do{ + errorCheck(rgvNo, category, runNun); + if(checkJt(rgvNo, category, runNun)){ + return; + } + Thread.sleep(ParamUtils.getInstructionIntervalRequestMilliseconds()); + if (result = executionBatterySwapService.executionResult(rgvNo)){ + errorCheck(rgvNo, category, runNun); + if(checkJt(rgvNo, category, runNun)){ + return; + } + WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method","testRgv"+category,"data", String.format("第[%s]次执行: 执行时间:[%s]; RRG:[%s]>>>>>执行拆电池操作完成, 下发装电池命令", (runNun+1), new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()), rgvNo)))); + InstructionWriteUtils.loadingNewBattery(rgvNo); + openResult = false; + do { + errorCheck(rgvNo, category, runNun); + if(checkJt(rgvNo, category, runNun)){ + return; + } + Thread.sleep(ParamUtils.getInstructionIntervalRequestMilliseconds()); + //跳开查询 + if (openResult = executionBatterySwapService.checkOpenExecutionResult(rgvNo)) { + WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method","testRgv"+category,"data", String.format("第[%s]次执行: 执行时间:[%s]; RRG:[%s]>>>>>执行装电池操作", (runNun+1), new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()), rgvNo)))); + result = false; + do { + errorCheck(rgvNo, category, runNun); + if(checkJt(rgvNo, category, runNun)){ + return; + } + Thread.sleep(ParamUtils.getInstructionIntervalRequestMilliseconds()); + if(result = executionBatterySwapService.executionResult(rgvNo)){ + errorCheck(rgvNo, category, runNun); + if(checkJt(rgvNo, category, runNun)){ + return; + } + WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method","testRgv"+category,"data", String.format("第[%s]次执行: 执行时间:[%s]; RRG:[%s]>>>>>执行装电池操作完成", (runNun+1),new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()), rgvNo)))); +// InstructionWriteUtils.rgvResetToZero(rgvNo); +// openResult = false; +// do { +// errorCheck(rgvNo, category, runNun); +// if(checkJt(rgvNo, category, runNun)){ +// return; +// } +// Thread.sleep(ParamUtils.getInstructionIntervalRequestMilliseconds()); +// //跳开查询 +// if (openResult = executionBatterySwapService.executionOpenResult(rgvNo)) { +// WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method","testRgv"+category,"data", String.format("第[%s]次执行: 执行时间:[%s]; RRG:[%s]>>>>>执行归位操作", (runNun+1),new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()), rgvNo)))); +// result = false; +// do{ +// errorCheck(rgvNo, category, runNun); +// if(checkJt(rgvNo, category, runNun)){ +// return; +// } +// Thread.sleep(ParamUtils.getInstructionIntervalRequestMilliseconds()); +// if(result = executionBatterySwapService.executionResult(rgvNo)){ +// //输出 +// WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method","testRgv"+category,"data",String.format("第[%s]次执行: 执行时间:[%s]; RRG:[%s]>>>>>归位操作完成",(runNun+1), new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()), rgvNo)))); +// errorCheck(rgvNo, category, runNun); +// } +// }while (!result); +// } +// }while (!openResult); + } + }while(!result); + } + }while (!openResult); + } + }while (!result); + } + }while (!openResult); + } catch (Exception e) { + try { + String message = e.getMessage(); + Map error = executionBatterySwapService.executionErrorResult(rgvNo); + if(Collections.isNotEmpty(error)){ + message = JSONObject.toJSONString(error); + } + closeRunMap.put(rgvNo, new AtomicBoolean(true)); + throw new RuntimeException(String.format(" 执行时间:[%s]; RRG:[%s] 出现异常; 机构区间为:{%s}; 执行类型为:{%s}; 执行次数为:{%s}; 异常信息为:{%s}",new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()), rgvNo, category, "拆装", (runNun+1), message), e); + } catch (InstructionException ex) { + throw new RuntimeException(ex); + } + } + } + WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method","testRgv"+category,"data",String.format("第[%s]次执行: 执行时间:[%s]; RRG:[%s]>>>>>全部执行完成, 准备执行下一次循环任务>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>",(runNun+1), new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()), rgvNo)))); + } + + //如果是拆装 + if(type.equals("2")){ + //首先执行开门操作 + try { + WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method","testRgv"+category,"data", String.format("%s 下发关闭开合门命令", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()))))); + InstructionWriteUtils.closeCenDoor(); + Boolean result = false; + do{ + Thread.sleep(ParamUtils.getInstructionIntervalRequestMilliseconds()); + if(result = InstructionReadUtils.getCenRbDoorCloseStatus()){ + WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method","testRgv"+category,"data",String.format("%s 开合门已关闭", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()))))); + } + }while (!result); + } catch (Exception e) { + throw new RuntimeException("关闭开合门出现异常:", e); + } + } + + WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method","testRgv"+category,"data",String.format("%s 执行完成", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()))))); + taskRgvMap.remove(rgvNo); + System.out.println("CompletableFuture 任务执行:" + Thread.currentThread().getName() + ",时间:" + System.currentTimeMillis()); + }, + // 延迟执行器:延迟 3 秒 + CompletableFuture.delayedExecutor(3, TimeUnit.SECONDS) + ); +// + // 可选:监听任务状态 + future.whenComplete((unused, throwable) -> { + if (throwable instanceof java.util.concurrent.CancellationException) { + System.out.println("开始---->任务已被取消"); + } else if (throwable != null) { + String message = throwable.getMessage(); + if(StringUtils.isNotEmpty(message)){ + message = message.replace("java.lang.RuntimeException: ",""); + WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method","testRgv"+category,"data",message))); + String[] mesArray = message.split(";"); + // RRG:[1] 出现异常; 机构区间为:{1}; 执行类型为:{取存}; 执行次数为:{1}; 异常信息为:{/ by zero} + CloudSendInfoUtils.sendAlarmWx(Collections.asMap("area","红旗换电站", "message", mesArray[4].replace("异常信息为:{","").replace("}",""))); + } + WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method","testRgv"+category,"data","时间:"+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date())+ "异常信息"+throwable.getMessage()))); + } else { + System.out.println("开始---->任务正常完成"); + taskRgvMap.remove(rgvNo); + } + }); + try { + taskRgvMap.put(rgvNo, future); + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException(e); + } + } + } + + return Result.getInstance().success(); + } + + + + + + + public Boolean checkJt(String rgvNo, String category, Integer runNun) { + if(closeRunMap.get(rgvNo).get()){ + WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method","testRgv"+category,"data", String.format("第[%s]次及以后得执行: 强制终止", (runNun+1))))); + return true; + } + return false; + } + + public void errorCheck(final String rgvNo, final String category, final Integer runNun) throws Exception { + Map error = executionBatterySwapService.executionErrorResult(rgvNo); + if(Collections.isNotEmpty(error)){ + String message = JSONObject.toJSONString(error); + stopRgv(rgvNo, rgvNo); + throw new RuntimeException(String.format("RRG:[%s] 出现异常; 机构区间为:{%s}; 执行类型为:{%s}; 执行次数为:{%s}; 异常信息为:{%s}",rgvNo, category, "取存", (runNun+1), message)); + } + + } + + + + + @ApiOperation("结束") + @PostMapping("/stop/{category}") + @ApiLog(value = "结束", type = LogTypeEnum.OPERATION) + public Result stop(@PathVariable("category") final String category, @RequestParam("rgvNo") final String rgvNo) throws Exception { + //检查RGV是否运作 + if(executionBatterySwapService.executionResult(rgvNo) && taskRgvMap.containsKey(rgvNo)){ + //睡了1秒再次检查, 防止空闲冲突 + Thread.sleep(1000); + if(executionBatterySwapService.executionResult(rgvNo)){ + taskRgvMap.remove(rgvNo); + } + }else{ + CompletableFuture future = taskRgvMap.get(rgvNo); + future.cancel(true); + closeRunMap.put(rgvNo, new AtomicBoolean(true)); + WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method", "testRgv" + category, "data", String.format("执行时间[%s]; RGV[%s]>>>>>下发停止操作命令", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()), rgvNo)))); + CompletableFuture closeFuture = CompletableFuture.runAsync( + () -> { + try { + Boolean openResult = false; + InstructionWriteUtils.rgvStopMove(rgvNo); + do { + errorCheck(rgvNo, category, 0); + if(checkJt(rgvNo, category, 0)){ + return; + } + //跳开查询 + if (openResult = executionBatterySwapService.checkOpenExecutionResult(rgvNo)) { + WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method", "testRgv" + category, "data", String.format("执行时间[%s]; RGV[%s]>>>>>开始执行停止操作", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()), rgvNo)))); + Boolean result = false; + do { + errorCheck(rgvNo, category, 0); + if(checkJt(rgvNo, category, 0)){ + return; + } + if(result = executionBatterySwapService.executionResult(rgvNo)) { + errorCheck(rgvNo, category, 0); + if(checkJt(rgvNo, category, 0)){ + return; + } + WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method", "testRgv" + category, "data", String.format("执行时间[%s]; RGV[%s]>>>>>停止操作完成, 下发归零操作命令", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()), rgvNo)))); + InstructionWriteUtils.rgvResetToZero(rgvNo); + Integer num = 0; + do { + errorCheck(rgvNo, category, 0); + if(checkJt(rgvNo, category, 0)){ + return; + } + num +=1; + //跳开查询 + if (openResult = (executionBatterySwapService.checkOpenExecutionResult(rgvNo) || num > 10)) { + WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method", "testRgv" + category, "data", String.format("执行时间[%s]; RGV[%s]>>>>>开始执行归零操作", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()), rgvNo)))); + result = false; + do { + errorCheck(rgvNo, category, 0); + if(checkJt(rgvNo, category, 0)){ + return; + } + if (result = executionBatterySwapService.executionResult(rgvNo)) { + //输出 + WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method", "testRgv" + category, "data", String.format("执行时间[%s]>>>>>归零操作完成, 任务停止", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()))))); + errorCheck(rgvNo,category, 0); + } + } while (!result); + } + }while (!openResult); + } + }while(!result); + } + }while (!openResult); + + } catch (Exception e) { + try { + String message = e.getMessage(); + Map error = executionBatterySwapService.executionErrorResult(rgvNo); + if(Collections.isNotEmpty(error)){ + message = JSONObject.toJSONString(error); + } + closeRunMap.put(rgvNo, new AtomicBoolean(true)); + throw new RuntimeException(String.format(" 执行时间:[%s]; RRG:[%s] 出现异常; 机构区间为:{%s}; 执行类型为:{%s}; 异常信息为:{%s}",new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()), rgvNo, category, "拆装", message), e); + } catch (InstructionException ex) { + throw new RuntimeException(ex); + } + } + }, + // 延迟执行器:延迟 3 秒 + CompletableFuture.delayedExecutor(1, TimeUnit.SECONDS) + ); + + // 可选:监听任务状态 + closeFuture.whenComplete((unused, throwable) -> { + if (throwable instanceof java.util.concurrent.CancellationException) { + System.out.println("开始---->任务已被取消"); + } else if (throwable != null) { + String message = throwable.getMessage(); + if(StringUtils.isNotEmpty(message)){ + message = message.replace("java.lang.RuntimeException: ",""); + WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method","testRgv"+category,"data",message))); + String[] mesArray = message.split(";"); + // RRG:[1] 出现异常; 机构区间为:{1}; 执行类型为:{取存}; 执行次数为:{1}; 异常信息为:{/ by zero} + CloudSendInfoUtils.sendAlarmWx(Collections.asMap("area","红旗换电站", "message", mesArray[4].replace("异常信息为:{","").replace("}",""))); + } + WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method","testRgv"+category,"data","执行停止操作出现异常, 时间:"+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date())+ "异常信息"+throwable.getMessage()))); + } else { + System.out.println("开始---->任务正常完成"); + taskRgvMap.remove(rgvNo); + } + }); + } + + return Result.getInstance().success(); + } + + public void stopRgv(String taskKey, String rgvNo) { + if(taskRgvMap.containsKey(taskKey) && closeRunMap.containsKey(taskKey) && categoryRunMap.containsKey(taskKey)){ + if(executionBatterySwapService == null){ + executionBatterySwapService = SpringUtil.getBean(ExecutionBatterySwapService.class); + } + try { + CompletableFuture future = taskRgvMap.get(taskKey); + future.cancel(true); + closeRunMap.put(taskKey, new AtomicBoolean(true)); + String category = categoryRunMap.get(taskKey); + categoryRunMap.remove(taskKey); + WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method", "testRgv" + category, "data", String.format("执行时间[%s]; RGV[%s]>>>>>下发急停操作命令", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()), rgvNo)))); + InstructionWriteUtils.emergencyStop(rgvNo); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + } + + + + + public void errorCheck(final String taskKey, final String rgvNo, final String category, final Integer runNun) throws Exception { + Map error = executionBatterySwapService.executionErrorResult(rgvNo); + if(Collections.isNotEmpty(error)){ + String message = JSONObject.toJSONString(error); + stopRgv(taskKey, rgvNo); + throw new RuntimeException(String.format("RRG:[%s] 出现异常; 机构区间为:{%s}; 执行类型为:{%s}; 执行次数为:{%s}; 异常信息为:{%s}",rgvNo, category, "取存", (runNun+1), message)); + } + + } + + + @ApiOperation("全量测试开始") + @PostMapping("/start/all/{category}") + @ApiLog(value = "全量测试开始", type = LogTypeEnum.OPERATION) + public Result startAll(@PathVariable("category") final String category, + @RequestParam("maxNum") final Integer maxNum, + @RequestParam("splitStorageRgvNo") final String splitStorageRgvNo, + @RequestParam("takePackRgvNo") final String takePackRgvNo, + @RequestParam(value = "putNo", required = false) final String putNo, + @RequestParam(value = "takeNo", required = false) final String takeNo) throws InstructionException, InterruptedException { + + //检查RGV是否运作 + if(executionBatterySwapService.executionResult(splitStorageRgvNo) && executionBatterySwapService.executionResult(takePackRgvNo)){ + //睡了1秒再次检查, 防止空闲冲突 + Thread.sleep(1000); + if(executionBatterySwapService.executionResult(splitStorageRgvNo) && executionBatterySwapService.executionResult(takePackRgvNo)){ + //进入循环之前, 只查询一次, 防止数据错乱 + List rgvTotal = AccessStrategyUtil.getEffectiveRgvNoList(); + if(Collections.isEmpty(rgvTotal)){ + return Result.getInstance().error("没有生效的RGV"); + } + String rgv = splitStorageRgvNo+"_"+takePackRgvNo; + + closeRunMap.put(rgv, new AtomicBoolean(false)); + categoryRunMap.put(rgv,category); + + CompletableFuture future = CompletableFuture.runAsync(() ->{ + + //执行开门 + CompletableFuture openFuture = CompletableFuture.supplyAsync(()->{ + try { + WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method","testRgv"+category,"data",String.format(" 执行时间:[%s]; 下发打开开合门命令", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()))))); + InstructionWriteUtils.openCenDoor(); + Boolean result = false; + do{ + Thread.sleep(ParamUtils.getInstructionIntervalRequestMilliseconds()); + if(result = InstructionReadUtils.getCenRbDoorOpenStatus()){ + WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method","testRgv"+category,"data",String.format(" 执行时间:[%s]; 开合门已打开", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()))))); + return true; + } + }while (!result); + } catch (Exception e) { + throw new RuntimeException("打开开合门出现异常:",e); + } + return false; + }); + + if(openFuture.join()) { + //如果是拆装 + for (int runNun = 0; runNun < maxNum; runNun++) { + //检查是否停止循环 + if (checkJt(rgv, category, runNun)) { + return; + } + //拆 + String splitStorageRgv = splitStorageRgvNo; + //装 + String loadingRgv = takePackRgvNo; + + if (rgvTotal.size() > 1) { + splitStorageRgv = (runNun % 2 == 0 ? splitStorageRgvNo : takePackRgvNo); + loadingRgv = (runNun % 2 == 0 ? takePackRgvNo : splitStorageRgvNo); + } + //存 + String putCwNo = (runNun % 2 == 0 ? putNo : takeNo); + //取 + String takeCwNo = (runNun % 2 == 0 ? takeNo : putNo); + + int finalRunNun = runNun + 1; + WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method", "testRgv" + category, "data", String.format("第[%s]次执行: 执行时间:[%s]; 拆存rgv[%s], 取装rgv[%s], 存仓[%s], 取仓[%s]>>>>>>", finalRunNun, new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()), splitStorageRgv, loadingRgv, putCwNo, takeCwNo)))); + String finalSplitStorageRgv = splitStorageRgv; + //根据开门结果, 执行拆除 + CompletableFuture splitFuture = CompletableFuture.supplyAsync(() -> { + //开始执行拆除 + try { + WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method", "testRgv" + category, "data", String.format("第[%s]次执行: 执行时间:[%s]; RRG:[%s]>>>>>下发拆电池命令", finalRunNun, new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()), finalSplitStorageRgv)))); + InstructionWriteUtils.splitOldBattery(finalSplitStorageRgv); + Boolean oResult = false; + do { + errorCheck(rgv, finalSplitStorageRgv, category, finalRunNun); + if (checkJt(rgv, category, finalRunNun)) { + return false; + } + Thread.sleep(ParamUtils.getInstructionIntervalRequestMilliseconds()); + //跳开查询 + if (oResult = executionBatterySwapService.checkOpenExecutionResult(finalSplitStorageRgv)) { + WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method", "testRgv" + category, "data", String.format("第[%s]次执行: 执行时间:[%s]; RRG:[%s]>>>>>执行拆电池操作", finalRunNun, new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()), finalSplitStorageRgv)))); + Boolean result = false; + do { + errorCheck(rgv, finalSplitStorageRgv, category, finalRunNun); + if (checkJt(rgv, category, finalRunNun)) { + return false; + } + Thread.sleep(ParamUtils.getInstructionIntervalRequestMilliseconds()); + if (result = executionBatterySwapService.executionResult(finalSplitStorageRgv)) { + WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method", "testRgv" + category, "data", String.format("第[%s]次执行: 执行时间:[%s]; RRG:[%s]>>>>>拆电执行完成", finalRunNun, new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()), finalSplitStorageRgv)))); + errorCheck(rgv, finalSplitStorageRgv, category, finalRunNun); + if (checkJt(rgv, category, finalRunNun)) { + return false; + } + return true; + } + } while (!result); + } + } while (!oResult); + } catch (Exception e) { + stopRgv(rgv, finalSplitStorageRgv); + return false; + } + return false; + }); + + //检查是否停止循环 + if (checkJt(rgv, category, runNun)) { + return; + } + //根据拆除结果执行存 + CompletableFuture put = splitFuture.thenApplyAsync(splitResult -> { + if (splitResult) { + try { + //拆除完成, 准备执行存 + WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method", "testRgv" + category, "data", String.format("第[%s]次执行: 执行时间:[%s]; RRG:[%s], 电池仓位[%s]>>>>>下发存入命令", finalRunNun, new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()), finalSplitStorageRgv, putCwNo)))); + InstructionWriteUtils.putOldBattery(finalSplitStorageRgv, Integer.valueOf(putCwNo)); + Boolean openResult = false; + do { + errorCheck(rgv, finalSplitStorageRgv, category, finalRunNun); + if (checkJt(rgv, category, finalRunNun)) { + return false; + } + Thread.sleep(ParamUtils.getInstructionIntervalRequestMilliseconds()); + //跳开查询 + if (openResult = executionBatterySwapService.checkOpenExecutionResult(finalSplitStorageRgv)) { + WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method", "testRgv" + category, "data", String.format("第[%s]次执行: 执行时间:[%s]; RRG:[%s], 电池仓位[%s]>>>>>执行存入操作", finalRunNun, new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()), finalSplitStorageRgv, putCwNo)))); + Boolean endResult = false; + do { + errorCheck(rgv, finalSplitStorageRgv, category, finalRunNun); + if (checkJt(rgv, category, finalRunNun)) { + return false; + } + Thread.sleep(ParamUtils.getInstructionIntervalRequestMilliseconds()); + //执行结束查询 + if (endResult = executionBatterySwapService.executionResult(finalSplitStorageRgv)) { + errorCheck(rgv, finalSplitStorageRgv, category, finalRunNun); + if (checkJt(rgv, category, finalRunNun)) { + return false; + } + WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method", "testRgv" + category, "data", String.format("第[%s]次执行: 执行时间:[%s]; RRG:[%s], 电池仓位[%s]>>>>>存入操作完成", finalRunNun, new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()), finalSplitStorageRgv, putCwNo)))); + return true; + } + } while (!endResult); + } + } while (!openResult); + } catch (Exception e) { + stopRgv(rgv, finalSplitStorageRgv); + return false; + } + } + return false; + }); + + //检查是否停止循环 + if (checkJt(rgv, category, runNun)) { + return; + } + + //取 + String finalLoadingRgv = loadingRgv; + CompletableFuture takeFuture = CompletableFuture.supplyAsync(() -> { + try { + WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method", "testRgv" + category, "data", String.format("第[%s]次执行: 执行时间:[%s]; RRG:[%s], 电池仓位[%s]>>>>>下发取出命令", finalRunNun, new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()), finalLoadingRgv, takeCwNo)))); + InstructionWriteUtils.takeNewBattery(finalLoadingRgv, Integer.valueOf(takeCwNo)); + Boolean openResult = false; + do { + errorCheck(rgv, finalLoadingRgv, category, finalRunNun); + if (checkJt(rgv, category, finalRunNun)) { + return false; + } + Thread.sleep(ParamUtils.getInstructionIntervalRequestMilliseconds()); + //跳开查询 + if (openResult = executionBatterySwapService.checkOpenExecutionResult(finalLoadingRgv)) { + WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method", "testRgv" + category, "data", String.format("第[%s]次执行: 执行时间:[%s]; RRG:[%s], 电池仓位[%s]>>>>>执行取出操作", finalRunNun, new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()), finalLoadingRgv, takeCwNo)))); + Boolean endResult = false; + do { + errorCheck(rgv, finalLoadingRgv, category, finalRunNun); + if (checkJt(rgv, category, finalRunNun)) { + return false; + } + Thread.sleep(ParamUtils.getInstructionIntervalRequestMilliseconds()); + //执行结束查询 + if (endResult = executionBatterySwapService.executionResult(finalLoadingRgv)) { + WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method", "testRgv" + category, "data", String.format("第[%s]次执行: 执行时间:[%s]; RRG:[%s], 电池仓位[%s]>>>>>执行取出完成", finalRunNun, new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()), finalLoadingRgv, takeCwNo)))); + errorCheck(rgv, finalLoadingRgv, category, finalRunNun); + if (checkJt(rgv, category, finalRunNun)) { + return false; + } + return true; + + } + } while (!endResult); + } + } while (!openResult); + } catch (Exception e) { + e.printStackTrace(); + stopRgv(rgv, finalLoadingRgv); + return false; + } + return false; + }); + + //检查是否停止循环 + if (checkJt(rgv, category, runNun)) { + return; + } + + //装 + //根据拆除结果, 和取出结果执行装 + CompletableFuture loading = CompletableFuture.allOf(splitFuture, takeFuture).thenApplyAsync(voidResult -> { + Boolean split = splitFuture.join(); + Boolean take = takeFuture.join(); + try { + if (split && take) { + WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method", "testRgv" + category, "data", String.format("第[%s]次执行: 执行时间:[%s]; RRG:[%s]>>>>>执行拆电池操作完成, 下发装电池命令", finalRunNun, new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()), finalLoadingRgv)))); + InstructionWriteUtils.loadingNewBattery(finalLoadingRgv); + Boolean openResult = false; + do { + errorCheck(rgv, finalLoadingRgv, category, finalRunNun); + if (checkJt(rgv, category, finalRunNun)) { + return false; + } + Thread.sleep(ParamUtils.getInstructionIntervalRequestMilliseconds()); + //跳开查询 + if (openResult = executionBatterySwapService.checkOpenExecutionResult(finalLoadingRgv)) { + WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method", "testRgv" + category, "data", String.format("第[%s]次执行: 执行时间:[%s]; RRG:[%s]>>>>>执行装电池操作", finalRunNun, new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()), finalLoadingRgv)))); + Boolean result = false; + do { + errorCheck(rgv, finalLoadingRgv, category, finalRunNun); + if (checkJt(rgv, category, finalRunNun)) { + return false; + } + Thread.sleep(ParamUtils.getInstructionIntervalRequestMilliseconds()); + if (result = executionBatterySwapService.executionResult(finalLoadingRgv)) { + errorCheck(rgv, finalLoadingRgv, category, finalRunNun); + if (checkJt(rgv, category, finalRunNun)) { + return false; + } + WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method", "testRgv" + category, "data", String.format("第[%s]次执行: 执行时间:[%s]; RRG:[%s]>>>>>执行装电池操作完成", finalRunNun, new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()), finalLoadingRgv)))); + return true; + } + } while (!result); + } + } while (!openResult); + + } else { + WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method", "testRgv" + category, "data", String.format("第[%s]次执行: 执行时间:[%s]; RRG:[%s]>>>>>进行安装失败", finalRunNun, new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()), finalLoadingRgv)))); + stopRgv(rgv, finalLoadingRgv); + return false; + } + } catch (Exception e) { + e.printStackTrace(); + stopRgv(rgv, finalLoadingRgv); + return false; + } + return false; + }); + + //检查是否停止循环 + if (checkJt(rgv, category, runNun)) { + return; + } + if (splitFuture.join() && put.join() && takeFuture.join() && loading.join()) { + //关门完成. 执行下次换电 + WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method", "testRgv" + category, "data", String.format("第[%s]次执行: 执行时间:[%s]; >>>>>全部执行完成, 准备执行下一次循环任务>>>>>>>>>>>>>>>>>>>>>", (runNun + 1), new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()))))); + } + } + + + //等待装完, 关门 + CompletableFuture closeFuture =CompletableFuture.supplyAsync(() ->{ + //开门完成 + //开始执行拆除 + try { + WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method","testRgv"+category,"data", String.format("%s 下发关闭开合门命令", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()))))); + InstructionWriteUtils.closeCenDoor(); + Boolean result = false; + do{ + Thread.sleep(ParamUtils.getInstructionIntervalRequestMilliseconds()); + if(result = InstructionReadUtils.getCenRbDoorCloseStatus()){ + WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method","testRgv"+category,"data",String.format("%s 开合门已关闭", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()))))); + return true; + } + }while (!result); + } catch (Exception e) { + throw new RuntimeException("关闭开合门出现异常:", e); + } + return false; + }); + + if(closeFuture.join()){ + WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method","testRgv"+category,"data",String.format("%s 执行次数{%s}全部完成", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()), maxNum)))); + } + } + + }, + // 延迟执行器:延迟 3 秒 + CompletableFuture.delayedExecutor(3, TimeUnit.SECONDS) + ); + + + + +// CompletableFuture future = CompletableFuture.runAsync( +// () -> { +// //如果是拆装 +// for (int runNun=0 ; runNun < maxNum; runNun++){ +// //检查是否停止循环 +// if(checkJt(rgv, category, runNun)){ +// return; +// } +// //计算存旧仓位和取新仓位 +// //拆 +// String splitStorageRgv = splitStorageRgvNo; +// //装 +// String loadingRgv = takePackRgvNo; +// +// if(rgvTotal.size() > 1){ +// splitStorageRgv = (runNun % 2 == 0 ? splitStorageRgvNo : takePackRgvNo); +// loadingRgv = (runNun % 2 == 0 ? takePackRgvNo : splitStorageRgvNo); +// } +// //存 +// String putCwNo = (runNun % 2 == 0 ? putNo : takeNo); +// //取 +// String takeCwNo = (runNun % 2 == 0 ? takeNo : putNo); +// +// int finalRunNun = runNun+1; +// +// WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method","testRgv"+category,"data",String.format("第[%s]次执行: 执行时间:[%s]; 拆存rgv[%s], 取装rgv[%s], 存仓[%s], 取仓[%s]>>>>>>",(runNun+1), new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()), splitStorageRgv, loadingRgv, putCwNo, takeCwNo)))); +// +//// //执行开门 +//// int finalRunNun = runNun+1; +//// CompletableFuture openFuture = CompletableFuture.supplyAsync(()->{ +//// try { +//// WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method","testRgv"+category,"data",String.format("第[%s]次执行: 执行时间:[%s]; 下发打开开合门命令", finalRunNun, new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()))))); +//// InstructionWriteUtils.openCenDoor(); +//// Boolean result = false; +//// do{ +//// Thread.sleep(ParamUtils.getInstructionIntervalRequestMilliseconds()); +//// if(result = InstructionReadUtils.getCenRbDoorOpenStatus()){ +//// WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method","testRgv"+category,"data",String.format("第[%s]次执行: 执行时间:[%s]; 开合门已打开", finalRunNun, new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()))))); +//// return true; +//// } +//// }while (!result); +//// } catch (Exception e) { +//// throw new RuntimeException("打开开合门出现异常:",e); +//// } +//// return false; +//// }); +// +// //检查是否停止循环 +//// if(checkJt(rgv, category, runNun)){ +//// return; +//// } +// +// +// String finalSplitStorageRgv = splitStorageRgv; +// //根据开门结果, 执行拆除 +// CompletableFuture splitFuture = openFuture.thenApplyAsync(openResult ->{ +// //开门完成 +// if(openResult){ +// //开始执行拆除 +// try { +// WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method","testRgv"+category,"data", String.format("第[%s]次执行: 执行时间:[%s]; RRG:[%s]>>>>>下发拆电池命令", finalRunNun, new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()), finalSplitStorageRgv)))); +// InstructionWriteUtils.splitOldBattery(finalSplitStorageRgv); +// Boolean oResult = false; +// do { +// errorCheck(rgv, finalSplitStorageRgv, category, finalRunNun); +// if (checkJt(rgv, category, finalRunNun)) { +// return false; +// } +// Thread.sleep(ParamUtils.getInstructionIntervalRequestMilliseconds()); +// //跳开查询 +// if (oResult = executionBatterySwapService.executionOpenResult(finalSplitStorageRgv)) { +// WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method", "testRgv" + category, "data", String.format("第[%s]次执行: 执行时间:[%s]; RRG:[%s]>>>>>执行拆电池操作", finalRunNun, new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()), finalSplitStorageRgv)))); +// Boolean result = false; +// do { +// errorCheck(rgv, finalSplitStorageRgv, category, finalRunNun); +// if (checkJt(rgv, category, finalRunNun)) { +// return false; +// } +// Thread.sleep(ParamUtils.getInstructionIntervalRequestMilliseconds()); +// if (result = executionBatterySwapService.executionResult(finalSplitStorageRgv)) { +// WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method","testRgv"+category,"data", String.format("第[%s]次执行: 执行时间:[%s]; RRG:[%s]>>>>>拆电执行完成", finalRunNun, new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()), finalSplitStorageRgv)))); +// errorCheck(rgv, finalSplitStorageRgv, category, finalRunNun); +// if (checkJt(rgv, category, finalRunNun)) { +// return false; +// } +// return true; +// } +// } while (!result); +// } +// }while (!oResult); +// } catch (Exception e) { +// stopRgv(rgv, finalSplitStorageRgv); +// return false; +// } +// } +// return false; +// }); +// +// //检查是否停止循环 +// if(checkJt(rgv, category, runNun)){ +// return; +// } +// +// //根据拆除结果执行存 +// CompletableFuture put = splitFuture.thenApplyAsync(splitResult->{ +// if(splitResult){ +// try { +// //拆除完成, 准备执行存 +// WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method","testRgv"+category,"data", String.format("第[%s]次执行: 执行时间:[%s]; RRG:[%s], 电池仓位[%s]>>>>>下发存入命令", finalRunNun, new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()), finalSplitStorageRgv, putCwNo)))); +// InstructionWriteUtils.putOldBattery(finalSplitStorageRgv, Integer.valueOf(putCwNo)); +// Boolean openResult = false; +// do{ +// errorCheck(rgv, finalSplitStorageRgv, category, finalRunNun); +// if(checkJt(rgv, category, finalRunNun)){ +// return false; +// } +// Thread.sleep(ParamUtils.getInstructionIntervalRequestMilliseconds()); +// //跳开查询 +// if(openResult = executionBatterySwapService.executionOpenResult(finalSplitStorageRgv)){ +// WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method","testRgv"+category,"data", String.format("第[%s]次执行: 执行时间:[%s]; RRG:[%s], 电池仓位[%s]>>>>>执行存入操作", finalRunNun, new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()),finalSplitStorageRgv, putCwNo)))); +// Boolean endResult = false; +// do{ +// errorCheck(rgv, finalSplitStorageRgv, category, finalRunNun); +// if(checkJt(rgv, category, finalRunNun)){ +// return false; +// } +// Thread.sleep(ParamUtils.getInstructionIntervalRequestMilliseconds()); +// //执行结束查询 +// if(endResult = executionBatterySwapService.executionResult(finalSplitStorageRgv)){ +// errorCheck(rgv, finalSplitStorageRgv, category, finalRunNun); +// if(checkJt(rgv, category, finalRunNun)){ +// return false; +// } +// WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method","testRgv"+category,"data",String.format("第[%s]次执行: 执行时间:[%s]; RRG:[%s], 电池仓位[%s]>>>>>存入操作完成", finalRunNun, new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()), finalSplitStorageRgv, putCwNo)))); +// return true; +// +//// InstructionWriteUtils.rgvResetToZero(finalSplitStorageRgv); +//// openResult = false; +//// do{ +//// errorCheck(rgv, finalSplitStorageRgv, category, finalRunNun); +//// if(checkJt(rgv, category, finalRunNun)){ +//// return false; +//// } +//// Thread.sleep(ParamUtils.getInstructionIntervalRequestMilliseconds()); +//// //跳开查询 +//// if(openResult = executionBatterySwapService.executionOpenResult(finalSplitStorageRgv)){ +//// WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method","testRgv"+category,"data",String.format("第[%s]次执行: 执行时间:[%s]; RRG:[%s]>>>>>执行归位操作", finalRunNun, new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()),finalSplitStorageRgv)))); +//// endResult = false; +//// do{ +//// errorCheck(rgv, finalSplitStorageRgv, category, finalRunNun); +//// if(checkJt(rgv, category, finalRunNun)){ +//// return false; +//// } +//// Thread.sleep(ParamUtils.getInstructionIntervalRequestMilliseconds()); +//// //执行结束查询 +//// if(endResult = executionBatterySwapService.executionResult(finalSplitStorageRgv)){ +//// //输出 +//// WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method","testRgv"+category,"data",String.format("第[%s]次执行: 执行时间:[%s]; RRG:[%s]>>>>>归位操作完成",finalRunNun, new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()), finalSplitStorageRgv)))); +//// errorCheck(rgv, finalSplitStorageRgv, category, finalRunNun); +//// if(checkJt(rgv, category, finalRunNun)){ +//// return false; +//// } +//// return true; +//// } +//// }while(!endResult); +//// } +//// }while (!openResult); +// } +// }while(!endResult); +// } +// }while (!openResult); +// } catch (Exception e) { +// stopRgv(rgv, finalSplitStorageRgv); +// return false; +// } +// } +// return false; +// }); +// +// //检查是否停止循环 +// if(checkJt(rgv, category, runNun)){ +// return; +// } +// +// //取 +// String finalLoadingRgv = loadingRgv; +// CompletableFuture takeFuture = CompletableFuture.supplyAsync(()->{ +// +// try { +// WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method","testRgv"+category,"data", String.format("第[%s]次执行: 执行时间:[%s]; RRG:[%s], 电池仓位[%s]>>>>>下发取出命令", finalRunNun, new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()), finalLoadingRgv, takeCwNo)))); +// InstructionWriteUtils.takeNewBattery(finalLoadingRgv, Integer.valueOf(takeCwNo)); +// Boolean openResult = false; +// do { +// errorCheck(rgv, finalLoadingRgv, category, finalRunNun); +// if (checkJt(rgv, category, finalRunNun)) { +// return false; +// } +// Thread.sleep(ParamUtils.getInstructionIntervalRequestMilliseconds()); +// //跳开查询 +// if (openResult = executionBatterySwapService.executionOpenResult(finalLoadingRgv)) { +// WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method", "testRgv" + category, "data", String.format("第[%s]次执行: 执行时间:[%s]; RRG:[%s], 电池仓位[%s]>>>>>执行取出操作", finalRunNun, new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()), finalLoadingRgv, takeCwNo)))); +// Boolean endResult = false; +// do { +// errorCheck(rgv, finalLoadingRgv, category, finalRunNun); +// if (checkJt(rgv, category, finalRunNun)) { +// return false; +// } +// Thread.sleep(ParamUtils.getInstructionIntervalRequestMilliseconds()); +// //执行结束查询 +// if (endResult = executionBatterySwapService.executionResult(finalLoadingRgv)) { +// WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method", "testRgv" + category, "data", String.format("第[%s]次执行: 执行时间:[%s]; RRG:[%s], 电池仓位[%s]>>>>>执行取出完成", finalRunNun, new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()), finalLoadingRgv, takeCwNo)))); +// errorCheck(rgv, finalLoadingRgv, category, finalRunNun); +// if (checkJt(rgv, category, finalRunNun)) { +// return false; +// } +// return true; +// +// } +// } while (!endResult); +// } +// }while (!openResult); +// } catch (Exception e) { +// e.printStackTrace(); +// stopRgv(rgv, finalLoadingRgv); +// return false; +// } +// return false; +// }); +// +// //检查是否停止循环 +// if(checkJt(rgv, category, runNun)){ +// return; +// } +// +// //装 +// //根据拆除结果, 和取出结果执行装 +// CompletableFuture loading = CompletableFuture.allOf(splitFuture, takeFuture).thenApplyAsync(voidResult -> { +// Boolean split = splitFuture.join(); +// Boolean take = takeFuture.join(); +// try { +// if(split && take){ +// WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method","testRgv"+category,"data", String.format("第[%s]次执行: 执行时间:[%s]; RRG:[%s]>>>>>执行拆电池操作完成, 下发装电池命令", finalRunNun, new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()), finalLoadingRgv)))); +// InstructionWriteUtils.loadingNewBattery(finalLoadingRgv); +// Boolean openResult = false; +// do { +// errorCheck(rgv, finalLoadingRgv, category, finalRunNun); +// if(checkJt(rgv, category, finalRunNun)){ +// return false; +// } +// Thread.sleep(ParamUtils.getInstructionIntervalRequestMilliseconds()); +// //跳开查询 +// if (openResult = executionBatterySwapService.executionOpenResult(finalLoadingRgv)) { +// WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method","testRgv"+category,"data", String.format("第[%s]次执行: 执行时间:[%s]; RRG:[%s]>>>>>执行装电池操作", finalRunNun, new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()), finalLoadingRgv)))); +// Boolean result = false; +// do { +// errorCheck(rgv, finalLoadingRgv, category, finalRunNun); +// if(checkJt(rgv, category, finalRunNun)){ +// return false; +// } +// Thread.sleep(ParamUtils.getInstructionIntervalRequestMilliseconds()); +// if(result = executionBatterySwapService.executionResult(finalLoadingRgv)){ +// errorCheck(rgv, finalLoadingRgv, category, finalRunNun); +// if(checkJt(rgv, category, finalRunNun)){ +// return false; +// } +// WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method","testRgv"+category,"data", String.format("第[%s]次执行: 执行时间:[%s]; RRG:[%s]>>>>>执行装电池操作完成", finalRunNun,new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()), finalLoadingRgv)))); +// return true; +//// InstructionWriteUtils.rgvResetToZero(finalLoadingRgv); +//// openResult = false; +//// do { +//// errorCheck(rgv, finalLoadingRgv, category, finalRunNun); +//// if(checkJt(rgv, category, finalRunNun)){ +//// return false; +//// } +//// Thread.sleep(ParamUtils.getInstructionIntervalRequestMilliseconds()); +//// //跳开查询 +//// if (openResult = executionBatterySwapService.executionOpenResult(finalLoadingRgv)) { +//// WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method","testRgv"+category,"data", String.format("第[%s]次执行: 执行时间:[%s]; RRG:[%s]>>>>>执行归位操作", finalRunNun,new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()), finalLoadingRgv)))); +//// result = false; +//// do{ +//// errorCheck(rgv, finalLoadingRgv, category, finalRunNun); +//// if(checkJt(rgv, category, finalRunNun)){ +//// return false; +//// } +//// Thread.sleep(ParamUtils.getInstructionIntervalRequestMilliseconds()); +//// if(result = executionBatterySwapService.executionResult(finalLoadingRgv)){ +//// //输出 +//// WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method","testRgv"+category,"data",String.format("第[%s]次执行: 执行时间:[%s]; RRG:[%s]>>>>>归位操作完成",finalRunNun, new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()), finalLoadingRgv)))); +//// errorCheck(rgv, finalLoadingRgv, category, finalRunNun); +//// if (checkJt(rgv, category, finalRunNun)) { +//// return false; +//// } +//// return true; +//// } +//// }while (!result); +//// } +//// }while (!openResult); +// } +// }while(!result); +// } +// }while (!openResult); +// +// }else{ +// WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method", "testRgv" + category, "data", String.format("第[%s]次执行: 执行时间:[%s]; RRG:[%s]>>>>>进行安装失败", finalRunNun, new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()), finalLoadingRgv)))); +// stopRgv(rgv, finalLoadingRgv); +// return false; +// } +// } catch (Exception e) { +// e.printStackTrace(); +// stopRgv(rgv, finalLoadingRgv); +// return false; +// } +// return false; +// }); +// +// //检查是否停止循环 +// if(checkJt(rgv, category, runNun)){ +// return; +// } +// +// //等待装完, 关门 +// CompletableFuture closeFuture =loading.thenApplyAsync(loadingResult ->{ +// //开门完成 +// if(loadingResult){ +// //开始执行拆除 +// try { +// WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method","testRgv"+category,"data", String.format("%s 下发关闭开合门命令", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()))))); +// InstructionWriteUtils.closeCenDoor(); +// Boolean result = false; +// do{ +// Thread.sleep(ParamUtils.getInstructionIntervalRequestMilliseconds()); +// if(result = InstructionReadUtils.getCenRbDoorCloseStatus()){ +// WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method","testRgv"+category,"data",String.format("%s 开合门已关闭", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()))))); +// return true; +// } +// }while (!result); +// } catch (Exception e) { +// stopRgv(rgv, finalLoadingRgv); +// throw new RuntimeException("关闭开合门出现异常:", e); +// } +// } +// return false; +// }); +// +// if(openFuture.join() && splitFuture.join() && put.join() && takeFuture.join() && loading.join() && closeFuture.join()){ +// //关门完成. 执行下次换电 +// WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method","testRgv"+category,"data",String.format("第[%s]次执行: 执行时间:[%s]; >>>>>全部执行完成, 准备执行下一次循环任务>>>>>>>>>>>>>>>>>>>>>",(runNun+1), new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()))))); +// } +// } +// taskRgvMap.remove(rgv); +// System.out.println("CompletableFuture 任务执行:" + Thread.currentThread().getName() + ",时间:" + System.currentTimeMillis()); +// }, +// // 延迟执行器:延迟 3 秒 +// CompletableFuture.delayedExecutor(3, TimeUnit.SECONDS) +// ); +// + // 可选:监听任务状态 + future.whenComplete((unused, throwable) -> { + if (throwable instanceof java.util.concurrent.CancellationException) { + System.out.println("开始---->任务已被取消"); + } else if (throwable != null) { + String message = throwable.getMessage(); + if(StringUtils.isNotEmpty(message)){ + message = message.replace("java.lang.RuntimeException: ",""); + WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method","testRgv"+category,"data",message))); + String[] mesArray = message.split(";"); + // RRG:[1] 出现异常; 机构区间为:{1}; 执行类型为:{取存}; 执行次数为:{1}; 异常信息为:{/ by zero} + CloudSendInfoUtils.sendAlarmWx(Collections.asMap("area","红旗换电站", "message", mesArray[4].replace("异常信息为:{","").replace("}",""))); + } + WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method","testRgv"+category,"data","时间:"+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date())+ "异常信息"+throwable.getMessage()))); + } else { + System.out.println("开始---->任务正常完成"); + taskRgvMap.remove(rgv); + } + }); + try { + taskRgvMap.put(rgv, future); + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException(e); + } + return Result.getInstance().success(); + }else{ + return Result.getInstance().error("当前RGV正在运行"); + } + }else{ + return Result.getInstance().error("当前RGV正在运行"); + } + + } + + + + @ApiOperation("全量结束") + @PostMapping("/stop/all/{category}") + @ApiLog(value = "全量结束", type = LogTypeEnum.OPERATION) + public Result stopAll(@PathVariable("category") final String category, @RequestParam("splitStorageRgvNo") final String splitStorageRgvNo, @RequestParam("takePackRgvNo") final String takePackRgvNo) throws Exception { + + String rgv = splitStorageRgvNo+"_"+takePackRgvNo; + + //检查RGV是否运作 + if(executionBatterySwapService.executionResult(splitStorageRgvNo) && executionBatterySwapService.executionResult(takePackRgvNo) && taskRgvMap.containsKey(rgv)){ + //睡了1秒再次检查, 防止空闲冲突 + Thread.sleep(1000); + if(executionBatterySwapService.executionResult(splitStorageRgvNo) && executionBatterySwapService.executionResult(takePackRgvNo)){ + taskRgvMap.remove(rgv); + } + }else{ + CompletableFuture future = taskRgvMap.get(rgv); + if(future == null){ + return Result.getInstance().success("当前任务已停止"); + } + future.cancel(true); + closeRunMap.put(rgv, new AtomicBoolean(true)); + WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method", "testRgv" + category, "data", String.format("执行时间[%s]; >>>>>下发停止操作命令", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()))))); + CompletableFuture closeFuture = CompletableFuture.runAsync( + () -> { + try { + Boolean openResult = false; + InstructionWriteUtils.rgvStopMove(splitStorageRgvNo); + InstructionWriteUtils.rgvStopMove(takePackRgvNo); + do { + errorCheck(rgv, splitStorageRgvNo, category, 0); + errorCheck(rgv, takePackRgvNo, category, 0); + if(checkJt(rgv, category, 0)){ + return; + } + //跳开查询 + if (openResult = (executionBatterySwapService.checkOpenExecutionResult(splitStorageRgvNo) && executionBatterySwapService.checkOpenExecutionResult(takePackRgvNo))) { + WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method", "testRgv" + category, "data", String.format("执行时间[%s]; RGV[%s]>>>>>开始执行停止操作", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()), splitStorageRgvNo+","+takePackRgvNo)))); + Boolean result = false; + do { + errorCheck(rgv, splitStorageRgvNo, category, 0); + errorCheck(rgv, takePackRgvNo, category, 0); + if(checkJt(rgv, category, 0)){ + return; + } + if(result = (executionBatterySwapService.executionResult(splitStorageRgvNo) && executionBatterySwapService.executionResult(takePackRgvNo))) { + errorCheck(rgv, splitStorageRgvNo, category, 0); + errorCheck(rgv, takePackRgvNo, category, 0); + if(checkJt(rgv, category, 0)){ + return; + } + WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method", "testRgv" + category, "data", String.format("执行时间[%s]; RGV[%s]>>>>>停止操作完成, 下发归零操作命令", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()), splitStorageRgvNo+","+takePackRgvNo)))); + InstructionWriteUtils.rgvResetToZero(splitStorageRgvNo); + InstructionWriteUtils.rgvResetToZero(takePackRgvNo); + Integer num = 0; + do { + errorCheck(rgv, splitStorageRgvNo, category, 0); + errorCheck(rgv, takePackRgvNo, category, 0); + if(checkJt(rgv, category, 0)){ + return; + } + num += 1; + //跳开查询 + if (openResult = ((executionBatterySwapService.checkOpenExecutionResult(splitStorageRgvNo) && executionBatterySwapService.checkOpenExecutionResult(takePackRgvNo)) || num > 10)) { + WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method", "testRgv" + category, "data", String.format("执行时间[%s]; RGV[%s]>>>>>开始执行归零操作", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()), splitStorageRgvNo+","+takePackRgvNo)))); + result = false; + do { + errorCheck(rgv, splitStorageRgvNo, category, 0); + errorCheck(rgv, takePackRgvNo, category, 0); + if(checkJt(rgv, category, 0)){ + return; + } + if (result = (executionBatterySwapService.executionResult(splitStorageRgvNo) && executionBatterySwapService.executionResult(takePackRgvNo))) { + //输出 + WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method", "testRgv" + category, "data", String.format("执行时间[%s]>>>>>归零操作完成, 任务停止", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()))))); + errorCheck(rgv, splitStorageRgvNo, category, 0); + errorCheck(rgv, takePackRgvNo, category, 0); + } + } while (!result); + } + }while (!openResult); + } + }while(!result); + } + }while (!openResult); + + } catch (Exception e) { + try { + String message = e.getMessage(); + Map error = executionBatterySwapService.executionErrorResult(splitStorageRgvNo); + if(Collections.isNotEmpty(error)){ + message = JSONObject.toJSONString(error); + } + Map error1 = executionBatterySwapService.executionErrorResult(takePackRgvNo); + if(Collections.isNotEmpty(error1)){ + message = JSONObject.toJSONString(error1); + } + closeRunMap.put(rgv, new AtomicBoolean(true)); + throw new RuntimeException(String.format(" 执行时间:[%s]; RRG:[%s] 出现异常; 机构区间为:{%s}; 执行类型为:{%s}; 异常信息为:{%s}",new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()), rgv, category, "拆装", message), e); + } catch (InstructionException ex) { + throw new RuntimeException(ex); + } + } + }, + // 延迟执行器:延迟 3 秒 + CompletableFuture.delayedExecutor(1, TimeUnit.SECONDS) + ); + + // 可选:监听任务状态 + closeFuture.whenComplete((unused, throwable) -> { + if (throwable instanceof java.util.concurrent.CancellationException) { + System.out.println("开始---->任务已被取消"); + } else if (throwable != null) { + String message = throwable.getMessage(); + if(StringUtils.isNotEmpty(message)){ + message = message.replace("java.lang.RuntimeException: ",""); + WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method","testRgv"+category,"data",message))); + String[] mesArray = message.split(";"); + // RRG:[1] 出现异常; 机构区间为:{1}; 执行类型为:{取存}; 执行次数为:{1}; 异常信息为:{/ by zero} + CloudSendInfoUtils.sendAlarmWx(Collections.asMap("area","红旗换电站", "message", mesArray[4].replace("异常信息为:{","").replace("}",""))); + } + WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method","testRgv"+category,"data","执行停止操作出现异常, 时间:"+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date())+ "异常信息"+throwable.getMessage()))); + } else { + System.out.println("开始---->任务正常完成"); + taskRgvMap.remove(rgv); + } + }); + } + + return Result.getInstance().success(); + } + + + + +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/converter/JsonMapper.java b/web-server/src/main/java/com/evotech/hd/webserver/converter/JsonMapper.java new file mode 100644 index 0000000..d835195 --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/converter/JsonMapper.java @@ -0,0 +1,70 @@ +/** + * Copyright © 2021-2026 space All rights reserved. + */ +package com.evotech.hd.webserver.converter; + +import cn.hutool.json.JSONNull; +import com.fasterxml.jackson.annotation.JsonInclude.Include; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParser.Feature; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.module.SimpleModule; +import com.fasterxml.jackson.databind.ser.std.NullSerializer; +import org.springframework.context.annotation.Primary; +import org.springframework.stereotype.Component; + +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.TimeZone; + +/** + * JSONMapper + * 定制返回给前台的数据格式 + * + * @551e606153114dcba79c336b23caa7dd + */ +@Component +@Primary +public class JsonMapper extends ObjectMapper { + + private static final long serialVersionUID = 1L; + + + public JsonMapper() { + this.setSerializationInclusion ( Include.NON_NULL ); + SimpleModule netSfJsonModule = new SimpleModule ( "net.sf.json" ); + netSfJsonModule.addSerializer ( JSONNull.class, NullSerializer.instance ); + this.registerModule ( netSfJsonModule ); + this.configure ( Feature.ALLOW_SINGLE_QUOTES, true ); + this.configure ( Feature.ALLOW_UNQUOTED_FIELD_NAMES, true ); + this.disable ( DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES ); + this.getSerializerProvider ( ).setNullValueSerializer ( new JsonSerializer ( ) { + @Override + public void serialize(Object value, JsonGenerator jsonGenerator, + SerializerProvider provider) throws IOException { + jsonGenerator.writeString ( "" ); + } + + } ); + // 进行HTML解码。 + this.registerModule ( new SimpleModule ( ).addSerializer ( String.class, new JsonSerializer ( ) { + @Override + public void serialize(String value, JsonGenerator jsonGenerator, + SerializerProvider provider) throws IOException { + //xss攻击避免 + jsonGenerator.writeString ( value ); + } + } ) ); + // 时间格式化 + this.configure ( DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false ); + this.setDateFormat ( new SimpleDateFormat ( "yyyy-MM-dd HH:mm:ss" ) ); //全局配置启用,不在前台进行格式化。启用时,导致保存工作流模型时出现bug已修复 + + // 设置时区 + this.setTimeZone ( TimeZone.getTimeZone ( "GMT+8:00" ) ); + } + + +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/converter/StringToDateConverter.java b/web-server/src/main/java/com/evotech/hd/webserver/converter/StringToDateConverter.java new file mode 100644 index 0000000..6d844fa --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/converter/StringToDateConverter.java @@ -0,0 +1,25 @@ +package com.evotech.hd.webserver.converter; + +import cn.hutool.core.util.StrUtil; +import org.springframework.core.convert.converter.Converter; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; + +public class StringToDateConverter implements Converter { + + @Override + public Date convert(String source) { + Date target = null; + if ( !StrUtil.isEmpty ( source ) ) { + SimpleDateFormat format = new SimpleDateFormat ( "yyyy-MM-dd HH:mm:ss" ); + try { + target = format.parse ( source ); + } catch (ParseException e) { + throw new RuntimeException ( String.format ( "parser %s to Date fail", source ) ); + } + } + return target; + } +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/job/JobConstant.java b/web-server/src/main/java/com/evotech/hd/webserver/job/JobConstant.java new file mode 100644 index 0000000..7f1bc1c --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/job/JobConstant.java @@ -0,0 +1,103 @@ +package com.evotech.hd.webserver.job; + +import com.evotech.hd.core.enums.BaseEnum; +import com.evotech.hd.webserver.job.job.ArrivalSignalJob; +import com.evotech.hd.webserver.job.job.LicensePlateMachineJob; +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 任务定时类 + * + * @ClassName:JobConstant + * @date: 2025年08月26日 14:56 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ + +public class JobConstant { + + @Getter + @AllArgsConstructor + public enum GroupEnum implements BaseEnum { + /*** + * 换电任务组 + */ + SWAP_GROUP("SYSTEM_RUNNING_GROUP", "换电任务组"), + /*** + * 基本任务组 + */ + BASE_GROUP("SYSTEM_GROUP", "基本任务组"), + /*** + * 充电任务组 + */ + CHARGING_GROUP("SYSTEM_CHARGING_GROUP", "充电任务组"), + ; + final String type; + + final String label; + + @Override + public String getLabel() { + return this.label; + } + + @Override + public String getValue() { + return this.type; + } + } +// public static String SWAP_GROUP = "SYSTEM_RUNNING_GROUP"; +// +// public static String BASE_GROUP = "SYSTEM_GROUP"; +// +// public static String CHARGING_GROUP = "SYSTEM_CHARGING_GROUP"; + /*** + * 车牌机任务id + */ + public static String LICENSE_PLATE_MACHINE_JOB = LicensePlateMachineJob.class.getSimpleName(); + /*** + * 车辆到位信息ID + */ + public static String ARRIVAL_SIGNAL_JOB = ArrivalSignalJob.class.getSimpleName(); +// /*** +// * 对中机构id +// */ +// public static String ACTIVATE_ALIGNMENT_MECHANISM_JOB = ActivateAlignmentMechanismJob.class.getSimpleName(); +// /*** +// * 打开开合门 +// */ +// public static String OPEN_DOOR_JOB = OpenDoorJob.class.getSimpleName(); +// /*** +// * 拆旧电池 +// */ +// public static String SPLIT_OLD_BATTERY_JOB = SplitOldBatteryJob.class.getSimpleName(); +// /*** +// * 存旧电池 +// */ +// public static String PUT_OLD_BATTERY_JOB = PutOldBatteryJob.class.getSimpleName(); +// /*** +// * 取新电池 +// */ +// public static String TAKE_NEW_BATTERY_JOB = TakeNewBatteryJob.class.getSimpleName(); +// /*** +// * 装新电池 +// */ +// public static String LOADING_NEW_BATTERY_JOB = LoadingNewBatteryJob.class.getSimpleName(); +// /*** +// * 关闭开合门 +// */ +// public static String CLOSE_DOOR_JOB = CloseDoorJob.class.getSimpleName(); +// /*** +// * RGV归零 +// */ +// public static String RGV_RESET_TO_ZERO_JOB = RgvResetToZeroJob.class.getSimpleName(); + /*** + * 车辆离位 + */ +// public static String VEHICLE_ABDICATION_JOB = VehicleAbdicationJob.class.getSimpleName(); + + + + +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/job/QuartzJobController.java b/web-server/src/main/java/com/evotech/hd/webserver/job/QuartzJobController.java new file mode 100644 index 0000000..5012ede --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/job/QuartzJobController.java @@ -0,0 +1,174 @@ +package com.evotech.hd.webserver.job; + +import com.evotech.hd.core.dtos.OptionDTO; +import com.evotech.hd.core.dtos.Result; +import com.evotech.hd.webserver.job.entity.QuartzJobInfo; +import com.evotech.hd.webserver.job.service.QuartzJobService; +import com.evotech.hd.webserver.logging.annotation.ApiLog; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.quartz.SchedulerException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +/** + * 类 + * + * @ClassName:QuartzJobController + * @date: 2025年08月23日 16:20 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ + +@RestController +@RequestMapping("/api/quartz") +@Api(tags = "定时器任务管理") +public class QuartzJobController { + + @Autowired + private QuartzJobService quartzJobService; + + + @ApiOperation("页面初始化") + @PostMapping("/init") + @ApiLog(value = "页面初始化") + public Result> init(){ + return Result.getInstance().buildList(OptionDTO.class).success(Arrays.stream(JobConstant.GroupEnum.values()).map(data ->new OptionDTO(data)).collect(Collectors.toList())); + } + + + @ApiOperation("定时任务列表查询") + @ApiLog(value = "定时任务列表查询") + @PostMapping("/list") + public Result> list(@RequestBody QuartzJobInfo jobInfo) { + try { + return Result.getInstance().buildList(QuartzJobInfo.class).success(quartzJobService.getAllJobs(jobInfo)); + } catch (Exception e) { + return Result.getInstance().buildList(QuartzJobInfo.class).error(e.getMessage()); + } + } + + /*** + * + * // POST /api/quartz/create + * { + * "jobId": "demoJob1", + * "jobName": "示例任务", + * "jobGroup": "DEFAULT_GROUP", + * "cronExpression": "0/10 * * * * ?", // 每10秒执行一次 + * "jobClassName": "com.example.demo.job.DemoJob", // 业务任务类全路径 + * "description": "这是一个示例任务", + * "status": true + * } + * + * + */ + /** + * 创建任务 + */ + @ApiOperation("创建任务") + @ApiLog(value = "创建任务") + @PostMapping("/create") + public String createJob(@RequestBody QuartzJobInfo jobInfo) { + try { + quartzJobService.createJob(jobInfo); + return "任务创建成功"; + } catch (SchedulerException e) { + return "任务创建失败: " + e.getMessage(); + } + } + + /** + * 更新任务Cron表达式 + */ + @ApiOperation("更新任务Cron表达式") + @ApiLog(value = "更新任务Cron表达式") + @PostMapping("/update") + public String updateJobCron(@RequestBody QuartzJobInfo jobInfo) { + try { + quartzJobService.updateJobCron(jobInfo); + return "任务更新成功"; + } catch (SchedulerException e) { + return "任务更新失败: " + e.getMessage(); + } + } + + /** + * 暂停任务 + */ + @ApiOperation("暂停任务") + @ApiLog(value = "暂停任务") + @PostMapping("/pause") + public String pauseJob(@RequestBody QuartzJobInfo jobInfo) { + try { + quartzJobService.pauseJob(jobInfo); + return "任务暂停成功"; + } catch (SchedulerException e) { + return "任务暂停失败: " + e.getMessage(); + } + } + + /** + * 恢复任务 + */ + @ApiOperation("恢复任务") + @ApiLog(value = "恢复任务") + @PostMapping("/resume") + public String resumeJob(@RequestBody QuartzJobInfo jobInfo) { + try { + quartzJobService.resumeJob(jobInfo); + return "任务恢复成功"; + } catch (SchedulerException e) { + return "任务恢复失败: " + e.getMessage(); + } + } + + /** + * 删除任务 + */ + @ApiOperation("删除任务") + @ApiLog(value = "删除任务") + @PostMapping("/delete") + public String deleteJob(@RequestBody QuartzJobInfo jobInfo) { + try { + quartzJobService.deleteJob(jobInfo); + return "任务删除成功"; + } catch (SchedulerException e) { + return "任务删除失败: " + e.getMessage(); + } + } + + /** + * 立即执行任务 + */ + @ApiOperation("立即执行任务") + @ApiLog(value = "立即执行任务") + @PostMapping("/runNow") + public String runJobNow(@RequestBody QuartzJobInfo jobInfo) { + try { + quartzJobService.runJobNow(jobInfo); + return "任务已触发执行"; + } catch (SchedulerException e) { + return "触发任务失败: " + e.getMessage(); + } + } + +// @ApiOperation("修改任务") + @ApiLog(value = "修改任务") + @PostMapping("/updateJob") + public String updateJob(@RequestBody QuartzJobInfo jobInfo) { + try { + quartzJobService.updateJob(jobInfo); + return "任务修改成功"; + } catch (SchedulerException e) { + return "任务修改失败: " + e.getMessage(); + } + } +} \ No newline at end of file diff --git a/web-server/src/main/java/com/evotech/hd/webserver/job/QuartzJobExecutor.java b/web-server/src/main/java/com/evotech/hd/webserver/job/QuartzJobExecutor.java new file mode 100644 index 0000000..a0a6311 --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/job/QuartzJobExecutor.java @@ -0,0 +1,49 @@ +package com.evotech.hd.webserver.job; + +import org.quartz.Job; +import org.quartz.JobDataMap; +import org.quartz.JobExecutionContext; +import org.quartz.JobExecutionException; +import org.springframework.util.ReflectionUtils; + +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; + +/** + * 定时器通用执行类 + * + * @ClassName:BaseJob + * @date: 2025年08月23日 16:18 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ + +public class QuartzJobExecutor implements Job { + @Override + public void execute(JobExecutionContext context) throws JobExecutionException { + // 从任务上下文获取目标类和方法信息 + // 1. 获取JobDataMap中的参数 + JobDataMap jobDataMap = context.getJobDetail().getJobDataMap(); + //获取执行的类信息 + String jobClassName = (String) jobDataMap.get("jobClassName"); + // 2. 提取业务参数(排除系统参数如jobClassName) + Map businessParams = new HashMap<>(); + for (Map.Entry entry : jobDataMap.entrySet()) { + if (!"jobClassName".equals(entry.getKey())) { + businessParams.put(entry.getKey(), entry.getValue()); + } + } + try { + // 反射加载类并执行run方法 + Class jobClass = Class.forName(jobClassName); + Object jobInstance = jobClass.newInstance(); + Method method = jobClass.getMethod("run", Map.class); + ReflectionUtils.invokeMethod(method, jobInstance, businessParams); + } catch (Exception e) { + e.printStackTrace(); + throw new JobExecutionException("任务执行失败: " + e.getMessage(), e); + } + } +} + diff --git a/web-server/src/main/java/com/evotech/hd/webserver/job/entity/QuartzJobInfo.java b/web-server/src/main/java/com/evotech/hd/webserver/job/entity/QuartzJobInfo.java new file mode 100644 index 0000000..4aa890a --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/job/entity/QuartzJobInfo.java @@ -0,0 +1,67 @@ +package com.evotech.hd.webserver.job.entity; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.Map; + +/** + * 类 + * + * @ClassName:QuartzJobInfo + * @date: 2025年08月23日 16:17 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ + +@Data +@ApiModel("定时任务") +public class QuartzJobInfo { + @ApiModelProperty("任务唯一标识") + private String jobId; // 任务唯一标识 + @ApiModelProperty("任务名称") + private String jobName; // 任务名称 + @ApiModelProperty("任务组名") + private String jobGroup; // 任务组名 + @ApiModelProperty("cron表达式") + private String cronExpression; // cron表达式 + @ApiModelProperty("任务执行类全路径") + private String jobClassName; // 任务执行类全路径 + @ApiModelProperty("任务描述") + private String description; // 任务描述 + @ApiModelProperty("任务状态:true-运行中,false-暂停") + private boolean status; // 任务状态:true-运行中,false-暂停 + @ApiModelProperty("任务状态名称") + private String statusName; + private Map params; + public QuartzJobInfo() { + } + + public QuartzJobInfo(String jobId, String jobGroup) { + this.jobId = jobId; + this.jobGroup = jobGroup; + } + + public QuartzJobInfo(String jobId, String jobGroup, String cronExpression) { + this(jobId, jobGroup); + this.cronExpression = cronExpression; + } + + public QuartzJobInfo(String jobId, String jobName, String jobGroup, String cronExpression, String jobClassName, String description) { + this(jobId, jobGroup, cronExpression); + this.jobName = jobName; + this.jobClassName = jobClassName; + this.description = description; + } + + public QuartzJobInfo(String jobId, String jobName, String jobGroup, String cronExpression, String jobClassName, String description, Map params) { + this(jobId, jobName, jobGroup,cronExpression, jobClassName, description); + this.params = params; + } + + public QuartzJobInfo(String jobId, String jobName, String jobGroup, String cronExpression, String jobClassName, String description, boolean status, Map params) { + this(jobId, jobName, jobGroup,cronExpression, jobClassName, description, params); + this.status = status; + } +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/job/job/ActivateAlignmentMechanismJob.java b/web-server/src/main/java/com/evotech/hd/webserver/job/job/ActivateAlignmentMechanismJob.java new file mode 100644 index 0000000..a12e4d6 --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/job/job/ActivateAlignmentMechanismJob.java @@ -0,0 +1,51 @@ +//package com.evotech.hd.webserver.job.job; +// +//import com.evotech.hd.core.enums.SwapBatteryStepEnum; +//import com.evotech.hd.utils.Collections; +//import com.evotech.hd.webserver.job.JobConstant; +//import com.evotech.hd.webserver.utils.instruction.InstructionReadUtils; +//import com.evotech.hd.webserver.utils.instruction.InstructionWriteUtils; +//import lombok.extern.slf4j.Slf4j; +//import org.quartz.SchedulerException; +//import org.springframework.stereotype.Component; +// +//import java.util.Map; +// +///** +// * 对中机构扫描 +// * +// * @ClassName:ActivateAlignmentMechanismJob +// * @date: 2025年08月27日 10:48 +// * @author: andy.shi +// * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 +// */ +//@Slf4j +//@Component +//public class ActivateAlignmentMechanismJob implements BaseJob{ +// +// @Override +// public void run(Map params) throws Exception{ +// Boolean openResult = false; +// Integer num = 0; +// do { +// num += 1; +// if(getExecutionBatterySwapService().executionCenNextStep(SwapBatteryStepEnum.INSTITUTIONS_FOR_CHINA)){ +// return; +// } +// if (openResult = getExecutionBatterySwapService().executionCenOpenResult()) { +// //如果为true,则证明执行结束 +// if(getExecutionBatterySwapService().executionCenResult()){ +// // 推送云端, 并查询是否存在异常 没有异常, 开始启动换电 +// if(getExecutionBatterySwapService().executionCenNextStep(SwapBatteryStepEnum.INSTITUTIONS_FOR_CHINA)){ +// //下发开合门打开指令 +// InstructionWriteUtils.openCenDoor(); +// getQuartzJobService().updateJobParams(JobConstant.OPEN_DOOR_JOB, JobConstant.GroupEnum.SWAP_GROUP.getType(), Collections.asMap("orderId", String.valueOf(params.get("orderId")))); +// getQuartzJobService().runJobNow(JobConstant.OPEN_DOOR_JOB, JobConstant.GroupEnum.SWAP_GROUP.getType()); +// } +// } +// } +// }while (!openResult && num <= 3); +// +// +// } +//} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/job/job/ArrivalSignalJob.java b/web-server/src/main/java/com/evotech/hd/webserver/job/job/ArrivalSignalJob.java new file mode 100644 index 0000000..c774679 --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/job/job/ArrivalSignalJob.java @@ -0,0 +1,75 @@ +package com.evotech.hd.webserver.job.job; + +import cn.hutool.extra.spring.SpringUtil; +import com.alibaba.fastjson2.JSONObject; +import com.evotech.hd.core.dtos.Result; +import com.evotech.hd.core.dtos.ResultUtil; +import com.evotech.hd.core.dtos.business.OrderSwapDTO; +import com.evotech.hd.core.dtos.system.ParamsDTO; +import com.evotech.hd.core.enums.SwapBatteryStepEnum; +import com.evotech.hd.exception.InstructionException; +import com.evotech.hd.utils.Collections; +import com.evotech.hd.webserver.job.JobConstant; +import com.evotech.hd.webserver.job.entity.QuartzJobInfo; +import com.evotech.hd.webserver.service.BatterySwapStepService; +import com.evotech.hd.webserver.service.OrderSwapService; +import com.evotech.hd.webserver.utils.sendCloud.CloudSendInfoUtils; +import com.evotech.hd.webserver.utils.instruction.InstructionUtils; +import com.evotech.hd.webserver.utils.instruction.InstructionWriteUtils; +import com.evotech.hd.webserver.utils.ParamUtils; +import com.evotech.hd.webserver.websocket.controller.WebSocketUtils; +import lombok.extern.slf4j.Slf4j; +import org.quartz.SchedulerException; +import org.springframework.stereotype.Component; + +import java.util.Date; +import java.util.Map; + + +/** + * 车辆到位扫描 + * + * @ClassName:LicensePlateMachineJob + * @date: 2025年08月25日 13:16 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@Slf4j +@Component +public class ArrivalSignalJob implements BaseJob{ + + public void run(Map params) throws InstructionException { + ParamsDTO arrivalSignal = ParamUtils.getArrivalSignal(); + JSONObject arrivalSignalResult = InstructionUtils.queryReadInteger(arrivalSignal.getName(), arrivalSignal.getParamCode()); + //检查通讯状态 + if (InstructionUtils.verifyStatusLog(arrivalSignal, arrivalSignalResult)) { + //符合预计结果, 直接发出告警信息 + if (InstructionUtils.getValue(arrivalSignalResult).equalsIgnoreCase(arrivalSignal.getExpectedResults())) { + //车辆到位, 直接停止任务调度 + try { + //关闭进门栏杆 + InstructionWriteUtils.closeRolling(ParamUtils.getDoorParams("入口")); + //暂停车辆到位扫秒 + getQuartzJobService().pauseJob(new QuartzJobInfo(JobConstant.ARRIVAL_SIGNAL_JOB, JobConstant.GroupEnum.SWAP_GROUP.getType())); + } catch (Exception e) { + e.printStackTrace(); + log.error("扫描到车辆到位信号, 停止扫秒出现错误", e); + } + + //到达指定位置, 本地记录到位信息 + Result runningOrderResult = SpringUtil.getBean(OrderSwapService.class).runningOrder(); + if (ResultUtil.verifyCode(runningOrderResult)) { + BatterySwapStepService batterySwapStepService = SpringUtil.getBean(BatterySwapStepService.class); + OrderSwapDTO orderSwapDTO = ResultUtil.getValue(runningOrderResult); + batterySwapStepService.addOrderSwapBatteryStepBySwapComplete(orderSwapDTO.getId(), SwapBatteryStepEnum.VEHICLE_POSITION.getCode(), new Date(), false, false, Collections.emptyMap()); + orderSwapDTO.setBatterySwapStepDTOList(batterySwapStepService.findListByOrderId(orderSwapDTO.getId())); + //推送页面广播 + WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method", "order", "data", JSONObject.toJSONString(orderSwapDTO)))); + // 发送通知到云端 + CloudSendInfoUtils.sendArrivalSignal(Collections.asMap("pkId", orderSwapDTO.getCloudOrderId(), "step", SwapBatteryStepEnum.VEHICLE_POSITION.getCode(), "stepTime", new Date())); + + } + } + } + } +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/job/job/BaseJob.java b/web-server/src/main/java/com/evotech/hd/webserver/job/job/BaseJob.java new file mode 100644 index 0000000..eb68a61 --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/job/job/BaseJob.java @@ -0,0 +1,101 @@ +package com.evotech.hd.webserver.job.job; + +import cn.hutool.extra.spring.SpringUtil; +import com.alibaba.fastjson2.JSONObject; +import com.evotech.hd.core.dtos.Result; +import com.evotech.hd.core.dtos.business.OrderSwapDTO; +import com.evotech.hd.core.enums.SwapBatteryStepEnum; +import com.evotech.hd.utils.Collections; +import com.evotech.hd.webserver.job.service.ExecutionBatterySwapService; +import com.evotech.hd.webserver.job.service.QuartzJobService; +import com.evotech.hd.webserver.logging.AsyncLogService; +import com.evotech.hd.webserver.service.*; +import com.evotech.hd.webserver.utils.ParamUtils; +import com.evotech.hd.webserver.utils.instruction.InstructionWriteUtils; +import com.evotech.hd.webserver.utils.sendCloud.CloudSendInfoUtils; + +import java.util.Map; + +/** + * 接口 + * + * @ClassName:BaseJob + * @date: 2025年09月03日 15:05 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +public interface BaseJob { + + final Map p = Collections.emptyMap(); + + public default ExecutionBatterySwapService getExecutionBatterySwapService(){ + if(p.get("executionBatterySwapService") == null){ + p.put("executionBatterySwapService", SpringUtil.getBean(ExecutionBatterySwapService.class)); + } + return (ExecutionBatterySwapService)p.get("executionBatterySwapService"); + } + public default QuartzJobService getQuartzJobService(){ + if(p.get("quartzJobService") == null){ + p.put("quartzJobService", SpringUtil.getBean(QuartzJobService.class)); + } + return (QuartzJobService)p.get("quartzJobService"); + } + + public default AsyncLogService getAsyncLogService(){ + if(p.get("asyncLogService") == null){ + p.put("asyncLogService", SpringUtil.getBean(AsyncLogService.class)); + } + return (AsyncLogService)p.get("asyncLogService"); + } + + public default BatteryCompartmentService getBatteryCompartmentService(){ + if(p.get("batteryCompartmentService") == null){ + p.put("batteryCompartmentService", SpringUtil.getBean(BatteryCompartmentService.class)); + } + return (BatteryCompartmentService)p.get("batteryCompartmentService"); + } + + public default BatteryService getBatteryService(){ + if(p.get("batteryService") == null){ + p.put("batteryService", SpringUtil.getBean(BatteryService.class)); + } + return (BatteryService)p.get("batteryService"); + } + + public default RunningInstructionsDetailService getRunningInstructionsDetailService(){ + if(p.get("runningInstructionsDetailService") == null){ + p.put("runningInstructionsDetailService", SpringUtil.getBean(RunningInstructionsDetailService.class)); + } + return (RunningInstructionsDetailService)p.get("runningInstructionsDetailService"); + } + + public default ParamsService getParamsService(){ + if(p.get("paramsService") == null){ + p.put("paramsService", SpringUtil.getBean(ParamsService.class)); + } + return (ParamsService)p.get("paramsService"); + } + + public default OrderSwapService getOrderSwapService(){ + if(p.get("orderSwapService") == null){ + p.put("orderSwapService", SpringUtil.getBean(OrderSwapService.class)); + } + return (OrderSwapService)p.get("orderSwapService"); + } + + + public default Boolean checkError(String rgvNo, SwapBatteryStepEnum swapBatteryStep) throws Exception { + Map error = getExecutionBatterySwapService().executionErrorResult(rgvNo); + if(Collections.isNotEmpty(error)){ + String message = JSONObject.toJSONString(error); + InstructionWriteUtils.emergencyStop(rgvNo); + Result running = getOrderSwapService().runningOrder(); + getExecutionBatterySwapService().executionResultPush(running.getData(), swapBatteryStep, Collections.isNotEmpty(error),error); + CloudSendInfoUtils.sendAlarmWx(Collections.asMap("area", ParamUtils.findStationName(), "message", message)); + return true; + } + return false; + } + + public void run(Map params) throws Exception; +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/job/job/CheckAlarmJob.java b/web-server/src/main/java/com/evotech/hd/webserver/job/job/CheckAlarmJob.java new file mode 100644 index 0000000..09a6b5b --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/job/job/CheckAlarmJob.java @@ -0,0 +1,113 @@ +package com.evotech.hd.webserver.job.job; + +import com.alibaba.fastjson2.JSONObject; +import com.evotech.hd.core.dtos.Result; +import com.evotech.hd.core.dtos.ResultUtil; +import com.evotech.hd.core.dtos.business.AccessStrategyDTO; +import com.evotech.hd.core.dtos.business.RunningInstructionsDetailDTO; +import com.evotech.hd.core.dtos.system.ParamsDTO; +import com.evotech.hd.exception.InstructionException; +import com.evotech.hd.utils.Collections; +import com.evotech.hd.webserver.controller.TestController; +import com.evotech.hd.webserver.service.ParamsService; +import com.evotech.hd.webserver.utils.AccessStrategyUtil; +import com.evotech.hd.webserver.utils.instruction.InstructionReadUtils; +import com.evotech.hd.webserver.utils.instruction.InstructionUtils; +import com.evotech.hd.webserver.utils.sendCloud.CloudSendInfoUtils; +import com.evotech.hd.webserver.websocket.controller.WebSocketUtils; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.Map; + +/** + * 首页告警扫描 + * @ClassName:CehckAlarmJob + * @date: 2025年08月23日 17:01 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@Slf4j +@Component +public class CheckAlarmJob implements BaseJob{ + + + // 任务执行方法(必须定义为public void run()) + public void run(Map params) throws InstructionException { + log.info("===== 执行检查报警任务 ====="); + Map alarmMessage = Collections.emptyMap(); + //获取所有的异常指令 + List instructions = getRunningInstructionsDetailService().findDictDetailDTOByTypeCode("check_alarm"); + if(Collections.isNotEmpty(instructions)){ + ParamsService paramsService = getParamsService(); + for (RunningInstructionsDetailDTO instruction : instructions){ + ParamsDTO paramsDTO = paramsService.getParamsDTOByCode(instruction.getDictValue()); + if("RGV_DevConnect".equals(paramsDTO.getParamCode())){ + AccessStrategyDTO accessStrategyDTO = AccessStrategyUtil.getValidAccessStrategyDTO(); + if(StringUtils.isNotEmpty(accessStrategyDTO.getEffectiveRgvNo())){ + String[] rgvArrays = accessStrategyDTO.getEffectiveRgvNo().split(","); + for (String rgvNo : rgvArrays){ + if(!InstructionUtils.getBoolean(paramsDTO, InstructionReadUtils.executionInteger(paramsDTO, rgvNo))){ + alarmMessage.put(paramsDTO.getName().replace(":num", rgvNo), paramsDTO.getErrMessage().replace(":num", rgvNo)); + } + } + }else{ + alarmMessage.put("RGV连接", "没有可用的RGV设备"); + } + }if("RGV_RB_ErrorCode".equals(paramsDTO.getParamCode())){ + AccessStrategyDTO accessStrategyDTO = AccessStrategyUtil.getValidAccessStrategyDTO(); + if(StringUtils.isNotEmpty(accessStrategyDTO.getEffectiveRgvNo())){ + String[] rgvArrays = accessStrategyDTO.getEffectiveRgvNo().split(","); + for (String rgvNo : rgvArrays){ + Map result = getExecutionBatterySwapService().executionErrorResult(rgvNo); + if(Collections.isNotEmpty(result)){ + alarmMessage.put(paramsDTO.getName().replace(":num", rgvNo), String.valueOf(result.get("errorMsg"))); + TestController.getInstance().stopRgv(rgvNo, rgvNo); + CloudSendInfoUtils.sendAlarmWx(Collections.asMap("area","红旗换电站", "message", String.valueOf(result.get("errorMsg")))); + } + } + }else{ + alarmMessage.put("RGV连接", "没有可用的RGV设备"); + } + }else{ + if(!InstructionUtils.getBoolean(paramsDTO, InstructionReadUtils.executionInteger(paramsDTO, null))){ + alarmMessage.put(paramsDTO.getName(), paramsDTO.getErrMessage()); + } + } + + } + } + //检查云平台链接 + Result result = CloudSendInfoUtils.sendKeepalive(); + Boolean keepalive = ResultUtil.getValue(result); + if(keepalive == null || !keepalive){ + alarmMessage.put("云平台", "连接丢失"); + } + + //存在告警, 推送数据 + if(Collections.isNotEmpty(alarmMessage)){ + WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method","alarm","data",JSONObject.toJSONString(alarmMessage)))); + + if(keepalive != null && keepalive){ + //推送数据 + Map alarmMap = Collections.emptyMap(); + if(alarmMessage.containsKey("消防")){ + alarmMap.put("fire", 2); //2为云平台现有的定义的站控的报警标识 + } + if(alarmMessage.containsKey("水浸")){ + alarmMap.put("water", 2); //2为云平台现有的定义的站控的报警标识 + } + if(alarmMessage.containsKey("防雷")){ + alarmMap.put("thunder", 2); //2为云平台现有的定义的站控的报警标识 + } + if(alarmMessage.containsKey("急停")){ + alarmMap.put("stop", 2); //2为云平台现有的定义的站控的报警标识 + } + CloudSendInfoUtils.sendAlarm(alarmMap); + } + } + log.info("检查报警任务执行完成"); + } +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/job/job/LicensePlateMachineJob.java b/web-server/src/main/java/com/evotech/hd/webserver/job/job/LicensePlateMachineJob.java new file mode 100644 index 0000000..68a4c71 --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/job/job/LicensePlateMachineJob.java @@ -0,0 +1,53 @@ +package com.evotech.hd.webserver.job.job; + +import cn.hutool.extra.spring.SpringUtil; +import com.alibaba.fastjson2.JSONObject; +import com.evotech.hd.core.dtos.system.ParamsDTO; +import com.evotech.hd.exception.InstructionException; +import com.evotech.hd.webserver.logging.LogUtils; +import com.evotech.hd.webserver.service.OrderReservationService; +import com.evotech.hd.webserver.utils.instruction.InstructionUtils; +import com.evotech.hd.webserver.utils.ParamUtils; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; + +import java.util.Map; + +/** + * 车牌机扫描 + * + * @ClassName:LicensePlateMachineJob + * @date: 2025年08月25日 13:16 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@Slf4j +@Service +public class LicensePlateMachineJob implements BaseJob{ + + public void run(Map params) throws Exception { + ParamsDTO plateDevConnect = ParamUtils.getPlateDevConnect(); + JSONObject plateDevConnectResult = InstructionUtils.queryReadInteger(plateDevConnect.getName(),plateDevConnect.getParamCode()); + //检查通讯状态 + if(InstructionUtils.verifyStatusLog(plateDevConnect, plateDevConnectResult)){ + //不符合预计结果, 直接发出告警信息 + if(InstructionUtils.getValue(plateDevConnectResult).equalsIgnoreCase(plateDevConnect.getExpectedResults())){ + getAsyncLogService().saveLog(LogUtils.getAlarmLog(plateDevConnect.getErrMessage(), -1, plateDevConnect.getParamCode(), plateDevConnectResult.toString(), null)); + } + } + //需要验证, 并且不符合预期结果 + ParamsDTO licensePlateMachine = ParamUtils.getLicensePlateMachine(); + JSONObject licensePlateMachineResult = InstructionUtils.queryReadString("车牌号扫描",licensePlateMachine.getParamCode()); + //检查通讯状态 + if(InstructionUtils.verifyStatusLog(licensePlateMachine, licensePlateMachineResult)){ + //检查是否查询到车牌 + String plateNum = InstructionUtils.getValue(licensePlateMachineResult); + if(StringUtils.isNotEmpty(plateNum)){ + getExecutionBatterySwapService().checkPlateNum(plateNum); + } + + } + } + +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/job/job/RgvResetToZeroJob.java b/web-server/src/main/java/com/evotech/hd/webserver/job/job/RgvResetToZeroJob.java new file mode 100644 index 0000000..1835d14 --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/job/job/RgvResetToZeroJob.java @@ -0,0 +1,59 @@ +//package com.evotech.hd.webserver.job.job; +// +//import com.alibaba.fastjson2.JSONObject; +//import com.evotech.hd.core.dtos.business.OrderSwapDTO; +//import com.evotech.hd.core.enums.SwapBatteryStepEnum; +//import com.evotech.hd.utils.Collections; +//import com.evotech.hd.webserver.job.JobConstant; +//import com.evotech.hd.webserver.utils.AccessStrategyUtil; +//import com.evotech.hd.webserver.utils.ParamUtils; +//import com.evotech.hd.webserver.utils.instruction.InstructionWriteUtils; +//import com.evotech.hd.webserver.websocket.controller.WebSocketUtils; +//import lombok.extern.slf4j.Slf4j; +//import org.apache.commons.lang3.StringUtils; +//import org.quartz.SchedulerException; +//import org.springframework.stereotype.Component; +// +//import java.util.List; +//import java.util.Map; +// +///** +// * RGV归位扫描 +// * @ClassName:LicensePlateMachineJob +// * @date: 2025年08月25日 13:16 +// * @author: andy.shi +// * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 +// */ +//@Slf4j +//@Component +//public class RgvResetToZeroJob implements BaseJob{ +// +// +// public void run(Map params) throws Exception { +// Boolean closeResult = false; +// Integer num = 0; +// do { +// num += 1; +// if (Collections.isNotEmpty(getExecutionBatterySwapService().executionCenErrorResult())) { +// return; +// } +// if(closeResult = getExecutionBatterySwapService().executionCenOpenResult()){ +// Boolean result = false; +// do { +// if(result = getExecutionBatterySwapService().executionCenResult()) { +// //推送空对象推送websocket, 刷新首页数据 +// WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method", "order", "data", JSONObject.toJSONString(new OrderSwapDTO(AccessStrategyUtil.getValidAccessStrategyDTO().getEffectiveRgvNo().split(",").length))))); +// //执行开门操作 +// InstructionWriteUtils.openRolling(ParamUtils.getDoorParams("出口")); +// getQuartzJobService().updateJobParams(JobConstant.VEHICLE_ABDICATION_JOB, JobConstant.GroupEnum.SWAP_GROUP.getType(), Collections.asMap("orderId", params.get("orderId"))); +// getQuartzJobService().runJobNow(JobConstant.VEHICLE_ABDICATION_JOB, JobConstant.GroupEnum.SWAP_GROUP.getType()); +// +// } +// }while (!result); +// +// } +// +// }while (!closeResult && num <=3); +// +// } +//} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/job/job/VehicleAbdicationJob.java b/web-server/src/main/java/com/evotech/hd/webserver/job/job/VehicleAbdicationJob.java new file mode 100644 index 0000000..019c48d --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/job/job/VehicleAbdicationJob.java @@ -0,0 +1,75 @@ +//package com.evotech.hd.webserver.job.job; +// +//import cn.hutool.extra.spring.SpringUtil; +//import com.alibaba.fastjson2.JSONObject; +//import com.evotech.hd.core.dtos.system.ParamsDTO; +//import com.evotech.hd.core.entity.business.OrderSwap; +//import com.evotech.hd.core.enums.CacheNames; +//import com.evotech.hd.core.enums.OrderStatusEnums; +//import com.evotech.hd.utils.DateUtils; +//import com.evotech.hd.webserver.config.redis.utils.RedisUtil; +//import com.evotech.hd.webserver.job.JobConstant; +//import com.evotech.hd.webserver.service.OrderSwapService; +//import com.evotech.hd.webserver.utils.instruction.InstructionUtils; +//import com.evotech.hd.webserver.utils.instruction.InstructionWriteUtils; +//import com.evotech.hd.webserver.utils.ParamUtils; +//import lombok.extern.slf4j.Slf4j; +//import org.quartz.SchedulerException; +//import org.springframework.stereotype.Component; +// +//import java.util.Map; +// +///** +// * 车辆离位扫描 +// * +// * @ClassName:VehicleAbdicationJob +// * @date: 2025年09月02日 11:44 +// * @author: andy.shi +// * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 +// */ +//@Slf4j +//@Component +//public class VehicleAbdicationJob implements BaseJob{ +// +// public void run(Map params) throws Exception { +// ParamsDTO arrivalSignal = ParamUtils.getArrivalSignal(); +// JSONObject arrivalSignalResult = InstructionUtils.queryReadInteger(arrivalSignal.getName(), arrivalSignal.getParamCode()); +// //检查通讯状态 +// if (InstructionUtils.verifyStatusLog(arrivalSignal, arrivalSignalResult)) { +// //符合预计结果, 直接发出告警信息 车辆离位是特殊消息, 这个是和预设值相反的参数, 直接指定 0 +// if (InstructionUtils.getValue(arrivalSignalResult).equals("0")) { +// try { +// //暂停车辆离位扫秒 +// getQuartzJobService().pauseJob(JobConstant.VEHICLE_ABDICATION_JOB, JobConstant.GroupEnum.SWAP_GROUP.getType()); +// } catch (SchedulerException e) { +// e.printStackTrace(); +// log.error("RGV归位扫描, 停止扫秒出现错误", e); +// } +// Thread.sleep(ParamUtils.getVehicleAbdicationWaitingMilliseconds()); +// //关闭进门栏杆 +// InstructionWriteUtils.closeRolling(ParamUtils.getDoorParams("出口")); +// OrderSwap orderSwap = SpringUtil.getBean(OrderSwapService.class).getById(String.valueOf(params.get("orderId"))); +// orderSwap.setStatus(OrderStatusEnums.SWAPOVER.getCode()); +// //清除锁定, 放开车牌机扫秒 +// String swapDay = DateUtils.parseDateToStr("yyyy-MM-dd", orderSwap.getServiceTimeBegin()); +// //存在当前key, 清除数据 +// if(RedisUtil.hasKey(CacheNames.SYSTEM_LOCK_PLATE+":"+swapDay+":"+orderSwap.getPlateNum())) { +// RedisUtil.del(CacheNames.SYSTEM_LOCK_PLATE + ":" + swapDay + ":" + orderSwap.getPlateNum()); +// } +// //推送换电完成事件到云端 +// SpringUtil.getBean(OrderSwapService.class).sendOrderCompleted(String.valueOf(params.get("orderId"))); +// try { +// //恢复车牌机扫描 +// getQuartzJobService().resumeJob(JobConstant.LICENSE_PLATE_MACHINE_JOB, JobConstant.GroupEnum.SWAP_GROUP.getType()); +// } catch (SchedulerException e) { +// e.printStackTrace(); +// log.error("恢复车牌机扫描出现错误", e); +// } +// +// } +// +// +// } +// } +// +//} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/job/job/charging/ChargingFullOfJob.java b/web-server/src/main/java/com/evotech/hd/webserver/job/job/charging/ChargingFullOfJob.java new file mode 100644 index 0000000..2a9a0f4 --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/job/job/charging/ChargingFullOfJob.java @@ -0,0 +1,85 @@ +package com.evotech.hd.webserver.job.job.charging; + +import cn.hutool.extra.spring.SpringUtil; +import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; +import com.evotech.hd.core.dtos.business.AccessStrategyDTO; +import com.evotech.hd.core.dtos.business.BatteryCompartmentDTO; +import com.evotech.hd.core.dtos.business.BatteryDTO; +import com.evotech.hd.core.entity.business.BatteryCompartment; +import com.evotech.hd.exception.InstructionException; +import com.evotech.hd.utils.Collections; +import com.evotech.hd.webserver.job.job.BaseJob; +import com.evotech.hd.webserver.service.BatteryCompartmentService; +import com.evotech.hd.webserver.service.BatteryService; +import com.evotech.hd.webserver.service.OrderChargingService; +import com.evotech.hd.webserver.utils.AccessStrategyUtil; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import java.util.List; +import java.util.Map; + +/** + * 充电充满扫描 ChargingFullOfJob + * + * @author andy.shi + * @ClassName:ChargingEndJob + * @date: 2025年10月07日 11:47 + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@Slf4j +@Component +public class ChargingFullOfJob implements BaseJob { + + BatteryCompartmentService batteryCompartmentService; + BatteryService batteryService; + OrderChargingService orderChargingService; + @PostConstruct + public void init(){ + if(batteryCompartmentService == null){ + batteryCompartmentService = getBatteryCompartmentService(); + } + if(batteryService == null){ + batteryService = getBatteryService(); + } + if(orderChargingService == null){ + orderChargingService = SpringUtil.getBean(OrderChargingService.class); + } + } + + + @Override + public void run(Map params) throws Exception { + AccessStrategyDTO accessStrategyDTO = AccessStrategyUtil.getValidAccessStrategyDTO(); + //获取所有正在充电中的电池仓 + List chargingIds = getBatteryCompartmentService().findChargingPositionIds(false); + if(Collections.isNotEmpty(chargingIds)){ + for (Integer id: chargingIds){ + checkChargingSoc(id, accessStrategyDTO.getFullOfLimitSoc()); + } + } + } + + public void checkChargingSoc(Integer id, Integer fullOfLimitSoc) throws InstructionException { + //刷新电池仓数据 + batteryCompartmentService.buildCompartment(id); + //刷新电池数据 + batteryService.buildBattery(id); + BatteryCompartmentDTO batteryCompartmentDTO = batteryCompartmentService.getInfo(id); + if(batteryCompartmentDTO != null && batteryCompartmentDTO.getOrderChargingId() != null && batteryCompartmentDTO.getBattery() != null){ + BatteryDTO batteryDTO = batteryCompartmentDTO.getBattery(); + //如果当前电池的soc >= 充满界定, 则认为充电完成 + if(Integer.valueOf(batteryDTO.getSoc()) >= fullOfLimitSoc){ + //首先停止充电 + batteryCompartmentService.stopChanging(id); + //然后停止充电订单 + if(orderChargingService.OrderChargingCompleted(batteryCompartmentDTO)){ + //清除电池仓的订单信息. 初始化是否全额充电标识 + batteryCompartmentService.update(new UpdateWrapper().lambda().set(BatteryCompartment::getOrderChargingId, null).set(BatteryCompartment::getAllPower, false).eq(BatteryCompartment::getId, batteryCompartmentDTO.getId())); + } + } + } + } + +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/job/job/charging/ChargingJob.java b/web-server/src/main/java/com/evotech/hd/webserver/job/job/charging/ChargingJob.java new file mode 100644 index 0000000..cdfc290 --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/job/job/charging/ChargingJob.java @@ -0,0 +1,33 @@ +package com.evotech.hd.webserver.job.job.charging; + +import com.evotech.hd.utils.Collections; +import com.evotech.hd.webserver.job.job.BaseJob; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.Map; + +/** + * 充电策略更换 ChargingJob + * + * @author andy.shi + * @ClassName:ChargingJob + * @date: 2025年10月07日 11:32 + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@Slf4j +@Component +public class ChargingJob implements BaseJob { + + + @Override + public void run(Map params) throws Exception { + //休眠1分钟, 防止时间重叠 + Thread.sleep(60000); + List chargingIds = getBatteryCompartmentService().findChargingPositionIds(true); + if(Collections.isNotEmpty(chargingIds)){ + chargingIds.stream().forEach(getBatteryCompartmentService()::charging); + } + } +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/job/job/electricitymeter/ElectricityMeterJob.java b/web-server/src/main/java/com/evotech/hd/webserver/job/job/electricitymeter/ElectricityMeterJob.java new file mode 100644 index 0000000..3bb72a7 --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/job/job/electricitymeter/ElectricityMeterJob.java @@ -0,0 +1,36 @@ +package com.evotech.hd.webserver.job.job.electricitymeter; + +import com.alibaba.fastjson2.JSONObject; +import com.evotech.hd.utils.Collections; +import com.evotech.hd.webserver.job.job.BaseJob; +import com.evotech.hd.webserver.utils.instruction.InstructionReadUtils; +import com.evotech.hd.webserver.websocket.controller.WebSocketUtils; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import java.util.Map; + +/** + * 电表箱 电表数据Job + * + * @author andy.shi + * @ClassName:ElectricityMeterJob + * @date: 2025年09月25日 16:28 + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@Slf4j +@Component +public class ElectricityMeterJob implements BaseJob { + + + public void run(Map params) throws Exception{ + + Boolean devConnect = InstructionReadUtils.energyMeterDevConnect(); + String meter = "--"; + if(devConnect){ + meter = String.valueOf(InstructionReadUtils.energyMeterEPImp()); + } + //推送页面广播 + WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method", "electricity", "data", JSONObject.toJSONString(Collections.asMap("devConnect",devConnect,"meter",meter))))); + } +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/job/job/swap/CloseDoorJob.java b/web-server/src/main/java/com/evotech/hd/webserver/job/job/swap/CloseDoorJob.java new file mode 100644 index 0000000..51a787c --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/job/job/swap/CloseDoorJob.java @@ -0,0 +1,40 @@ +//package com.evotech.hd.webserver.job.job.swap; +// +//import com.evotech.hd.webserver.job.JobConstant; +//import com.evotech.hd.webserver.job.job.BaseJob; +//import com.evotech.hd.webserver.utils.instruction.InstructionReadUtils; +//import com.evotech.hd.webserver.utils.instruction.InstructionWriteUtils; +//import lombok.extern.slf4j.Slf4j; +//import org.springframework.stereotype.Component; +// +//import java.util.Map; +// +///** +// * 开合门关闭CloseDoorJob +// * +// * @author andy.shi +// * @ClassName:CloseDoorJob +// * @date: 2025年10月29日 17:06 +// * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 +// */ +//@Slf4j +//@Component +//public class CloseDoorJob implements BaseJob { +// @Override +// public void run(Map params) throws Exception { +// Boolean closeResult = false; +// do { +// if(closeResult = InstructionReadUtils.getCenRbDoorCloseStatus()){ +// //释放对中机构 +// InstructionWriteUtils.closeCorrectInstitution(); +// //等待开始执行 +//// if(getExecutionBatterySwapService().executionCenOpenResult()){ +// //启动RGV归零扫秒 +// getQuartzJobService().runJobNow(JobConstant.RGV_RESET_TO_ZERO_JOB, JobConstant.GroupEnum.SWAP_GROUP.getType()); +//// } +// +// } +// }while (closeResult); +// +// } +//} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/job/job/swap/LoadingNewBatteryJob.java b/web-server/src/main/java/com/evotech/hd/webserver/job/job/swap/LoadingNewBatteryJob.java new file mode 100644 index 0000000..e74a55a --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/job/job/swap/LoadingNewBatteryJob.java @@ -0,0 +1,61 @@ +//package com.evotech.hd.webserver.job.job.swap; +// +//import com.evotech.hd.core.entity.business.Battery; +//import com.evotech.hd.core.entity.business.BatteryCompartment; +//import com.evotech.hd.core.enums.SwapBatteryStepEnum; +//import com.evotech.hd.webserver.config.redis.utils.RedisUtil; +//import com.evotech.hd.webserver.job.job.BaseJob; +//import lombok.extern.slf4j.Slf4j; +//import org.springframework.stereotype.Component; +// +//import java.util.Map; +// +///** +// * 装新电池扫描 +// * +// * @ClassName:SplitOldBatteryJob +// * @date: 2025年08月27日 15:00 +// * @author: andy.shi +// * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 +// */ +//@Slf4j +//@Component +//public class LoadingNewBatteryJob implements BaseJob { +// +// public void run(Map params) throws Exception{ +// +// Boolean openResult = false; +// do { +// if (checkError(String.valueOf(params.get("rgvNo")), SwapBatteryStepEnum.INSTALL_NEW_ELECTRICITY)) { +// return; +// } +// if (openResult = getExecutionBatterySwapService().executionOpenResult(String.valueOf(params.get("rgvNo")))) { +// Boolean result = false; +// do { +// if (checkError(String.valueOf(params.get("rgvNo")), SwapBatteryStepEnum.INSTALL_NEW_ELECTRICITY)) { +// return; +// } +// if (result = getExecutionBatterySwapService().executionResult(String.valueOf(params.get("rgvNo")))) { +// //清除拆除标记 +// RedisUtil.set("splitOldBattery", "0"); +// //标记数据, 电池仓标记离仓 +// BatteryCompartment batteryCompartment = getBatteryCompartmentService().getById(String.valueOf(params.get("compartmentId"))); +// batteryCompartment.setExistsBattery(false); +// getBatteryCompartmentService().updateById(batteryCompartment); +// //电池标记不在仓 +// Battery battery = getBatteryService().getById(String.valueOf(params.get("batteryId"))); +// battery.setStorageStatus(0); +// getBatteryService().updateById(battery); +// //如果为true,则证明没有异常, 开始启动换电完成 +// if (getExecutionBatterySwapService().executionNextStep(String.valueOf(params.get("rgvNo")), SwapBatteryStepEnum.INSTALL_NEW_ELECTRICITY)) { +// getExecutionBatterySwapService().completed(String.valueOf(params.get("orderId"))); +// } +// +// } +// } while (!result); +// } +// }while (!openResult); +// +// } +// +//} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/job/job/swap/OpenDoorJob.java b/web-server/src/main/java/com/evotech/hd/webserver/job/job/swap/OpenDoorJob.java new file mode 100644 index 0000000..4f2f07a --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/job/job/swap/OpenDoorJob.java @@ -0,0 +1,34 @@ +//package com.evotech.hd.webserver.job.job.swap; +// +//import com.evotech.hd.webserver.job.JobConstant; +//import com.evotech.hd.webserver.job.job.BaseJob; +//import com.evotech.hd.webserver.utils.instruction.InstructionReadUtils; +//import lombok.extern.slf4j.Slf4j; +//import org.springframework.stereotype.Component; +// +//import java.util.Map; +// +///** +// * 开合门打开CloseDoorJob +// * +// * @author andy.shi +// * @ClassName:CloseDoorJob +// * @date: 2025年10月29日 17:06 +// * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 +// */ +//@Slf4j +//@Component +//public class OpenDoorJob implements BaseJob { +// @Override +// public void run(Map params) throws Exception { +// Boolean openResult = false; +// do { +// //结果不为1, 重新启动对中机构扫描 +// if(openResult = InstructionReadUtils.getCenRbDoorOpenStatus()){ +// // 执行换电需求 +// getExecutionBatterySwapService().startBattery(String.valueOf(params.get("orderId"))); +// } +// }while (!openResult); +// +// } +//} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/job/job/swap/PutOldBatteryJob.java b/web-server/src/main/java/com/evotech/hd/webserver/job/job/swap/PutOldBatteryJob.java new file mode 100644 index 0000000..f74663b --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/job/job/swap/PutOldBatteryJob.java @@ -0,0 +1,100 @@ +//package com.evotech.hd.webserver.job.job.swap; +// +//import cn.hutool.extra.spring.SpringUtil; +//import com.evotech.hd.core.entity.business.Battery; +//import com.evotech.hd.core.entity.business.BatteryCompartment; +//import com.evotech.hd.core.entity.business.OrderSwap; +//import com.evotech.hd.core.enums.SwapBatteryStepEnum; +//import com.evotech.hd.webserver.config.redis.utils.RedisUtil; +//import com.evotech.hd.webserver.job.job.BaseJob; +//import com.evotech.hd.webserver.job.service.OldExecutionBatterySwapService; +//import com.evotech.hd.webserver.logging.LogUtils; +//import com.evotech.hd.webserver.service.OrderSwapService; +//import com.evotech.hd.webserver.utils.AccessStrategyUtil; +//import lombok.extern.slf4j.Slf4j; +//import org.apache.commons.lang3.ObjectUtils; +//import org.springframework.stereotype.Component; +// +//import java.util.Map; +// +///** +// * 存旧电池扫描 +// * +// * @ClassName:SplitOldBatteryJob +// * @date: 2025年08月27日 15:00 +// * @author: andy.shi +// * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 +// */ +//@Slf4j +//@Component +//public class PutOldBatteryJob implements BaseJob { +// +// +// public void run(Map params) throws Exception{ +// Boolean openResult = false; +// do { +// if (checkError(String.valueOf(params.get("rgvNo")), SwapBatteryStepEnum.DISCHARGE_OLD_ELECTRICITY)) { +// return; +// } +// if (openResult = getExecutionBatterySwapService().executionOpenResult(String.valueOf(params.get("rgvNo")))) { +// Boolean result = false; +// do { +// if (checkError(String.valueOf(params.get("rgvNo")), SwapBatteryStepEnum.DISCHARGE_OLD_ELECTRICITY)) { +// return; +// } +// if(result = getExecutionBatterySwapService().executionResult(String.valueOf(params.get("rgvNo")))) { +// //电池仓位号信息, 同id保持一致 +// Integer id = Integer.valueOf(String.valueOf(params.get("batteryCompartmentNo"))); +// //执行开始充电 +// getBatteryCompartmentService().charging(id); +// //如果为true,则证明没有异常, 开始启动换电 +// if (getExecutionBatterySwapService().executionNextStep(String.valueOf(params.get("rgvNo")), SwapBatteryStepEnum.DISCHARGE_OLD_ELECTRICITY)) { +// String orderId = String.valueOf(params.get("orderId")); +// //存旧电池成功, 调用数据查询接口, 记录数据信息; +// +// OrderSwap orderSwap = SpringUtil.getBean(OrderSwapService.class).getById(orderId); +// BatteryCompartment batteryCompartment = getBatteryCompartmentService().buildCompartment(id); +// try { +// if(batteryCompartment != null){ +// //记录返回电池仓位号 +// orderSwap.setReturnBatNo(String.valueOf(batteryCompartment.getId())); +// } +// } catch (Exception e) { +// e.printStackTrace(); +// getAsyncLogService().saveLog(LogUtils.getAlarmLog("更新电池仓信息出现错误", -1, "PutOldBatteryJob.buildCompartment", "", e)); +// } +// try { +// Battery battery = getBatteryService().buildBattery(id); +// if(battery != null){ +// orderSwap.setReturnBatCode(battery.getBatteryCode()); +// orderSwap.setReturnBatSoc(battery.getSoc()); +// } +// } catch (Exception e) { +// e.printStackTrace(); +// getAsyncLogService().saveLog(LogUtils.getAlarmLog("更新电池信息出现错误", -1, "PutOldBatteryJob.buildBattery", "", e)); +// } +// +// try { +// //同步更新电池仓,相关信息 +// if(getOrderSwapService().updateById(orderSwap) && batteryCompartment != null){ +// batteryCompartment.setExistsBattery(true); +// getBatteryCompartmentService().updateById(batteryCompartment); +// } +// } catch (Exception e) { +// e.printStackTrace(); +// getAsyncLogService().saveLog(LogUtils.getAlarmLog("更新订单相关信息出现错误", -1, "orderSwapService.updateById", "", e)); +// } +// Object obj = RedisUtil.get(OldExecutionBatterySwapService.RGV_NUM); +// if(ObjectUtils.isNotEmpty(obj) && "1".equals(String.valueOf(obj))){ +// // 执行取新电池需求 +// getExecutionBatterySwapService().takeNewBattery(String.valueOf(params.get("rgvNo")), orderId, AccessStrategyUtil.getValidAccessStrategyDTO().getLowestBatterySwapSoc()); +// } +// } +// } +// +// } while (!result); +// } +// }while (!openResult); +// +// } +//} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/job/job/swap/SplitOldBatteryJob.java b/web-server/src/main/java/com/evotech/hd/webserver/job/job/swap/SplitOldBatteryJob.java new file mode 100644 index 0000000..5f6dd85 --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/job/job/swap/SplitOldBatteryJob.java @@ -0,0 +1,52 @@ +//package com.evotech.hd.webserver.job.job.swap; +// +//import com.alibaba.fastjson2.JSONObject; +//import com.evotech.hd.core.enums.SwapBatteryStepEnum; +//import com.evotech.hd.utils.Collections; +//import com.evotech.hd.webserver.config.redis.utils.RedisUtil; +//import com.evotech.hd.webserver.job.JobConstant; +//import com.evotech.hd.webserver.job.job.BaseJob; +//import lombok.extern.slf4j.Slf4j; +//import org.quartz.SchedulerException; +//import org.springframework.stereotype.Component; +// +//import java.util.Map; +// +///** +// * 拆旧电池扫描 +// * +// * @ClassName:SplitOldBatteryJob +// * @date: 2025年08月27日 15:00 +// * @author: andy.shi +// * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 +// */ +//@Slf4j +//@Component +//public class SplitOldBatteryJob implements BaseJob { +// +// public void run(Map params) throws Exception{ +// Boolean openResult = false; +// do { +// if(checkError(String.valueOf(params.get("rgvNo")), SwapBatteryStepEnum.DISMANTLE_OLD_ELECTRICITY)){ +// return; +// } +// if(openResult = getExecutionBatterySwapService().executionOpenResult(String.valueOf(params.get("rgvNo")))){ +// Boolean result = false; +// do { +// if(checkError(String.valueOf(params.get("rgvNo")), SwapBatteryStepEnum.DISMANTLE_OLD_ELECTRICITY)){ +// return; +// } +// if(result = getExecutionBatterySwapService().executionResult(String.valueOf(params.get("rgvNo")))) { +// //记录标识, 拆旧电完成 +// RedisUtil.set("splitOldBattery", "1"); +// //如果为true,则证明没有异常, 开始启动换电 +// if (getExecutionBatterySwapService().executionNextStep(String.valueOf(params.get("rgvNo")), SwapBatteryStepEnum.DISMANTLE_OLD_ELECTRICITY)) { +// // 执行存旧电池需求 +// getExecutionBatterySwapService().putOldBattery(String.valueOf(params.get("rgvNo")), String.valueOf(params.get("orderId"))); +// } +// } +// }while (!result); +// } +// }while (!openResult); +// } +//} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/job/job/swap/TakeNewBatteryJob.java b/web-server/src/main/java/com/evotech/hd/webserver/job/job/swap/TakeNewBatteryJob.java new file mode 100644 index 0000000..b5ed140 --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/job/job/swap/TakeNewBatteryJob.java @@ -0,0 +1,73 @@ +//package com.evotech.hd.webserver.job.job.swap; +// +//import com.evotech.hd.core.enums.SwapBatteryStepEnum; +//import com.evotech.hd.webserver.config.redis.utils.RedisUtil; +//import com.evotech.hd.webserver.job.JobConstant; +//import com.evotech.hd.webserver.job.job.BaseJob; +//import com.evotech.hd.webserver.utils.instruction.InstructionWriteUtils; +//import lombok.extern.slf4j.Slf4j; +//import org.apache.commons.lang3.ObjectUtils; +//import org.quartz.SchedulerException; +//import org.springframework.stereotype.Component; +// +//import java.util.Map; +// +///** +// * 取新电池扫描 +// * +// * @ClassName:SplitOldBatteryJob +// * @date: 2025年08月27日 15:00 +// * @author: andy.shi +// * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 +// */ +//@Slf4j +//@Component +//public class TakeNewBatteryJob implements BaseJob { +// +// public void run(Map params) throws Exception{ +// Boolean openResult = false; +// do { +// if (checkError(String.valueOf(params.get("rgvNo")), SwapBatteryStepEnum.GET_NEW_ELECTRICITY)) { +// return; +// } +// if (openResult = getExecutionBatterySwapService().executionOpenResult(String.valueOf(params.get("rgvNo")))) { +// Boolean result = false; +// c: do { +// if (checkError(String.valueOf(params.get("rgvNo")), SwapBatteryStepEnum.GET_NEW_ELECTRICITY)) { +// return; +// } +// if(result = getExecutionBatterySwapService().executionResult(String.valueOf(params.get("rgvNo")))) { +// // 如果为true,则证明没有异常, 开始启动换电 +// if (getExecutionBatterySwapService().executionNextStep(String.valueOf(params.get("rgvNo")), SwapBatteryStepEnum.GET_NEW_ELECTRICITY)) { +// //检查是否拆电完成 +// Object splitOldBattery = RedisUtil.get("splitOldBattery"); +// //拆旧电池完成, 并且rgv取点动作完成 取新电池没有异常 +// if(ObjectUtils.isEmpty(splitOldBattery) || !"1".equals(String.valueOf(splitOldBattery))){ +// //证明拆旧电池没有完成 +// result = false; +// //终止本次循环, 重新进入下次循环; +// break c; +// } +// //bms下电 +// try { +// InstructionWriteUtils.BMSPowerOff(Integer.valueOf(String.valueOf(params.get("compartmentId")))); +// } catch (Exception e) { +// log.error("取新电池后, BMS下电出现错误", e); +// e.printStackTrace(); +// } +// // 执行装新电池需求 +// getExecutionBatterySwapService().loadingNewBattery(params); +// } +// } +// } while (!result); +// } +// }while (!openResult); +// +// +// +// +// +// +// } +// +//} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/job/service/ExecutionBatterySwapService.java b/web-server/src/main/java/com/evotech/hd/webserver/job/service/ExecutionBatterySwapService.java new file mode 100644 index 0000000..c889eea --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/job/service/ExecutionBatterySwapService.java @@ -0,0 +1,827 @@ +package com.evotech.hd.webserver.job.service; + +import cn.hutool.extra.spring.SpringUtil; +import com.alibaba.fastjson2.JSONObject; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.evotech.hd.core.dtos.Result; +import com.evotech.hd.core.dtos.ResultUtil; +import com.evotech.hd.core.dtos.business.AccessStrategyDTO; +import com.evotech.hd.core.dtos.business.BatteryCompartmentDTO; +import com.evotech.hd.core.dtos.business.OrderSwapDTO; +import com.evotech.hd.core.dtos.system.ParamsDTO; +import com.evotech.hd.core.entity.business.Battery; +import com.evotech.hd.core.entity.business.BatteryCompartment; +import com.evotech.hd.core.entity.business.OrderReservation; +import com.evotech.hd.core.entity.business.OrderSwap; +import com.evotech.hd.core.enums.CacheNames; +import com.evotech.hd.core.enums.OrderStatusEnums; +import com.evotech.hd.core.enums.SwapBatteryStepEnum; +import com.evotech.hd.core.mapstruct.OrderSwapWrapper; +import com.evotech.hd.exception.InstructionException; +import com.evotech.hd.utils.Collections; +import com.evotech.hd.utils.DateUtils; +import com.evotech.hd.webserver.config.redis.utils.RedisUtil; +import com.evotech.hd.webserver.job.JobConstant; +import com.evotech.hd.webserver.job.entity.QuartzJobInfo; +import com.evotech.hd.webserver.logging.AsyncLogService; +import com.evotech.hd.webserver.logging.LogUtils; +import com.evotech.hd.webserver.service.*; +import com.evotech.hd.webserver.utils.AccessStrategyUtil; +import com.evotech.hd.webserver.utils.ParamUtils; +import com.evotech.hd.webserver.utils.instruction.InstructionReadUtils; +import com.evotech.hd.webserver.utils.instruction.InstructionUtils; +import com.evotech.hd.webserver.utils.instruction.InstructionWriteUtils; +import com.evotech.hd.webserver.utils.sendCloud.CloudSendInfoUtils; +import com.evotech.hd.webserver.websocket.controller.WebSocketUtils; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.ObjectUtils; +import org.apache.commons.lang3.StringUtils; +import org.quartz.SchedulerException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicReference; + +/** + * 换电步骤执行类 + * + * @ClassName:HomeServiceImpl + * @date: 2025年08月27日 9:24 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@Slf4j +@Service +public class ExecutionBatterySwapService { + + @Autowired + private QuartzJobService quartzJobService; + @Resource + OrderSwapService orderSwapService; + @Autowired + OrderSwapWrapper orderSwapWrapper; + @Resource + BatterySwapStepService batterySwapStepService; + @Resource + OrderReservationService orderReservationService; + @Resource + BatteryCompartmentService batteryCompartmentService; + @Resource + BatteryService batteryService; + @Resource + AsyncLogService asyncLogService; + + public final static String RGV_NUM = "regNum"; + public final static String RGV_NO = "regNo"; + + public Result checkPlateNum(String plateNum) throws Exception { + String swapDay = DateUtils.parseDateToStr("yyyy-MM-dd", new Date()); + //不存在当前key + if(!RedisUtil.hasKey(CacheNames.SYSTEM_LOCK_PLATE+":"+swapDay+":"+plateNum)){ + RedisUtil.set(CacheNames.SYSTEM_LOCK_PLATE+":"+swapDay+":"+plateNum, true); + + OrderReservation orderReservation = orderReservationService.getOne(new LambdaQueryWrapper().eq(OrderReservation::getPlateNum, plateNum).eq(OrderReservation::getStatus,1).eq(OrderReservation::getSwapDay, swapDay), false); + //抓取到数据信息 + if(ObjectUtils.isNotEmpty(orderReservation) && orderReservation.getId() != null){ + try { + //停止扫码机轮询 + quartzJobService.pauseJob(new QuartzJobInfo(JobConstant.LICENSE_PLATE_MACHINE_JOB, JobConstant.GroupEnum.SWAP_GROUP.getType())); + } catch (SchedulerException e) { + e.printStackTrace(); + log.error("车辆进站, 暂停车牌机扫秒出现错误", e); + } + Result result = orderSwapService.addOrderSwapByReservation(orderReservation); + if(ResultUtil.verifyCode(result)){ + //不通知云端, 由创建订单的推送时, 自己取消 + orderReservationService.updateStatus(orderReservation.getId(),2, true); + } + //查询正在运行的订单 + Result runningOrderResult = orderSwapService.runningOrder(); + if(ResultUtil.verifyCode(runningOrderResult)){ + //推送页面广播 + WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method","order","data",JSONObject.toJSONString(ResultUtil.getValue(runningOrderResult))))); + //下发物理接口, 抬杆放行 TODO + InstructionWriteUtils.openRolling(ParamUtils.getDoorParams("入口")); + try { + //开启车辆到位扫秒 + quartzJobService.resumeJob(new QuartzJobInfo(JobConstant.ARRIVAL_SIGNAL_JOB, JobConstant.GroupEnum.SWAP_GROUP.getType())); + } catch (SchedulerException e) { + e.printStackTrace(); + log.error("车辆到位信息扫秒出现错误", e); + } + } + return result; + } + } + return Result.getInstance().build(Boolean.class).error("无法创建订单, 当前车辆没有预约", false); + } + + + + /*** + * 启动对中机构 + * @throws Exception + */ + @Async + public void activateAlignmentMechanism() { + CompletableFuture.runAsync(() -> { + try { + //获取生效的RGV编号 + AccessStrategyDTO accessStrategyDTO = AccessStrategyUtil.getValidAccessStrategyDTO(); + if(StringUtils.isEmpty(accessStrategyDTO.getEffectiveRgvNo())){ + asyncLogService.saveLog(LogUtils.getAlarmLog("开始换电启动失败, 没有找到可用的RGV", -1, "ExecutionBatterySwapService.startBattery", "", null)); + } + //启动对中结构 + if(buildCenOpenFuture().get()){ + //打开开合门 + if(buildOpenFuture().get()){ + //获取仓位信息 + Map result = calculationPosition(accessStrategyDTO); + //查询当前运行订单 + Result running = orderSwapService.runningOrder(); + OrderSwapDTO orderSwapDTO = running.getData(); + CompletableFuture startBatterySwappingFuture = startBatterySwapping( + Integer.valueOf(result.get("rgvNum")), + result.get("splitStorageRgv"), + Integer.valueOf(result.get("putNo")), + result.get("loadingRgv"), + Integer.valueOf(result.get("takeNo")), + orderSwapDTO.getId() + ); + //执行完成, 关闭门口 + if(startBatterySwappingFuture.get()){ + orderSwapDTO.setStatus(OrderStatusEnums.SWAPOVER.getCode()); + orderSwapDTO.setServiceTimeEnd(new Date()); + orderSwapService.updateById(orderSwapWrapper.toEntity(orderSwapDTO));//更新换电完成状态 + //执行换电完成 + executionResultPush(orderSwapDTO, SwapBatteryStepEnum.COMPLETED, false, Collections.emptyMap()); + orderSwapService.sendOrderCompleted(orderSwapDTO.getId()); + completed(orderSwapDTO, Integer.valueOf(result.get("rgvNum"))); + } + } + } + + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException(e); + } + }); + } + + public void sleep() throws InterruptedException { + Thread.sleep(ParamUtils.getInstructionIntervalRequestMilliseconds()); + } + //计算仓位信息 + public Map calculationPosition(AccessStrategyDTO accessStrategyDTO){ + Map result = Collections.emptyMap(); + //查看生效的rgv + String[] rgvArrays = accessStrategyDTO.getEffectiveRgvNo().split(","); + + + /*** + * 如果生效RGV > 1 则全站可计算去取出, 存入 + * 如果生效RGV == 1 则计算在当前RGV可控范围内的仓位计算 + */ + //拆存RGV + AtomicReference splitStorageRgv = new AtomicReference<>(""); + //取装RGV + AtomicReference loadingRgv = new AtomicReference<>(""); + Integer putNo = null; + Integer takeNo = null; + + List rgvList = Collections.emptyList(); + Map> egvMap = ParamUtils.getBatteryCompartmentPosition(); + for (String rgv : rgvArrays){ + rgvList.addAll(egvMap.get(rgv)); + } + //如果是双RGV, 需要提出 + //使用允许范围内的仓位号, 计算取出 + BatteryCompartmentDTO batteryCompartmentDTO =batteryCompartmentService.getBatterySwap(accessStrategyDTO.getLowestBatterySwapSoc(), rgvList); + takeNo = batteryCompartmentDTO.getId(); + //根据取出仓位计算取装RGV + Integer finalTakeNo = takeNo; + egvMap.keySet().forEach(rgvKey->{ + if(egvMap.get(rgvKey).contains(finalTakeNo)){ + loadingRgv.set(rgvKey); + } + }); + //如果是双RGV + if(rgvArrays.length > 1){ + //去除掉取装rgv的仓位信息 + rgvList.removeAll(egvMap.get(loadingRgv.get())); + } + //使用允许范围内的仓位号, 计算存入 + putNo = batteryCompartmentService.getBatteryCompartmentId(rgvList); + //如果存仓为null, 则反向计算, 优先计算存储仓位, 然后计算取出仓位 + if(putNo == null){ + rgvList.clear(); + for (String rgv : rgvArrays){ + rgvList.addAll(egvMap.get(rgv)); + } + //使用允许范围内的仓位号, 计算存入 + putNo = batteryCompartmentService.getBatteryCompartmentId(rgvList); + Integer finalPutNo = putNo; + egvMap.keySet().forEach(rgvKey->{ + if(egvMap.get(rgvKey).contains(finalPutNo)){ + splitStorageRgv.set(rgvKey); + } + }); + //如果是双RGV + if(rgvArrays.length > 1){ + //去除掉取装rgv的仓位信息 + rgvList.removeAll(egvMap.get(splitStorageRgv.get())); + } + //使用允许范围内的仓位号, 计算取出 + batteryCompartmentDTO = batteryCompartmentService.getBatterySwap(accessStrategyDTO.getLowestBatterySwapSoc(), rgvList); + takeNo = batteryCompartmentDTO.getId(); + + Integer finalTakeNo1 = takeNo; + egvMap.keySet().forEach(rgvKey->{ + if(egvMap.get(rgvKey).contains(finalTakeNo1)){ + loadingRgv.set(rgvKey); + } + }); + }else{ + Integer finalPutNo = putNo; + egvMap.keySet().forEach(rgvKey->{ + if(egvMap.get(rgvKey).contains(finalPutNo)){ + splitStorageRgv.set(rgvKey); + } + }); + } + + result.put("splitStorageRgv", splitStorageRgv.get()); + result.put("takeNo", String.valueOf(takeNo)); + result.put("loadingRgv", loadingRgv.get()); + result.put("putNo", String.valueOf(putNo)); + result.put("rgvNum", String.valueOf(rgvArrays.length)); + return result; + } + //开始换电 + public CompletableFuture startBatterySwapping(final Integer rgvNum, final String splitRgv, final Integer putNo, final String loadingRgv, final Integer takeNo, final Integer orderId){ + return CompletableFuture.supplyAsync(()->{ + try { + if(rgvNum > 1){ + //拆 + CompletableFuture splitFuture = buildSplitFuture(splitRgv); + //存, 依托于拆 + CompletableFuture putFuture = splitFuture.thenApplyAsync(splitResult ->{ + if (splitResult) { + try { + return buildPutFuture(splitRgv, putNo, orderId).get(); + } catch (Exception e) { + log.error("存旧电池失败, 原因是{}", e.getMessage(), e); + return false; + } + } + return false; + }); + //取 + CompletableFuture takeFuture =buildTakeFuture(loadingRgv, takeNo, orderId); + //装. 依托于 拆, 取 + CompletableFuture loadingFuture = CompletableFuture.allOf(splitFuture, takeFuture).thenApplyAsync(voidResult->{ + Boolean split = splitFuture.join(); + Boolean take = takeFuture.join(); + try { + if (split && take) { + return buildLoadingFuture(loadingRgv).get(); + } + }catch (Exception e) { + log.error("装旧电池失败, 原因是{}", e.getMessage(), e); + return false; + } + return false; + }); + return CompletableFuture.allOf(splitFuture, putFuture, takeFuture, loadingFuture).thenApplyAsync(voidResult->{ + return (splitFuture.join() && putFuture.join() && takeFuture.join() && loadingFuture.join()); + }).get(); + }else{ + //拆除旧电池走完, 并且正常结束 + if(buildSplitFuture(splitRgv).get()){ + //存 + if(buildPutFuture(splitRgv, putNo, orderId).get()){ + //取 + if(buildTakeFuture(loadingRgv, takeNo, orderId).get()){ + //装 + return buildLoadingFuture(loadingRgv).get(); + } + } + }; + } + } catch (Exception e) { + log.error("换电操作出现错误: {}", e.getMessage(), e); + return false; + } + return false; + }); + } + //启动对中机构 + public CompletableFuture buildCenOpenFuture(){ + return CompletableFuture.supplyAsync(()->{ + try { + InstructionWriteUtils.startCorrectInstitution(); + Boolean openResult = false; + do { + //检查是否存在异常 + if(checkCenError()){ + return false; + } + sleep(); + if (openResult = checkCenOpenExecutionResult()) { + log.info("对中机构正在运行, 当前时间为: "+ new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date())); + Boolean result = false; + do { + sleep(); + if(result = checkCenExecutionResult()){ + log.info("对中机构执行完成, 当前时间为: "+ new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date())); + // 推送云端, 并查询是否存在异常 没有异常, 开始启动换电 + if(checkCenError(SwapBatteryStepEnum.INSTITUTIONS_FOR_CHINA)){ + //对中机构打开完成 + return true; + } + return false; + } + }while (!result); + //如果为true,则证明执行结束 + } + }while (!openResult); + } catch (Exception e) { + log.error("对中机构启动失败: {}", e.getMessage(), e); + return false; + } + return false; + }); + } + //打开开合门 + public CompletableFuture buildOpenFuture(){ + return CompletableFuture.supplyAsync(()->{ + //开始执行拆除 + try { + InstructionWriteUtils.openCenDoor(); + Boolean result = false; + do{ + sleep(); + if(result = InstructionReadUtils.getCenRbDoorOpenStatus()){ + return true; + } + }while (!result); + } catch (Exception e) { + log.error("打开开合门出现异常, {}", e.getMessage(), e); + return false; + } + return false; + }); + } + //拆旧电池 + public CompletableFuture buildSplitFuture(final String rgv){ + return CompletableFuture.supplyAsync(() -> { + try { + //下发拆除命令 + InstructionWriteUtils.splitOldBattery(rgv); + Boolean openResult = false; + do { + if(checkError(rgv, SwapBatteryStepEnum.DISMANTLE_OLD_ELECTRICITY)){ + return false; + } + sleep(); + if(openResult = checkOpenExecutionResult(rgv)){ + Boolean result = false; + do { + if(checkError(rgv, SwapBatteryStepEnum.DISMANTLE_OLD_ELECTRICITY)){ + return false; + } + sleep(); + if(result = executionResult(rgv)) { + //如果为true,则证明没有异常, 开始启动换电 + if (executionNextStep(String.valueOf(rgv), SwapBatteryStepEnum.DISMANTLE_OLD_ELECTRICITY)) { + // 执行存旧电池需求 + return true; + } + } + }while (!result); + } + }while (!openResult); + } catch (Exception e) { + log.error("拆除旧电池失败, {}", e.getMessage(), e); + return false; + } + return false; + }); + } + //存旧 + public CompletableFuture buildPutFuture(final String rgv, final Integer putNo, final Integer orderId){ + return CompletableFuture.supplyAsync(() -> { + try { + Boolean openResult = false; + //命令下发 + InstructionWriteUtils.putOldBattery(rgv, putNo); + do { + //错误查询 + if (checkError(rgv, SwapBatteryStepEnum.DISCHARGE_OLD_ELECTRICITY)) { + return false; + } + sleep(); + //跳开查询 + if (openResult = checkOpenExecutionResult(rgv)) { + Boolean result = false; + do { + if (checkError(rgv, SwapBatteryStepEnum.DISCHARGE_OLD_ELECTRICITY)) { + return false; + } + sleep(); + //执行结束查询 + if (result = executionResult(rgv)) { + //电池仓位号信息, 同id保持一致 + //如果为true,则证明没有异常, 开始启动换电 + if (executionNextStep(rgv, SwapBatteryStepEnum.DISCHARGE_OLD_ELECTRICITY)) { + //存旧电池成功, 调用数据查询接口, 记录数据信息; + OrderSwap orderSwap = orderSwapService.getById(orderId); + //记录电池数据 + BatteryCompartment batteryCompartment = batteryCompartmentService.buildCompartment(putNo); + try { + if (batteryCompartment != null) { + //记录返回电池仓位号 + orderSwap.setReturnBatNo(String.valueOf(batteryCompartment.getId())); + } + } catch (Exception e) { + e.printStackTrace(); + asyncLogService.saveLog(LogUtils.getAlarmLog("更新电池仓信息出现错误", -1, "PutOldBatteryJob.buildCompartment", "", e)); + } + try { + Battery battery = batteryService.buildBattery(putNo); + if (battery != null) { + orderSwap.setReturnBatCode(battery.getBatteryCode()); + orderSwap.setReturnBatSoc(battery.getSoc()); + } + } catch (Exception e) { + e.printStackTrace(); + asyncLogService.saveLog(LogUtils.getAlarmLog("更新电池信息出现错误", -1, "PutOldBatteryJob.buildBattery", "", e)); + } + + try { + //同步更新电池仓,相关信息 + if (orderSwapService.updateById(orderSwap) && batteryCompartment != null) { + batteryCompartment.setExistsBattery(true); + batteryCompartmentService.updateById(batteryCompartment); + } + } catch (Exception e) { + e.printStackTrace(); + asyncLogService.saveLog(LogUtils.getAlarmLog("更新订单相关信息出现错误", -1, "orderSwapService.updateById", "", e)); + } + //执行开始充电 + batteryCompartmentService.charging(putNo); + return true; + } + } + } while (!result); + } + } while (!openResult); + } catch (Exception e) { + log.error("存放旧电池出现异常: {}", e.getMessage(), e); + return false; + } + return false; + }); + } + //取新 + public CompletableFuture buildTakeFuture(final String rgv, final Integer taskNo, final Integer orderId){ + return CompletableFuture.supplyAsync(() -> { + try { + OrderSwap orderSwap = orderSwapService.getById(orderId); + orderSwap.setRentBatNo(String.valueOf(taskNo)); + Battery battery = batteryService.getBatteryByCompartmentId(taskNo); + orderSwap.setRentBatCode(battery.getBatteryCode()); + orderSwap.setRentBatSoc(battery.getSoc()); + orderSwapService.updateById(orderSwap); + InstructionWriteUtils.takeNewBattery(rgv, taskNo); + Boolean openResult = false; + do { + if (checkError(rgv, SwapBatteryStepEnum.GET_NEW_ELECTRICITY)) { + return false; + } + sleep(); + if (openResult = checkOpenExecutionResult(rgv)) { + Boolean result = false; + do { + if (checkError(rgv, SwapBatteryStepEnum.GET_NEW_ELECTRICITY)) { + return false; + } + sleep(); + if (result = executionResult(rgv)) { + // 如果为true,则证明没有异常, 开始启动换电 + if (executionNextStep(rgv, SwapBatteryStepEnum.GET_NEW_ELECTRICITY)) { + try { + //标记数据, 电池仓标记离仓 + BatteryCompartment batteryCompartment = batteryCompartmentService.getById(taskNo); + batteryCompartment.setExistsBattery(false); + batteryCompartmentService.updateById(batteryCompartment); + //更新电池离仓信息 + battery.setStorageStatus(0); + batteryService.updateById(battery); + //bms下电 + InstructionWriteUtils.BMSPowerOff(taskNo); + } catch (Exception e) { + log.error("取新电池后, BMS下电出现错误", e); + e.printStackTrace(); + } + return true; + } + return false; + } + } while (!result); + } + }while (!openResult); + } catch (Exception e) { + log.error("取出新电池出现错误: {}", e.getMessage(), e); + return false; + } + return false; + }); + } + //装 + public CompletableFuture buildLoadingFuture(final String rgv){ + return CompletableFuture.supplyAsync(() -> { + try { + //下发安装命令 + InstructionWriteUtils.loadingNewBattery(rgv); + Boolean openResult = false; + do { + if (checkError(rgv, SwapBatteryStepEnum.INSTALL_NEW_ELECTRICITY)) { + return false; + } + sleep(); + if (openResult = checkOpenExecutionResult(rgv)) { + Boolean result = false; + do { + if (checkError(rgv, SwapBatteryStepEnum.INSTALL_NEW_ELECTRICITY)) { + return false; + } + sleep(); + if (result = executionResult(rgv)) { + //如果为true,则证明没有异常, 开始启动换电完成 + if (executionNextStep(rgv, SwapBatteryStepEnum.INSTALL_NEW_ELECTRICITY)) { + return true; + } + return false; + } + } while (!result); + } + }while (!openResult); + } catch (Exception e) { + log.error("新电池安装出现错误: {}", e.getMessage(), e); + return false; + } + return false; + }); + } + //关闭开合门 + public CompletableFuture buildCloseFuture(){ + return CompletableFuture.supplyAsync(() -> { + //关门操作 + try { + InstructionWriteUtils.closeCenDoor(); + Boolean result = false; + do{ + sleep(); + if(result = InstructionReadUtils.getCenRbDoorCloseStatus()){ + return true; + } + }while (!result); + } catch (Exception e) { + log.error("关闭开合门出现异常: {}", e.getMessage(), e); + return false; + } + return false; + }); + } + //检查车辆离位 + public CompletableFuture buildVehicleAbdicationFuture(final Date serviceTimeBegin, final String plateNum){ + return CompletableFuture.supplyAsync(() -> { + try{ + ParamsDTO arrivalSignal = ParamUtils.getArrivalSignal(); + Boolean result = false; + do { + sleep(); + JSONObject arrivalSignalResult = InstructionUtils.queryReadInteger(arrivalSignal.getName(), arrivalSignal.getParamCode()); + //检查通讯状态 + if (result = (InstructionUtils.verifyStatusLog(arrivalSignal, arrivalSignalResult) && InstructionUtils.getValue(arrivalSignalResult).equals("0"))) { + //睡眠5分钟, 然后在进行关门操作 + Thread.sleep(ParamUtils.getVehicleAbdicationWaitingMilliseconds()); + //关闭进门栏杆 + InstructionWriteUtils.closeRolling(ParamUtils.getDoorParams("出口")); + //查询key + String swapDay = DateUtils.parseDateToStr("yyyy-MM-dd", serviceTimeBegin); + //存在当前key, 清除数据 + if(RedisUtil.hasKey(CacheNames.SYSTEM_LOCK_PLATE+":"+swapDay+":"+plateNum)) { + RedisUtil.del(CacheNames.SYSTEM_LOCK_PLATE + ":" + swapDay + ":" + plateNum); + } + return true; + } + }while (!result); + }catch (Exception e){ + log.error("车辆离位信息错误: {}", e.getMessage(), e); + return false; + } + return false; + }); + } + + /*** 检查对中机构是否开始执行 + * @return + */ + public Boolean checkCenOpenExecutionResult() throws Exception { + ParamsDTO beatActMoving = ParamUtils.getCenActMoving(); + JSONObject js = InstructionUtils.queryReadInteger(beatActMoving.getName(),beatActMoving.getParamValue()); + //如果为1, 则开始执行 + return InstructionUtils.verifyStatusLog(beatActMoving, js) && beatActMoving.getVerifyResult() && !InstructionUtils.getValue(js).equals(beatActMoving.getExpectedResults()); + } + /*** 检查对中机构执行结果 + * @return + */ + public Boolean checkCenExecutionResult() throws InstructionException { + ParamsDTO beatActMoving = ParamUtils.getCenActMoving(); + JSONObject js = InstructionUtils.queryReadInteger(beatActMoving.getName(),beatActMoving.getParamValue()); + //如果为0, 则执行结束 + return InstructionUtils.verifyStatusLog(beatActMoving, js) && beatActMoving.getVerifyResult() && InstructionUtils.getValue(js).equals(beatActMoving.getExpectedResults()); + } + //对中机构异常查询 + public Boolean checkCenError() throws InstructionException { + //如果为0, 则执行结束 + return Collections.isNotEmpty(executionResult(ParamUtils.getCenErrorCode())); + } + //对中机构异常查询 + public Boolean checkCenError(SwapBatteryStepEnum swapBatteryStep) throws InstructionException { + //如果为0, 则执行结束 + Map errorInfoMap = executionResult(ParamUtils.getCenErrorCode()); + Result running = orderSwapService.runningOrder(); + return executionResultPush(running.getData(), swapBatteryStep, Collections.isNotEmpty(errorInfoMap),errorInfoMap); + } + + /*** + * 执行结果跳开查询 + * @return + */ + public Boolean checkOpenExecutionResult(String rgvNo) throws Exception { + ParamsDTO beatActMoving = ParamUtils.getBeatActMoving(); + InstructionUtils.initParamValue(beatActMoving,rgvNo); + //执行查询开始查询 + JSONObject js = InstructionUtils.queryReadInteger(beatActMoving.getName(),beatActMoving.getParamValue()); + //如果为1, 则开始执行 + return InstructionUtils.verifyStatusLog(beatActMoving, js) && !InstructionUtils.getValue(js).equals(beatActMoving.getExpectedResults()); + } + /*** 换电执行结果查询 + * @return + */ + public Boolean executionResult(String rgvNo) throws InstructionException { + ParamsDTO beatActMoving = ParamUtils.getBeatActMoving(); + InstructionUtils.initParamValue(beatActMoving,rgvNo); + JSONObject js = InstructionUtils.queryReadInteger(beatActMoving.getName(),beatActMoving.getParamValue()); + //如果为0, 则执行结束 + return InstructionUtils.verifyStatusLog(beatActMoving, js) && InstructionUtils.getValue(js).equals(beatActMoving.getExpectedResults()); + } + /*** 换电步骤的执行结果, 检查接口 + * @param swapBatteryStep + * @return + */ + public Boolean executionNextStep(String rgvNo, SwapBatteryStepEnum swapBatteryStep) throws InstructionException { + if(executionResult(rgvNo)){ + //如果为0, 则执行结束 + Map errorInfoMap = executionErrorResult(rgvNo); + Result running = orderSwapService.runningOrder(); + return executionResultPush(running.getData(), swapBatteryStep, Collections.isNotEmpty(errorInfoMap),errorInfoMap); + } + //默认出现错误, 禁止执行 + return false; + } + // 查询异常错误信息 + public Boolean checkError(String rgvNo, SwapBatteryStepEnum swapBatteryStep) throws Exception { + Map error = executionErrorResult(rgvNo); + if(Collections.isNotEmpty(error)){ + String message = JSONObject.toJSONString(error); + InstructionWriteUtils.emergencyStop(rgvNo); + Result running = orderSwapService.runningOrder(); + executionResultPush(running.getData(), swapBatteryStep, Collections.isNotEmpty(error),error); + CloudSendInfoUtils.sendAlarmWx(Collections.asMap("area", ParamUtils.findStationName(), "message", message)); + return true; + } + return false; + } + /*** 执行结果过程是否存在异常 + * @return + */ + public Map executionErrorResult(String rgvNo) throws InstructionException { + ParamsDTO errorCode = ParamUtils.getErrorCode(); + InstructionUtils.initParamValue(errorCode, rgvNo); + return executionResult(errorCode); + } + + + + private Map executionResult(ParamsDTO errorCode) throws InstructionException { + Map errorInfoMap = Collections.emptyMap(); + JSONObject error = InstructionUtils.queryReadInteger(errorCode.getName(),errorCode.getParamValue()); + if(InstructionUtils.verifyStatusLog(errorCode, error)) { + String val = InstructionUtils.getValue(error); + //记录换电步骤, 默认成功 + if(!val.equals(errorCode.getExpectedResults())){ + //出现异常, 记录错误信息 + ParamsDTO errorInfo = ParamUtils.getParamsDTO(val); + errorInfoMap.put("errorMsg", errorInfo.getName()); + } + }else{ + errorInfoMap.put("errorMsg", "查询错误信息通讯失败"); + } + + return errorInfoMap; + } + + /*** 推送云端和站端页面 + * @param swapBatteryStep 当前换电步骤 + * @param isError 物理设备是否执行错误 + * @param errorInfoMap 错误信息 + * @return 物理设备执行结果 + */ + public Boolean executionResultPush(OrderSwapDTO orderSwapDTO, SwapBatteryStepEnum swapBatteryStep, Boolean isError, Map errorInfoMap){ + Boolean isAdd = batterySwapStepService.addOrderSwapBatteryStepBySwapComplete(orderSwapDTO.getId(), swapBatteryStep.getCode(), new Date(), false, isError, errorInfoMap); + orderSwapDTO.setBatterySwapStepDTOList(batterySwapStepService.findListByOrderId(orderSwapDTO.getId())); + orderSwapDTO.setRgvNum(AccessStrategyUtil.getValidAccessStrategyDTO().getEffectiveRgvNo().split(",").length); + //推送页面广播 + WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method", "order", "data", JSONObject.toJSONString(orderSwapDTO)))); + if(!isError && isAdd){ + // 发送通知到云端 + CloudSendInfoUtils.sendArrivalSignal(Collections.asMap("pkId", orderSwapDTO.getCloudOrderId(), "step", swapBatteryStep.getCode(), "stepTime", new Date())); + } + return !isError; + } + + + + //检查执行命令 + public Map controllerExecutionResult(String rgvNo) throws InstructionException { + if(executionResult(rgvNo)){ + Map result = executionErrorResult(rgvNo); + if(Collections.isNotEmpty(result)){ + return result; + } + //检查其他rgv是否在交集区中 + Map> map = ParamUtils.getBatteryCompartmentPosition(); + ParamsDTO rgvHShaftLocation = ParamUtils.getRgvHShaftLocation(); + for (String rgv : map.keySet()){ + Integer currentPosition = InstructionReadUtils.getRgvHShaftLocation(rgvHShaftLocation, rgv); + if(currentPosition.compareTo(0) > 0){ + return Collections.asMap("errorMsg", (rgv.equals(rgvNo) ? "当前RGV正在运行" : "当前其他RGV正在运行, 禁止运动")); + } + } + + return Collections.emptyMap(); + } + return Collections.asMap("errorMsg", "当前RGV正在运行"); + } + + /** 换电完成 + * @throws Exception + */ + public void completed(OrderSwapDTO orderSwapDTO, Integer rgvNum) throws Exception{ + orderSwapDTO.setStatus(OrderStatusEnums.SWAPOVER.getCode()); + orderSwapDTO.setServiceTimeEnd(new Date()); + orderSwapService.updateById(orderSwapWrapper.toEntity(orderSwapDTO));//更新换电完成状态 + //执行换电完成 + executionResultPush(orderSwapDTO, SwapBatteryStepEnum.COMPLETED, false, Collections.emptyMap()); + //关闭开合门 + if(buildCloseFuture().get()){ + + //释放对中机构 + InstructionWriteUtils.closeCorrectInstitution(); + //推送空对象推送websocket, 刷新首页数据 + WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method", "order", "data", JSONObject.toJSONString(new OrderSwapDTO((rgvNum != null ? rgvNum : AccessStrategyUtil.getValidAccessStrategyDTO().getEffectiveRgvNo().split(",").length)))))); + //执行开门操作 + InstructionWriteUtils.openRolling(ParamUtils.getDoorParams("出口")); + //等待车辆离位置 + if(buildVehicleAbdicationFuture(orderSwapDTO.getServiceTimeBegin(), orderSwapDTO.getPlateNum()).get()){ + try { + //推送换电完成事件到云端 + orderSwapService.sendOrderCompleted(orderSwapDTO.getId()); + //恢复车牌机扫描 + quartzJobService.resumeJob(JobConstant.LICENSE_PLATE_MACHINE_JOB, JobConstant.GroupEnum.SWAP_GROUP.getType()); + } catch (SchedulerException e) { + e.printStackTrace(); + log.error("恢复车牌机扫描出现错误", e); + } + } + } + +// //释放对中机构 +// InstructionWriteUtils.closeCorrectInstitution(); +// //启动RGV归零扫秒 +// quartzJobService.updateJobParams(JobConstant.RGV_RESET_TO_ZERO_JOB, JobConstant.GroupEnum.SWAP_GROUP.getType(), Collections.asMap("orderId", orderId, "rgvStr", String.join(",",rgvList))); +// quartzJobService.resumeJob(JobConstant.RGV_RESET_TO_ZERO_JOB, JobConstant.GroupEnum.SWAP_GROUP.getType()); + } + +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/job/service/OldExecutionBatterySwapService.java b/web-server/src/main/java/com/evotech/hd/webserver/job/service/OldExecutionBatterySwapService.java new file mode 100644 index 0000000..8912e72 --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/job/service/OldExecutionBatterySwapService.java @@ -0,0 +1,539 @@ +//package com.evotech.hd.webserver.job.service; +// +//import com.alibaba.fastjson2.JSONObject; +//import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +//import com.evotech.hd.core.dtos.Result; +//import com.evotech.hd.core.dtos.ResultUtil; +//import com.evotech.hd.core.dtos.business.AccessStrategyDTO; +//import com.evotech.hd.core.dtos.business.OrderSwapDTO; +//import com.evotech.hd.core.dtos.system.ParamsDTO; +//import com.evotech.hd.core.entity.business.OrderReservation; +//import com.evotech.hd.core.entity.business.OrderSwap; +//import com.evotech.hd.core.enums.CacheNames; +//import com.evotech.hd.core.enums.OrderStatusEnums; +//import com.evotech.hd.core.enums.SwapBatteryStepEnum; +//import com.evotech.hd.core.mapstruct.OrderSwapWrapper; +//import com.evotech.hd.exception.InstructionException; +//import com.evotech.hd.utils.Collections; +//import com.evotech.hd.utils.DateUtils; +//import com.evotech.hd.webserver.config.redis.utils.RedisUtil; +//import com.evotech.hd.webserver.job.JobConstant; +//import com.evotech.hd.webserver.job.entity.QuartzJobInfo; +//import com.evotech.hd.webserver.logging.AsyncLogService; +//import com.evotech.hd.webserver.logging.LogUtils; +//import com.evotech.hd.webserver.service.*; +//import com.evotech.hd.webserver.utils.AccessStrategyUtil; +//import com.evotech.hd.webserver.utils.ParamUtils; +//import com.evotech.hd.webserver.utils.instruction.InstructionReadUtils; +//import com.evotech.hd.webserver.utils.instruction.InstructionUtils; +//import com.evotech.hd.webserver.utils.instruction.InstructionWriteUtils; +//import com.evotech.hd.webserver.utils.sendCloud.CloudSendInfoUtils; +//import com.evotech.hd.webserver.websocket.controller.WebSocketUtils; +//import lombok.extern.slf4j.Slf4j; +//import org.apache.commons.lang3.ObjectUtils; +//import org.apache.commons.lang3.StringUtils; +//import org.quartz.SchedulerException; +//import org.springframework.beans.factory.annotation.Autowired; +//import org.springframework.scheduling.annotation.Async; +//import org.springframework.stereotype.Service; +// +//import javax.annotation.Resource; +//import java.math.BigDecimal; +//import java.util.Date; +//import java.util.List; +//import java.util.Map; +//import java.util.concurrent.CompletableFuture; +// +///** +// * 换电步骤执行类 +// * +// * @ClassName:HomeServiceImpl +// * @date: 2025年08月27日 9:24 +// * @author: andy.shi +// * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 +// */ +//@Slf4j +//@Service +//public class OldExecutionBatterySwapService { +// +// @Autowired +// private QuartzJobService quartzJobService; +// @Resource +// OrderSwapService orderSwapService; +// @Autowired +// OrderSwapWrapper orderSwapWrapper; +// @Resource +// BatterySwapStepService batterySwapStepService; +// @Resource +// OrderReservationService orderReservationService; +// @Resource +// BatteryCompartmentService batteryCompartmentService; +// @Resource +// BatteryService batteryService; +// @Resource +// AsyncLogService asyncLogService; +// +// public final static String RGV_NUM = "regNum"; +// public final static String RGV_NO = "regNo"; +// +// public Result checkPlateNum(String plateNum) throws Exception { +// String swapDay = DateUtils.parseDateToStr("yyyy-MM-dd", new Date()); +// //不存在当前key +// if(!RedisUtil.hasKey(CacheNames.SYSTEM_LOCK_PLATE+":"+swapDay+":"+plateNum)){ +// RedisUtil.set(CacheNames.SYSTEM_LOCK_PLATE+":"+swapDay+":"+plateNum, true); +// +// OrderReservation orderReservation = orderReservationService.getOne(new LambdaQueryWrapper().eq(OrderReservation::getPlateNum, plateNum).eq(OrderReservation::getStatus,1).eq(OrderReservation::getSwapDay, swapDay), false); +// //抓取到数据信息 +// if(ObjectUtils.isNotEmpty(orderReservation) && orderReservation.getId() != null){ +// try { +// //停止扫码机轮询 +// quartzJobService.pauseJob(new QuartzJobInfo(JobConstant.LICENSE_PLATE_MACHINE_JOB, JobConstant.GroupEnum.SWAP_GROUP.getType())); +// } catch (SchedulerException e) { +// e.printStackTrace(); +// log.error("车辆进站, 暂停车牌机扫秒出现错误", e); +// } +// Result result = orderSwapService.addOrderSwapByReservation(orderReservation); +// if(ResultUtil.verifyCode(result)){ +// //不通知云端, 由创建订单的推送时, 自己取消 +// orderReservationService.updateStatus(orderReservation.getId(),2, true); +// } +// //查询正在运行的订单 +// Result runningOrderResult = orderSwapService.runningOrder(); +// if(ResultUtil.verifyCode(runningOrderResult)){ +// //推送页面广播 +// WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method","order","data",JSONObject.toJSONString(ResultUtil.getValue(runningOrderResult))))); +// //下发物理接口, 抬杆放行 TODO +// InstructionWriteUtils.openRolling(ParamUtils.getDoorParams("入口")); +// try { +// //开启车辆到位扫秒 +// quartzJobService.resumeJob(new QuartzJobInfo(JobConstant.ARRIVAL_SIGNAL_JOB, JobConstant.GroupEnum.SWAP_GROUP.getType())); +// } catch (SchedulerException e) { +// e.printStackTrace(); +// log.error("车辆到位信息扫秒出现错误", e); +// } +// } +// return result; +// } +// } +// return Result.getInstance().build(Boolean.class).error("无法创建订单, 当前车辆没有预约", false); +// } +// +// +// +// /*** +// * 启动对中机构 +// * @throws Exception +// */ +// public void activateAlignmentMechanism() throws Exception { +// //获取生效的RGV编号 +// AccessStrategyDTO accessStrategyDTO = AccessStrategyUtil.getValidAccessStrategyDTO(); +// if(StringUtils.isEmpty(accessStrategyDTO.getEffectiveRgvNo())){ +// asyncLogService.saveLog(LogUtils.getAlarmLog("开始换电启动失败, 没有找到可用的RGV", -1, "ExecutionBatterySwapService.startBattery", "", null)); +//// return Result.getInstance().build(Boolean.class).error("开始换电启动失败, 没有找到可用的RGV", false); +// } +// InstructionWriteUtils.startCorrectInstitution(); +//// Boolean openResult = false; +//// Integer num = 0; +//// do{ +//// num +=1; +//// if(openResult = executionCenOpenResult()){ +// //启动对中机构的扫秒结果 +// quartzJobService.updateJobParams(JobConstant.ACTIVATE_ALIGNMENT_MECHANISM_JOB, JobConstant.GroupEnum.SWAP_GROUP.getType(), Collections.asMap("orderId", String.valueOf(orderSwapService.runningOrder().getData().getId()))); +//// quartzJobService.resumeJob(JobConstant.ACTIVATE_ALIGNMENT_MECHANISM_JOB, JobConstant.GroupEnum.SWAP_GROUP.getType()); +// quartzJobService.runJobNow(JobConstant.ACTIVATE_ALIGNMENT_MECHANISM_JOB, JobConstant.GroupEnum.SWAP_GROUP.getType()); +//// return Result.getInstance().build(Boolean.class).success(true); +//// } +//// }while (!openResult && num <= 3); +// +//// return Result.getInstance().build(Boolean.class).error("对中机构启动失败",false); +// } +// //开始换电 PS此处的作用根据设定的双RGV还是单RGV确定执行那条操作 +// public void startBattery(String orderId) throws Exception { +// //单RGV +// //获取生效的RGV编号 +// AccessStrategyDTO accessStrategyDTO = AccessStrategyUtil.getValidAccessStrategyDTO(); +// //开始换电前 验证是否存在可用的rgv +// if(StringUtils.isNotEmpty(accessStrategyDTO.getEffectiveRgvNo())){ +// String[] rgvArrays = accessStrategyDTO.getEffectiveRgvNo().split(","); +// if(rgvArrays.length ==1){ +// RedisUtil.set(RGV_NUM, "1"); +// RedisUtil.del(RGV_NO); +// splitOldBattery(rgvArrays[0], orderId); +// } +// //如果>1 只去前2个 +// if(rgvArrays.length > 1){ +// RedisUtil.set(RGV_NUM, "2"); +// RedisUtil.set(RGV_NO, rgvArrays[1]); +// asyncSplitOldBattery(rgvArrays[0], orderId); +// Thread.sleep(1000);//睡眠800毫秒 +// //rgv2 负责取装 +// asyncTakeNewBattery(rgvArrays[1], orderId, AccessStrategyUtil.getValidAccessStrategyDTO().getLowestBatterySwapSoc()); +// } +// } +// } +// +// @Async +// public void asyncSplitOldBattery(final String rgvNo, final String orderId){ +// CompletableFuture.runAsync(() -> { +// try { +// splitOldBattery(rgvNo, orderId); +// } catch (Exception e) { +// log.error("拆旧电池出现异常", e); +// } +// }); +// } +// +// @Async +// public void asyncTakeNewBattery(final String rgvNo, final String orderId, final Integer lowestSoc){ +// CompletableFuture.runAsync(() -> { +// try { +// takeNewBattery(rgvNo, orderId, lowestSoc); +// } catch (Exception e) { +// log.error("取新电池出现异常", e); +// } +// }); +// } +// +// /*** +// * 拆旧电池 +// * @throws Exception +// */ +// public void splitOldBattery(String rgvNo, String orderId) throws Exception { +// //拆旧电池, +// InstructionWriteUtils.splitOldBattery(rgvNo); +// //查询执行结果, 成功后后才执行 +//// if(executionOpenResult(rgvNo,SwapBatteryStepEnum.DISMANTLE_OLD_ELECTRICITY)){ +// //启动对中机构的扫秒结果 +// quartzJobService.updateJobParams(JobConstant.SPLIT_OLD_BATTERY_JOB, JobConstant.GroupEnum.SWAP_GROUP.getType(), Collections.asMap("orderId", orderId, "rgvNo", rgvNo)); +// //quartzJobService.resumeJob(JobConstant.SPLIT_OLD_BATTERY_JOB, JobConstant.GroupEnum.SWAP_GROUP.getType()); +// quartzJobService.runJobNow(JobConstant.SPLIT_OLD_BATTERY_JOB, JobConstant.GroupEnum.SWAP_GROUP.getType()); +//// } +// +// } +// +// /*** 存旧电池 +// * @throws Exception +// */ +// public void putOldBattery(String rgvNo, String orderId) throws Exception { +// //获取存储策略 +//// Integer batteryCompartmentNo = batteryCompartmentService.getBatteryCompartmentId(); +//// InstructionWriteUtils.putOldBattery(rgvNo, batteryCompartmentNo); +//// //线程睡眠, 防止指令下发后, 立即执行, 导致设备还没有运作,返回的运作结束 +//// //启动存新电池扫秒 +////// if(executionOpenResult(rgvNo,SwapBatteryStepEnum.DISCHARGE_OLD_ELECTRICITY)){ +//// quartzJobService.updateJobParams(JobConstant.PUT_OLD_BATTERY_JOB, JobConstant.GroupEnum.SWAP_GROUP.getType(), Collections.asMap("orderId", orderId, "batteryCompartmentNo", String.valueOf(batteryCompartmentNo), "rgvNo", rgvNo)); +//// //quartzJobService.resumeJob(JobConstant.PUT_OLD_BATTERY_JOB, JobConstant.GroupEnum.SWAP_GROUP.getType()); +//// quartzJobService.runJobNow(JobConstant.PUT_OLD_BATTERY_JOB, JobConstant.GroupEnum.SWAP_GROUP.getType()); +////// } +// } +// +// +// /** 取新电池 +// * @throws Exception +// */ +// public void takeNewBattery(String rgvNo, String orderId, Integer lowestSoc) throws Exception{ +//// BatteryCompartmentDTO batteryCompartmentDTO =batteryCompartmentService.getBatterySwap(lowestSoc); +//// OrderSwap orderSwap = orderSwapService.getById(orderId); +//// orderSwap.setRentBatNo(String.valueOf(batteryCompartmentDTO.getId())); +//// Battery battery = batteryService.getBatteryByCompartmentId(batteryCompartmentDTO.getId()); +//// orderSwap.setRentBatCode(battery.getBatteryCode()); +//// orderSwap.setRentBatSoc(battery.getSoc()); +//// orderSwapService.updateById(orderSwap); +//// InstructionWriteUtils.takeNewBattery(rgvNo, batteryCompartmentDTO.getId()); +//// +////// if(executionOpenResult(rgvNo,SwapBatteryStepEnum.GET_NEW_ELECTRICITY)){ +//// //启动存新电池扫秒 +//// quartzJobService.updateJobParams(JobConstant.TAKE_NEW_BATTERY_JOB, JobConstant.GroupEnum.SWAP_GROUP.getType(), Collections.asMap("orderId", orderId, "compartmentId", String.valueOf(batteryCompartmentDTO.getId()), "batteryId", String.valueOf(battery.getId()), "rgvNo", rgvNo)); +////// quartzJobService.resumeJob(JobConstant.TAKE_NEW_BATTERY_JOB, JobConstant.GroupEnum.SWAP_GROUP.getType()); +//// quartzJobService.runJobNow(JobConstant.TAKE_NEW_BATTERY_JOB, JobConstant.GroupEnum.SWAP_GROUP.getType()); +////// } +// +// } +// +// /** 装新电池 +// * @throws Exception +// */ +// public void loadingNewBattery(Map params) throws Exception{ +// +// try { +// OrderSwap orderSwap = orderSwapService.getById(String.valueOf(params.get("orderId"))); +// orderSwap.setElectricityQuantity(new BigDecimal(orderSwap.getRentBatSoc()).subtract(new BigDecimal(orderSwap.getReturnBatSoc())).toString()); +// orderSwapService.updateById(orderSwap); +// } catch (Exception e) { +// log.error("更新电池耗电量出现异常", e); +// } +// InstructionWriteUtils.loadingNewBattery(String.valueOf(params.get("rgvNo"))); +// //启动存新电池扫秒 +//// if(executionOpenResult(String.valueOf(params.get("rgvNo")),SwapBatteryStepEnum.INSTALL_NEW_ELECTRICITY)){ +// quartzJobService.updateJobParams(JobConstant.LOADING_NEW_BATTERY_JOB, JobConstant.GroupEnum.SWAP_GROUP.getType(), Collections.asMap("orderId", params.get("orderId"), "compartmentId", params.get("compartmentId"), "batteryId", params.get("batteryId"), "rgvNo", params.get("rgvNo"))); +// //quartzJobService.resumeJob(JobConstant.LOADING_NEW_BATTERY_JOB, JobConstant.GroupEnum.SWAP_GROUP.getType()); +// quartzJobService.runJobNow(JobConstant.LOADING_NEW_BATTERY_JOB, JobConstant.GroupEnum.SWAP_GROUP.getType()); +//// } +// +// } +// +// /** 换电完成 +// * @throws Exception +// */ +// public void completed(String orderId) throws Exception{ +// Result running = orderSwapService.runningOrder(); +// OrderSwapDTO orderSwapDTO = running.getData(); +// orderSwapDTO.setStatus(OrderStatusEnums.SWAPOVER.getCode()); +// orderSwapDTO.setServiceTimeEnd(new Date()); +// orderSwapService.updateById(orderSwapWrapper.toEntity(orderSwapDTO));//更新换电完成状态 +// //执行换电完成 +// executionResultPush(orderSwapDTO, SwapBatteryStepEnum.COMPLETED, false, Collections.emptyMap()); +// // RGV归0 对中机构会同步归0 下发释放对中机构指令 +// +// //获取生效的RGV编号 +//// AccessStrategyDTO accessStrategyDTO = AccessStrategyUtil.getValidAccessStrategyDTO(); +//// List rgvList = Collections.emptyList(); +// //开始换电前 验证是否存在可用的rgv +//// Boolean result = false; +//// if(StringUtils.isNotEmpty(accessStrategyDTO.getEffectiveRgvNo())){ +//// String[] rgvArrays = accessStrategyDTO.getEffectiveRgvNo().split(","); +//// rgvList.addAll(Arrays.asList(rgvArrays)); +//// if(rgvArrays.length ==1){ +//// InstructionWriteUtils.rgvResetToZero(rgvArrays[0]); +//// result = executionOpenResult(rgvArrays[0],SwapBatteryStepEnum.COMPLETED); +//// } +//// //如果>1 只去前2个 +//// if(rgvArrays.length > 1){ +//// InstructionWriteUtils.rgvResetToZero(rgvArrays[0]); +//// Thread.sleep(800);//睡眠800毫秒 +//// InstructionWriteUtils.rgvResetToZero(rgvArrays[1]); +//// result = (executionOpenResult(rgvArrays[0],SwapBatteryStepEnum.COMPLETED) && executionOpenResult(rgvArrays[1],SwapBatteryStepEnum.COMPLETED)); +//// } +//// } +// //关闭开合门 +// InstructionWriteUtils.closeCenDoor(); +// quartzJobService.runJobNow(JobConstant.CLOSE_DOOR_JOB, JobConstant.GroupEnum.SWAP_GROUP.getType()); +// +//// //释放对中机构 +//// InstructionWriteUtils.closeCorrectInstitution(); +//// //启动RGV归零扫秒 +//// quartzJobService.updateJobParams(JobConstant.RGV_RESET_TO_ZERO_JOB, JobConstant.GroupEnum.SWAP_GROUP.getType(), Collections.asMap("orderId", orderId, "rgvStr", String.join(",",rgvList))); +//// quartzJobService.resumeJob(JobConstant.RGV_RESET_TO_ZERO_JOB, JobConstant.GroupEnum.SWAP_GROUP.getType()); +// } +// +// +// +// public Boolean executionCenNextStep(SwapBatteryStepEnum swapBatteryStep) throws InstructionException { +// if(executionCenResult()){ +// //如果为0, 则执行结束 +// Map errorInfoMap = executionCenErrorResult(); +// Result running = orderSwapService.runningOrder(); +// return executionResultPush(running.getData(), swapBatteryStep, Collections.isNotEmpty(errorInfoMap),errorInfoMap); +//// } +// } +// //默认出现错误, 禁止执行 +// return false; +// } +// +// +// /*** 换电步骤的执行结果, 检查接口 +// * @param swapBatteryStep +// * @return +// */ +// public Boolean executionNextStep(String rgvNo, SwapBatteryStepEnum swapBatteryStep) throws InstructionException { +// if(executionResult(rgvNo)){ +// //如果为0, 则执行结束 +// Map errorInfoMap = executionErrorResult(rgvNo); +// Result running = orderSwapService.runningOrder(); +// return executionResultPush(running.getData(), swapBatteryStep, Collections.isNotEmpty(errorInfoMap),errorInfoMap); +//// } +// } +// //默认出现错误, 禁止执行 +// return false; +// } +// +// +// +// +// /*** +// * 执行结果过程是否存在异常 +// * @return +// */ +// public Map executionErrorResult(String rgvNo) throws InstructionException { +// ParamsDTO errorCode = ParamUtils.getErrorCode(); +// InstructionUtils.initParamValue(errorCode, rgvNo); +// return executionResult(errorCode); +// } +// +// /*** +// * 对中结构执行结果过程是否存在异常 +// * @return +// */ +// public Map executionCenErrorResult() throws InstructionException { +// return executionResult(ParamUtils.getCenErrorCode()); +// } +// +// +// private Map executionResult(ParamsDTO errorCode) throws InstructionException { +// Map errorInfoMap = Collections.emptyMap(); +// JSONObject error = InstructionUtils.queryReadInteger(errorCode.getName(),errorCode.getParamValue()); +// if(InstructionUtils.verifyStatusLog(errorCode, error)) { +// String val = InstructionUtils.getValue(error); +// //记录换电步骤, 默认成功 +// if(!val.equals(errorCode.getExpectedResults())){ +// //出现异常, 记录错误信息 +// ParamsDTO errorInfo = ParamUtils.getParamsDTO(val); +// errorInfoMap.put("errorMsg", errorInfo.getName()); +// } +// }else{ +// errorInfoMap.put("errorMsg", "查询错误信息通讯失败"); +// } +// +// return errorInfoMap; +// } +// +// +// /*** +// * 执行结果查询 +// * @return +// */ +// public Boolean executionCenResult() throws InstructionException { +// ParamsDTO beatActMoving = ParamUtils.getCenActMoving(); +// JSONObject js = InstructionUtils.queryReadInteger(beatActMoving.getName(),beatActMoving.getParamValue()); +// //如果为0, 则执行结束 +// return InstructionUtils.verifyStatusLog(beatActMoving, js) && beatActMoving.getVerifyResult() && InstructionUtils.getValue(js).equals(beatActMoving.getExpectedResults()); +// } +// +// /*** +// * 执行结果查询 +// * @return +// */ +// public Boolean executionResult(String rgvNo) throws InstructionException { +// ParamsDTO beatActMoving = ParamUtils.getBeatActMoving(); +// InstructionUtils.initParamValue(beatActMoving,rgvNo); +// JSONObject js = InstructionUtils.queryReadInteger(beatActMoving.getName(),beatActMoving.getParamValue()); +// //如果为0, 则执行结束 +// return InstructionUtils.verifyStatusLog(beatActMoving, js) && InstructionUtils.getValue(js).equals(beatActMoving.getExpectedResults()); +// } +// +// /*** +// * 执行结果查询 +// * @return +// */ +// public Boolean executionCenOpenResult() throws Exception { +// ParamsDTO beatActMoving = ParamUtils.getCenActMoving(); +// Boolean result = false; +// //执行查询开始查询 +// for (int i = 0; i < 10; i++) { +// JSONObject js = InstructionUtils.queryReadInteger(beatActMoving.getName(),beatActMoving.getParamValue()); +// //如果为1, 则开始执行 +// result = InstructionUtils.verifyStatusLog(beatActMoving, js) && beatActMoving.getVerifyResult() && !InstructionUtils.getValue(js).equals(beatActMoving.getExpectedResults()); +// if(!result){ +// //睡眠等待 +// Thread.sleep(ParamUtils.getInstructionIntervalRequestMilliseconds()); +// //等待睡眠完成, 重新执行 +// continue; +// } +// break; +// } +// if(!result){ +// executionResultPush(orderSwapService.runningOrder().getData(), SwapBatteryStepEnum.INSTITUTIONS_FOR_CHINA, true, Collections.asMap("errorMsg","对中机构执行开始失败")); +// } +// return result; +// } +// +// +// /*** +// * 查询运作开始指令 +// * @return +// */ +// public Boolean executionOpenResult(String rgvNo) throws Exception { +// Boolean result = false; +// ParamsDTO beatActMoving = ParamUtils.getBeatActMoving(); +// InstructionUtils.initParamValue(beatActMoving,rgvNo); +// //执行查询开始查询 +// for (int i = 0; i < 10; i++) { +// JSONObject js = InstructionUtils.queryReadInteger(beatActMoving.getName(),beatActMoving.getParamValue()); +// //如果为1, 则开始执行 +// result = InstructionUtils.verifyStatusLog(beatActMoving, js) && !InstructionUtils.getValue(js).equals(beatActMoving.getExpectedResults()); +// if(!result){ +// //睡眠等待 +// Thread.sleep(ParamUtils.getInstructionIntervalRequestMilliseconds()); +// //等待睡眠完成, 重新执行 +// continue; +// } +// break; +// } +// return result; +// } +// +// /*** +// * 查询运作开始指令 +// * @return +// */ +// public Boolean executionOpenResult(String rgvNo, SwapBatteryStepEnum swapBatteryStep) throws Exception { +// Boolean result = executionOpenResult(rgvNo); +// if(!result){ +// executionResultPush(orderSwapService.runningOrder().getData(), swapBatteryStep, true, Collections.asMap("errorMsg",swapBatteryStep.getName() + "执行开始失败")); +// } +// return result; +// } +// +// +// //检查执行命令 +// public Map controllerExecutionResult(String rgvNo) throws InstructionException { +// if(executionResult(rgvNo)){ +// Map result = executionErrorResult(rgvNo); +// if(Collections.isNotEmpty(result)){ +// return result; +// } +// //检查其他rgv是否在交集区中 +// Map> map = ParamUtils.getBatteryCompartmentPosition(); +// ParamsDTO rgvHShaftLocation = ParamUtils.getRgvHShaftLocation(); +// for (String rgv : map.keySet()){ +// Integer currentPosition = InstructionReadUtils.getRgvHShaftLocation(rgvHShaftLocation, rgv); +// if(currentPosition.compareTo(0) > 0){ +// return Collections.asMap("errorMsg", (rgv.equals(rgvNo) ? "当前RGV正在运行" : "当前其他RGV正在运行, 禁止运动")); +// } +// } +// +// return Collections.emptyMap(); +// } +// return Collections.asMap("errorMsg", "当前RGV正在运行"); +// } +// +// /*** +// * 执行结果查询 +// * @return +// */ +//// public Boolean executionNotJobResult(){ +//// ParamsDTO beatActMoving = ParamUtils.getBeatActMoving(); +//// Integer expectedResults = Integer.valueOf(beatActMoving.getExpectedResults()); +//// Integer result = 1; +//// while (result != expectedResults){ +//// JSONObject js = InstructionUtils.queryReadInteger(beatActMoving.getName(),beatActMoving.getParamValue()); +//// if(InstructionUtils.verifyStatusLog(beatActMoving, js)){ +//// //如果为0, 则执行结束 +//// result = Integer.valueOf(InstructionUtils.getValue(js)); +//// } +//// } +//// return true; +//// } +// +// +// +// +// /*** 推送云端和站端页面 +// * @param swapBatteryStep 当前换电步骤 +// * @param isError 物理设备是否执行错误 +// * @param errorInfoMap 错误信息 +// * @return 物理设备执行结果 +// */ +// public Boolean executionResultPush(OrderSwapDTO orderSwapDTO, SwapBatteryStepEnum swapBatteryStep, Boolean isError, Map errorInfoMap){ +// Boolean isAdd = batterySwapStepService.addOrderSwapBatteryStepBySwapComplete(orderSwapDTO.getId(), swapBatteryStep.getCode(), new Date(), false, isError, errorInfoMap); +// orderSwapDTO.setBatterySwapStepDTOList(batterySwapStepService.findListByOrderId(orderSwapDTO.getId())); +// orderSwapDTO.setRgvNum(AccessStrategyUtil.getValidAccessStrategyDTO().getEffectiveRgvNo().split(",").length); +// //推送页面广播 +// WebSocketUtils.broadCastMessage(JSONObject.toJSONString(Collections.asMap("method", "order", "data", JSONObject.toJSONString(orderSwapDTO)))); +// if(!isError && isAdd){ +// // 发送通知到云端 +// CloudSendInfoUtils.sendArrivalSignal(Collections.asMap("pkId", orderSwapDTO.getCloudOrderId(), "step", swapBatteryStep.getCode(), "stepTime", new Date())); +// } +// return !isError; +// } +//} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/job/service/QuartzJobService.java b/web-server/src/main/java/com/evotech/hd/webserver/job/service/QuartzJobService.java new file mode 100644 index 0000000..cf12802 --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/job/service/QuartzJobService.java @@ -0,0 +1,239 @@ +package com.evotech.hd.webserver.job.service; + +import com.evotech.hd.webserver.job.QuartzJobExecutor; +import com.evotech.hd.webserver.job.entity.QuartzJobInfo; +import org.apache.commons.lang3.StringUtils; +import org.quartz.*; +import org.quartz.impl.matchers.GroupMatcher; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * 类 + * + * @ClassName:QuartzJobService + * @date: 2025年08月23日 16:19 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ + + +@Service +public class QuartzJobService { + + @Autowired + private Scheduler scheduler; + + + public List getAllJobs(QuartzJobInfo jobInfo){ + GroupMatcher matcher; + // 根据是否传入分组参数创建不同的匹配器 + if (StringUtils.isNotEmpty(jobInfo.getJobGroup())) { + matcher = GroupMatcher.jobGroupEquals(jobInfo.getJobGroup()); + } else { + // 匹配所有分组 + matcher = GroupMatcher.anyJobGroup(); + } + Set jobKeys = null; + List jobList = new ArrayList<>(); + try { + jobKeys = scheduler.getJobKeys(matcher); + for (JobKey jobKey : jobKeys) { + JobDetail jobDetail = scheduler.getJobDetail(jobKey); + String jobName = String.valueOf(jobDetail.getJobDataMap().get("jobName")); + if (StringUtils.isNotEmpty(jobInfo.getJobName())) { + // 这里使用contains进行模糊匹配,也可以根据需要改为startsWith或endsWith + if (!jobInfo.getJobName().equals(jobName)) { + continue; // 不匹配则跳过当前任务 + } + } + + List triggers = scheduler.getTriggersOfJob(jobKey); + for (Trigger trigger : triggers) { + QuartzJobInfo job = new QuartzJobInfo(); + job.setJobId(jobKey.getName()); + job.setJobName(jobName); + job.setJobGroup(jobKey.getGroup()); + job.setJobClassName(String.valueOf(jobDetail.getJobDataMap().get("jobClassName"))); + job.setCronExpression("触发器:" + trigger.getKey()); + job.setDescription(String.valueOf(jobDetail.getJobDataMap().get("description"))); + Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey()); + job.setStatusName(getStatusName(triggerState.name())); + if (trigger instanceof CronTrigger) { + CronTrigger cronTrigger = (CronTrigger) trigger; + String cronExpression = cronTrigger.getCronExpression(); + job.setCronExpression(cronExpression); + } + jobList.add(job); + } + } + } catch (SchedulerException e) { + e.printStackTrace(); + } + return jobList; + } + +public static String getStatusName(String name){ + switch (name){ + case "NONE": + return "未定义"; + case "NORMAL": + return "正常"; + case "PAUSED": + return "暂停"; + case "COMPLETE": + return "完成"; + case "ERROR": + return "错误"; + case "BLOCKED": + return "阻塞"; + default: + return "未知"; + } +} + + /** + * 创建定时任务 + */ + public void createJob(QuartzJobInfo jobInfo) throws SchedulerException { + // 1. 定义JobDetail + JobDetail jobDetail = JobBuilder.newJob(QuartzJobExecutor.class) + .withIdentity(jobInfo.getJobId(), jobInfo.getJobGroup()) // 任务ID和组名 + .withDescription(jobInfo.getDescription()) + // .usingJobData("jobClassName", jobInfo.getJobClassName()) // 传递目标类名 + .storeDurably(true) // 任务持久化 + .requestRecovery(true) // 进程崩溃后可恢复 + .build(); + + // 2. 注入动态参数到JobDataMap + JobDataMap jobDataMap = jobDetail.getJobDataMap(); + // 存储任务执行类(反射用) + jobDataMap.put("jobClassName", jobInfo.getJobClassName()); + jobDataMap.put("jobName", jobInfo.getJobName()); + jobDataMap.put("description", jobInfo.getDescription()); + // 存储动态参数(如果有) + if (jobInfo.getParams() != null && !jobInfo.getParams().isEmpty()) { + jobInfo.getParams().forEach(jobDataMap::put); + } + + // 2. 定义Trigger (Cron表达式触发器) + Trigger trigger = TriggerBuilder.newTrigger() + .withIdentity(jobInfo.getJobId(), jobInfo.getJobGroup()) + .withSchedule(CronScheduleBuilder.cronSchedule(jobInfo.getCronExpression())) + .startNow() // 立即生效 + .build(); + + // 3. 注册任务到调度器 + scheduler.scheduleJob(jobDetail, trigger); + } + + public void updateJob(QuartzJobInfo jobInfo) throws SchedulerException { + JobKey jobKey = JobKey.jobKey(jobInfo.getJobId(), jobInfo.getJobGroup()); + JobDetail jobDetail = scheduler.getJobDetail(jobKey); + // 更新JobDataMap中的参数 + JobDataMap jobDataMap = jobDetail.getJobDataMap(); + jobDataMap.put("jobName", jobInfo.getJobName()); + jobDataMap.put("description", jobInfo.getDescription()); + // 重新存储任务定义(持久化场景下需调用) + scheduler.addJob(jobDetail, true); + } + + /** + * 更新任务的动态参数 + */ + public void updateJobParams(String jobId, String jobGroup, Map newParams) throws SchedulerException { + JobKey jobKey = JobKey.jobKey(jobId, jobGroup); + JobDetail jobDetail = scheduler.getJobDetail(jobKey); + + // 更新JobDataMap中的参数 + JobDataMap jobDataMap = jobDetail.getJobDataMap(); + newParams.forEach(jobDataMap::put); // 覆盖或新增参数 + // 重新存储任务定义(持久化场景下需调用) + scheduler.addJob(jobDetail, true); + } + + /** + * 更新任务的Cron表达式 + */ + public void updateJobCron(QuartzJobInfo jobInfo) throws SchedulerException { + TriggerKey triggerKey = TriggerKey.triggerKey(jobInfo.getJobId(), jobInfo.getJobGroup()); + CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey); + + // 重新设置Cron表达式 + trigger = trigger.getTriggerBuilder() + .withIdentity(triggerKey) + .withSchedule(CronScheduleBuilder.cronSchedule(jobInfo.getCronExpression())) + .build(); + + // 更新触发器 + scheduler.rescheduleJob(triggerKey, trigger); + } + + /** + * 暂停任务 + */ + public void pauseJob(QuartzJobInfo jobInfo) throws SchedulerException { + JobKey jobKey = JobKey.jobKey(jobInfo.getJobId(), jobInfo.getJobGroup()); + scheduler.pauseJob(jobKey); + } + public void pauseJob(String name, String group) throws SchedulerException { + JobKey jobKey = JobKey.jobKey(name, group); + scheduler.pauseJob(jobKey); + } + + /** + * 恢复任务 + */ + public void resumeJob(QuartzJobInfo jobInfo) throws SchedulerException { + JobKey jobKey = JobKey.jobKey(jobInfo.getJobId(), jobInfo.getJobGroup()); + scheduler.resumeJob(jobKey); + } + public void resumeJob(String name, String group) throws SchedulerException { + JobKey jobKey = JobKey.jobKey(name, group); + scheduler.resumeJob(jobKey); + } + + /** + * 删除任务 + */ + public void deleteJob(QuartzJobInfo jobInfo) throws SchedulerException { + JobKey jobKey = JobKey.jobKey(jobInfo.getJobId(), jobInfo.getJobGroup()); + // 先暂停触发器 + scheduler.pauseTrigger(TriggerKey.triggerKey(jobInfo.getJobId(), jobInfo.getJobGroup())); + // 移除触发器 + scheduler.unscheduleJob(TriggerKey.triggerKey(jobInfo.getJobId(), jobInfo.getJobGroup())); + // 删除任务 + scheduler.deleteJob(jobKey); + } + + /** + * 立即执行一次任务(不影响原调度) + */ + public void runJobNow(QuartzJobInfo jobInfo) throws SchedulerException { + JobKey jobKey = JobKey.jobKey(jobInfo.getJobId(), jobInfo.getJobGroup()); + scheduler.triggerJob(jobKey); + } + public void runJobNow(String name, String group) throws SchedulerException { + JobKey jobKey = JobKey.jobKey(name, group); + scheduler.triggerJob(jobKey); + } + + /*** + * 检查任务是否存在 + * @param jobId + * @param jobGroup + * @return + * @throws SchedulerException + */ + public Boolean checkExists(String jobId, String jobGroup) throws SchedulerException { + JobKey jobKey = JobKey.jobKey(jobId, jobGroup); + // 重新存储任务定义(持久化场景下需调用) + return scheduler.checkExists(jobKey); + } + +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/logging/AsyncLogService.java b/web-server/src/main/java/com/evotech/hd/webserver/logging/AsyncLogService.java new file mode 100644 index 0000000..1cc3840 --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/logging/AsyncLogService.java @@ -0,0 +1,49 @@ +/** + * Copyright © 2021-2026 space All rights reserved. + */ +package com.evotech.hd.webserver.logging; + +import com.evotech.hd.core.entity.system.InstructionLog; +import com.evotech.hd.core.entity.system.Log; +import com.evotech.hd.webserver.service.InstructionLogService; +import com.evotech.hd.webserver.service.LogService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Service; + +/** + * 异步日志服务 + * + * @author space + * @version 2021-06-11 + */ +@Service +public class AsyncLogService { + + @Autowired + private LogService logService; + + @Autowired + private InstructionLogService instructionLogService; + + /** + * 异步保存日志 + * + * @throws Throwable + */ + @Async + public void saveLog(Log log) { + logService.save(log); + } + + /** + * 异步指令日志 + * + * @throws Throwable + */ + @Async + public void saveInstructionLog(InstructionLog log) { + instructionLogService.save(log); + } + +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/logging/LogUtils.java b/web-server/src/main/java/com/evotech/hd/webserver/logging/LogUtils.java new file mode 100644 index 0000000..1e08045 --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/logging/LogUtils.java @@ -0,0 +1,185 @@ +package com.evotech.hd.webserver.logging; + +import cn.hutool.extra.servlet.ServletUtil; +import cn.hutool.json.JSONUtil; +import com.alibaba.fastjson2.JSONObject; +import com.evotech.hd.core.dtos.LoginForm; +import com.evotech.hd.core.entity.system.InstructionLog; +import com.evotech.hd.core.entity.system.Log; +import com.evotech.hd.webserver.logging.annotation.ApiLog; +import com.evotech.hd.webserver.logging.enums.InstructionLogTypeEnum; +import com.evotech.hd.webserver.logging.enums.LogTypeEnum; +import com.evotech.hd.webserver.utils.ParamUtils; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.reflect.MethodSignature; + +import javax.servlet.http.HttpServletRequest; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.lang.reflect.Method; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +/** + * 日志工具类 + * + * @author space + * @version 2021-06-11 + */ +@Slf4j +public class LogUtils { + + /** + * 设置请求参数 + * + * @param paramMap + */ + private static String getRequestParams(JoinPoint joinPoint, Map paramMap) { + Map map = new HashMap(); + if (paramMap == null) { + return ""; + } + for (Map.Entry param : ((Map) paramMap).entrySet()) { + String paramValue = (param.getValue() != null && param.getValue().length > 0 ? param.getValue()[0] : ""); + map.put(param.getKey(), StringUtils.abbreviate(StringUtils.endsWithIgnoreCase(param.getKey(), "password") ? "安全考虑,密码已隐藏" : paramValue, 100)); + } + if(map.size() == 0){ + map = getParamDetails(joinPoint); + } + return JSONUtil.toJsonStr(map); + } + + private static Map getParamDetails(JoinPoint joinPoint) { + Map map = new HashMap(); + // 获取方法签名(强转为MethodSignature,针对方法连接点) + MethodSignature signature = (MethodSignature) joinPoint.getSignature(); + // 获取参数名称(需要JDK 8+,且编译时保留参数名,默认开启) + String[] paramNames = signature.getParameterNames(); + // 获取参数类型 +// Class[] paramTypes = signature.getParameterTypes(); + // 获取参数值 + Object[] paramValues = joinPoint.getArgs(); + + // 遍历参数信息 + for (int i = 0; i < paramNames.length; i++) { + //如果当前是登录 + if(StringUtils.endsWithIgnoreCase(paramNames[i], LoginForm.class.getSimpleName())){ + //{"loginForm":"LoginForm(username=admin, password=123456)"} + try { + LoginForm loginForm = (LoginForm)paramValues[i]; + //安全考虑,密码已隐藏 + loginForm.setPassword("xxxxxxxx"); + map.put(paramNames[i], StringUtils.abbreviate( new ObjectMapper().writeValueAsString(loginForm), 2000)); + } catch (JsonProcessingException e) { + e.printStackTrace(); + log.error("Failed to parse JSON", e); + } + }else{ + map.put(paramNames[i], StringUtils.abbreviate( String.valueOf(paramValues[i]), 2000)); + } + } + return map; + } + + /** + * 解析日志 + * + * @param request + * @param joinPoint + * @param recordTime + * @param result + * @param e + * @return + */ + public static Log getLog(HttpServletRequest request, JoinPoint joinPoint, long recordTime, String result, Throwable e) { + MethodSignature signature = (MethodSignature) joinPoint.getSignature(); + Method method = signature.getMethod(); + // 注解日志 + ApiLog aopLog = method.getAnnotation(ApiLog.class); + String functionName = aopLog.value(); + LogTypeEnum logTypeEnum = aopLog.type(); + Log log = getLog(functionName, logTypeEnum, request.getRequestURI(), recordTime, request.getMethod(), joinPoint.getSignature().toString(), result, e); + // 保存日志 + log.setRemoteAddr(ServletUtil.getClientIP(request)); + log.setUserAgent(request.getHeader("user-agent")); + log.setParams(getRequestParams(joinPoint, request.getParameterMap())); + return log; + } + + public static Log getAlarmLog(String functionName, long recordTime, String method, String result, Throwable e) { + return getAlarmLog(functionName,recordTime, method, "", result, e); + } + + public static Log getAlarmLog(String functionName, long recordTime, String method, String params, String result, Throwable e) { + // 保存日志 + Log log = getLog(functionName, LogTypeEnum.ALARM, ParamUtils.getInstructionRequestUrl(), recordTime, "POST", method, result, e); + log.setParams(params); + return log; + } + + public static Log getSendCloudLog(String functionName, long recordTime, JSONObject json, String result, Throwable e) { + // 保存日志 + Log log = getLog(functionName, LogTypeEnum.PUSH_CLOUD, ParamUtils.getCloudRequestUrl(), recordTime, "POST", json.getString("method"), result, e); + log.setParams(json.toString()); + return log; + } + + public static InstructionLog getSendInstructionLog(String functionName, InstructionLogTypeEnum logType, String methodName, long recordTime, Map params, String result, Throwable e) { + InstructionLog log = new InstructionLog(); + log.setRecordTime(recordTime); + log.setName(functionName); + log.setType(logType.getValue()); + log.setRequestUri(ParamUtils.getInstructionRequestUrl()); + log.setParams(JSONObject.toJSONString(params)); + log.setMethod(methodName); + log.setResult(result); + log.setExecutionTime(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date())); + // 处理异常信息 + String exception = ""; + if (e != null) { + StringWriter stringWriter = new StringWriter(); + e.printStackTrace(new PrintWriter(stringWriter)); + exception = stringWriter.toString(); + log.setException(exception); + log.setResult(e.getMessage()); + // 非login类型日志修改未异常类型 + } + return log; + } + + + /*** + * 生成 log + * @return + */ + private static Log getLog(String functionName, LogTypeEnum logTypeEnum, String requestUri, long recordTime, String requestType, String method, String result, Throwable e){ + Log log = new Log(); + log.setRecordTime(recordTime); + log.setName(functionName); + log.setType(logTypeEnum.getValue()); + log.setRequestUri(requestUri); + log.setRequestType(requestType); + log.setMethod(method); + log.setResult(result); + // 处理异常信息 + String exception = ""; + if (e != null) { + StringWriter stringWriter = new StringWriter(); + e.printStackTrace(new PrintWriter(stringWriter)); + exception = stringWriter.toString(); + log.setException(exception); + log.setResult(e.getMessage()); + // 非login类型日志修改未异常类型 + if (!log.getType().equals(LogTypeEnum.LOGIN.getValue())) { + log.setType(LogTypeEnum.EXCEPTION.getValue()); + } + } + return log; + } +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/logging/LoggingAspect.java b/web-server/src/main/java/com/evotech/hd/webserver/logging/LoggingAspect.java new file mode 100644 index 0000000..36ab643 --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/logging/LoggingAspect.java @@ -0,0 +1,83 @@ +package com.evotech.hd.webserver.logging; + +import com.evotech.hd.core.entity.system.Log; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.AfterThrowing; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Pointcut; +import org.springframework.core.env.Environment; +import org.springframework.stereotype.Component; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import javax.servlet.http.HttpServletRequest; +import java.util.Objects; + +/** + * 日志切面类 + */ +@Aspect +@Component +public class LoggingAspect { + + private final Environment env; + + private final AsyncLogService asyncLogService; + + + public LoggingAspect(Environment env, AsyncLogService asyncLogService) { + this.env = env; + this.asyncLogService = asyncLogService; + } + + public HttpServletRequest getHttpServletRequest() { + return ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest(); + } + + + /** + * 切点,扫描ApiLog注解的类 + */ + @Pointcut("@annotation(com.evotech.hd.webserver.logging.annotation.ApiLog)") + public void applicationPackagePointcut() { + // 方法为空,因为这只是一个切入点,实现在advices. + } + + /** + * 记录异常日志 + * + * @param joinPoint + * @param e + * @throws Throwable + */ + @AfterThrowing(pointcut = "applicationPackagePointcut()", throwing = "e") + public void logAfterThrowing(JoinPoint joinPoint, Throwable e) throws Throwable { + HttpServletRequest request = getHttpServletRequest(); + Log log = LogUtils.getLog(request, joinPoint, 0, null, e); + asyncLogService.saveLog(log); + + } + + /** + * 在进入和退出方法时记录日志 + * + * @param joinPoint + * @return + * @throws Throwable + */ + @Around("applicationPackagePointcut()") + public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable { + /** + * 登录前和退出后中,获取不到currentUser信息,所以在这里做特殊处理 + */ + long startTime = System.currentTimeMillis(); + Object result = joinPoint.proceed(); + long time = System.currentTimeMillis() - startTime; + HttpServletRequest request = getHttpServletRequest(); + Log log = LogUtils.getLog(request, joinPoint, time, result != null ? result.toString() : "", null); + asyncLogService.saveLog(log); + return result; + } +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/logging/annotation/ApiLog.java b/web-server/src/main/java/com/evotech/hd/webserver/logging/annotation/ApiLog.java new file mode 100644 index 0000000..a52537a --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/logging/annotation/ApiLog.java @@ -0,0 +1,18 @@ +package com.evotech.hd.webserver.logging.annotation; + + +import com.evotech.hd.webserver.logging.enums.LogTypeEnum; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface ApiLog { + + LogTypeEnum type() default LogTypeEnum.ACCESS; + + String value() default ""; +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/logging/enums/InstructionLogTypeEnum.java b/web-server/src/main/java/com/evotech/hd/webserver/logging/enums/InstructionLogTypeEnum.java new file mode 100644 index 0000000..e4d03de --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/logging/enums/InstructionLogTypeEnum.java @@ -0,0 +1,38 @@ +package com.evotech.hd.webserver.logging.enums; + +/** + * 日志类型 + */ +public enum InstructionLogTypeEnum { + READ("1", "读取日志"), + WRITE("2", "写入日志"), + ; + + /** + * 类型值 + */ + private String value; + + /** + * 类型标签 + */ + private String label; + + private InstructionLogTypeEnum(String value, String label) { + this.value = value; + this.label = label; + } + + public String getValue() { + return value; + } + + public String getLabel() { + return label; + } + + @Override + public String toString() { + return this.value; + } +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/logging/enums/LogTypeEnum.java b/web-server/src/main/java/com/evotech/hd/webserver/logging/enums/LogTypeEnum.java new file mode 100644 index 0000000..62d8a60 --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/logging/enums/LogTypeEnum.java @@ -0,0 +1,48 @@ +package com.evotech.hd.webserver.logging.enums; + +import com.evotech.hd.core.enums.BaseEnum; + +/** + * 日志类型 + */ +public enum LogTypeEnum implements BaseEnum { + LOGIN("1", "登录日志"), + ACCESS("2", "访问日志"), + EXCEPTION("3", "异常日志"), + OPERATION("4", "操作日志"), + PUSH_CLOUD("5", "推送云日志"), + ALARM("6", "报警日志"), + + ; + + /** + * 类型值 + */ + private String value; + + /** + * 类型标签 + */ + private String label; + + private LogTypeEnum(String value, String label) { + this.value = value; + this.label = label; + } + + @Override + public String getLabel() { + return this.label; + } + + @Override + public String getValue() { + return this.value; + } + + + @Override + public String toString() { + return this.value; + } +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/mapper/AccessStrategyMapper.java b/web-server/src/main/java/com/evotech/hd/webserver/mapper/AccessStrategyMapper.java new file mode 100644 index 0000000..43b3ba3 --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/mapper/AccessStrategyMapper.java @@ -0,0 +1,23 @@ +package com.evotech.hd.webserver.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.evotech.hd.core.dtos.business.AccessStrategyDTO; +import com.evotech.hd.core.dtos.business.BatterySwapStepDTO; +import com.evotech.hd.core.entity.business.BatterySwapStep; +import com.evotech.hd.core.entity.system.AccessStrategy; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 接口 + * + * @ClassName:LogMapper + * @date: 2025年08月18日 14:45 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +public interface AccessStrategyMapper extends BaseMapper { + + public AccessStrategyDTO getValidAccessStrategyDTO(); +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/mapper/BatteryCompartmentMapper.java b/web-server/src/main/java/com/evotech/hd/webserver/mapper/BatteryCompartmentMapper.java new file mode 100644 index 0000000..5117007 --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/mapper/BatteryCompartmentMapper.java @@ -0,0 +1,49 @@ +package com.evotech.hd.webserver.mapper; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Constants; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.evotech.hd.core.dtos.business.BatteryCompartmentDTO; +import com.evotech.hd.core.dtos.business.HomeCompartmentDTO; +import com.evotech.hd.core.entity.business.BatteryCompartment; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 接口 + * + * @ClassName:LogMapper + * @date: 2025年08月18日 14:45 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +public interface BatteryCompartmentMapper extends BaseMapper { + /** + * @param queryWrapper + * @return + */ + IPage findPage(Page page, @Param(Constants.WRAPPER) QueryWrapper queryWrapper); + + BatteryCompartmentDTO getInfo(@Param("id")Integer id); + + List findChargingPositionIds(@Param("notAllPower") Boolean isNotAllPower); + + public HomeCompartmentDTO findHomeCompartmentDTO(@Param("maxSoc")Integer maxSoc); + +// public BatteryCompartmentDTO findSequenceInfo(@Param("existsBattery")Integer existsBattery); +// +// public List findBatteryCompartmentNoList(@Param("existsBattery")Integer existsBattery); + + public BatteryCompartmentDTO findSequenceInfo(@Param("existsBattery")Integer existsBattery, @Param("scopeIds")List scopeIds); + + public List findBatteryCompartmentNoList(@Param("existsBattery")Integer existsBattery, @Param("scopeIds")List scopeIds); + + BatteryCompartmentDTO getBatterySwap(@Param("lowestBatterySwapSoc")Integer lowestBatterySwapSoc, @Param("scopeIds")List scopeIds); + + Integer getFullOfNumber(@Param("maxSoc")Integer maxSoc); + + List getExistsBattery(); +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/mapper/BatteryMapper.java b/web-server/src/main/java/com/evotech/hd/webserver/mapper/BatteryMapper.java new file mode 100644 index 0000000..cbcc1f3 --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/mapper/BatteryMapper.java @@ -0,0 +1,15 @@ +package com.evotech.hd.webserver.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.evotech.hd.core.entity.business.Battery; + +/** + * 接口 + * + * @ClassName:BatteryMapper + * @date: 2025年08月28日 11:24 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +public interface BatteryMapper extends BaseMapper { +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/mapper/BatteryStrategyMapper.java b/web-server/src/main/java/com/evotech/hd/webserver/mapper/BatteryStrategyMapper.java new file mode 100644 index 0000000..430a381 --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/mapper/BatteryStrategyMapper.java @@ -0,0 +1,30 @@ +package com.evotech.hd.webserver.mapper; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Constants; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.evotech.hd.core.dtos.business.BatteryStrategyDTO; +import com.evotech.hd.core.entity.system.BatteryStrategy; +import org.apache.ibatis.annotations.Param; + +import java.util.Date; + +/** + * BatteryStrategyMapper + * + * @author andy.shi + * @ClassName:BatteryStrategyMapper + * @date: 2025年09月27日 11:24 + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +public interface BatteryStrategyMapper extends BaseMapper { + + + IPage findPage(Page page, @Param(Constants.WRAPPER) QueryWrapper queryWrapper); + + Long checkOverlapData(@Param("beginTime") String beginTime, @Param("endTime") String endTime, @Param("id") Integer id); + + BatteryStrategyDTO getBatteryStrategyByTime(@Param("hour") String hour); +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/mapper/BatterySwapStepMapper.java b/web-server/src/main/java/com/evotech/hd/webserver/mapper/BatterySwapStepMapper.java new file mode 100644 index 0000000..68c6175 --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/mapper/BatterySwapStepMapper.java @@ -0,0 +1,23 @@ +package com.evotech.hd.webserver.mapper; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.toolkit.Constants; +import com.evotech.hd.core.dtos.business.BatterySwapStepDTO; +import com.evotech.hd.core.entity.business.BatterySwapStep; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 接口 + * + * @ClassName:LogMapper + * @date: 2025年08月18日 14:45 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +public interface BatterySwapStepMapper extends BaseMapper { + + List findListByOrderId(@Param("orderId")Integer orderId); +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/mapper/GroupMapper.java b/web-server/src/main/java/com/evotech/hd/webserver/mapper/GroupMapper.java new file mode 100644 index 0000000..f7ae874 --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/mapper/GroupMapper.java @@ -0,0 +1,15 @@ +package com.evotech.hd.webserver.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.evotech.hd.core.entity.system.Group; + +/** + * GroupMapper + * + * @author andy.shi + * @ClassName:GroupMapper + * @date: 2025年10月17日 13:46 + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +public interface GroupMapper extends BaseMapper { +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/mapper/InstructionLogMapper.java b/web-server/src/main/java/com/evotech/hd/webserver/mapper/InstructionLogMapper.java new file mode 100644 index 0000000..6a8b831 --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/mapper/InstructionLogMapper.java @@ -0,0 +1,28 @@ +package com.evotech.hd.webserver.mapper; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Constants; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.evotech.hd.core.dtos.system.InstructionLogDTO; +import com.evotech.hd.core.entity.system.InstructionLog; +import org.apache.ibatis.annotations.Param; + +/** + * 接口 + * + * @ClassName:LogMapper + * @date: 2025年08月18日 14:45 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +public interface InstructionLogMapper extends BaseMapper { + + /** + * 获日志列表 + * @param queryWrapper + * @return + */ + IPage findPage(Page page, @Param(Constants.WRAPPER) QueryWrapper queryWrapper); +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/mapper/LogMapper.java b/web-server/src/main/java/com/evotech/hd/webserver/mapper/LogMapper.java new file mode 100644 index 0000000..f8270ef --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/mapper/LogMapper.java @@ -0,0 +1,29 @@ +package com.evotech.hd.webserver.mapper; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Constants; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.evotech.hd.core.dtos.business.OrderReservationDTO; +import com.evotech.hd.core.dtos.system.LogDTO; +import com.evotech.hd.core.entity.system.Log; +import org.apache.ibatis.annotations.Param; + +/** + * 接口 + * + * @ClassName:LogMapper + * @date: 2025年08月18日 14:45 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +public interface LogMapper extends BaseMapper { + /** + * 获日志列表 + * + * @param queryWrapper + * @return + */ + IPage findPage(Page page, @Param(Constants.WRAPPER) QueryWrapper queryWrapper); +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/mapper/OrderChargingMapper.java b/web-server/src/main/java/com/evotech/hd/webserver/mapper/OrderChargingMapper.java new file mode 100644 index 0000000..cf48a1a --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/mapper/OrderChargingMapper.java @@ -0,0 +1,28 @@ +package com.evotech.hd.webserver.mapper; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Constants; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.evotech.hd.core.dtos.business.OrderChargingDTO; +import com.evotech.hd.core.entity.business.OrderCharging; +import org.apache.ibatis.annotations.Param; + +/** + * 接口 + * + * @ClassName:LogMapper + * @date: 2025年08月18日 14:45 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +public interface OrderChargingMapper extends BaseMapper { + + /** + * @param queryWrapper + * @return + */ + IPage findPage(Page page, @Param(Constants.WRAPPER) QueryWrapper queryWrapper); + +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/mapper/OrderChargingStrategyLogMapper.java b/web-server/src/main/java/com/evotech/hd/webserver/mapper/OrderChargingStrategyLogMapper.java new file mode 100644 index 0000000..1ac1bca --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/mapper/OrderChargingStrategyLogMapper.java @@ -0,0 +1,17 @@ +package com.evotech.hd.webserver.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.evotech.hd.core.entity.business.OrderChargingStrategyLog; + +/** + * 接口 + * + * @ClassName:LogMapper + * @date: 2025年08月18日 14:45 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +public interface OrderChargingStrategyLogMapper extends BaseMapper { + + +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/mapper/OrderReservationMapper.java b/web-server/src/main/java/com/evotech/hd/webserver/mapper/OrderReservationMapper.java new file mode 100644 index 0000000..1de144a --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/mapper/OrderReservationMapper.java @@ -0,0 +1,28 @@ +package com.evotech.hd.webserver.mapper; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Constants; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.evotech.hd.core.dtos.business.OrderReservationDTO; +import com.evotech.hd.core.entity.business.OrderReservation; +import org.apache.ibatis.annotations.Param; + +/** + * 接口 + * + * @ClassName:LogMapper + * @date: 2025年08月18日 14:45 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +public interface OrderReservationMapper extends BaseMapper { + /** + * 获日志列表 + * + * @param queryWrapper + * @return + */ + IPage findPage(Page page, @Param(Constants.WRAPPER) QueryWrapper queryWrapper); +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/mapper/OrderSwapMapper.java b/web-server/src/main/java/com/evotech/hd/webserver/mapper/OrderSwapMapper.java new file mode 100644 index 0000000..6eb67c7 --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/mapper/OrderSwapMapper.java @@ -0,0 +1,34 @@ +package com.evotech.hd.webserver.mapper; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Constants; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.evotech.hd.core.dtos.business.OrderSwapDTO; +import com.evotech.hd.core.entity.business.OrderSwap; +import org.apache.ibatis.annotations.Param; + +/** + * 接口 + * + * @ClassName:LogMapper + * @date: 2025年08月18日 14:45 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +public interface OrderSwapMapper extends BaseMapper { + + /** + * @param queryWrapper + * @return + */ + IPage findPage(Page page, @Param(Constants.WRAPPER) QueryWrapper queryWrapper); + /** + * 获日志列表 + * + * @param queryWrapper + * @return + */ + IPage findList(Page page, @Param(Constants.WRAPPER) QueryWrapper queryWrapper); +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/mapper/ParamsMapper.java b/web-server/src/main/java/com/evotech/hd/webserver/mapper/ParamsMapper.java new file mode 100644 index 0000000..8467edd --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/mapper/ParamsMapper.java @@ -0,0 +1,32 @@ +package com.evotech.hd.webserver.mapper; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Constants; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.evotech.hd.core.dtos.system.ParamsDTO; +import com.evotech.hd.core.entity.system.Params; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 接口 + * + * @ClassName:LogMapper + * @date: 2025年08月18日 14:45 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +public interface ParamsMapper extends BaseMapper { + /** + * 获日志列表 + * + * @param queryWrapper + * @return + */ + IPage findPage(Page page, @Param(Constants.WRAPPER) QueryWrapper queryWrapper); + + List findList(@Param(Constants.WRAPPER) QueryWrapper queryWrapper); +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/mapper/RunningInstructionsDetailMapper.java b/web-server/src/main/java/com/evotech/hd/webserver/mapper/RunningInstructionsDetailMapper.java new file mode 100644 index 0000000..54f8a53 --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/mapper/RunningInstructionsDetailMapper.java @@ -0,0 +1,33 @@ +package com.evotech.hd.webserver.mapper; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Constants; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.evotech.hd.core.dtos.business.RunningInstructionsDetailDTO; +import com.evotech.hd.core.entity.business.RunningInstructionsDetail; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 接口 + * + * @ClassName:LogMapper + * @date: 2025年08月18日 14:45 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +public interface RunningInstructionsDetailMapper extends BaseMapper { + /** + * 获指令列表 + * + * @param queryWrapper + * @return + */ + IPage findPage(Page page, @Param(Constants.WRAPPER) QueryWrapper queryWrapper); + + + List findDictDetailDTOByTypeCode(@Param("typeCode")String typeCode); +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/mapper/RunningInstructionsMapper.java b/web-server/src/main/java/com/evotech/hd/webserver/mapper/RunningInstructionsMapper.java new file mode 100644 index 0000000..651c7fe --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/mapper/RunningInstructionsMapper.java @@ -0,0 +1,16 @@ +package com.evotech.hd.webserver.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.evotech.hd.core.entity.business.RunningInstructions; + +/** + * 接口 + * + * @ClassName:LogMapper + * @date: 2025年08月18日 14:45 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +public interface RunningInstructionsMapper extends BaseMapper { + +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/mapper/UserMapper.java b/web-server/src/main/java/com/evotech/hd/webserver/mapper/UserMapper.java new file mode 100644 index 0000000..1a8f98b --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/mapper/UserMapper.java @@ -0,0 +1,16 @@ +package com.evotech.hd.webserver.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.evotech.hd.core.entity.system.User; + +/** + * 接口 + * + * @ClassName:UserMapper + * @date: 2025年08月12日 11:44 + * @author: andy.shi + * @contact: 17330188597 + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +public interface UserMapper extends BaseMapper { +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/mqtt/MessageTopic.java b/web-server/src/main/java/com/evotech/hd/webserver/mqtt/MessageTopic.java new file mode 100644 index 0000000..5ea4689 --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/mqtt/MessageTopic.java @@ -0,0 +1,29 @@ +package com.evotech.hd.webserver.mqtt; + +import lombok.Getter; +import lombok.Setter; + +import java.io.Serializable; + +@Getter +@Setter +public class MessageTopic implements Serializable { + + private static final long serialVersionUID = 7238663818955151985L; + + private String businessType = "YTHD"; + + private String stationCode; + + private String dataDirection = "S2M"; + + private String messageType; + + @Override + public String toString() { + return businessType + "/" + stationCode + "/" + dataDirection + "/" + messageType; + } + + + +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/mqtt/MessageUtilService.java b/web-server/src/main/java/com/evotech/hd/webserver/mqtt/MessageUtilService.java new file mode 100644 index 0000000..0c8a4e0 --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/mqtt/MessageUtilService.java @@ -0,0 +1,59 @@ +package com.evotech.hd.webserver.mqtt; + +import cn.hutool.crypto.Mode; +import cn.hutool.crypto.Padding; +import cn.hutool.crypto.symmetric.AES; +import cn.hutool.crypto.symmetric.SymmetricCrypto; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.evotech.hd.webserver.config.redis.utils.RedisUtil; +import com.evotech.hd.webserver.utils.ParamUtils; +import lombok.extern.slf4j.Slf4j; +import org.eclipse.paho.client.mqttv3.MqttMessage; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.util.Base64; + +@Service +@Slf4j +public class MessageUtilService { + + @Resource + private RedisUtil redisUtil; + + /** + * 解密AES类型的消息 + * @param topic + * @param message + * @return + */ + public JSONObject decryptAesMessage(MessageTopic topic, MqttMessage message) { + SymmetricCrypto aes = getAes(topic.getStationCode()); + String decrypt = aes.decryptStr(new String(message.getPayload())); + + return JSONUtil.parseObj(decrypt); + } + + public SymmetricCrypto getAes(String stationCode) { + JSONObject aesJo = getAESKey(); + String aesSecretKey = aesJo.getStr("encryptKey"); + String aesIv = aesJo.getStr("encryptVector"); + SymmetricCrypto aes = new AES(Mode.CBC, Padding.PKCS5Padding, Base64.getDecoder().decode(aesSecretKey), aesIv.getBytes()); + return aes; + } + + + /** + * 获取AES秘钥和IV + * @param stationCode + * @return + */ + public JSONObject getAESKey() { + JSONObject jo = new JSONObject(); + jo.set("encryptKey", ParamUtils.findStationAesPublicKey()); + jo.set("encryptVector", ParamUtils.findStationAesSecretKey()); + return jo; + } + +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/mqtt/MqttMessageHandleService.java b/web-server/src/main/java/com/evotech/hd/webserver/mqtt/MqttMessageHandleService.java new file mode 100644 index 0000000..d6e141e --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/mqtt/MqttMessageHandleService.java @@ -0,0 +1,48 @@ +package com.evotech.hd.webserver.mqtt; + +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.evotech.hd.webserver.job.service.ExecutionBatterySwapService; +import com.evotech.hd.webserver.service.BatteryStrategyService; +import com.evotech.hd.webserver.service.OrderReservationService; +import org.eclipse.paho.client.mqttv3.MqttMessage; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; + +/** + * Mqtt消息处理 + */ +@Service +public class MqttMessageHandleService { + + @Resource + MessageUtilService messageUtilService; + @Resource + OrderReservationService orderReservationService; + @Resource + BatteryStrategyService batteryStrategyService; + @Resource + ExecutionBatterySwapService executionBatterySwapService; + + public void handle(MessageTopic topic, MqttMessage message) throws Exception { + JSONObject jo = messageUtilService.decryptAesMessage(topic, message); + MqttMessageHeader header = JSONUtil.toBean(jo.getJSONObject("header"), MqttMessageHeader.class); + com.alibaba.fastjson2.JSONObject dataBody = com.alibaba.fastjson2.JSONObject.parseObject(jo.getStr("dataBody")); + //站端比较特殊, 只有2个mq消息, 1个是下发预约单, 一个是点击开始 + //不做特殊设计, 直接if-else 判定就好了 + if ("pushReservation".equalsIgnoreCase(header.getFunction())){ + //接收云端订单 + orderReservationService.acceptReservations(dataBody); + }else if("BatterySwapReq".equalsIgnoreCase(header.getFunction())){ + //接收云端的开始换电, 启动对中机构 + try { + executionBatterySwapService.activateAlignmentMechanism(); + } catch (Exception e) { + e.printStackTrace(); + } + }else if("chargingStrategy".equalsIgnoreCase(header.getFunction())){ + batteryStrategyService.manageCloudData(dataBody); + } + } +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/mqtt/MqttMessageHeader.java b/web-server/src/main/java/com/evotech/hd/webserver/mqtt/MqttMessageHeader.java new file mode 100644 index 0000000..95c0fb8 --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/mqtt/MqttMessageHeader.java @@ -0,0 +1,21 @@ +package com.evotech.hd.webserver.mqtt; + +import lombok.Data; + +@Data +public class MqttMessageHeader { + + private String version; + + private String timeStamp; + + private String index; + + private String function; + + private String userId; + + private String pwd; + + private int reason; +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/serializer/StringToNullDeserializer.java b/web-server/src/main/java/com/evotech/hd/webserver/serializer/StringToNullDeserializer.java new file mode 100644 index 0000000..37ff9cf --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/serializer/StringToNullDeserializer.java @@ -0,0 +1,33 @@ +package com.evotech.hd.webserver.serializer; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; + +import java.io.IOException; + +/** + * 类 + * + * @ClassName:StringToNullDeserializer + * @date: 2025年08月18日 17:41 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +public class StringToNullDeserializer extends JsonDeserializer{ + + @Override + public String deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException { + String value = jsonParser.getText(); + if(checkStr(value)) { + return null; + } + return value; + } + + public static Boolean checkStr(String value){ + return "undefined".equals(value) || "null".equals(value); + } + +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/service/AccessStrategyService.java b/web-server/src/main/java/com/evotech/hd/webserver/service/AccessStrategyService.java new file mode 100644 index 0000000..4665635 --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/service/AccessStrategyService.java @@ -0,0 +1,24 @@ +package com.evotech.hd.webserver.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.evotech.hd.core.dtos.Result; +import com.evotech.hd.core.dtos.business.AccessStrategyDTO; +import com.evotech.hd.core.entity.system.AccessStrategy; + +/** + * 接口 + * + * @ClassName:AccessStrategyService + * @date: 2025年08月27日 16:03 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +public interface AccessStrategyService extends IService { + + + AccessStrategyDTO getValidAccessStrategyDTO(); + + + Result update(AccessStrategyDTO accessStrategyDTO); + +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/service/BatteryCompartmentService.java b/web-server/src/main/java/com/evotech/hd/webserver/service/BatteryCompartmentService.java new file mode 100644 index 0000000..bf6787e --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/service/BatteryCompartmentService.java @@ -0,0 +1,154 @@ +package com.evotech.hd.webserver.service; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.IService; +import com.evotech.hd.core.dtos.Result; +import com.evotech.hd.core.dtos.business.BatteryCompartmentDTO; +import com.evotech.hd.core.dtos.business.HomeCompartmentDTO; +import com.evotech.hd.core.entity.business.BatteryCompartment; +import com.evotech.hd.exception.InstructionException; + +import java.util.List; + +/** + * 电池仓接口接口 + * @ClassName:OrderSwapService + * @date: 2025年08月21日 11:02 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +public interface BatteryCompartmentService extends IService { + /*** + * 初始化仓位信息 + * @param maxNo + * @return + */ + Result init(Integer maxNo); + /*** + * 分页查询数据 + * @param page + * @param queryWrapper + * @return + */ + IPage findPageList(Page page, QueryWrapper queryWrapper); + /*** + * 获取非全额充电中的仓位信息 + * @return + */ + List findChargingPositionIds(Boolean isNotAllPower); + + public BatteryCompartmentDTO getInfo(Integer id) throws InstructionException; + /*** + * 设置电池仓的最大功率 + * @param maxPower + * @return + */ + Result settingMaxPower(Integer id, Integer maxPower) throws Exception; + /*** + * 设置电池仓禁用启用 + * @param state + * @return + */ + Result settingState(Integer id, Integer state) throws Exception; + /*** + * 开始充电 + * @param id + * @return + */ + Result startChanging(Integer id) throws Exception; + + /*** + * 一键开始充电 + * @return + */ + Result startChangingAll() throws Exception; + /*** + * BMS上电 + * @param id + * @return + */ + Result BMSPowerOn(Integer id) throws Exception; + + /*** + * 一键BMS上电 + * @return + */ + Result BMSPowerOnAll() throws Exception; + /*** + * 停止充电 + * @param id + * @return + */ + Result stopChanging(Integer id) throws InstructionException; + + /*** + * 一键停止充电 + * @return + */ + Result stopChangingAll() throws Exception; + /*** + * BMS下电 + * @param id + * @return + */ + Result BMSPowerOff(Integer id) throws Exception; + /*** + * 一键BMS下电 + * @return + */ + Result BMSPowerOffAll() throws Exception; + + /*** + * 查询首页电池信息 + * @return + */ + public HomeCompartmentDTO findHomeCompartmentDTO(); + /*** + * 获取顺序仓位 + * @return + */ + BatteryCompartmentDTO findSequenceInfo(Integer existsBattery, List compartmentNums); + /*** + * 获取编号信息 + * @param existsBattery + * @return + */ + List findBatteryCompartmentNoList(Integer existsBattery, List compartmentNums); + /*** + * 获取带换电的电池 + * @param lowestBatterySwapSoc + * @return + */ + public BatteryCompartmentDTO getBatterySwap(Integer lowestBatterySwapSoc, List compartmentNums); + /*** + * 获取充满数量 + * @return + */ + public Integer getFullOfNumber(Integer maxSoc); + /*** + * 获取存储的仓位号 + * @return + */ + public Integer getBatteryCompartmentId(List compartmentNums); + + /*** + * 获取存储的仓位号 + * @return + */ +// public Integer getBatteryCompartmentId(); + + /*** + * 执行开始充电 + * @param id + */ + public void charging(Integer id); + + /*** + * 记录电池仓数据 + * @param batteryCompartmentNo 电池仓Id + * @return + */ + public BatteryCompartment buildCompartment(Integer batteryCompartmentNo) throws InstructionException; +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/service/BatteryService.java b/web-server/src/main/java/com/evotech/hd/webserver/service/BatteryService.java new file mode 100644 index 0000000..4cf8e91 --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/service/BatteryService.java @@ -0,0 +1,30 @@ +package com.evotech.hd.webserver.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.evotech.hd.core.dtos.business.BatteryDTO; +import com.evotech.hd.core.entity.business.Battery; +import com.evotech.hd.exception.InstructionException; + +/** + * 接口 + * + * @ClassName:BatteryService + * @date: 2025年08月28日 11:21 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +public interface BatteryService extends IService { + + Battery getBatteryByCode(String code); + + Battery getBatteryByCompartmentId(Integer byCompartmentId); + + BatteryDTO getBatteryDTOByCompartmentId(Integer byCompartmentId); + /*** + * 记录电池信心 + * @param batteryCompartmentNo + * @return + * @throws Exception + */ + Battery buildBattery(Integer batteryCompartmentNo) throws InstructionException; +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/service/BatteryStrategyService.java b/web-server/src/main/java/com/evotech/hd/webserver/service/BatteryStrategyService.java new file mode 100644 index 0000000..88e7095 --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/service/BatteryStrategyService.java @@ -0,0 +1,43 @@ +package com.evotech.hd.webserver.service; + +import com.alibaba.fastjson2.JSONObject; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.IService; +import com.evotech.hd.core.dtos.Result; +import com.evotech.hd.core.dtos.business.AccessStrategyDTO; +import com.evotech.hd.core.dtos.business.BatteryCompartmentDTO; +import com.evotech.hd.core.dtos.business.BatteryStrategyDTO; +import com.evotech.hd.core.dtos.business.OrderReservationDTO; +import com.evotech.hd.core.entity.system.AccessStrategy; +import com.evotech.hd.core.entity.system.BatteryStrategy; + +import java.util.Date; + +/** + * 接口 + * + * @ClassName:BatteryStrategyService + * @date: 2025年08月27日 16:03 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +public interface BatteryStrategyService extends IService { + + /*** + * 分页查询数据 + * @param page + * @param queryWrapper + * @return + */ + IPage findPageList(Page page, QueryWrapper queryWrapper); + + Result saveOrUpdate(BatteryStrategyDTO batteryStrategyDTO); + + Result manageCloudData(JSONObject json); + + Result del(Integer id); + + BatteryStrategyDTO getBatteryStrategyByTime(Date date); +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/service/BatterySwapStepService.java b/web-server/src/main/java/com/evotech/hd/webserver/service/BatterySwapStepService.java new file mode 100644 index 0000000..ceab894 --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/service/BatterySwapStepService.java @@ -0,0 +1,25 @@ +package com.evotech.hd.webserver.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.evotech.hd.core.dtos.Result; +import com.evotech.hd.core.dtos.business.BatterySwapStepDTO; +import com.evotech.hd.core.entity.business.BatterySwapStep; +import com.evotech.hd.core.entity.business.OrderReservation; + +import java.util.Date; +import java.util.List; +import java.util.Map; + +/** + * 换电订单接口 + * @ClassName:OrderSwapService + * @date: 2025年08月21日 11:02 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +public interface BatterySwapStepService extends IService { + + public Boolean addOrderSwapBatteryStepBySwapComplete(Integer orderId, Integer step, Date stepTime, Boolean humanOperation, Boolean isError, Map message); + + List findListByOrderId(Integer orderId); +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/service/GroupService.java b/web-server/src/main/java/com/evotech/hd/webserver/service/GroupService.java new file mode 100644 index 0000000..fcc9ed2 --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/service/GroupService.java @@ -0,0 +1,18 @@ +package com.evotech.hd.webserver.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.evotech.hd.core.entity.system.Group; + +import java.util.List; + +/** + * GroupService + * + * @author andy.shi + * @ClassName:GroupService + * @date: 2025年10月17日 13:42 + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +public interface GroupService extends IService { + +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/service/InstructionLogService.java b/web-server/src/main/java/com/evotech/hd/webserver/service/InstructionLogService.java new file mode 100644 index 0000000..e56204e --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/service/InstructionLogService.java @@ -0,0 +1,24 @@ +package com.evotech.hd.webserver.service; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.IService; +import com.evotech.hd.core.dtos.system.InstructionLogDTO; +import com.evotech.hd.core.dtos.system.LogDTO; +import com.evotech.hd.core.entity.system.InstructionLog; +import com.evotech.hd.core.entity.system.Log; + +/** + * 日志接口 + * + * @ClassName:LogService + * @date: 2025年08月18日 14:43 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +public interface InstructionLogService extends IService { + + IPage findPageList(Page page, QueryWrapper queryWrapper); + +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/service/LogService.java b/web-server/src/main/java/com/evotech/hd/webserver/service/LogService.java new file mode 100644 index 0000000..66d3810 --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/service/LogService.java @@ -0,0 +1,21 @@ +package com.evotech.hd.webserver.service; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.IService; +import com.evotech.hd.core.dtos.system.LogDTO; +import com.evotech.hd.core.entity.system.Log; + +/** + * 日志接口 + * + * @ClassName:LogService + * @date: 2025年08月18日 14:43 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +public interface LogService extends IService { + + IPage findPageList(Page page, QueryWrapper queryWrapper); +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/service/OrderChargingService.java b/web-server/src/main/java/com/evotech/hd/webserver/service/OrderChargingService.java new file mode 100644 index 0000000..dd020fd --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/service/OrderChargingService.java @@ -0,0 +1,38 @@ +package com.evotech.hd.webserver.service; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.IService; +import com.evotech.hd.core.dtos.Result; +import com.evotech.hd.core.dtos.business.BatteryCompartmentDTO; +import com.evotech.hd.core.dtos.business.OrderChargingDTO; +import com.evotech.hd.core.entity.business.BatteryCompartment; +import com.evotech.hd.core.entity.business.OrderCharging; + +/** + * 换电订单接口 + * @ClassName:OrderSwapService + * @date: 2025年08月21日 11:02 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +public interface OrderChargingService extends IService { + + /*** + * 分页查询数据 + * @param page + * @param queryWrapper + * @return + */ + IPage findPageList(Page page, QueryWrapper queryWrapper); + + /*** + * 创建充电订单 + * @param batteryCompartment + * @return + */ + OrderCharging addOrderChargingByBatteryCompartment(BatteryCompartment batteryCompartment, String strategyInfo); + + Boolean OrderChargingCompleted(BatteryCompartmentDTO batteryCompartmentDTO); +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/service/OrderChargingStrategyLogService.java b/web-server/src/main/java/com/evotech/hd/webserver/service/OrderChargingStrategyLogService.java new file mode 100644 index 0000000..4c64fcf --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/service/OrderChargingStrategyLogService.java @@ -0,0 +1,26 @@ +package com.evotech.hd.webserver.service; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.IService; +import com.evotech.hd.core.dtos.business.OrderChargingDTO; +import com.evotech.hd.core.entity.business.BatteryCompartment; +import com.evotech.hd.core.entity.business.OrderCharging; +import com.evotech.hd.core.entity.business.OrderChargingStrategyLog; + +/** + * 充电订单策略日志 + * @ClassName:OrderChargingStrategyLogService + * @date: 2025年08月21日 11:02 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +public interface OrderChargingStrategyLogService extends IService { + + /*** + * 创建充电订单过程中的策略 + * @return + */ + OrderChargingStrategyLog addOrderChargingStrategyLog(Integer orderChargingId, String strategyInfo); +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/service/OrderReservationService.java b/web-server/src/main/java/com/evotech/hd/webserver/service/OrderReservationService.java new file mode 100644 index 0000000..021702f --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/service/OrderReservationService.java @@ -0,0 +1,34 @@ +package com.evotech.hd.webserver.service; + +import com.alibaba.fastjson2.JSONObject; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.IService; +import com.evotech.hd.core.dtos.Result; +import com.evotech.hd.core.dtos.business.OrderReservationDTO; +import com.evotech.hd.core.entity.business.OrderReservation; +import org.quartz.SchedulerException; + +/** + * 预约单接口 + * + * @ClassName:OrderReservationController + * @date: 2025年08月20日 14:38 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +public interface OrderReservationService extends IService { + + IPage findPageList(Page page, QueryWrapper queryWrapper); + + Result save(OrderReservationDTO orderReservationTO) throws Exception; + + Result cancel(Integer id); + + Result use(Integer id); + + Result acceptReservations(JSONObject json) throws Exception; + + Result updateStatus(Integer id, Integer status, Boolean notSendCloud); +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/service/OrderSwapService.java b/web-server/src/main/java/com/evotech/hd/webserver/service/OrderSwapService.java new file mode 100644 index 0000000..9d2e0c5 --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/service/OrderSwapService.java @@ -0,0 +1,38 @@ +package com.evotech.hd.webserver.service; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.IService; +import com.evotech.hd.core.dtos.Result; +import com.evotech.hd.core.dtos.business.OrderSwapDTO; +import com.evotech.hd.core.entity.business.OrderReservation; +import com.evotech.hd.core.entity.business.OrderSwap; + +import java.io.Serializable; + +/** + * 换电订单接口 + * @ClassName:OrderSwapService + * @date: 2025年08月21日 11:02 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +public interface OrderSwapService extends IService { + + /*** + * 分页查询数据 + * @param page + * @param queryWrapper + * @return + */ + IPage findPageList(Page page, QueryWrapper queryWrapper); + + Result addOrderSwapByReservation(OrderReservation reservation); + + Result runningOrder(); + + Result sendOrderCompleted(Serializable orderId); + + Result updateOrderSwapDTO(OrderSwapDTO orderSwapDTO) throws Exception ; +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/service/ParamsService.java b/web-server/src/main/java/com/evotech/hd/webserver/service/ParamsService.java new file mode 100644 index 0000000..35dfa00 --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/service/ParamsService.java @@ -0,0 +1,34 @@ +package com.evotech.hd.webserver.service; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.IService; +import com.evotech.hd.core.dtos.system.ParamsDTO; +import com.evotech.hd.core.entity.system.Params; + +import java.util.List; + +/** + * 系统参数接口 + * + * @ClassName:ParamsService + * @date: 2025年08月18日 16:08 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +public interface ParamsService extends IService { + + IPage findPageList(Page page, QueryWrapper queryWrapper); + + List findList(QueryWrapper queryWrapper); + + + ParamsDTO getParamsDTO(String name, String code, String defVal, String des); + + public Params checkParamsByCode(String code); + + ParamsDTO getParamsDTOByCode(String code); + + ParamsDTO saveParamsDTO(ParamsDTO paramsDTO); +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/service/RunningInstructionsDetailService.java b/web-server/src/main/java/com/evotech/hd/webserver/service/RunningInstructionsDetailService.java new file mode 100644 index 0000000..75cceb4 --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/service/RunningInstructionsDetailService.java @@ -0,0 +1,27 @@ +package com.evotech.hd.webserver.service; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.IService; +import com.evotech.hd.core.dtos.business.RunningInstructionsDetailDTO; +import com.evotech.hd.core.entity.business.RunningInstructionsDetail; + +import java.util.List; + +/** + * 接口 + * + * @ClassName:DictDetailService + * @date: 2025年08月26日 16:40 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +public interface RunningInstructionsDetailService extends IService { + + IPage findPageList(Page page, QueryWrapper queryWrapper); + + List findDictDetailDTOByTypeCode(String typeCode); + + Boolean saveDTO(RunningInstructionsDetailDTO runningInstructionsDTO); +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/service/RunningInstructionsService.java b/web-server/src/main/java/com/evotech/hd/webserver/service/RunningInstructionsService.java new file mode 100644 index 0000000..9f7a5bc --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/service/RunningInstructionsService.java @@ -0,0 +1,26 @@ +package com.evotech.hd.webserver.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.evotech.hd.core.dtos.business.RunningInstructionsDTO; +import com.evotech.hd.core.dtos.business.RunningInstructionsDetailDTO; +import com.evotech.hd.core.entity.business.RunningInstructions; +import com.evotech.hd.core.entity.business.RunningInstructionsDetail; + +import java.util.List; + +/** + * 接口 + * + * @ClassName:DictDetailService + * @date: 2025年08月26日 16:40 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +public interface RunningInstructionsService extends IService { + + List findList(); + + RunningInstructions getOneByCode(String code); + + Boolean saveDTO(RunningInstructionsDTO runningInstructionsDTO); +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/service/UserService.java b/web-server/src/main/java/com/evotech/hd/webserver/service/UserService.java new file mode 100644 index 0000000..f3f5d51 --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/service/UserService.java @@ -0,0 +1,17 @@ +package com.evotech.hd.webserver.service; + +import com.evotech.hd.core.dtos.UserDTO; + +/** + * 用户接口 + * + * @ClassName:UserService + * @date: 2025年08月12日 11:37 + * @author: andy.shi + * @contact: 17330188597 + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +public interface UserService { + + UserDTO getUserByLoginName(String name); +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/service/impl/AccessStrategyServiceImpl.java b/web-server/src/main/java/com/evotech/hd/webserver/service/impl/AccessStrategyServiceImpl.java new file mode 100644 index 0000000..7cda188 --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/service/impl/AccessStrategyServiceImpl.java @@ -0,0 +1,44 @@ +package com.evotech.hd.webserver.service.impl; + +import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.evotech.hd.core.dtos.Result; +import com.evotech.hd.core.dtos.business.AccessStrategyDTO; +import com.evotech.hd.core.entity.system.AccessStrategy; +import com.evotech.hd.core.mapstruct.AccessStrategyWrapper; +import com.evotech.hd.webserver.mapper.AccessStrategyMapper; +import com.evotech.hd.webserver.service.AccessStrategyService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** + * 类 + * + * @ClassName:OrderSwapServiceImpl + * @date: 2025年08月21日 11:04 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@Service +public class AccessStrategyServiceImpl extends ServiceImpl implements AccessStrategyService { + + @Autowired + AccessStrategyWrapper accessStrategyWrapper; + + @Override + public AccessStrategyDTO getValidAccessStrategyDTO() { + return getBaseMapper().getValidAccessStrategyDTO(); + } + + @Override + public Result update(AccessStrategyDTO accessStrategyDTO) { + AccessStrategy accessStrategy = accessStrategyWrapper.toEntity(accessStrategyDTO); + accessStrategy.setValid("valid"); + if(save(accessStrategy)){ + //重置所有数据为无效 + update(new UpdateWrapper().lambda().set(AccessStrategy::getValid, "invalid").ne(AccessStrategy::getId, accessStrategy.getId())); + return Result.getInstance().build(AccessStrategyDTO.class).success(accessStrategyWrapper.toDTO(accessStrategy)); + } + return Result.getInstance().build(AccessStrategyDTO.class).error("保存失败"); + } +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/service/impl/BatteryCompartmentServiceImpl.java b/web-server/src/main/java/com/evotech/hd/webserver/service/impl/BatteryCompartmentServiceImpl.java new file mode 100644 index 0000000..5888742 --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/service/impl/BatteryCompartmentServiceImpl.java @@ -0,0 +1,537 @@ +package com.evotech.hd.webserver.service.impl; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.evotech.hd.core.dtos.Result; +import com.evotech.hd.core.dtos.ResultUtil; +import com.evotech.hd.core.dtos.business.AccessStrategyDTO; +import com.evotech.hd.core.dtos.business.BatteryCompartmentDTO; +import com.evotech.hd.core.dtos.business.BatteryStrategyDTO; +import com.evotech.hd.core.dtos.business.HomeCompartmentDTO; +import com.evotech.hd.core.entity.business.Battery; +import com.evotech.hd.core.entity.business.BatteryCompartment; +import com.evotech.hd.core.entity.business.OrderCharging; +import com.evotech.hd.core.enums.ChargerStatusEnums; +import com.evotech.hd.exception.InstructionException; +import com.evotech.hd.utils.Collections; +import com.evotech.hd.webserver.mapper.BatteryCompartmentMapper; +import com.evotech.hd.webserver.service.BatteryCompartmentService; +import com.evotech.hd.webserver.service.BatteryService; +import com.evotech.hd.webserver.service.BatteryStrategyService; +import com.evotech.hd.webserver.service.OrderChargingService; +import com.evotech.hd.webserver.utils.AccessStrategyUtil; +import com.evotech.hd.webserver.utils.instruction.InstructionReadUtils; +import com.evotech.hd.webserver.utils.instruction.InstructionWriteUtils; +import org.apache.commons.lang3.ObjectUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.util.*; +import java.util.stream.Collectors; + +/** + * 类 + * + * @ClassName:OrderSwapServiceImpl + * @date: 2025年08月21日 11:04 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@Service +public class BatteryCompartmentServiceImpl extends ServiceImpl implements BatteryCompartmentService { + + @Resource + private BatteryService batteryService; + @Resource + BatteryStrategyService batteryStrategyService; + @Resource + OrderChargingService orderChargingService; + + + @Override + public Result init(Integer maxNo) { + for (int i=1; i <= maxNo; i++){ + try { + if(buildCompartmentResult(i)){ + batteryService.buildBattery(i); + }else{ + break; + } + + } catch (Exception e) { + throw new RuntimeException(e); + } + } + return Result.getInstance().build(Boolean.class).success(true); + } + + @Override + public IPage findPageList(Page page, QueryWrapper queryWrapper) { + queryWrapper.orderByAsc("a.id");// 根据预约日期和预约时间段倒序排序 + return getBaseMapper().findPage(page, queryWrapper); + } + + @Override + public List findChargingPositionIds(Boolean isNotAllPower) { + return getBaseMapper().findChargingPositionIds(isNotAllPower); + } + + @Override + public BatteryCompartmentDTO getInfo(Integer id) throws InstructionException { + BatteryCompartmentDTO result = getBaseMapper().getInfo(id); + Map params = result.getExtendeds(); + if(params == null){ + params = Collections.emptyMap(); + } + params.put("batChgInfo", InstructionReadUtils.queryBSLBatChgInfo(id)); + result.setParams(params); + return result; + } + + @Override + public Result settingMaxPower(Integer id, Integer maxPower) throws Exception { + InstructionWriteUtils.settingMaxPower(id, maxPower); + buildCompartment(id); + return Result.getInstance().build(Boolean.class).success(true); + } + + @Override + public Result settingState(Integer id, Integer state) throws Exception { + //如果指令为禁用 + if(state == 2){ + //BMS下电 + BMSPowerOff(id); + //停止充电 + stopChanging(id); + } + BatteryCompartment batteryCompartment = getById(id); + batteryCompartment.setState(state); + return Result.getInstance().build(Boolean.class).success(updateById(batteryCompartment)); + } + + + @Override + public Result startChanging(Integer id) throws Exception { + //检查当前仓位是否存在电池, + BatteryCompartment batteryCompartment = getById(id); + if(!batteryCompartment.getExistsBattery()){ + return Result.getInstance().build(Boolean.class).error("当前仓位没有电池", false); + } + if(Integer.valueOf(2).equals(batteryCompartment.getState())){ + return Result.getInstance().build(Boolean.class).error("当前仓位已被禁用", false); + } + Battery battery = batteryService.getBatteryByCompartmentId(id); + if(battery == null){ + return Result.getInstance().build(Boolean.class).error("当前仓位没有电池", false); + } + //检查电池是否已经充满电 + if (StringUtils.isEmpty(battery.getSoc()) || Integer.valueOf("100").compareTo(Integer.valueOf(battery.getSoc())) >0){ + //检查充电机 PS: 这种情况是不会出现的, 但是以防万一, 做个处理, 不做数据调整, 但是需要返回异常信息,需要站控人员调整 + if(!InstructionReadUtils.queryBatExist(id)){ + return Result.getInstance().build(Boolean.class).error("当前仓位返回没有电池, 请确认电池是否存在,或者是否对接到位", false); + } + //下发充电 + InstructionWriteUtils.startCharging(id); + //查询充电结果 查询充电机是否工作, 更新数据状态 + if(buildCompartmentResult(id)){ + //重新获取数据, 检查是否开始充电 + batteryCompartment = getById(id); + if(ChargerStatusEnums.SINGLE_POWER.getCode().equals(batteryCompartment.getChargerStatus()) || ChargerStatusEnums.DOUBLE_POWER.getCode().equals(batteryCompartment.getChargerStatus())){ + return Result.getInstance().build(Boolean.class).success(); + }else{ + return Result.getInstance().build(Boolean.class).error("未开始充电, 当前充电机状态未: "+ ChargerStatusEnums.getByCode(batteryCompartment.getChargerStatus()).getName(), false); + } + } + } + return Result.getInstance().build(Boolean.class).error("当前电池已充满", false); + } + + @Override + public Result startChangingAll() throws Exception { + //获取存在电池的电池仓id +// List compartmentIds = getBaseMapper().getExistsBattery(); + List compartmentIds = list(new LambdaQueryWrapper().eq(BatteryCompartment::getStatus,1).eq(BatteryCompartment::getState,1).eq(BatteryCompartment::getExistsBattery, true).eq(BatteryCompartment::getBatteryPlaceStatus, 1).select(BatteryCompartment::getId)).stream().map(BatteryCompartment::getId).collect(Collectors.toList()); + if(Collections.isEmpty(compartmentIds)){ + return Result.getInstance().build(Boolean.class).error("没有需要充电的电池"); + } + + StringBuilder erMsg = new StringBuilder(""); + for (Integer id : compartmentIds){ + Result changingResult = startChanging(id); + if(!ResultUtil.verifyCode(changingResult)){ + erMsg.append("仓位[").append(id).append("]出现充电失败, 原因是: ").append(changingResult.getMsg()).append(";\n"); + } + } + if(StringUtils.isNotEmpty(erMsg.toString())){ + return Result.getInstance().build(Boolean.class).success(true,erMsg.toString()); + } + return Result.getInstance().build(Boolean.class).success(true); + } + + @Override + public Result BMSPowerOn(Integer id) throws Exception { + //检查当前仓位是否存在电池, + BatteryCompartment batteryCompartment = getById(id); + if(!batteryCompartment.getExistsBattery()){ + return Result.getInstance().build(Boolean.class).error("当前仓位没有电池", false); + } + if(Integer.valueOf(2).equals(batteryCompartment.getState())){ + return Result.getInstance().build(Boolean.class).error("当前仓位已被禁用", false); + } + Battery battery = batteryService.getBatteryByCompartmentId(id); + if(battery == null){ + return Result.getInstance().build(Boolean.class).error("当前仓位没有电池", false); + } + //下发BMS上电指令 + InstructionWriteUtils.BMSPowerOn(id); + //更新数据状态 + buildCompartment(id); + return Result.getInstance().build(Boolean.class).success(true); + } + + @Override + public Result BMSPowerOnAll() throws Exception { + //一件停止充电, 不管是否存在电池, 只要是设备在正常状态, 全部停止 + List compartmentIds = list(new LambdaQueryWrapper().eq(BatteryCompartment::getStatus,1).eq(BatteryCompartment::getState,1).eq(BatteryCompartment::getBatteryPlaceStatus, 1).select(BatteryCompartment::getId)).stream().map(BatteryCompartment::getId).collect(Collectors.toList()); + if(Collections.isEmpty(compartmentIds)){ + return Result.getInstance().build(Boolean.class).error("没有需要上电的的电池仓"); + } + StringBuilder erMsg = new StringBuilder(""); + for (Integer id : compartmentIds){ + Result changingResult = BMSPowerOn(id); + if(!ResultUtil.verifyCode(changingResult)){ + erMsg.append("仓位[").append(id).append("]出现上电失败, 原因是: ").append(changingResult.getMsg()).append(";\n"); + } + } + if(StringUtils.isNotEmpty(erMsg.toString())){ + return Result.getInstance().build(Boolean.class).success(true,erMsg.toString()); + } + return Result.getInstance().build(Boolean.class).success(true); + } + + @Override + public Result stopChanging(Integer id) throws InstructionException { + BatteryCompartment batteryCompartment = getById(id); + if(Integer.valueOf(2).equals(batteryCompartment.getState())){ + return Result.getInstance().build(Boolean.class).error("当前仓位已被禁用", false); + } + try { + //下发停止充电 + InstructionWriteUtils.stopCharging(id); + } catch (Exception e) { + e.printStackTrace(); + log.error("下发停止充电命令出现异常: "+e.getMessage(), e); + } + //更新数据状态 + buildCompartment(id); + return Result.getInstance().build(Boolean.class).success(); + } + + @Override + public Result stopChangingAll() throws Exception { + //一件停止充电, 不管是否存在电池, 只要是设备在正常状态, 全部停止 + List compartmentIds = list(new LambdaQueryWrapper().eq(BatteryCompartment::getStatus,1).eq(BatteryCompartment::getState,1).eq(BatteryCompartment::getBatteryPlaceStatus, 1).select(BatteryCompartment::getId)).stream().map(BatteryCompartment::getId).collect(Collectors.toList()); + if(Collections.isEmpty(compartmentIds)){ + return Result.getInstance().build(Boolean.class).error("没有需要停止充电的的电池仓"); + } + StringBuilder erMsg = new StringBuilder(""); + for (Integer id : compartmentIds){ + Result changingResult = stopChanging(id); + if(!ResultUtil.verifyCode(changingResult)){ + erMsg.append("仓位[").append(id).append("]出现停止充电失败, 原因是: ").append(changingResult.getMsg()).append(";\n"); + } + } + if(StringUtils.isNotEmpty(erMsg.toString())){ + return Result.getInstance().build(Boolean.class).success(true,erMsg.toString()); + } + return Result.getInstance().build(Boolean.class).success(true); + } + + @Override + public Result BMSPowerOff(Integer id) throws Exception { + BatteryCompartment batteryCompartment = getById(id); + if(Integer.valueOf(2).equals(batteryCompartment.getState())){ + return Result.getInstance().build(Boolean.class).error("当前仓位已被禁用", false); + } + //下发充电 + InstructionWriteUtils.BMSPowerOff(id); + //更新数据状态 + buildCompartment(id); + return Result.getInstance().build(Boolean.class).success(); + } + + @Override + public Result BMSPowerOffAll() throws Exception { + //一件停止充电, 不管是否存在电池, 只要是设备在正常状态, 全部停止 + List compartmentIds = list(new LambdaQueryWrapper().eq(BatteryCompartment::getStatus,1).eq(BatteryCompartment::getState,1).eq(BatteryCompartment::getBatteryPlaceStatus, 1).select(BatteryCompartment::getId)).stream().map(BatteryCompartment::getId).collect(Collectors.toList()); + if(Collections.isEmpty(compartmentIds)){ + return Result.getInstance().build(Boolean.class).error("没有需要BMS下电的的电池仓"); + } + StringBuilder erMsg = new StringBuilder(""); + for (Integer id : compartmentIds){ + Result changingResult = BMSPowerOff(id); + if(!ResultUtil.verifyCode(changingResult)){ + erMsg.append("仓位[").append(id).append("]出现BMS下电失败, 原因是: ").append(changingResult.getMsg()).append(";\n"); + } + } + if(StringUtils.isNotEmpty(erMsg.toString())){ + return Result.getInstance().build(Boolean.class).success(true,erMsg.toString()); + } + return Result.getInstance().build(Boolean.class).success(true); + } + + + @Override + public HomeCompartmentDTO findHomeCompartmentDTO() { + return getBaseMapper().findHomeCompartmentDTO(AccessStrategyUtil.getValidAccessStrategyDTO().getFullOfLimitSoc()); + } + + @Override + public BatteryCompartmentDTO findSequenceInfo(Integer existsBattery, List compartmentNums) { + return getBaseMapper().findSequenceInfo(existsBattery, compartmentNums); + } + + @Override + public List findBatteryCompartmentNoList(Integer existsBattery, List compartmentNums) { + return getBaseMapper().findBatteryCompartmentNoList(existsBattery, compartmentNums); + } + + @Override + public BatteryCompartmentDTO getBatterySwap(Integer lowestBatterySwapSoc, List compartmentNums) { + return getBaseMapper().getBatterySwap(lowestBatterySwapSoc, compartmentNums) ; + } + + @Override + public Integer getFullOfNumber(Integer maxSoc) { + return getBaseMapper().getFullOfNumber(maxSoc) ; + } + + @Override + public Integer getBatteryCompartmentId(List compartmentNums) { + //获取存储策略hypothesis + AccessStrategyDTO accessStrategyDTO = AccessStrategyUtil.getValidAccessStrategyDTO(); + Integer batteryCompartmentNo = null; + //计算存储仓位号 + switch (accessStrategyDTO.getStrategy()){ + case 1: //存取相同, 单RGV无法实现, 暂不做处理 + break; + case 2: //顺序存储, 存在电池则跳过 + batteryCompartmentNo = getSequenceInfo(0, compartmentNums); + break; + case 3: //间隔存储 + batteryCompartmentNo = getAdjacentNumbers(findBatteryCompartmentNoList(0, compartmentNums)); + break; + case 4: //随机存储 + batteryCompartmentNo = Collections.getRandomElement(findBatteryCompartmentNoList(0, compartmentNums)); + break; + default: //默认顺序存储 + batteryCompartmentNo = getSequenceInfo(0, compartmentNums); + break; + } + + //如果存储仓位号为空, 则顺序获取仓位号, 必定不能为空 + if(batteryCompartmentNo ==null){ + batteryCompartmentNo = getSequenceInfo(0, compartmentNums); + } + return batteryCompartmentNo; + } + +// @Override +// public Integer getBatteryCompartmentId() { +// //获取存储策略hypothesis +// AccessStrategyDTO accessStrategyDTO = AccessStrategyUtil.getValidAccessStrategyDTO(); +// Integer batteryCompartmentNo = null; +// //计算存储仓位号 +// switch (accessStrategyDTO.getStrategy()){ +// case 1: //存取相同, 单RGV无法实现, 暂不做处理 +// break; +// case 2: //顺序存储, 存在电池则跳过 +// batteryCompartmentNo = getSequenceInfo(0); +// break; +// case 3: //间隔存储 +// batteryCompartmentNo = getAdjacentNumbers(findBatteryCompartmentNoList(0)); +// break; +// case 4: //随机存储 +// batteryCompartmentNo = Collections.getRandomElement(findBatteryCompartmentNoList(0)); +// break; +// default: //默认顺序存储 +// batteryCompartmentNo = getSequenceInfo(0); +// break; +// } +// +// //如果存储仓位号为空, 则顺序获取仓位号, 必定不能为空 +// if(batteryCompartmentNo ==null){ +// batteryCompartmentNo = getSequenceInfo(0); +// } +// return batteryCompartmentNo; +// } + + @Override + public void charging(Integer id) { + + try { + /** + * 执行完了, 检查充电策略 + * 1. 如果是立即充电, 直接执行供电 + * 2. 如果不是, 则检查当前时间在那个策略中, 按照策略执行, 并记录策略Id + */ + BatteryCompartment batteryCompartment = getById(id); + //获取策略 + AccessStrategyDTO accessStrategyDTO = AccessStrategyUtil.getValidAccessStrategyDTO(); + BatteryStrategyDTO batteryStrategyDTO = null; + Map strategyInfoMap = Collections.asMap("className", "null", "strategyInfo", "0"); + //即存即充 + if(accessStrategyDTO != null && accessStrategyDTO.getMinimumSafetyStock() != null && accessStrategyDTO.getMinimumSafetyStock() < getFullOfNumber(accessStrategyDTO.getFullOfLimitSoc())){ + InstructionWriteUtils.settingMaxPower(id, accessStrategyDTO.getInstantChargePower()); + InstructionWriteUtils.BMSPowerOn(id); + InstructionWriteUtils.startCharging(id); + strategyInfoMap = Collections.asMap("className", AccessStrategyDTO.class.getName(), "strategyInfo", JSONObject.toJSONString(accessStrategyDTO)); + batteryCompartment.setAllPower(true); + }else{ + //获取当前时间, 查询当前功率 + batteryStrategyDTO = batteryStrategyService.getBatteryStrategyByTime(new Date()); + if(batteryStrategyDTO != null){ + InstructionWriteUtils.settingMaxPower(id, ((batteryStrategyDTO != null && batteryStrategyDTO.getChargingPower() != null) ? batteryStrategyDTO.getChargingPower(): 0)); + InstructionWriteUtils.BMSPowerOn(id); + InstructionWriteUtils.startCharging(id); + strategyInfoMap = Collections.asMap("className", BatteryStrategyDTO.class.getName(), "strategyInfo", JSONObject.toJSONString(batteryStrategyDTO)); + }else{ + InstructionWriteUtils.BMSPowerOn(id); + } + //不管是否存在充电策略, 统一调整为false + batteryCompartment.setAllPower(false); + } + + //创建充电订单 + try { + if(batteryStrategyDTO != null){ + OrderCharging orderCharging = orderChargingService.addOrderChargingByBatteryCompartment(batteryCompartment, JSONObject.toJSONString(strategyInfoMap)); + if(batteryCompartment.getOrderChargingId() == null){ + batteryCompartment.setOrderChargingId(orderCharging.getId()); + } + } + saveOrUpdate(batteryCompartment); + } catch (Exception e) { + e.printStackTrace(); + log.error("更新充电订单出现错误", e); + } + + + } catch (Exception e) { + e.printStackTrace(); + log.error("旧电池开始充电出现异常", e); + } + } + + private Integer getSequenceInfo(Integer existsBattery, List compartmentNums){ + BatteryCompartmentDTO batteryCompartmentDTO = findSequenceInfo(existsBattery, compartmentNums); + if(ObjectUtils.isNotEmpty(batteryCompartmentDTO)){ + return batteryCompartmentDTO.getId(); + } + return null; + } + + + /*** 获取间隔存储的仓位数据 + * @param numbers + * @return + */ + private static Integer getAdjacentNumbers(List numbers) { + // 空列表处理 + if (numbers == null || numbers.isEmpty()) { + return null; + } + // 转换为Set提高查找效率 + Set numberSet = new HashSet<>(numbers); + // 遍历每个元素并检查相邻数 + for (Integer num : numbers) { + // 处理null元素 + if (num == null) { + continue; + } + //符合预期, 返回当前数 + if(numberSet.contains(num - 1) && numberSet.contains(num + 1)){ + return num; + } + } + return null; + } + + private Boolean buildCompartmentResult(Integer batteryCompartmentNo) throws InstructionException { + return ObjectUtils.isNotEmpty(buildCompartment(batteryCompartmentNo)); + } + + @Override + public BatteryCompartment buildCompartment(Integer batteryCompartmentNo) throws InstructionException { + BatteryCompartment batteryCompartment = getById(batteryCompartmentNo); + Map extendeds = Collections.emptyMap(); + Boolean isAdd = false; + if(batteryCompartment == null){ + batteryCompartment = new BatteryCompartment(); + batteryCompartment.setId(batteryCompartmentNo); + batteryCompartment.setStatus(1); + batteryCompartment.setState(1); + isAdd = true; + } + if(Collections.isNotEmpty(batteryCompartment.getExtendeds())){ + extendeds = batteryCompartment.getExtendeds(); + } + + //获取设备信息 + JSONObject deviceInfo = InstructionReadUtils.queryDeviceInfo(batteryCompartmentNo); + if(deviceInfo == null){ + return null; + } + if(deviceInfo != null){ + batteryCompartment.setName(deviceInfo.getString("DevName")); + batteryCompartment.setBatteryCompartmentNo(deviceInfo.getString("DevUUID")); + batteryCompartment.setExistsBattery(deviceInfo.getBoolean("BatPlSta")); //记录设备信息 + if(deviceInfo.getBoolean("BatPlSta")){ + batteryCompartment.setDepositTime(new Date()); + } + batteryCompartment.setBatteryCommunicationStatus(deviceInfo.getBoolean("BatOlSta")); + batteryCompartment.setDirectCurrentCommunicationStatus(deviceInfo.getBoolean("DcmOlSta")); + batteryCompartment.setChargerStatus(deviceInfo.getInteger("DevSta")); + extendeds.put("deviceInfo", deviceInfo.toString()); + } + + //获取仓位充电模块信息 + JSONObject powModInfo = InstructionReadUtils.queryPowModInfo(batteryCompartmentNo); + if(powModInfo != null){ + batteryCompartment.setMaximumChargingPower(powModInfo.getInteger("MaxOpPow")); + extendeds.put("powModInfo", powModInfo.toString()); + } + + //获取仓位直流电表信息 + JSONObject dcmInfo = InstructionReadUtils.queryDCMInfo(batteryCompartmentNo); + if(dcmInfo != null){ + //直流表电压 + batteryCompartment.setDirectCurrentMeterVoltage(dcmInfo.getString("DcmVol")); + //记录设备信息 + extendeds.put("DCMInfo", dcmInfo.toString()); + } + //记录探针温度 + JSONObject batProbeTemp = InstructionReadUtils.queryBatProbeTemp(batteryCompartmentNo); + if(batProbeTemp != null){ + //记录设备信息 + extendeds.put("batProbeTemp", batProbeTemp.toString()); + } + batteryCompartment.setExtendeds(JSON.toJSONString(extendeds)); + if(isAdd){ + save(batteryCompartment); + }else{ + saveOrUpdate(batteryCompartment); + } + return batteryCompartment; + } + + + + +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/service/impl/BatteryServiceImpl.java b/web-server/src/main/java/com/evotech/hd/webserver/service/impl/BatteryServiceImpl.java new file mode 100644 index 0000000..22a0a3b --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/service/impl/BatteryServiceImpl.java @@ -0,0 +1,123 @@ +package com.evotech.hd.webserver.service.impl; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.evotech.hd.core.dtos.business.BatteryDTO; +import com.evotech.hd.core.entity.business.Battery; +import com.evotech.hd.core.mapstruct.BatteryWrapper; +import com.evotech.hd.exception.InstructionException; +import com.evotech.hd.utils.Collections; +import com.evotech.hd.webserver.mapper.BatteryMapper; +import com.evotech.hd.webserver.service.BatteryService; +import com.evotech.hd.webserver.utils.instruction.InstructionReadUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.Map; + +/** + * 类 + * + * @ClassName:BatteryServiceImpl + * @date: 2025年08月28日 11:22 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@Service +public class BatteryServiceImpl extends ServiceImpl implements BatteryService { + + @Resource + BatteryWrapper batteryWrapper; + @Override + public Battery getBatteryByCode(String code) { + return getOne(new LambdaQueryWrapper().eq(Battery::getBatteryCode, code), false); + } + + @Override + public Battery getBatteryByCompartmentId(Integer byCompartmentId) { + return getOne(new LambdaQueryWrapper().eq(Battery::getBatteryCompartmentId, byCompartmentId).eq(Battery::getStorageStatus, 1), false); + } + + @Override + public BatteryDTO getBatteryDTOByCompartmentId(Integer byCompartmentId) { + Battery battery = getBatteryByCompartmentId(byCompartmentId); + if(battery != null){ + return batteryWrapper.toDTO(battery); + } + return null; + } + + @Override + public Battery buildBattery(Integer batteryCompartmentNo) throws InstructionException { + Battery battery = null; + + //检查当前仓内是否存在电池, 不存在直接返回false + if(!InstructionReadUtils.checkExistsBattery(String.valueOf(batteryCompartmentNo))){ + return null; + } + Map extendeds = Collections.emptyMap(); + + JSONObject batDataInfo1 = InstructionReadUtils.queryBattery(batteryCompartmentNo); + if(batDataInfo1 != null){ + //设备型号 + battery = getBatteryByCode(batDataInfo1.getString("sBatSN")); + if(battery ==null){ + battery = new Battery(); + battery.setBatteryCode(batDataInfo1.getString("sBatSN")); + }else{ + extendeds = battery.getExtendeds(); + } + battery.setBatteryType(batDataInfo1.getString("BatType")); + battery.setBatteryCompartmentId(batteryCompartmentNo); + battery.setStorageStatus(1); + battery.setRatedCapacity(batDataInfo1.getString("BatCap")); + //记录设备信息 + extendeds.put("batDataInfo1", batDataInfo1.toString()); + } + + //如果电池信息还为null. 其他数据不需要记录, 抛出异常日志 + if(battery == null){ + log.error("未抓取到电池信息"); + return null; + } + + //获取电池状态信息 + JSONObject batStatusInfo = InstructionReadUtils.queryBatStatusInfo(batteryCompartmentNo); + if(batStatusInfo != null){ + battery.setBatteryMode(batStatusInfo.getString("BatMode")); + //记录设备信息 + extendeds.put("batStatusInfo", batStatusInfo.toString()); + } + + JSONObject batDataInfo2 = InstructionReadUtils.queryBatDataInfo2(batteryCompartmentNo); + if(batDataInfo2 != null){ + battery.setSoh(batDataInfo2.getString("BatSOH")); + if(StringUtils.isNotEmpty(battery.getSoh())){ + battery.setSoh(new BigDecimal(battery.getSoh()).divide(new BigDecimal(10), 2, RoundingMode.DOWN).toString()); + } + battery.setSoc(batDataInfo2.getString("BatSOC")); + if(StringUtils.isNotEmpty(battery.getSoc())){ + battery.setSoc(new BigDecimal(battery.getSoc()).divide(new BigDecimal(10), 2, RoundingMode.DOWN).toString()); + } + battery.setMonomerQuantity(batDataInfo2.getInteger("BatCellNo")); + battery.setNumberOfProbes(batDataInfo2.getInteger("TempProNo")); + //记录设备信息 + extendeds.put("batDataInfo2", batDataInfo2.toString()); + } + //记录单体电池电压 + JSONObject batCellVol = InstructionReadUtils.queryBatCellVol(batteryCompartmentNo); + if(batCellVol != null){ + //记录设备信息 + extendeds.put("batCellVol", batCellVol.toString()); + } + + battery.setExtendeds(JSON.toJSONString(extendeds)); + saveOrUpdate(battery); + return battery; + } +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/service/impl/BatteryStrategyServiceImpl.java b/web-server/src/main/java/com/evotech/hd/webserver/service/impl/BatteryStrategyServiceImpl.java new file mode 100644 index 0000000..7dfe390 --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/service/impl/BatteryStrategyServiceImpl.java @@ -0,0 +1,219 @@ +package com.evotech.hd.webserver.service.impl; + +import com.alibaba.fastjson2.JSONObject; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.evotech.hd.core.dtos.Result; +import com.evotech.hd.core.dtos.ResultUtil; +import com.evotech.hd.core.dtos.business.BatteryStrategyDTO; +import com.evotech.hd.core.entity.system.BatteryStrategy; +import com.evotech.hd.core.mapstruct.BatteryStrategyWrapper; +import com.evotech.hd.utils.Collections; +import com.evotech.hd.utils.DateUtils; +import com.evotech.hd.utils.EntityUtils; +import com.evotech.hd.webserver.job.JobConstant; +import com.evotech.hd.webserver.job.entity.QuartzJobInfo; +import com.evotech.hd.webserver.job.service.QuartzJobService; +import com.evotech.hd.webserver.mapper.BatteryStrategyMapper; +import com.evotech.hd.webserver.service.BatteryStrategyService; +import com.evotech.hd.webserver.utils.sendCloud.CloudSendInfoUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.time.Duration; +import java.time.LocalTime; +import java.util.Date; +import java.util.List; + +/** + * BatteryStrategyServiceImpl + * + * @author andy.shi + * @ClassName:BatteryStrategyServiceImpl + * @date: 2025年09月27日 11:24 + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@Service +public class BatteryStrategyServiceImpl extends ServiceImpl implements BatteryStrategyService { + + @Autowired + BatteryStrategyWrapper batteryStrategyWrapper; + @Autowired + private QuartzJobService quartzJobService; + + @Override + public IPage findPageList(Page page, QueryWrapper queryWrapper) { + queryWrapper.orderByAsc("a.create_time");// 根据预约日期和预约时间段倒序排序 + queryWrapper.eq("a.del", false); + return getBaseMapper().findPage(page, queryWrapper); + } + + @Override + public Result saveOrUpdate(BatteryStrategyDTO batteryStrategyDTO) { + if(getBaseMapper().checkOverlapData(batteryStrategyDTO.getBeginTime(), batteryStrategyDTO.getEndTime(), batteryStrategyDTO.getId()) > 0){ + return Result.getInstance().build(Boolean.class).error("当前时间范围重叠, 请调整后重试", false); + } + BatteryStrategy batteryStrategy = batteryStrategyWrapper.toEntity(batteryStrategyDTO); + batteryStrategy.setDel(false); + Boolean result = saveOrUpdate(batteryStrategy); + //推送云端 + if(result){ + Result cloudResult = CloudSendInfoUtils.sendStrategy(EntityUtils.converToMap(batteryStrategy)); + JSONObject cloudResultValue = ResultUtil.getValue(cloudResult); + if(cloudResultValue != null && batteryStrategy.getCloudId() == null){ + batteryStrategy.setCloudId(cloudResultValue.getInteger("pkId")); + updateById(batteryStrategy); + } + } + if(result){ + //生成充电定时策略, 如果存在的话, 则是更改定时策略 + refreshJob(); + } + return result ? Result.getInstance().build(Boolean.class).success(true) : Result.getInstance().build(Boolean.class).error("持久化失败", false); + } + + @Override + public Result manageCloudData(JSONObject json) { + JSONObject jsonData = json.getJSONObject("dataBody"); + + Integer dataType = jsonData.getInteger("dataType"); + if(dataType != null){ + switch (dataType){ + case 1: //新增 + case 2: //修改 + return saveOrUpdate(jsonData); + case 3: //删除 + return delByCloudId(jsonData.getInteger("pkId")); + default: + return Result.getInstance().build(Boolean.class).error("未知的数据类型"); + } + } + return Result.getInstance().build(Boolean.class).error("未知的数据类型"); + } + + public Result saveOrUpdate(JSONObject jsonData){ + Integer cloudId = jsonData.getInteger("pkId"); + BatteryStrategy batteryStrategy = getOne(new LambdaQueryWrapper().eq(BatteryStrategy::getCloudId, cloudId)); + if(batteryStrategy == null){ + batteryStrategy = new BatteryStrategy(); + batteryStrategy.setCloudId(cloudId); + batteryStrategy.setDel(false); + } + batteryStrategy.setBeginTime(jsonData.getString("beginTime")); + batteryStrategy.setEndTime(jsonData.getString("endTime")); + batteryStrategy.setChargingPower(jsonData.getInteger("chargingPower")); + Boolean result = saveOrUpdate(batteryStrategy); + if(result){ + //刷新任务策略 + refreshJob(); + } + return result ? Result.getInstance().build(Boolean.class).success(true) : Result.getInstance().build(Boolean.class).error("持久化失败", false); + } + + public Result delByCloudId(Integer cloudId) { + return (update(new UpdateWrapper().lambda().set(BatteryStrategy::getDel,true).eq(BatteryStrategy::getCloudId, cloudId)) ? Result.getInstance().build(Boolean.class).success(true) : Result.getInstance().build(Boolean.class).error("删除充电策略失败了", false) ); + } + + @Override + public Result del(Integer id) { + return (update(new UpdateWrapper().lambda().set(BatteryStrategy::getDel,true).eq(BatteryStrategy::getId, id)) ? Result.getInstance().build(Boolean.class).success(true) : Result.getInstance().build(Boolean.class).error("删除充电策略失败了", false) ); + } + + @Override + public BatteryStrategyDTO getBatteryStrategyByTime(Date date) { + String hour = DateUtils.parseDateToStr("HH:mm", date); + return getBaseMapper().getBatteryStrategyByTime(hour); + } + + + private void refreshJob(){ + try { + List batteryStrategyList = list(new LambdaQueryWrapper().ne(BatteryStrategy::getDel, true).orderByAsc(BatteryStrategy::getBeginTime)); + if(Collections.isNotEmpty(batteryStrategyList)){ + for (int i = 0; i < batteryStrategyList.size(); i++) { + BatteryStrategy strategy = batteryStrategyList.get(i); + //记录更新开始充电策略 + refreshJob("chargingBeginJob"+strategy.getId(), strategy.getName(), strategy.getBeginTime(), "开始充电策略", strategy.getId(), true); + final String nextBeginTime = getString(i, batteryStrategyList); + if(checkBetweenMinutes(strategy.getEndTime(), nextBeginTime)){ + //超出5分钟界限, //记录更新开始充电策略 + refreshJob("chargingEndJob"+strategy.getId(), strategy.getName(), strategy.getBeginTime(), "结束充电策略", strategy.getId(), false); + }else{ + //未超出界限, 删除结束定时 + if(quartzJobService.checkExists("chargingEndJob"+strategy.getId(), JobConstant.GroupEnum.CHARGING_GROUP.getType())){ + //修改 + quartzJobService.deleteJob(new QuartzJobInfo("chargingEndJob"+strategy.getId(), JobConstant.GroupEnum.CHARGING_GROUP.getType())); + } + } + + + } + } + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + /*** + * 更新创建充电策略 + * @param jobId 任务Id + * @param jobName 任务名称 + * @param time 任务时间 + * @param desc 描述 + * @param strategyId 策略Id + * @param isStart 是否开始 + * @throws Exception + */ + public void refreshJob(String jobId, String jobName, String time, String desc, Integer strategyId, Boolean isStart) throws Exception { + if(quartzJobService.checkExists(jobId, JobConstant.GroupEnum.CHARGING_GROUP.getType())){ + //修改 + quartzJobService.updateJobCron(new QuartzJobInfo(jobId, JobConstant.GroupEnum.CHARGING_GROUP.getType(), getCronExpression(time))); + }else{ + //创建 + quartzJobService.createJob(new QuartzJobInfo(jobId, jobName, JobConstant.GroupEnum.CHARGING_GROUP.getType(), getCronExpression(time),"com.evotech.hd.webserver.job.job.charging.ChargingJob",desc, Collections.asMap("strategyId", String.valueOf(strategyId), "type", String.valueOf(isStart)))); + } + } + + private String getString(int i, List batteryStrategyList) { + String nextBeginTime; + //判断当前是否为最后一个 + if(i +1< batteryStrategyList.size()){ + //不是最后一个, 获取下一个的策略信息, 检查当前策略信息的结束时间和下一个策略的开始时间相差是否大于5分钟, 如果<=5分钟, 则删除当前策略的结束时间 + nextBeginTime = batteryStrategyList.get(i +1).getBeginTime(); + }else{ + //如果当期为最后一个策略, 则获取第一个策略信息, 检查当前策略信息的结束时间和第一个策略的开始时间相差是否大于5分钟, 如果<=5分钟, 则删除当前策略的结束时间 + nextBeginTime = batteryStrategyList.get(0).getBeginTime(); + } + return nextBeginTime; + } + + public Boolean checkBetweenMinutes(String endTime, String nextBeginTime){ + String[] endTimes = endTime.split(":"); + String[] nextBeginTimes = nextBeginTime.split(":"); + // 创建两个LocalTime对象 + LocalTime time1 = LocalTime.of(Integer.valueOf(endTimes[0]), Integer.valueOf(endTimes[1])); // 8:30 + LocalTime time2 = LocalTime.of(Integer.valueOf(nextBeginTimes[0]), Integer.valueOf(nextBeginTimes[1])); // 17:45 + // 计算两个时间之间的差异 + Duration duration = Duration.between(time1, time2); + // 将差异转换为分钟 + Long minutes = duration.toMinutes(); + return (minutes.compareTo(5l)> 0); + } + + public String getCronExpression(String time){ + if(StringUtils.isEmpty(time)){ + throw new RuntimeException("无法格式化时间"); + } + String[] times = time.split(":"); + String hour = times[0]; + String minutes = times[1]; + if(minutes == "00") minutes = "0"; + + return "0 "+minutes+" "+hour+" * * ?"; + } +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/service/impl/BatterySwapStepServiceImpl.java b/web-server/src/main/java/com/evotech/hd/webserver/service/impl/BatterySwapStepServiceImpl.java new file mode 100644 index 0000000..7a20b3f --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/service/impl/BatterySwapStepServiceImpl.java @@ -0,0 +1,63 @@ +package com.evotech.hd.webserver.service.impl; + +import com.alibaba.druid.support.json.JSONUtils; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.evotech.hd.core.dtos.business.BatterySwapStepDTO; +import com.evotech.hd.core.entity.business.BatterySwapStep; +import com.evotech.hd.core.enums.SwapBatteryStepEnum; +import com.evotech.hd.utils.Collections; +import com.evotech.hd.webserver.mapper.BatterySwapStepMapper; +import com.evotech.hd.webserver.service.BatterySwapStepService; +import org.apache.commons.lang3.ObjectUtils; +import org.springframework.stereotype.Service; + +import java.util.Date; +import java.util.List; +import java.util.Map; + +/** + * 类 + * + * @ClassName:OrderSwapServiceImpl + * @date: 2025年08月21日 11:04 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@Service +public class BatterySwapStepServiceImpl extends ServiceImpl implements BatterySwapStepService { + + @Override + public Boolean addOrderSwapBatteryStepBySwapComplete(Integer orderId, Integer step, Date stepTime, Boolean humanOperation, Boolean isError, Map message) { + synchronized (this){ + Boolean isAdd = false; + BatterySwapStep orderSwapBatteryStep = getOne(new LambdaQueryWrapper().eq(BatterySwapStep::getOrderId, orderId).eq(BatterySwapStep::getStep,step)); + if(ObjectUtils.isEmpty(orderSwapBatteryStep)){ + orderSwapBatteryStep = new BatterySwapStep(); + isAdd = true; + } + orderSwapBatteryStep.setOrderId(orderId); + orderSwapBatteryStep.setStep(step); + orderSwapBatteryStep.setStepTime(stepTime); + orderSwapBatteryStep.setHumanOperation(humanOperation); + orderSwapBatteryStep.setError(isError); + if(Collections.isNotEmpty(message)){ + orderSwapBatteryStep.setExtendeds(JSONUtils.toJSONString(message)); + } + saveOrUpdate(orderSwapBatteryStep); + return isAdd; + } + + } + + @Override + public List findListByOrderId(Integer orderId) { + List list = getBaseMapper().findListByOrderId(orderId); + if(Collections.isNotEmpty(list)){ + list.forEach(data ->{ + data.setStepName(SwapBatteryStepEnum.getName(data.getStep())); + }); + } + return list; + } +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/service/impl/GroupServiceImpl.java b/web-server/src/main/java/com/evotech/hd/webserver/service/impl/GroupServiceImpl.java new file mode 100644 index 0000000..6bdc554 --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/service/impl/GroupServiceImpl.java @@ -0,0 +1,19 @@ +package com.evotech.hd.webserver.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.evotech.hd.core.entity.system.Group; +import com.evotech.hd.webserver.mapper.GroupMapper; +import com.evotech.hd.webserver.service.GroupService; +import org.springframework.stereotype.Service; + +/** + * GroupServiceImpl + * + * @author andy.shi + * @ClassName:GroupServiceImpl + * @date: 2025年10月17日 13:46 + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@Service +public class GroupServiceImpl extends ServiceImpl implements GroupService { +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/service/impl/InstructionLogServiceImpl.java b/web-server/src/main/java/com/evotech/hd/webserver/service/impl/InstructionLogServiceImpl.java new file mode 100644 index 0000000..5830950 --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/service/impl/InstructionLogServiceImpl.java @@ -0,0 +1,30 @@ +package com.evotech.hd.webserver.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.evotech.hd.core.dtos.system.InstructionLogDTO; +import com.evotech.hd.core.entity.system.InstructionLog; +import com.evotech.hd.webserver.mapper.InstructionLogMapper; +import com.evotech.hd.webserver.service.InstructionLogService; +import org.springframework.stereotype.Service; + +/** + * 类 + * + * @ClassName:LogServiceImpl + * @date: 2025年08月18日 14:44 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@Service +public class InstructionLogServiceImpl extends ServiceImpl implements InstructionLogService { + + @Override + public IPage findPageList(Page page, QueryWrapper queryWrapper) { + queryWrapper.orderByDesc("a.create_time");// 根据预约日期和预约时间段倒序排序 + return getBaseMapper().findPage(page, queryWrapper); + } + +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/service/impl/LogServiceImpl.java b/web-server/src/main/java/com/evotech/hd/webserver/service/impl/LogServiceImpl.java new file mode 100644 index 0000000..2c6067f --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/service/impl/LogServiceImpl.java @@ -0,0 +1,31 @@ +package com.evotech.hd.webserver.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.evotech.hd.core.dtos.business.OrderReservationDTO; +import com.evotech.hd.core.dtos.system.LogDTO; +import com.evotech.hd.core.entity.system.Log; +import com.evotech.hd.webserver.mapper.LogMapper; +import com.evotech.hd.webserver.service.LogService; +import org.springframework.stereotype.Service; + +/** + * 类 + * + * @ClassName:LogServiceImpl + * @date: 2025年08月18日 14:44 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@Service +public class LogServiceImpl extends ServiceImpl implements LogService { + + + @Override + public IPage findPageList(Page page, QueryWrapper queryWrapper) { + queryWrapper.orderByDesc("a.create_time");// 根据预约日期和预约时间段倒序排序 + return getBaseMapper().findPage(page, queryWrapper); + } +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/service/impl/OrderChargingServiceImpl.java b/web-server/src/main/java/com/evotech/hd/webserver/service/impl/OrderChargingServiceImpl.java new file mode 100644 index 0000000..6b06fb7 --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/service/impl/OrderChargingServiceImpl.java @@ -0,0 +1,81 @@ +package com.evotech.hd.webserver.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.evotech.hd.core.dtos.business.BatteryCompartmentDTO; +import com.evotech.hd.core.dtos.business.BatteryDTO; +import com.evotech.hd.core.dtos.business.OrderChargingDTO; +import com.evotech.hd.core.entity.business.Battery; +import com.evotech.hd.core.entity.business.BatteryCompartment; +import com.evotech.hd.core.entity.business.OrderCharging; +import com.evotech.hd.webserver.mapper.OrderChargingMapper; +import com.evotech.hd.webserver.service.BatteryService; +import com.evotech.hd.webserver.service.OrderChargingService; +import com.evotech.hd.webserver.service.OrderChargingStrategyLogService; +import com.evotech.hd.webserver.utils.OrderNoUtils; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.math.BigDecimal; +import java.util.Date; + +/** + * 类 + * + * @ClassName:OrderSwapServiceImpl + * @date: 2025年08月21日 11:04 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@Service +public class OrderChargingServiceImpl extends ServiceImpl implements OrderChargingService { + + @Resource + private BatteryService batteryService; + @Resource + private OrderChargingStrategyLogService orderChargingStrategyLogService; + + @Override + public IPage findPageList(Page page, QueryWrapper queryWrapper) { + return getBaseMapper().findPage(page,queryWrapper); + } + + @Override + public OrderCharging addOrderChargingByBatteryCompartment(BatteryCompartment batteryCompartment, String strategyInfo) { + //电池信息 + Battery battery = batteryService.getBatteryByCompartmentId(batteryCompartment.getId()); + OrderCharging orderCharging = new OrderCharging(); + if(batteryCompartment.getOrderChargingId() != null){ + orderCharging = getById(batteryCompartment.getOrderChargingId()); + }else{ + orderCharging.setChargingNo(OrderNoUtils.orderChargingNo()); + orderCharging.setBatteryCode(battery.getBatteryCode()); + orderCharging.setBatteryCompartmentId(batteryCompartment.getId()); + orderCharging.setStartElectric(new BigDecimal(battery.getSoc()).intValue()); + orderCharging.setBeginTime(new Date()); + orderCharging.setStatus(2); + } + orderCharging.setStrategyInfo(strategyInfo); + //更新修改成功 + if(saveOrUpdate(orderCharging)){ + //记录当前的策略信息, 用于记录充电过程中使用到的策略 + orderChargingStrategyLogService.addOrderChargingStrategyLog(orderCharging.getId(), strategyInfo); + } + return orderCharging; + } + + @Override + public Boolean OrderChargingCompleted(BatteryCompartmentDTO batteryCompartmentDTO) { + OrderCharging orderCharging = getById(batteryCompartmentDTO.getOrderChargingId()); + BatteryDTO battery = batteryCompartmentDTO.getBattery(); + + orderCharging.setEndElectric(Integer.valueOf(battery.getSoc())); + orderCharging.setEndTime(new Date()); + orderCharging.setPowerConsumption(orderCharging.getEndElectric()-orderCharging.getStartElectric()); + orderCharging.setStatus(1); + saveOrUpdate(orderCharging); + return true; + } +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/service/impl/OrderChargingStrategyLogServiceImpl.java b/web-server/src/main/java/com/evotech/hd/webserver/service/impl/OrderChargingStrategyLogServiceImpl.java new file mode 100644 index 0000000..85e7258 --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/service/impl/OrderChargingStrategyLogServiceImpl.java @@ -0,0 +1,46 @@ +package com.evotech.hd.webserver.service.impl; + +import com.alibaba.fastjson2.JSONObject; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.evotech.hd.core.dtos.business.OrderChargingDTO; +import com.evotech.hd.core.entity.business.Battery; +import com.evotech.hd.core.entity.business.BatteryCompartment; +import com.evotech.hd.core.entity.business.OrderCharging; +import com.evotech.hd.core.entity.business.OrderChargingStrategyLog; +import com.evotech.hd.webserver.mapper.OrderChargingMapper; +import com.evotech.hd.webserver.mapper.OrderChargingStrategyLogMapper; +import com.evotech.hd.webserver.service.BatteryService; +import com.evotech.hd.webserver.service.OrderChargingService; +import com.evotech.hd.webserver.service.OrderChargingStrategyLogService; +import com.evotech.hd.webserver.utils.OrderNoUtils; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.util.Date; + +/** + * 类 + * + * @ClassName:OrderSwapServiceImpl + * @date: 2025年08月21日 11:04 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@Service +public class OrderChargingStrategyLogServiceImpl extends ServiceImpl implements OrderChargingStrategyLogService { + + + @Override + public OrderChargingStrategyLog addOrderChargingStrategyLog(Integer orderChargingId, String strategyInfo) { + JSONObject js = JSONObject.parseObject(strategyInfo); + OrderChargingStrategyLog orderChargingStrategyLog = new OrderChargingStrategyLog(); + orderChargingStrategyLog.setOrderChargingId(orderChargingId); + orderChargingStrategyLog.setClassName(js.getString("className")); + orderChargingStrategyLog.setStrategyInfo(js.getString("strategyInfo")); + saveOrUpdate(orderChargingStrategyLog); + return orderChargingStrategyLog; + } +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/service/impl/OrderReservationServiceImpl.java b/web-server/src/main/java/com/evotech/hd/webserver/service/impl/OrderReservationServiceImpl.java new file mode 100644 index 0000000..0f161a3 --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/service/impl/OrderReservationServiceImpl.java @@ -0,0 +1,168 @@ +package com.evotech.hd.webserver.service.impl; + +import com.alibaba.fastjson2.JSONObject; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.evotech.hd.core.dtos.Result; +import com.evotech.hd.core.dtos.ResultUtil; +import com.evotech.hd.core.dtos.business.OrderReservationDTO; +import com.evotech.hd.core.entity.business.OrderReservation; +import com.evotech.hd.core.enums.CacheNames; +import com.evotech.hd.core.mapstruct.OrderReservationWrapper; +import com.evotech.hd.utils.Collections; +import com.evotech.hd.utils.DateUtils; +import com.evotech.hd.webserver.config.redis.utils.RedisUtil; +import com.evotech.hd.webserver.job.JobConstant; +import com.evotech.hd.webserver.job.entity.QuartzJobInfo; +import com.evotech.hd.webserver.job.service.QuartzJobService; +import com.evotech.hd.webserver.mapper.OrderReservationMapper; +import com.evotech.hd.webserver.service.OrderReservationService; +import com.evotech.hd.webserver.service.OrderSwapService; +import com.evotech.hd.webserver.utils.ParamUtils; +import com.evotech.hd.webserver.utils.sendCloud.CloudSendInfoUtils; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Date; + +/** + * 类 + * + * @ClassName:OrderReservationServiceImpl + * @date: 2025年08月20日 14:42 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@Slf4j +@Service +@Transactional +public class OrderReservationServiceImpl extends ServiceImpl implements OrderReservationService { + + @Autowired + OrderReservationWrapper orderReservationWrapper; + @Autowired + OrderSwapService orderSwapService; + @Autowired + private QuartzJobService quartzJobService; + + @Override + public IPage findPageList(Page page, QueryWrapper queryWrapper) { + queryWrapper.orderByDesc("swap_day","swap_duration","reservation_time");// 根据预约日期和预约时间段倒序排序 + return getBaseMapper().findPage(page, queryWrapper); + } + + @Override + public Result save(OrderReservationDTO orderReservationDTO) throws Exception { + //新增成功, 推送云端, 等待返回结果, 记录云端数据信息 + Date reservationTime = new Date(); + String swapDay = DateUtils.parseDateToStr("yyyy-MM-dd", reservationTime); + String startHour = DateUtils.parseDateToStr("HH", reservationTime); + if(startHour.length() ==1){ + startHour = "0"+startHour; + } + String endHour = String.valueOf(Integer.valueOf(startHour)+1); + if(endHour.length() ==1){ + endHour = "0"+endHour; + } + String swapDuration = startHour+":00-"+endHour+":00"; + orderReservationDTO.setSource("3"); + orderReservationDTO.setReservationTime(reservationTime); + orderReservationDTO.setSwapDay(swapDay); + orderReservationDTO.setSwapDuration(swapDuration); + orderReservationDTO.setStatus(1); + Result result = CloudSendInfoUtils.sendOrderReservation(Collections.asMap("source",orderReservationDTO.getSource(),"stationCode",ParamUtils.findStationCode(),"stationName",ParamUtils.findStationName(),"reservationTime",orderReservationDTO.getReservationTime(),"swapDay",DateUtils.parseDateToStr("yyyyMMdd", reservationTime),"swapDuration",orderReservationDTO.getSwapDuration(),"uname",orderReservationDTO.getReservationName(),"phone",orderReservationDTO.getReservationPhone(),"plateNum",orderReservationDTO.getPlateNum())); + JSONObject cloudResult = ResultUtil.getValue(result); + if(cloudResult != null){ + //防止并发, 停止车牌机扫描, + quartzJobService.pauseJob(JobConstant.LICENSE_PLATE_MACHINE_JOB,JobConstant.GroupEnum.SWAP_GROUP.getType()); + orderReservationDTO.setCloudReservationId(cloudResult.getInteger("pkId")); + orderReservationDTO.setWechatUserId(cloudResult.getString("ucode")); + orderReservationDTO.setWechatUserName(cloudResult.getString("uname")); + //存在当前key, 清除数据 + if(RedisUtil.hasKey(CacheNames.SYSTEM_LOCK_PLATE+":"+swapDay+":"+orderReservationDTO.getPlateNum())) { + RedisUtil.del(CacheNames.SYSTEM_LOCK_PLATE + ":" + swapDay + ":" + orderReservationDTO.getPlateNum()); + } + //恢复车牌机任务 + quartzJobService.resumeJob(JobConstant.LICENSE_PLATE_MACHINE_JOB,JobConstant.GroupEnum.SWAP_GROUP.getType()); + return Result.getInstance().build(Boolean.class).success(save(orderReservationWrapper.toEntity(orderReservationDTO))); + } + return Result.getInstance().build(Boolean.class).error(result.getMsg(), false); + } + + @Override + public Result cancel(Integer id) { + return updateStatus(id,3, false); + } + + @Override + public Result use(Integer id) { + return updateStatus(id,2, false); + } + + @Override + public Result acceptReservations(JSONObject json) throws Exception { + try { + JSONObject jsonData = json.getJSONObject("data"); + OrderReservation newOrderReservation = new OrderReservation(); + newOrderReservation.setSource(jsonData.getString("source")); + newOrderReservation.setCloudReservationId(jsonData.getInteger("pkId")); + newOrderReservation.setReservationName(jsonData.getString("uname")); + newOrderReservation.setReservationPhone(jsonData.getString("phone")); + newOrderReservation.setPlateNum(jsonData.getString("plateNum")); + newOrderReservation.setWechatUserId(jsonData.getString("ucode")); + newOrderReservation.setWechatUserName(jsonData.getString("uname")); + newOrderReservation.setReservationTime(jsonData.getDate("reservationTime")); + newOrderReservation.setSwapDay(DateUtils.parseDateToStr("yyyy-MM-dd",DateUtils.dateTime("yyyyMMdd", jsonData.getString("swapDay")))); + newOrderReservation.setSwapDuration(jsonData.getString("swapDuration").trim()); + newOrderReservation.setStatus(jsonData.getInteger("status")); + if(save(newOrderReservation)){ + //防止并发, 停止车牌机扫描, + quartzJobService.pauseJob(JobConstant.LICENSE_PLATE_MACHINE_JOB,JobConstant.GroupEnum.SWAP_GROUP.getType()); + //暂停车辆到位扫秒 + quartzJobService.pauseJob(new QuartzJobInfo(JobConstant.ARRIVAL_SIGNAL_JOB, JobConstant.GroupEnum.SWAP_GROUP.getType())); + //存在进场锁, 放开重新进场 + if(RedisUtil.hasKey(CacheNames.SYSTEM_LOCK_PLATE+":"+newOrderReservation.getSwapDay()+":"+newOrderReservation.getPlateNum())) { + RedisUtil.del(CacheNames.SYSTEM_LOCK_PLATE + ":" + newOrderReservation.getSwapDay() + ":" + newOrderReservation.getPlateNum()); + } + //恢复车牌机任务 + quartzJobService.resumeJob(JobConstant.LICENSE_PLATE_MACHINE_JOB,JobConstant.GroupEnum.SWAP_GROUP.getType()); + return Result.getInstance().build(Boolean.class).success(true); + } + return Result.getInstance().build(Boolean.class).success(false); + } finally { + //恢复车辆扫码 + quartzJobService.resumeJob(new QuartzJobInfo(JobConstant.ARRIVAL_SIGNAL_JOB, JobConstant.GroupEnum.SWAP_GROUP.getType())); + } + } + + @Override + public Result updateStatus(Integer id, Integer status, Boolean notSendCloud){ + OrderReservation orderReservation = getById(id); + if(!orderReservation.getStatus().equals(1)){ + if(orderReservation.getStatus().equals(2)){ + return Result.getInstance().build(Boolean.class).error("当前订单已到店使用", false); + } + if(orderReservation.getStatus().equals(3)){ + return Result.getInstance().build(Boolean.class).error("当前订单已取消", false); + } + if(orderReservation.getStatus().equals(4)){ + return Result.getInstance().build(Boolean.class).error("当前订单已过期", false); + } + } + orderReservation.setStatus(status); + //不推送云端, 直接处理数据 + if(notSendCloud){ + return Result.getInstance().build(Boolean.class).success(updateById(orderReservation)); + } + Result result = CloudSendInfoUtils.sendOrderReservationStatus(Collections.asMap("pkId", orderReservation.getCloudReservationId(), "status",orderReservation.getStatus())); + if(ResultUtil.verifyCode(result)){ + return Result.getInstance().build(Boolean.class).success(updateById(orderReservation)); + } + return Result.getInstance().build(Boolean.class).error(result.getMsg(), false); + } + +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/service/impl/OrderSwapServiceImpl.java b/web-server/src/main/java/com/evotech/hd/webserver/service/impl/OrderSwapServiceImpl.java new file mode 100644 index 0000000..67b898d --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/service/impl/OrderSwapServiceImpl.java @@ -0,0 +1,208 @@ +package com.evotech.hd.webserver.service.impl; + +import com.alibaba.fastjson2.JSONObject; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.evotech.hd.core.dtos.Result; +import com.evotech.hd.core.dtos.ResultUtil; +import com.evotech.hd.core.dtos.business.BatterySwapStepDTO; +import com.evotech.hd.core.dtos.business.OrderSwapDTO; +import com.evotech.hd.core.entity.business.Battery; +import com.evotech.hd.core.entity.business.OrderReservation; +import com.evotech.hd.core.entity.business.OrderSwap; +import com.evotech.hd.core.enums.OrderStatusEnums; +import com.evotech.hd.core.enums.SwapBatteryStepEnum; +import com.evotech.hd.core.mapstruct.OrderSwapWrapper; +import com.evotech.hd.utils.Collections; +import com.evotech.hd.utils.CommonUtil; +import com.evotech.hd.webserver.job.service.ExecutionBatterySwapService; +import com.evotech.hd.webserver.mapper.OrderSwapMapper; +import com.evotech.hd.webserver.service.BatteryCompartmentService; +import com.evotech.hd.webserver.service.BatteryService; +import com.evotech.hd.webserver.service.BatterySwapStepService; +import com.evotech.hd.webserver.service.OrderSwapService; +import com.evotech.hd.webserver.utils.AccessStrategyUtil; +import com.evotech.hd.webserver.utils.ParamUtils; +import com.evotech.hd.webserver.utils.sendCloud.CloudSendInfoUtils; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.ObjectUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; +import java.util.List; +import java.util.stream.Collectors; + +/** + * 类 + * + * @ClassName:OrderSwapServiceImpl + * @date: 2025年08月21日 11:04 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@Slf4j +@Service +public class OrderSwapServiceImpl extends ServiceImpl implements OrderSwapService { + + @Autowired + BatterySwapStepService batterySwapStepService; + @Autowired + BatteryCompartmentService batteryCompartmentService; + @Autowired + BatteryService batteryService; + @Autowired + OrderSwapWrapper orderSwapWrapper; + @Autowired + ExecutionBatterySwapService executionBatterySwapService; + + @Override + public IPage findPageList(Page page, QueryWrapper queryWrapper) { + queryWrapper.orderByDesc("a.create_time");// 根据预约日期和预约时间段倒序排序 + return getBaseMapper().findPage(page, queryWrapper); + } + + @Override + public Result addOrderSwapByReservation(OrderReservation reservation) { + // 生成订单 + OrderSwap order = new OrderSwap(); + order.setOrderNo(CommonUtil.swapBatteryOrderNo(ParamUtils.findStationCode())); + order.setOrderPreId(reservation.getId()); + order.setPreWechatId(reservation.getWechatUserId()); + order.setOrderPreName(reservation.getReservationName()); + order.setPlateNum(reservation.getPlateNum()); + order.setOrderTime(new Date()); + order.setOrderType("1"); + //站端不会推送换电中事件, 所以根据预约单创建订单, 即为换电中 + order.setStatus(OrderStatusEnums.SWAP.getCode()); + order.setServiceTimeBegin(order.getOrderTime()); + order.setPushStatus(2); + //推送云端 + Result result = CloudSendInfoUtils.sendOrder(Collections.asMap("preId",reservation.getCloudReservationId())); + JSONObject cloudResult = ResultUtil.getValue(result); + if(cloudResult != null){ + order.setCloudOrderId(cloudResult.getInteger("pkId")); + //order.setPushStatus(1); + } + if(save(order)){ + batterySwapStepService.addOrderSwapBatteryStepBySwapComplete(order.getId(), SwapBatteryStepEnum.VEHICLES_ENTER_STATION.getCode(), new Date(),false, false, Collections.emptyMap()); + return new Result().success(true); + } + return new Result().error("订单生成异常", false); + } + + @Override + public Result runningOrder() { + OrderSwap orderSwap = getOne(new LambdaQueryWrapper().eq(OrderSwap::getStatus, OrderStatusEnums.SWAP.getCode()), false); + if(ObjectUtils.isNotEmpty(orderSwap)){ + OrderSwapDTO orderSwapDTO = orderSwapWrapper.toDTO(orderSwap); + orderSwapDTO.setBatterySwapStepDTOList(batterySwapStepService.findListByOrderId(orderSwapDTO.getId())); + orderSwapDTO.setRgvNum(AccessStrategyUtil.getValidAccessStrategyDTO().getEffectiveRgvNo().split(",").length); + return Result.getInstance().build(OrderSwapDTO.class).success(orderSwapDTO); + } + return Result.getInstance().build(OrderSwapDTO.class).success(new OrderSwapDTO(AccessStrategyUtil.getValidAccessStrategyDTO().getEffectiveRgvNo().split(",").length)); + } + + @Override + public Result sendOrderCompleted(Serializable orderId) { + OrderSwap orderSwap = getById(orderId); + Result result = CloudSendInfoUtils.sendOrderCompleted(Collections.asMap("pkId",orderSwap.getCloudOrderId(), + "rentBatNo",orderSwap.getRentBatNo(), + "rentBatCode",orderSwap.getRentBatCode(), + "rentBatSoc",orderSwap.getRentBatSoc(), + "returnBatCode",orderSwap.getReturnBatCode(), + "returnBatNo",orderSwap.getReturnBatNo(), + "returnBatSoc",orderSwap.getReturnBatSoc(), + "changeMode",orderSwap.getChangeMode(), + "changeLane",orderSwap.getChangeLane(), + "nowReturnBatCarOdo","" + )); + + JSONObject cloudResult = ResultUtil.getValue(result); + if(cloudResult != null){ + orderSwap.setPushStatus(1); + orderSwap.setStatus(cloudResult.getInteger("status")); + JSONObject orderDetail = cloudResult.getJSONObject("orderDetail"); + if(orderDetail != null){ + orderSwap.setFeeType(orderDetail.getString("feeType")); + orderSwap.setChangeLane(orderDetail.getString("changeLane")); + } + } + return Result.getInstance().build(Boolean.class).success(updateById(orderSwap)); + } + + @Override + public Result updateOrderSwapDTO(OrderSwapDTO orderSwapDTO) throws Exception { + //归还 电池包编码 or 电池包 soc 为空 电池仓位号不为空 + if((StringUtils.isEmpty(orderSwapDTO.getReturnBatCode()) || StringUtils.isEmpty(orderSwapDTO.getReturnBatSoc())) && StringUtils.isNotEmpty(orderSwapDTO.getReturnBatNo())){ + //需要bms上电 + batteryCompartmentService.charging(Integer.valueOf(orderSwapDTO.getReturnBatNo())); + Battery battery = batteryService.buildBattery(Integer.valueOf(orderSwapDTO.getReturnBatNo())); + if(battery == null){ + return Result.getInstance().build(Boolean.class).error("当前归还仓位没有电池",false); + } + orderSwapDTO.setReturnBatCode(battery.getBatteryCode()); + orderSwapDTO.setReturnBatSoc(battery.getSoc()); + } + //租借 电池包编码 or 电池包 soc 为空 电池仓位号不为空 + if((StringUtils.isEmpty(orderSwapDTO.getRentBatCode()) || StringUtils.isEmpty(orderSwapDTO.getRentBatSoc())) && StringUtils.isNotEmpty(orderSwapDTO.getRentBatNo())){ + Battery battery = batteryService.getBatteryByCompartmentId(Integer.valueOf(orderSwapDTO.getRentBatNo())); + orderSwapDTO.setRentBatCode(battery.getBatteryCode()); + orderSwapDTO.setRentBatSoc(battery.getSoc()); + orderSwapDTO.setElectricityQuantity(new BigDecimal(battery.getSoc()).subtract(new BigDecimal(orderSwapDTO.getReturnBatSoc())).toString()); + } + orderSwapDTO.setStatus(OrderStatusEnums.SWAPOVER.getCode()); + orderSwapDTO.setChangeMode("3"); + //修改成功, + if(updateById(orderSwapWrapper.toEntity(orderSwapDTO))){ + //修改成功, 处理换点步骤问题 + List swapList = batterySwapStepService.findListByOrderId(orderSwapDTO.getId()).stream().map(BatterySwapStepDTO::getStep).collect(Collectors.toList()); + //处理换点步骤数据 + for (SwapBatteryStepEnum swapBatteryStep : SwapBatteryStepEnum.values()){ + if(!swapList.contains(swapBatteryStep.getCode())){ + //更新电池步骤 + batterySwapStepService.addOrderSwapBatteryStepBySwapComplete(orderSwapDTO.getId(), swapBatteryStep.getCode(), new Date(), false, true, Collections.asMap("errorMsg","换点异常, 手动操作")); + swapList.add(swapBatteryStep.getCode()); + } + } + + // 执行换电完成操作, 更新云端信息, 释放对中机构, 打开出口闸机 + executionBatterySwapService.completed(orderSwapDTO, null); + } + + //抓取电池数据更新到订单 + //更新成功了, 推送云端 +// if(updateById(orderSwapWrapper.toEntity(orderSwapDTO))){ +// try { +// for (SwapBatteryStepEnum swapBatteryStep : SwapBatteryStepEnum.values()){ +// if(!swapList.contains(swapBatteryStep.getCode())){ +// batterySwapStepService.addOrderSwapBatteryStepBySwapComplete(orderSwapDTO.getId(), swapBatteryStep.getCode(), new Date(), true, true, Collections.asMap("errorMsg", "手动操作换电完成")); +// swapList.add(swapBatteryStep.getCode()); +// } +// } +// } catch (Exception e) { +// e.printStackTrace(); +// log.error("手动换电完成-生成换电步骤异常", e); +// } +// sendCloud(orderSwapDTO.getId()); +// } + return Result.getInstance().build(Boolean.class).success(true); + } + +// public static void sendCloud(final Integer id){ +// CompletableFuture.runAsync(() -> { +// try { +// SpringUtil.getBean(OrderSwapService.class).sendOrderCompleted(String.valueOf(id)); +// } catch (Exception e) { +// e.printStackTrace(); +// } +// log.info("当前推送云端的订单id为:" + id); +// }); +// } +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/service/impl/ParamsServiceImpl.java b/web-server/src/main/java/com/evotech/hd/webserver/service/impl/ParamsServiceImpl.java new file mode 100644 index 0000000..1119bc1 --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/service/impl/ParamsServiceImpl.java @@ -0,0 +1,86 @@ +package com.evotech.hd.webserver.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.evotech.hd.core.dtos.system.ParamsDTO; +import com.evotech.hd.core.entity.system.Params; +import com.evotech.hd.core.mapstruct.ParamsWrapper; +import com.evotech.hd.core.enums.CacheNames; +import com.evotech.hd.webserver.mapper.ParamsMapper; +import com.evotech.hd.webserver.service.ParamsService; +import org.apache.commons.lang3.ObjectUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +/** + * 系统参数接口 + * + * @ClassName:ParamsService + * @date: 2025年08月18日 16:08 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@Service +@Transactional +public class ParamsServiceImpl extends ServiceImpl implements ParamsService { + + @Autowired + ParamsWrapper paramsWrapper; + + @Override + public IPage findPageList(Page page, QueryWrapper queryWrapper) { + return getBaseMapper().findPage(page, queryWrapper); + } + + @Override + public List findList(QueryWrapper queryWrapper) { + return getBaseMapper().findList(queryWrapper); + } + //新增修改删除,使用 @CacheEvict(value = CacheNames.SYS_CACHE_DICT_MAP, allEntries = true) + + @Override + @Cacheable(cacheNames = CacheNames.SYSTEM_RUNNING_PARAMS, key = "#code") +// @CacheEvict(value = CacheNames.SYSTEM_RUNNING_PARAMS, key = "#code", allEntries = true) + public ParamsDTO getParamsDTO(String name, String code, String defVal, String des) { + Params param = getOne(new LambdaQueryWrapper().eq(Params::getParamCode, code), false); + if(ObjectUtils.isEmpty(param)){ + param = new Params(); + param.setName(name); + param.setParamCode(code); + param.setParamValue(defVal); + param.setDescription(des); + save(param); + } + return paramsWrapper.toDTO(param); + } + + @Override + @Cacheable(cacheNames = CacheNames.SYSTEM_RUNNING_PARAMS, key = "#code") +// @CacheEvict(value = CacheNames.SYSTEM_RUNNING_PARAMS, key = "#code", allEntries = true) + public ParamsDTO getParamsDTOByCode(String code) { + Params param = getOne(new LambdaQueryWrapper().eq(Params::getParamCode, code), false); + return paramsWrapper.toDTO(param); + } + @Override + public Params checkParamsByCode(String code) { + return getOne(new LambdaQueryWrapper().eq(Params::getParamCode, code), false); + } + + @Override +// @Cacheable(cacheNames = CacheNames.SYSTEM_RUNNING_PARAMS, key = "#paramsDTO.paramCode") + @CacheEvict(value = CacheNames.SYSTEM_RUNNING_PARAMS, allEntries = true) + public ParamsDTO saveParamsDTO(ParamsDTO paramsDTO) { + saveOrUpdate(paramsWrapper.toEntity(paramsDTO)); + return paramsDTO; + } + + +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/service/impl/RunningInstructionsDetailServiceImpl.java b/web-server/src/main/java/com/evotech/hd/webserver/service/impl/RunningInstructionsDetailServiceImpl.java new file mode 100644 index 0000000..cc6fe75 --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/service/impl/RunningInstructionsDetailServiceImpl.java @@ -0,0 +1,52 @@ +package com.evotech.hd.webserver.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.evotech.hd.core.dtos.business.RunningInstructionsDetailDTO; +import com.evotech.hd.core.entity.business.RunningInstructionsDetail; +import com.evotech.hd.core.enums.CacheNames; +import com.evotech.hd.core.mapstruct.RunningInstructionsDetailWrapper; +import com.evotech.hd.webserver.mapper.RunningInstructionsDetailMapper; +import com.evotech.hd.webserver.service.RunningInstructionsDetailService; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.util.List; + +/** + * 类 + * + * @ClassName:DictDetailServiceImpl + * @date: 2025年08月26日 16:50 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@Service +public class RunningInstructionsDetailServiceImpl extends ServiceImpl implements RunningInstructionsDetailService { + + @Resource + RunningInstructionsDetailWrapper runningInstructionsDetailWrapper; + + @Override + public IPage findPageList(Page page, QueryWrapper queryWrapper) { + queryWrapper.orderByAsc("a.sort"); + return getBaseMapper().findPage(page, queryWrapper); + } + + @Override + @Cacheable(cacheNames = CacheNames.SYSTEM_RUNNING_DICT_DETAIL, key = "#typeCode") + public List findDictDetailDTOByTypeCode(String typeCode) { + return getBaseMapper().findDictDetailDTOByTypeCode(typeCode); + } + + @Override +// @Cacheable(cacheNames = CacheNames.SYSTEM_RUNNING_DICT_DETAIL, key = "#runningInstructionsDTO.typeCode") +// @Cacheable(cacheNames = CacheNames.SYSTEM_RUNNING_DICT_DETAIL, key = "#runningInstructionsDTO.typeCode") + @Cacheable(cacheNames = CacheNames.SYSTEM_RUNNING_DICT_DETAIL, key = "#runningInstructionsDTO.typeCode") + public Boolean saveDTO(RunningInstructionsDetailDTO runningInstructionsDTO) { + return saveOrUpdate(runningInstructionsDetailWrapper.toEntity(runningInstructionsDTO)); + } +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/service/impl/RunningInstructionsServiceImpl.java b/web-server/src/main/java/com/evotech/hd/webserver/service/impl/RunningInstructionsServiceImpl.java new file mode 100644 index 0000000..1d7b6ae --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/service/impl/RunningInstructionsServiceImpl.java @@ -0,0 +1,43 @@ +package com.evotech.hd.webserver.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.evotech.hd.core.dtos.business.RunningInstructionsDTO; +import com.evotech.hd.core.entity.business.RunningInstructions; +import com.evotech.hd.core.mapstruct.RunningInstructionsWrapper; +import com.evotech.hd.webserver.mapper.RunningInstructionsMapper; +import com.evotech.hd.webserver.service.RunningInstructionsService; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.util.List; +import java.util.stream.Collectors; + +/** + * 类 + * + * @ClassName:DictDetailServiceImpl + * @date: 2025年08月26日 16:50 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@Service +public class RunningInstructionsServiceImpl extends ServiceImpl implements RunningInstructionsService { + + @Resource + RunningInstructionsWrapper runningInstructionsWrapper; + @Override + public List findList() { + return list().stream().map(runningInstructionsWrapper::toDTO).collect(Collectors.toList()); + } + + @Override + public RunningInstructions getOneByCode(String code) { + return getOne(new LambdaQueryWrapper().eq(RunningInstructions::getCode, code), false); + } + + @Override + public Boolean saveDTO(RunningInstructionsDTO runningInstructionsDTO) { + return saveOrUpdate(runningInstructionsWrapper.toEntity(runningInstructionsDTO)); + } +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/service/impl/UserServiceImpl.java b/web-server/src/main/java/com/evotech/hd/webserver/service/impl/UserServiceImpl.java new file mode 100644 index 0000000..23b8b08 --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/service/impl/UserServiceImpl.java @@ -0,0 +1,44 @@ +package com.evotech.hd.webserver.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.evotech.hd.core.dtos.UserDTO; +import com.evotech.hd.core.entity.system.User; +import com.evotech.hd.core.enums.CacheNames; +import com.evotech.hd.webserver.mapper.UserMapper; +import com.evotech.hd.webserver.service.UserService; +import org.springframework.beans.BeanUtils; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.stereotype.Service; + +/** + * 类 + * + * @ClassName:UserServiceImpl + * @date: 2025年08月12日 11:43 + * @author: andy.shi + * @contact: 17330188597 + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@Service +public class UserServiceImpl extends ServiceImpl implements UserService { + + + @Override + @Cacheable(cacheNames = CacheNames.USER_CACHE_LOGIN_NAME, key = "#name") + public UserDTO getUserByLoginName(String name) { + User user = null; + try { + user = getBaseMapper().selectOne(new LambdaQueryWrapper() + .or(i -> i.and(j -> j.eq(User::getName, name)) + .or(j -> j.eq(User::getLoginName, name)))); + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException(e); + } + UserDTO dto = new UserDTO(); + BeanUtils.copyProperties(user,dto); + return dto; + + } +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/utils/AccessStrategyUtil.java b/web-server/src/main/java/com/evotech/hd/webserver/utils/AccessStrategyUtil.java new file mode 100644 index 0000000..397bc43 --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/utils/AccessStrategyUtil.java @@ -0,0 +1,47 @@ +package com.evotech.hd.webserver.utils; + +import com.evotech.hd.core.dtos.business.AccessStrategyDTO; +import com.evotech.hd.utils.Collections; +import com.evotech.hd.webserver.controller.TestController; +import com.evotech.hd.webserver.service.AccessStrategyService; +import com.evotech.hd.webserver.utils.sendCloud.CloudSendInfoUtils; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; + +/** + * AccessStrategyUtil + * + * @author andy.shi + * @ClassName:AccessStrategyUtil + * @date: 2025年10月14日 15:18 + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@Service +@Slf4j +public class AccessStrategyUtil { + + private static AccessStrategyService accessStrategyService; + + @Autowired + public AccessStrategyUtil(AccessStrategyService accessStrategyService) { + this.accessStrategyService = accessStrategyService; + } + + public static AccessStrategyDTO getValidAccessStrategyDTO(){ + return accessStrategyService.getValidAccessStrategyDTO(); + } + + public static List getEffectiveRgvNoList(){ + AccessStrategyDTO accessStrategyDTO = getValidAccessStrategyDTO(); + if(StringUtils.isNotEmpty(accessStrategyDTO.getEffectiveRgvNo())){ + return Collections.asList(accessStrategyDTO.getEffectiveRgvNo().split(",")); + } + return Collections.emptyList(); + } + +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/utils/OrderNoUtils.java b/web-server/src/main/java/com/evotech/hd/webserver/utils/OrderNoUtils.java new file mode 100644 index 0000000..0cc7a92 --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/utils/OrderNoUtils.java @@ -0,0 +1,39 @@ +package com.evotech.hd.webserver.utils; + +import cn.hutool.core.util.RandomUtil; +import com.evotech.hd.utils.DateUtils; + +import java.util.Date; + +/** + * OrderNoUtils + * + * @author andy.shi + * @ClassName:OrderNoUtils + * @date: 2025年10月07日 9:29 + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ + +public class OrderNoUtils { + + /*** + * 获取充电订单单号 + * @return + */ + public static String orderChargingNo(){ + StringBuffer chargingNo = new StringBuffer(ParamUtils.findStationCode()).append("CD").append("-"); + if(!checkLength(chargingNo.toString())){ + return chargingNo.toString(); + } + chargingNo.append(DateUtils.parseDateToStr("yyyyMMddHHmmss", new Date())).append("-"); + if(!checkLength(chargingNo.toString())){ + return chargingNo.substring(0,31).toString(); + } + chargingNo.append(RandomUtil.randomStringUpper(32-chargingNo.length())); + return chargingNo.toString(); + } + + private static Boolean checkLength(String orderNo){ + return orderNo.length()<32; + } +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/utils/ParamUtils.java b/web-server/src/main/java/com/evotech/hd/webserver/utils/ParamUtils.java new file mode 100644 index 0000000..7689aab --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/utils/ParamUtils.java @@ -0,0 +1,387 @@ +package com.evotech.hd.webserver.utils; + +import com.alibaba.fastjson2.JSONObject; +import com.evotech.hd.core.dtos.system.ParamsDTO; +import com.evotech.hd.webserver.service.ParamsService; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * 全局参数获取配置类 + * + * @ClassName:ParamUtils + * @date: 2025年05月23日 16:50 + * @author: andy.shi + * @contact: 17330188597 + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@Service +@Slf4j +public class ParamUtils { + + private static ParamsService paramService; + + @Autowired + public ParamUtils(ParamsService paramService) { + this.paramService = paramService; + } + + + /*** + * 系统参数code的常量类 + */ + public class ParamsConstant{ + /*** + * 站端运行状态code + */ + public static final String STATION_RUNNING_STATUS = "station_running_status"; + /*** + * 站端名称 + */ + static final String STATION_NAME = "station_name"; + /*** + * 站端code + */ + public static final String STATION_CODE = "station_code"; + /*** + * 站端私钥 + */ + static final String STATION_SECRET_KEY = "station_secret_key"; + /*** + * 站端公钥 + */ + static final String STATION_PUBLIC_KEY = "station_public_key"; + + /*** + * 站端AES私钥 + */ + static final String STATION_AES_SECRET_KEY = "station_aes_secret_key"; + /*** + * 站端AES公钥 + */ + static final String STATION_AES_PUBLIC_KEY = "station_aes_public_key"; + /*** + * 最大重试次数 + */ + static final String MAX_RETRY_NUM = "max_retry_num"; + + /*** + * 云平台请求地址 + */ + static final String CLOUD_REQUEST_URL = "cloud_request_url"; + /*** + * 设备指令下发地址 + */ + static final String INSTRUCTION_SEND_URL = "instruction_send_url"; + + /*** + * 指令间隔时间 + */ + static final String INSTRUCTION_INTERVAL_REQUEST_TIME = "instruction_interval_request_time"; + + /*** + * 车辆离站等待时间 + */ + static final String VEHICLE_ABDICATION_WAITING_TIME = "vehicle_abdication_waiting_time"; + + /*** + * 电池仓位置 + */ + static final String BATTERY_COMPARTMENT_POSITION = "battery_compartment_position"; + /*** + * RGV H轴互斥限位 + */ + static final String RGV_H_MUTUAL_EXCLUSION = "rgv_h_mutual_exclusion"; + /*** + * 门的参数信息 + */ + static final String DOOR = "sys_door"; + + } + + /*** + * 最大重试次数 + * @return + */ + public static Integer findMaxRetryNum(){ + ParamsDTO param= paramService.getParamsDTO("最大重试次数", ParamsConstant.MAX_RETRY_NUM,"3","最大重试次数"); + //检查是否为假期 + return Integer.valueOf(param.getParamValue()); + } + + /*** + * 查询站点状态是否运营 + * @return + */ + public static Boolean findStationRunningStatus(){ + ParamsDTO param= paramService.getParamsDTO("站点的运行状态", ParamsConstant.STATION_RUNNING_STATUS,"false","站点的运行状态, 初次新增,默认为未运营状态; 参数说明: true-运行, false-未运行"); + //检查是否为假期 + return Boolean.valueOf(param.getParamValue()); + } + /*** + * 查询站点编码 + * @return + */ + public static String findStationName(){ + ParamsDTO param= paramService.getParamsDTO("站点的名称", ParamsConstant.STATION_NAME,"2号换电站","软件初始安装时, 直接初始化"); + return param.getParamValue(); + } + /*** + * 查询站点编码 + * @return + */ + public static String findStationCode(){ + ParamsDTO param= paramService.getParamsDTO("站点的code编码", ParamsConstant.STATION_CODE,"123456","软件初始安装时, 直接初始化"); + return param.getParamValue(); + } + /*** + * 站点私钥 + * @return + */ + public static String findStationSecretKey(){ + ParamsDTO param= paramService.getParamsDTO("站点私钥", ParamsConstant.STATION_SECRET_KEY,"MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBAKXEG3/V8DUxpOzb8lJLkJaZGAHSfS+oHoPzZy+Xbr7mb8Jfc7tXRd+H1mmQOXz36HGdPiMP7nK1kPr+ddEJe+yofCkpr5y/Mavki9cLQ3Z/BHUH/t0E7mjyXKhlyn7pfudlW7rqNfQx3eboir+9ppgFe0knW6xHffFECIdfmrt9AgMBAAECgYARw9AMpbFeCgl6RuIT1rxSn2qWu2emJVbL3liHHawFMm30v0UIZUR8PbMJUicPEQRstKTVnh34ViQI+h9HPUR1/rmdI/jdM2n44ai+4AvT1iONgLS4JBBpc5a+ctV9uAgfSU65cRVAzKTzq3Etcqwgzrn8sWultKAyb3pHmjeYswJBANFHjh5LfGSSEpAOdfcNopNAGSaEhNKpm2/0f1VwEyM3/utpTMfHVo/M2OtdpU7JH0+BdtJfAEXtMOMAfzka6EcCQQDKxbuEeYit5zqTbsMLG9y016KjH6oySPnCL4RnA1eeCwK9j0LuYC+rhBg8Km2iLsDwoUDZUjaX/oipB1Ydc+QbAkEAjCG4tOpgucrhqRo1vR6hLK4v6Q21DoZJMXbqyF/KQLve8uzIX8FHYfSNj1ReO1oKoCcVVBoOycPgBzAvACLXQwJBAMCbyl8vwnDN74oT8BkhQihVnBsu/M//GZ8m27GuLw/kjZnZ903O9/kH5K1h7/naR1NLGPpVaZ4/HTjRyy724nkCQQDLEogix/P5ZwI6RmKKAkyyZkMPJIn9kA1j/zeFACdVBzar87GB4DNHiuwByzL4V/CLnQOEocpoq5PciXy2Nbi6","软件初始安装时, 直接初始化"); + return param.getParamValue(); + } + /*** + * 站点公钥 + * @return + */ + public static String findStationPublicKey(){ + ParamsDTO param= paramService.getParamsDTO("站点公钥", ParamsConstant.STATION_PUBLIC_KEY,"MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQClxBt/1fA1MaTs2/JSS5CWmRgB0n0vqB6D82cvl26+5m/CX3O7V0Xfh9ZpkDl89+hxnT4jD+5ytZD6/nXRCXvsqHwpKa+cvzGr5IvXC0N2fwR1B/7dBO5o8lyoZcp+6X7nZVu66jX0Md3m6Iq/vaaYBXtJJ1usR33xRAiHX5q7fQIDAQAB","软件初始安装时, 直接初始化"); + return param.getParamValue(); + } + + /*** + * 站点对称AES私钥 + * @return + */ + public static String findStationAesSecretKey(){ + ParamsDTO param= paramService.getParamsDTO("站点私钥", ParamsConstant.STATION_AES_SECRET_KEY,"BkGGxHUdYkaJssNH","软件初始安装时, 直接初始化"); + return param.getParamValue(); + } + /*** + * 站点对称AES公钥 + * @return + */ + public static String findStationAesPublicKey(){ + ParamsDTO param= paramService.getParamsDTO("站点公钥", ParamsConstant.STATION_AES_PUBLIC_KEY,"qBOmt5Z6uZ7s4NdYzt+S5w==","软件初始安装时, 直接初始化"); + return param.getParamValue(); + } + + /*** + * 云平台接口地址 + * @return + */ + public static String getCloudRequestUrl(){ + ParamsDTO param= paramService.getParamsDTO("云平台接口地址", ParamsConstant.CLOUD_REQUEST_URL,"https://api.evo-techina.com/cloud/open/station/message","软件初始安装时, 直接初始化"); + return param.getParamValue(); + } + + /*** + * 设备指令下发接口地址 + * @return + */ + public static String getInstructionRequestUrl(){ + // ParamsDTO param= paramService.getParamsDTO("云平台接口地址", ParamsConstant.CLOUD_REQUEST_URL,"https://api.evo-techina.com/cloud/open/station","软件初始安装时, 直接初始化"); + ParamsDTO param= paramService.getParamsDTO("物理设备指令下发接口", ParamsConstant.INSTRUCTION_SEND_URL,"http://localhost:8088/RPC2","软件初始安装时, 直接初始化"); + return param.getParamValue(); + } + + /*** + * 指令结果间隔时间(毫秒) + * @return + */ + public static Long getInstructionIntervalRequestMilliseconds(){ + ParamsDTO param= paramService.getParamsDTO("延迟等待时间", ParamsConstant.INSTRUCTION_INTERVAL_REQUEST_TIME,"500","指令下发后, 间隔多久开始执行查询命令"); + return Long.valueOf(param.getParamValue()); + } + + /*** + * 车辆离位等待时间(毫秒) + * @return + */ + public static Long getVehicleAbdicationWaitingMilliseconds(){ + ParamsDTO param= paramService.getParamsDTO("车辆离位等待时间", ParamsConstant.VEHICLE_ABDICATION_WAITING_TIME,"300000","车辆离位等待时间(毫秒), 默认5分钟"); + return Long.valueOf(param.getParamValue()); + } + + /*** + * 电池仓所在机构位置 + * @return + */ + public static Map> getBatteryCompartmentPosition(){ + ParamsDTO param = paramService.getParamsDTO("电池仓所在设备", ParamsConstant.BATTERY_COMPARTMENT_POSITION,"{\"1\":\"1,2,3,4,5,6,7,8\",\"2\":\"9,10,11,12,13,14,15,16\"}","电池仓所在设备,各站控可自行调整"); + String val = param.getParamValue(); + if(StringUtils.isEmpty(val)) return null; + Map result = JSONObject.parseObject(val, Map.class); + List rgvList = AccessStrategyUtil.getEffectiveRgvNoList(); + return result.entrySet().stream().filter(d->rgvList.contains(d.getKey())).collect(Collectors.toMap(d->d.getKey(), d-> Arrays.stream(d.getValue().split(",")).map(Integer::valueOf).collect(Collectors.toList()))); + } + + /*** + * 电池仓所在机构位置 + * @return + */ + public static String getDoorParams(String door){ + ParamsDTO param = paramService.getParamsDTO("换电站门的信息", ParamsConstant.DOOR,"{\"入口\":\"1\",\"出口\":\"2\"}","电池仓所在设备,各站控可自行调整"); + String val = param.getParamValue(); + if(StringUtils.isEmpty(val)) return null; + Map doorMaps = JSONObject.parseObject(val, Map.class); + return doorMaps.get(door); + } + + /*** + * RGV互斥限位 + * @return + */ + public static Map getRgvHMutualExclusion(){ + ParamsDTO param = paramService.getParamsDTO("RGV机构H轴互斥限位", ParamsConstant.RGV_H_MUTUAL_EXCLUSION,"{\"1\":\"3\",\"2\":\"3\"}","RGV机构H轴互斥限位,各站控可自行调整"); + String val = param.getParamValue(); + if(StringUtils.isEmpty(val)) return null; + return JSONObject.parseObject(val, Map.class); + } + + + /*** + * 扫描车牌机链接状态 + * @return + */ + public static ParamsDTO getPlateDevConnect(){ + return getParamsDTO("Plate_DevConnect"); + } + /*** + * 扫描车牌号 + * @return + */ + public static ParamsDTO getLicensePlateMachine(){ + return getParamsDTO("Plate_Data"); + } + /*** + * 车辆到位信息 + * @return + */ + public static ParamsDTO getArrivalSignal(){ + return getParamsDTO("CEN_RB_Car_Inplace"); + } + + /*** + * 当前是否正在执行 + * @return + */ + public static ParamsDTO getBeatActMoving(){ + return getParamsDTO("RGV_RB_BeatActMoving"); + } + + /*** + * 检查code的错误编码 + * @return + */ + public static ParamsDTO getErrorCode(){ + return getParamsDTO("RGV_RB_ErrorCode"); + } + + + /*** + * 当前是对中机构否正在执行 + * @return + */ + public static ParamsDTO getCenActMoving(){ + return getParamsDTO("CEN_RB_BeatActMoving"); + } + /*** + * 检查对中机构code的错误编码 + * @return + */ + public static ParamsDTO getCenErrorCode(){ + return getParamsDTO("CEN_RB_ErrorCode"); + } + + /*** + * 当前是否正在执行 + * @return + */ + public static ParamsDTO getRgvHShaftLocation(){ + return getParamsDTO("RGV_RB_H_CurPos"); + } + + /*** + * 获取仓位信息 + * @return + */ + public static ParamsDTO getDeviceInfo(){ + return getParamsDTO("BSL_DeviceInfo"); + } + /*** + * 获取仓位充电模块信息 + * @return + */ + public static ParamsDTO getPowModInfo(){ + return getParamsDTO("BSL_PowModInfo"); + } + /*** + * 获取仓位直流电表信息 + * @return + */ + public static ParamsDTO getDCMInfo(){ + return getParamsDTO("BSL_DCMInfo"); + } + /*** + * 获取仓位温度探针信息 + * @return + */ + public static ParamsDTO getBatProbeTemp(){ + return getParamsDTO("BSL_BatProbeTemp"); + } + + /*** + * 获取电池状态信息 + * @return + */ + public static ParamsDTO getBatStatusInfo(){ + return getParamsDTO("BSL_BatStatusInfo"); + } + /*** + * 获取电池数据信息1 + * @return + */ + public static ParamsDTO getBatDataInfo1(){ + return getParamsDTO("BSL_BatDataInfo1"); + } + /*** + * 获取电池数据信息2 + * @return + */ + public static ParamsDTO getBatDataInfo2(){ + return getParamsDTO("BSL_BatDataInfo2"); + } + /*** + * 获取电池单体电压 + * @return + */ + public static ParamsDTO getBatCellVol(){ + return getParamsDTO("BSL_BatCellVol"); + } + /*** + * 获取电池数据信息2 + * @return + */ + public static ParamsDTO getBSLBatChgInfo(){ + return getParamsDTO("BSL_BatChgInfo"); + } + + /*** + * 获取参数信息 + * @return + */ + public static ParamsDTO getParamsDTO(String code){ + return paramService.getParamsDTOByCode(code); + } + +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/utils/ReflectionUtils.java b/web-server/src/main/java/com/evotech/hd/webserver/utils/ReflectionUtils.java new file mode 100644 index 0000000..716f59e --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/utils/ReflectionUtils.java @@ -0,0 +1,155 @@ +package com.evotech.hd.webserver.utils; + +/** + * 类 + * + * @ClassName:ReflectionUtils + * @date: 2025年09月23日 9:16 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; + +public class ReflectionUtils { + + /** + * 判断属性是否可读(存在公共的getter方法) + * @param obj 目标对象 + * @param fieldName 属性名称 + * @return 是否可读 + */ + public static boolean isFieldReadable(Object obj, String fieldName) { + if (obj == null || fieldName == null || fieldName.isEmpty()) { + return false; + } + + Class clazz = obj.getClass(); + Method getterMethod = getGetterMethod(clazz, fieldName); + return getterMethod != null && Modifier.isPublic(getterMethod.getModifiers()); + } + + /** + * 判断属性是否有值(不为null,对于基本类型则始终为true) + * @param obj 目标对象 + * @param fieldName 属性名称 + * @return 是否有值 + * @throws Exception 反射操作异常 + */ + public static boolean hasFieldValue(Object obj, String fieldName) throws Exception { + Object value = getFieldValue(obj, fieldName); + if (value == null) { + return false; + } + + // 对于基本数据类型,即使是默认值也视为有值 + Class fieldType = getFieldType(obj, fieldName); + return fieldType.isPrimitive() || value != null; + } + + /** + * 获取属性值(优先通过getter方法,没有则直接访问字段) + * @param obj 目标对象 + * @param fieldName 属性名称 + * @return 属性值 + * @throws Exception 反射操作异常 + */ + public static Object getFieldValue(Object obj, String fieldName) throws Exception { + if (obj == null || fieldName == null || fieldName.isEmpty()) { + return null; + } + + Class clazz = obj.getClass(); + + // 尝试通过getter方法获取值 + Method getterMethod = getGetterMethod(clazz, fieldName); + if (getterMethod != null) { + getterMethod.setAccessible(true); + return getterMethod.invoke(obj); + } + + // 如果没有getter方法,直接访问字段 + Field field = getField(clazz, fieldName); + if (field != null) { + field.setAccessible(true); + return field.get(obj); + } + + throw new NoSuchFieldException("Field " + fieldName + " not found in class " + clazz.getName()); + } + + /** + * 获取字段类型 + * @param obj 目标对象 + * @param fieldName 属性名称 + * @return 字段类型 + * @throws NoSuchFieldException 字段不存在异常 + */ + public static Class getFieldType(Object obj, String fieldName) throws NoSuchFieldException { + if (obj == null || fieldName == null || fieldName.isEmpty()) { + return null; + } + + Field field = getField(obj.getClass(), fieldName); + return field.getType(); + } + + /** + * 获取字段对象(包括父类字段) + * @param clazz 类对象 + * @param fieldName 字段名称 + * @return 字段对象 + * @throws NoSuchFieldException 字段不存在异常 + */ + private static Field getField(Class clazz, String fieldName) throws NoSuchFieldException { + try { + return clazz.getDeclaredField(fieldName); + } catch (NoSuchFieldException e) { + // 如果当前类没有,检查父类 + if (clazz.getSuperclass() != null) { + return getField(clazz.getSuperclass(), fieldName); + } + throw e; + } + } + + /** + * 获取getter方法(考虑boolean类型的isXXX情况) + * @param clazz 类对象 + * @param fieldName 字段名称 + * @return getter方法 + */ + private static Method getGetterMethod(Class clazz, String fieldName) { + // 处理布尔类型的getter方法(isXXX) + if (fieldName.length() > 0) { + String booleanGetter = "is" + + fieldName.substring(0, 1).toUpperCase() + + fieldName.substring(1); + try { + Method method = clazz.getMethod(booleanGetter); + // 检查返回类型是否为boolean + if (method.getReturnType() == boolean.class || method.getReturnType() == Boolean.class) { + return method; + } + } catch (NoSuchMethodException e) { + // 没有isXXX方法,继续尝试getXXX + } + } + + // 标准的getter方法(getXXX) + String standardGetter = "get" + + fieldName.substring(0, 1).toUpperCase() + + fieldName.substring(1); + try { + return clazz.getMethod(standardGetter); + } catch (NoSuchMethodException e) { + // 检查父类 + if (clazz.getSuperclass() != null) { + return getGetterMethod(clazz.getSuperclass(), fieldName); + } + return null; + } + } +} \ No newline at end of file diff --git a/web-server/src/main/java/com/evotech/hd/webserver/utils/ResponseUtil.java b/web-server/src/main/java/com/evotech/hd/webserver/utils/ResponseUtil.java new file mode 100644 index 0000000..0e2bcd9 --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/utils/ResponseUtil.java @@ -0,0 +1,67 @@ +/** + * Copyright © 2021-2026 space All rights reserved. + */ +package com.evotech.hd.webserver.utils; + +import org.springframework.http.ResponseEntity; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.List; + + +/** + * 返回包装wrapper + * + * @author + */ +public class ResponseUtil extends HashMap implements Serializable { + + public static ResponseUtil getInstance(){ + return new ResponseUtil(); + } + public ResponseUtil build(Class cls) { + return (ResponseUtil) this; + } + + public ResponseUtil> buildList(Class cls) { + return (ResponseUtil>) this; + } + + + public ResponseUtil add(String key, Object value) { + super.put(key, value); + return this; + } + + public ResponseEntity ok() { + return ResponseEntity.ok(this); + } + + public ResponseEntity error() { + return ResponseEntity.badRequest().body(this); + } + + public ResponseEntity ok(String msg) { + this.put("msg", msg); + return ResponseEntity.ok(this); + } + + public ResponseEntity ok(T data) { + this.put("data", data); + return ResponseEntity.ok(this); + } + + public ResponseEntity ok(T data, String msg) { + this.put("data", data); + this.put("msg", msg); + return ResponseEntity.ok(this); + } + + public ResponseEntity error(String msg) { + this.put("msg", msg); + return ResponseEntity.badRequest().body(this); + } + + +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/utils/instruction/InstructionReadUtils.java b/web-server/src/main/java/com/evotech/hd/webserver/utils/instruction/InstructionReadUtils.java new file mode 100644 index 0000000..4750cd3 --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/utils/instruction/InstructionReadUtils.java @@ -0,0 +1,463 @@ +package com.evotech.hd.webserver.utils.instruction; + +import com.alibaba.fastjson2.JSONObject; +import com.evotech.hd.core.dtos.system.ParamsDTO; +import com.evotech.hd.exception.InstructionException; +import com.evotech.hd.utils.DataUtils; +import com.evotech.hd.webserver.cnstant.ParamSysConstants; +import com.evotech.hd.webserver.utils.ParamUtils; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import java.math.BigDecimal; + +/** + * 类 + * + * @ClassName:InstructionUtils + * @date: 2025年08月26日 16:28 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@Slf4j +@Component +public class InstructionReadUtils { + + /** 读取电池仓信息 + * @param batteryCompartmentNo + * @return + */ + public static JSONObject queryDeviceInfo(Integer batteryCompartmentNo) throws InstructionException { + ParamsDTO paramsDTO = ParamUtils.getDeviceInfo() ; + return InstructionUtils.getJson(execution(paramsDTO, String.valueOf(batteryCompartmentNo))); + } + /** 读取电池仓充电模块信息 + * @param batteryCompartmentNo + * @return + */ + public static JSONObject queryPowModInfo( Integer batteryCompartmentNo) throws InstructionException { + ParamsDTO paramsDTO = ParamUtils.getPowModInfo(); + return InstructionUtils.getJson(execution(paramsDTO, String.valueOf(batteryCompartmentNo))); + } + /** 读取电池仓温度探针信息 + * @param batteryCompartmentNo + * @return + */ + public static JSONObject queryBatProbeTemp(Integer batteryCompartmentNo) throws InstructionException { + ParamsDTO paramsDTO = ParamUtils.getBatProbeTemp(); + return InstructionUtils.getJson(execution(paramsDTO, String.valueOf(batteryCompartmentNo))); + } + + /** 读取直流电表信息 + * @param batteryCompartmentNo + * @return + */ + public static JSONObject queryDCMInfo(Integer batteryCompartmentNo) throws InstructionException { + ParamsDTO paramsDTO = ParamUtils.getDCMInfo(); + return InstructionUtils.getJson(execution(paramsDTO, String.valueOf(batteryCompartmentNo))); + } + /** 读取电池信息 + * @param batteryCompartmentNo + * @return + */ + public static JSONObject queryBattery(Integer batteryCompartmentNo) throws InstructionException { + ParamsDTO paramsDTO = ParamUtils.getBatDataInfo1(); + return InstructionUtils.getJson(execution(paramsDTO, String.valueOf(batteryCompartmentNo))); + } + /** 读取电池状态信息 + * @param batteryCompartmentNo + * @return + */ + public static JSONObject queryBatStatusInfo(Integer batteryCompartmentNo) throws InstructionException { + ParamsDTO paramsDTO = ParamUtils.getBatStatusInfo(); + return InstructionUtils.getJson(execution(paramsDTO, String.valueOf(batteryCompartmentNo))); + + } + /** 电池数据信息 + * @param batteryCompartmentNo + * @return + */ + public static JSONObject queryBatDataInfo2(Integer batteryCompartmentNo) throws InstructionException { + ParamsDTO paramsDTO = ParamUtils.getBatDataInfo2(); + return InstructionUtils.getJson(execution(paramsDTO, String.valueOf(batteryCompartmentNo))); + } + /** 电池充电信息 + * @param batteryCompartmentNo + * @return + */ + public static JSONObject queryBSLBatChgInfo(Integer batteryCompartmentNo) throws InstructionException { + ParamsDTO paramsDTO = ParamUtils.getBSLBatChgInfo(); + return InstructionUtils.getJson(execution(paramsDTO, String.valueOf(batteryCompartmentNo))); + } + + /** 电池单体电压 + * @param batteryCompartmentNo + * @return + */ + public static JSONObject queryBatCellVol(Integer batteryCompartmentNo) throws InstructionException { + ParamsDTO paramsDTO = ParamUtils.getBatCellVol(); + return InstructionUtils.getJson(execution(paramsDTO, String.valueOf(batteryCompartmentNo))); + } + + /*** 检查电池仓是否存在电池 + * @param: batteryCompartmentNo + * @return true 存在 false 不存在 + */ + public static Boolean queryBatExist(Integer batteryCompartmentNo) throws InstructionException { + ParamsDTO paramsDTO = ParamUtils.getParamsDTO(ParamSysConstants.EXISTS_BATTERY); + return InstructionUtils.getBoolean(paramsDTO, executionInteger(paramsDTO, String.valueOf(batteryCompartmentNo))); + + } + + + /*** 检查空调是否连接 + * @return true 连接 false 未连接 + */ + public static Boolean airConnectDevConnect() throws InstructionException { + ParamsDTO paramsDTO = ParamUtils.getParamsDTO(ParamSysConstants.AIR_DEVICE_CONNECT); + return InstructionUtils.getBoolean(paramsDTO, executionInteger(paramsDTO, null)); + } + + /*** 检查空调运行状态 + * @return true 连接 false 未连接 + */ + public static Boolean airRunning() throws InstructionException { + ParamsDTO paramsDTO = ParamUtils.getParamsDTO(ParamSysConstants.AIR_DEVICE_RUNNING); + return InstructionUtils.getBoolean(paramsDTO, executionInteger(paramsDTO, null)); + } + + /*** 空调自检状态 + * @return true 连接 false 未连接 + */ + public static Boolean airSelfCheck() throws InstructionException { + ParamsDTO paramsDTO = ParamUtils.getParamsDTO(ParamSysConstants.AIR_DEVICE_SELF_CHECK); + return InstructionUtils.getBoolean(paramsDTO, executionInteger(paramsDTO, null)); + } + + /*** 轴流风机状态 + * @return true 连接 false 未连接 + */ + public static Boolean axialFlowFan() throws InstructionException { + ParamsDTO paramsDTO = ParamUtils.getParamsDTO(ParamSysConstants.AXIAL_FAN); + return InstructionUtils.getBoolean(paramsDTO, executionInteger(paramsDTO, null)); + } + + /*** 内风机运行状态 + * @return true 连接 false 未连接 + */ + public static Boolean airConIntFanRun() throws InstructionException { + ParamsDTO paramsDTO = ParamUtils.getParamsDTO(ParamSysConstants.AIR_CON_INT_FAN); + return InstructionUtils.getBoolean(paramsDTO, executionInteger(paramsDTO, null)); + } + + /*** 内风机告警 + * @return true 连接 false 未连接 + */ + public static Boolean airConIntFanAlarm() throws InstructionException { + ParamsDTO paramsDTO = ParamUtils.getParamsDTO(ParamSysConstants.AIR_CON_INT_FAN_ALM); + return InstructionUtils.getBoolean(paramsDTO, executionInteger(paramsDTO, null)); + } + /*** 内风机电流 + */ + public static Integer airConIntFanCurrent() throws InstructionException { + ParamsDTO paramsDTO = ParamUtils.getParamsDTO(ParamSysConstants.AIR_CON_INT_FAN_CUR); + return InstructionUtils.getInteger(executionInteger(paramsDTO, null)); + } + /*** 内风机待机开启温度 + */ + public static Integer airConIntFanStartTemp() throws InstructionException { + ParamsDTO paramsDTO = ParamUtils.getParamsDTO(ParamSysConstants.AIR_CON_INT_FAN_START_TEMP); + return resultConvert(InstructionUtils.getInteger(executionInteger(paramsDTO, null)), 180, 2,null); + } + /*** 内风机待机停止回差温度 + */ + public static Integer airConIntFanStopTemp() throws InstructionException { + ParamsDTO paramsDTO = ParamUtils.getParamsDTO(ParamSysConstants.AIR_CON_INT_FAN_STOP_TEMP); + return resultConvert(InstructionUtils.getInteger(executionInteger(paramsDTO, null)), 180, 2,null); + } + + /*** 外风机运行状态 + * @return true 连接 false 未连接 + */ + public static Boolean airConExtFanRun() throws InstructionException { + ParamsDTO paramsDTO = ParamUtils.getParamsDTO(ParamSysConstants.AIR_CON_EXT_FAN); + return InstructionUtils.getBoolean(paramsDTO, executionInteger(paramsDTO, null)); + } + + /*** 内风机告警 + * @return true 连接 false 未连接 + */ + public static Boolean airConExtFanAlarm() throws InstructionException { + ParamsDTO paramsDTO = ParamUtils.getParamsDTO(ParamSysConstants.AIR_CON_EXT_FAN_ALM); + return InstructionUtils.getBoolean(paramsDTO, executionInteger(paramsDTO, null)); + } + /*** 内风机电流 + */ + public static Integer airConExtFanCurrent() throws InstructionException { + ParamsDTO paramsDTO = ParamUtils.getParamsDTO(ParamSysConstants.AIR_CON_EXT_FAN_CUR); + return InstructionUtils.getInteger(executionInteger(paramsDTO, null)); + } + + /*** 压缩机运行状态 + * @return true 连接 false 未连接 + */ + public static Boolean airConCmpRun() throws InstructionException { + ParamsDTO paramsDTO = ParamUtils.getParamsDTO(ParamSysConstants.AIR_CON_CMP_STATE); + return InstructionUtils.getBoolean(paramsDTO, executionInteger(paramsDTO, null)); + } + + /*** 压缩机欠流故障 + * @return true 连接 false 未连接 + */ + public static Boolean airConCmpLowCurAlarm() throws InstructionException { + ParamsDTO paramsDTO = ParamUtils.getParamsDTO(ParamSysConstants.AIR_CON_CMP_LOW_CUR_ERROR); + return InstructionUtils.getBoolean(paramsDTO, executionInteger(paramsDTO, null)); + } + + /*** 压缩机过载故障 + * @return true 连接 false 未连接 + */ + public static Boolean airConCmpOverLoadAlarm() throws InstructionException { + ParamsDTO paramsDTO = ParamUtils.getParamsDTO(ParamSysConstants.AIR_CON_CMP_OVER_LOAD_ERROR); + return InstructionUtils.getBoolean(paramsDTO, executionInteger(paramsDTO, null)); + } + /***压缩机启动温度 + */ + public static Integer airConCmpStartTemp() throws InstructionException { + ParamsDTO paramsDTO = ParamUtils.getParamsDTO(ParamSysConstants.AIR_CON_CMP_START_TEMP); + return resultConvert(InstructionUtils.getInteger(executionInteger(paramsDTO, null)), 180, 2,null); + } + + /***压缩机停止回差值 + */ + public static Integer airConCmpStopTemp() throws InstructionException { + ParamsDTO paramsDTO = ParamUtils.getParamsDTO(ParamSysConstants.AIR_CON_CMP_STOP_TEMP); + return resultConvert(InstructionUtils.getInteger(executionInteger(paramsDTO, null)), 180, 2,null); + } + + /***压缩机启动温度 + */ + public static Integer airConCmpHeatCurrent() throws InstructionException { + ParamsDTO paramsDTO = ParamUtils.getParamsDTO(ParamSysConstants.AIR_CON_CMP_HEAT_CUR); + return InstructionUtils.getInteger(executionInteger(paramsDTO, null)); + } + + + /*** 柜内温度传感器故障 + * @return true 连接 false 未连接 + */ + public static Boolean airConIntTempSensorAlarm() throws InstructionException { + ParamsDTO paramsDTO = ParamUtils.getParamsDTO(ParamSysConstants.AIR_CON_INT_TEMP_SENSOR_ERROR); + return InstructionUtils.getBoolean(paramsDTO, executionInteger(paramsDTO, null)); + } + /*** 柜内湿度传感器故障 + * @return true 连接 false 未连接 + */ + public static Boolean airConIntHumSensorAlarm() throws InstructionException { + ParamsDTO paramsDTO = ParamUtils.getParamsDTO(ParamSysConstants.AIR_CON_INT_HUM_SENSOR_ERROR); + return InstructionUtils.getBoolean(paramsDTO, executionInteger(paramsDTO, null)); + } + /*** 柜内高温告警 + * @return true 连接 false 未连接 + */ + public static Boolean airConIntOverTempAlarm() throws InstructionException { + ParamsDTO paramsDTO = ParamUtils.getParamsDTO(ParamSysConstants.AIR_CON_INT_OVER_TEMP_ALM); + return InstructionUtils.getBoolean(paramsDTO, executionInteger(paramsDTO, null)); + } + /*** 柜内低温告警 + * @return true 连接 false 未连接 + */ + public static Boolean airConIntLowTempAlarm() throws InstructionException { + ParamsDTO paramsDTO = ParamUtils.getParamsDTO(ParamSysConstants.AIR_CON_INT_LOW_TEMP_ALM); + return InstructionUtils.getBoolean(paramsDTO, executionInteger(paramsDTO, null)); + } + /***柜内回风温度 + * 柜内回风温度(IO值=实际值*2+180) + */ + public static Integer airConIntTemp() throws InstructionException { + ParamsDTO paramsDTO = ParamUtils.getParamsDTO(ParamSysConstants.AIR_CON_INT_TEMP); + return resultConvert(InstructionUtils.getInteger(executionInteger(paramsDTO, null)), 180, 2,null); + } + /***柜内湿度 + */ + public static Integer airConIntHum() throws InstructionException { + ParamsDTO paramsDTO = ParamUtils.getParamsDTO(ParamSysConstants.AIR_CON_INT_HUM); + return InstructionUtils.getInteger(executionInteger(paramsDTO, null)); + } + /***柜内温度变送电流值 + */ + public static Integer airConIntTempSndCurrent() throws InstructionException { + ParamsDTO paramsDTO = ParamUtils.getParamsDTO(ParamSysConstants.AIR_CON_INT_TEMP_SND_CUR); + return resultConvert(InstructionUtils.getInteger(executionInteger(paramsDTO, null)), null, 1000,null); + } + /***柜内湿度变送电流值 + */ + public static Integer airConIntHumSndCurrent() throws InstructionException { + ParamsDTO paramsDTO = ParamUtils.getParamsDTO(ParamSysConstants.AIR_CON_INT_HUM_SND_CUR); + return resultConvert(InstructionUtils.getInteger(executionInteger(paramsDTO, null)), null, 1000,null); + } + /***柜内高温告警温度值 + */ + public static Integer airConOverAlmTemp() throws InstructionException { + ParamsDTO paramsDTO = ParamUtils.getParamsDTO(ParamSysConstants.AIR_CON_OVER_ALM_TEMP); + return resultConvert(InstructionUtils.getInteger(executionInteger(paramsDTO, null)), 180, 2,null); + } + /***柜内低温告警温度值 + */ + public static Integer airConLowAlmTemp() throws InstructionException { + ParamsDTO paramsDTO = ParamUtils.getParamsDTO(ParamSysConstants.AIR_CON_LOW_ALM_TEMP); + return resultConvert(InstructionUtils.getInteger(executionInteger(paramsDTO, null)), 180, 2,null); + } + + /*** 柜外温度传感器故障 + * @return true 连接 false 未连接 + */ + public static Boolean airConExtTempSensorAlarm() throws InstructionException { + ParamsDTO paramsDTO = ParamUtils.getParamsDTO(ParamSysConstants.AIR_CON_EXT_TEMP_SENSOR_ERROR); + return InstructionUtils.getBoolean(paramsDTO, executionInteger(paramsDTO, null)); + } + + /*** 柜外温度传感器故障 + * @return true 连接 false 未连接 + */ + public static Boolean airConExtErrorAlarm() throws InstructionException { + ParamsDTO paramsDTO = ParamUtils.getParamsDTO(ParamSysConstants.AIR_CON_EXT_ERROR_ALM); + return InstructionUtils.getBoolean(paramsDTO, executionInteger(paramsDTO, null)); + } + /***柜外环境温度 + */ + public static Integer airConExtTemp() throws InstructionException { + ParamsDTO paramsDTO = ParamUtils.getParamsDTO(ParamSysConstants.AIR_CON_EXT_TEMP); + return resultConvert(InstructionUtils.getInteger(executionInteger(paramsDTO, null)), 180, 2,null); + } + + /*** 冷凝盘管温度传感器故障 + * @return true 连接 false 未连接 + */ + public static Boolean airConCondTempSensorError() throws InstructionException { + ParamsDTO paramsDTO = ParamUtils.getParamsDTO(ParamSysConstants.AIR_CON_COND_TEMP_SENSOR_ERROR); + return InstructionUtils.getBoolean(paramsDTO, executionInteger(paramsDTO, null)); + } + + /*** 电源电压超限告警 + * @return true 连接 false 未连接 + */ + public static Boolean airConSrcVolOverAlarm() throws InstructionException { + ParamsDTO paramsDTO = ParamUtils.getParamsDTO(ParamSysConstants.AIR_CON_SRC_VOL_OVER_ALM); + return InstructionUtils.getBoolean(paramsDTO, executionInteger(paramsDTO, null)); + } + /***系统供电电压 + */ + public static Integer airConSrcVol() throws InstructionException { + ParamsDTO paramsDTO = ParamUtils.getParamsDTO(ParamSysConstants.AIR_CON_SRC_VOL); + return InstructionUtils.getInteger(executionInteger(paramsDTO, null)); + } + + + /*** 电箱温湿度传感器连接状态 + * @return true 连接 false 未连接 + */ + public static Boolean htSensorDevConnect() throws InstructionException { + ParamsDTO paramsDTO = ParamUtils.getParamsDTO(ParamSysConstants.HT_SENSOR_DEV_CONNECT); + return InstructionUtils.getBoolean(paramsDTO, executionInteger(paramsDTO, null)); + } + + /*** 电箱内湿度值 + * @return true 连接 false 未连接 + */ + public static Integer htSensorHumidity() throws InstructionException { + ParamsDTO paramsDTO = ParamUtils.getParamsDTO(ParamSysConstants.HT_SENSOR_HUMIDITY); + return resultConvert(InstructionUtils.getInteger(executionInteger(paramsDTO, null)), null, 10, null); + } + /***电箱内温度值 + */ + public static Integer htSensorTemperature() throws InstructionException { + ParamsDTO paramsDTO = ParamUtils.getParamsDTO(ParamSysConstants.HT_SENSOR_TEMPERATURE); + return resultConvert(InstructionUtils.getInteger(executionInteger(paramsDTO, null)), null, 10, null); + } + + + /*** 交流电表是否连接 + * @return true 连接 false 未连接 + */ + public static Boolean energyMeterDevConnect() throws InstructionException { + ParamsDTO paramsDTO = ParamUtils.getParamsDTO(ParamSysConstants.ENERGY_METER_DEV_CONNECT); + return InstructionUtils.getBoolean(paramsDTO, executionInteger(paramsDTO, null)); + } + /***交流电表正向有功总电能 + */ + public static Integer energyMeterEPImp() throws InstructionException { + ParamsDTO paramsDTO = ParamUtils.getParamsDTO(ParamSysConstants.ENERGY_METER_EP_IMP); + return resultConvert(InstructionUtils.getInteger(executionInteger(paramsDTO, null)),null,null,0.1); + } + + /***查询当前RGV是否在互斥区 + */ + public static Integer getRgvHShaftLocation(ParamsDTO paramsDTO, String replaceParamsValue) throws InstructionException { + return InstructionUtils.getInteger(executionInteger(paramsDTO, replaceParamsValue)); + } + + + /***消防设备是否连接 + */ + public static Boolean checkExistsBattery(String batteryCompartmentNo) throws InstructionException { + ParamsDTO paramsDTO = ParamUtils.getParamsDTO(ParamSysConstants.BSL_BAT_EXIST); + return InstructionUtils.getBoolean(paramsDTO, executionInteger(paramsDTO, batteryCompartmentNo)); + } + + + /***查询当前RGV是否在互斥区 + */ + public static Boolean getCenRbDoorOpenStatus() throws InstructionException { + ParamsDTO paramsDTO = ParamUtils.getParamsDTO(ParamSysConstants.CEN_RB_DOOR_OPEN_STATUS); + return InstructionUtils.getBoolean(paramsDTO, executionInteger(paramsDTO, null)); + } + + /***查询当前RGV是否在互斥区 + */ + public static Boolean getCenRbDoorCloseStatus() throws InstructionException { + ParamsDTO paramsDTO = ParamUtils.getParamsDTO(ParamSysConstants.CEN_RB_DOOR_CLOSE_STATUS); + return InstructionUtils.getBoolean(paramsDTO, executionInteger(paramsDTO, null)); + } + + + + + + private static JSONObject execution(ParamsDTO paramsDTO, String replaceParamsValue) throws InstructionException { + InstructionUtils.initParamValue(paramsDTO, replaceParamsValue); + //查询电池数据, 是否存在当前电池信息, 如果存在做更新, 不存在做新增操作 + return checkJSON(paramsDTO,InstructionUtils.queryReadString(paramsDTO.getName(), paramsDTO.getParamValue())); + } + + public static JSONObject executionInteger(ParamsDTO paramsDTO, String replaceParamsValue) throws InstructionException { + InstructionUtils.initParamValue(paramsDTO, replaceParamsValue); + //查询电池数据, 是否存在当前电池信息, 如果存在做更新, 不存在做新增操作 + return checkJSON(paramsDTO,InstructionUtils.queryReadInteger(paramsDTO.getName(), paramsDTO.getParamValue())); + } + + + private static JSONObject checkJSON(ParamsDTO paramsDTO,JSONObject jsonObject){ + //获取仓位直流电表信息 + if(InstructionUtils.verifyStatusLog(paramsDTO, jsonObject)) { + return jsonObject; + } + return null; + } + + public static Integer resultConvert(Integer baseVal, Integer offsetVal, Integer divideVal, Double multiplyVal){ + BigDecimal result = new BigDecimal(DataUtils.findDefaultValue(baseVal,0)); + if(offsetVal != null){ + result = result.subtract(new BigDecimal(offsetVal)); + } + if(divideVal != null){ + result = result.divide(new BigDecimal(divideVal)); + } + if(multiplyVal != null){ + result = result.multiply(new BigDecimal(multiplyVal)); + } + return result.intValue(); + } + + +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/utils/instruction/InstructionUtils.java b/web-server/src/main/java/com/evotech/hd/webserver/utils/instruction/InstructionUtils.java new file mode 100644 index 0000000..02b91bb --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/utils/instruction/InstructionUtils.java @@ -0,0 +1,195 @@ +package com.evotech.hd.webserver.utils.instruction; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.evotech.hd.core.dtos.system.ParamsDTO; +import com.evotech.hd.exception.InstructionException; +import com.evotech.hd.utils.Collections; +import com.evotech.hd.webserver.logging.AsyncLogService; +import com.evotech.hd.webserver.logging.LogUtils; +import com.evotech.hd.webserver.logging.enums.InstructionLogTypeEnum; +import com.evotech.hd.webserver.utils.ParamUtils; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.apache.xmlrpc.XmlRpcException; +import org.apache.xmlrpc.client.XmlRpcClient; +import org.apache.xmlrpc.client.XmlRpcClientConfigImpl; +import org.jetbrains.annotations.NotNull; +import org.springframework.boot.ApplicationArguments; +import org.springframework.boot.ApplicationRunner; +import org.springframework.stereotype.Component; + +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Map; + +/** + * 指令下发工具类 + * + * @ClassName:InstructionSendInfoUtils + * @date: 2025年08月22日 17:48 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@Slf4j +@Component +public class InstructionUtils implements ApplicationRunner { + + // 接口常量定义(便于维护) + private static String RPC_URL = "http://127.0.0.1:8088/RPC2"; + private static Integer MAX_RETRY_NUM = 1; // 参数名严格匹配接口定义 + private static String PARAM_NAME = "IONAME"; // 参数名严格匹配接口定义 + private static Map clientMap = Collections.emptyMap(); + + private static AsyncLogService asyncLogService; + + + public InstructionUtils(AsyncLogService asyncLogService) { + this.asyncLogService = asyncLogService; + } + + public static Boolean verifyStatusLog(ParamsDTO paramsDTO, JSONObject json){ + if(!verifyStatus(json)){ + asyncLogService.saveLog(LogUtils.getAlarmLog(paramsDTO.getName(), -1, paramsDTO.getParamCode(), json.toString(), null)); + return false; + } + return true; + } + + public static Boolean verifyStatus(JSONObject json){ + return Integer.valueOf(0).equals(json.getInteger("retstatus")); + } + public static String getValue(JSONObject json){ + return json.getString("value"); + } + public static Integer getIntegerValue(JSONObject json){ + return json.getInteger("value"); + } + public static Double getDoubleValue(JSONObject json){ + return json.getDoubleValue("value"); + } + + // ioNameValue替换为实际的IO名称 + public static JSONObject queryReadInteger(String functionDesc,String ioNameValue) throws InstructionException { + return send(functionDesc, InstructionLogTypeEnum.READ,"IOServer.ReadInteger", Collections.asMap(PARAM_NAME, ioNameValue)); + } + + public static JSONObject queryReadDouble(String functionDesc,String ioNameValue) throws InstructionException { + return send(functionDesc, InstructionLogTypeEnum.READ, "IOServer.ReadDouble", Collections.asMap(PARAM_NAME, ioNameValue)); + } + + public static JSONObject queryReadString(String functionDesc,String ioNameValue) throws InstructionException { + return send(functionDesc, InstructionLogTypeEnum.READ, "IOServer.ReadString", Collections.asMap(PARAM_NAME, ioNameValue)); + } + + public static JSONObject writeInteger(String functionDesc,String ioNameValue, Integer val) throws InstructionException { + return send(functionDesc, InstructionLogTypeEnum.WRITE, "IOServer.WriteInteger", Collections.asMap(PARAM_NAME, ioNameValue, "VALUE", val)); + } + + private static JSONObject send(String functionDesc, InstructionLogTypeEnum logType, String methodName, Map params) throws InstructionException { + return send(1, functionDesc, logType, methodName, params); + } + private static JSONObject send(Integer sendNumber, String functionDesc, InstructionLogTypeEnum logType, String methodName, Map params) throws InstructionException { + Long startTime = System.currentTimeMillis(); + if(sendNumber > MAX_RETRY_NUM){ + throw new InstructionException(String.format("指令下发出现异常, 超出最大请求次数, 指令描述:{%s}, 接口方法:{%s}, 当前请求次数: {%s}, 最大允许重试次数:{%s}, 参数信息:{%s}", functionDesc, methodName, sendNumber, MAX_RETRY_NUM, JSONObject.toJSONString(params))); + } + try { + XmlRpcClient client = getXmlRpcClient(); + Object result = client.execute(methodName, new Object[]{params}); + asyncLogService.saveInstructionLog(LogUtils.getSendInstructionLog(functionDesc, logType, methodName, (System.currentTimeMillis()-startTime), params, JSON.toJSONString(result), null)); + JSONObject json = JSONObject.parseObject(JSON.toJSONString(result)); + if(!verifyStatus(json)){ + try { + Thread.sleep(ParamUtils.getInstructionIntervalRequestMilliseconds()); + } catch (InterruptedException e) { + log.error("延迟等待出现异常: {}", e.getMessage(), e); + } + return send(sendNumber+1, functionDesc, logType, methodName, params); + } + return json; + }catch (MalformedURLException e){ + e.printStackTrace(); + log.error("获取指令下发客户端出现异常", e); + asyncLogService.saveInstructionLog(LogUtils.getSendInstructionLog(functionDesc, logType, methodName, (System.currentTimeMillis()-startTime), params, "", e)); + JSONObject result = new JSONObject(); + result.put("code", 0); + return result; + }catch (XmlRpcException e) { + e.printStackTrace(); + log.error("指令下发出现错误", e); + asyncLogService.saveInstructionLog(LogUtils.getSendInstructionLog(functionDesc, logType, methodName, (System.currentTimeMillis()-startTime), params, "", e)); + JSONObject result = new JSONObject(); + result.put("code", 0); + return result; + } + } + + /* catch (InstructionException e) { + e.printStackTrace(); + log.error("指令下发超过最大重试次数", e); + asyncLogService.saveInstructionLog(LogUtils.getSendInstructionLog(functionDesc, logType, methodName, (System.currentTimeMillis()-startTime), params, "", e)); + JSONObject result = new JSONObject(); + result.put("code", 0); + return result; + }*/ + + @NotNull + private static XmlRpcClient getXmlRpcClient() throws MalformedURLException { + if( clientMap.containsKey("client")){ + return clientMap.get("client"); + } + synchronized (clientMap){ + if(clientMap.containsKey("client")){ + return clientMap.get("client"); + } + // 1. 初始化RPC客户端配置 + XmlRpcClientConfigImpl config = new XmlRpcClientConfigImpl(); + config.setServerURL(new URL(RPC_URL)); + config.setContentLengthOptional(true); // 兼容服务器无Content-Length响应 + // 2. 创建客户端实例 + XmlRpcClient client = new XmlRpcClient(); + client.setConfig(config); + clientMap.put("client", client); + return client; + } + + } + + + public static void initParamValue(ParamsDTO paramsDTO, String replaceParamsValue) { + if (StringUtils.isNotEmpty(replaceParamsValue)){ + paramsDTO.setParamValue(paramsDTO.getParamValue().replace(":num",replaceParamsValue)); + } + } + + + public static JSONObject getJson(JSONObject jsonObject){ + if(jsonObject == null){ + return null; + } + //获取仓位直流电表信息 + return JSONObject.parseObject(InstructionUtils.getValue(jsonObject)); + } + + public static Boolean getBoolean(ParamsDTO paramsDTO, JSONObject jsonObject){ + if(jsonObject == null){ + return false; + } + //获取仓位直流电表信息 + return Integer.valueOf(paramsDTO.getExpectedResults()).equals(InstructionUtils.getIntegerValue(jsonObject)); + } + + public static Integer getInteger(JSONObject jsonObject){ + if(jsonObject == null){ + return 0; + } + return InstructionUtils.getIntegerValue(jsonObject); + } + + @Override + public void run(ApplicationArguments args) throws Exception { + MAX_RETRY_NUM = ParamUtils.findMaxRetryNum(); + RPC_URL = ParamUtils.getInstructionRequestUrl(); + } +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/utils/instruction/InstructionWriteUtils.java b/web-server/src/main/java/com/evotech/hd/webserver/utils/instruction/InstructionWriteUtils.java new file mode 100644 index 0000000..370fae6 --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/utils/instruction/InstructionWriteUtils.java @@ -0,0 +1,292 @@ +package com.evotech.hd.webserver.utils.instruction; + +import com.evotech.hd.core.dtos.business.RunningInstructionsDetailDTO; +import com.evotech.hd.core.dtos.system.ParamsDTO; +import com.evotech.hd.exception.InstructionException; +import com.evotech.hd.webserver.cnstant.InstructionConstants; +import com.evotech.hd.webserver.cnstant.ParamSysConstants; +import com.evotech.hd.webserver.service.ParamsService; +import com.evotech.hd.webserver.service.RunningInstructionsDetailService; +import com.evotech.hd.webserver.utils.ParamUtils; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.ObjectUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.List; + +/** + * 类 + * + * @ClassName:InstructionUtils + * @date: 2025年08月26日 16:28 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@Slf4j +@Component +public class InstructionWriteUtils { + + private static RunningInstructionsDetailService runningInstructionsDetailService; + + private static ParamsService paramsService; + + @Autowired + public InstructionWriteUtils(RunningInstructionsDetailService runningInstructionsDetailService, ParamsService paramsService) { + this.runningInstructionsDetailService = runningInstructionsDetailService; + this.paramsService = paramsService; + } + + /** 打开卷帘门 + */ + public static void openRolling(String doorNo) throws Exception { + executeInstructions(runningInstructionsDetailService.findDictDetailDTOByTypeCode(InstructionConstants.OPEN_ROLLING), doorNo, null); + } + /** 关闭卷帘门 + */ + public static void closeRolling(String doorNo) throws Exception { + executeInstructions(runningInstructionsDetailService.findDictDetailDTOByTypeCode(InstructionConstants.CLOSE_ROLLING), doorNo, null); + } + /** 启动对中机构 + */ + public static void startCorrectInstitution() throws Exception { + executeInstructions(runningInstructionsDetailService.findDictDetailDTOByTypeCode(InstructionConstants.START_CORRECT_INSTITUTION), null, null); + } + /** 关闭对中机构 + */ + public static void closeCorrectInstitution() throws Exception { + executeInstructions(runningInstructionsDetailService.findDictDetailDTOByTypeCode(InstructionConstants.CLOSE_CORRECT_INSTITUTION), null, null); + } + /*** 打开开合门 + * @param + */ + public static void openCenDoor() throws Exception { + executeInstructions(runningInstructionsDetailService.findDictDetailDTOByTypeCode(InstructionConstants.CEN_CMD_OPEN_DOOR), null, null); + } + /*** 关闭开合门 + * @param + */ + public static void closeCenDoor() throws Exception { + executeInstructions(runningInstructionsDetailService.findDictDetailDTOByTypeCode(InstructionConstants.CEN_CMD_CLOSE_DOOR), null, null); + } + + /** 拆旧电池 + */ + public static void splitOldBattery(String rgvNo) throws Exception { + executeInstructions(runningInstructionsDetailService.findDictDetailDTOByTypeCode(InstructionConstants.SPLIT_OLD_BATTERY), rgvNo, null); + + } + /** 存旧电池 + */ + public static void putOldBattery(String rgvNo, Integer compartmentId) throws Exception { + List instructions = runningInstructionsDetailService.findDictDetailDTOByTypeCode(InstructionConstants.PUT_OLD_BATTERY); + //第一条指令单独操作 + executeInstructionReplaceParamsValue(instructions.get(0), rgvNo, compartmentId); + //清除第一条指令 + instructions.remove(0); + //轮询后续指令 + executeInstructions(instructions, rgvNo, null); + } + /** 取电池 + */ + public static void takeNewBattery(String rgvNo, Integer compartmentId) throws Exception { + List instructions = runningInstructionsDetailService.findDictDetailDTOByTypeCode(InstructionConstants.TAKE_NEW_BATTERY); + //第一条指令单独操作 + executeInstructionReplaceParamsValue(instructions.get(0), rgvNo ,compartmentId); + //清除第一条指令 + instructions.remove(0); + //轮询后续指令 + executeInstructions(instructions, rgvNo, null); + } + /** 装新电池 + */ + public static void loadingNewBattery(String rgvNo) throws Exception { + executeInstructions(runningInstructionsDetailService.findDictDetailDTOByTypeCode(InstructionConstants.LOADING_NEW_BATTERY), rgvNo, null); + + } + /** RGV归0 + */ + public static void rgvResetToZero(String rgvNo) throws Exception { + executeInstructions(runningInstructionsDetailService.findDictDetailDTOByTypeCode(InstructionConstants.RGV_RESET_TO_ZERO), rgvNo, null); + } + /** RGV停止移动 + */ + public static void rgvStopMove(String rgvNo) throws Exception { + executeInstructions(runningInstructionsDetailService.findDictDetailDTOByTypeCode(InstructionConstants.RGV_STOP_MOVE), rgvNo, null, false); + } + /** 急停 + */ + public static void emergencyStop(String rgvNo) throws Exception { + executeInstructions(runningInstructionsDetailService.findDictDetailDTOByTypeCode(InstructionConstants.EMERGENCY_BIN), rgvNo, null, false); + } + + /** 消防动作 + */ + public static void fireFightingAction(String rgvNo, Integer compartmentId) throws Exception { + List instructions = runningInstructionsDetailService.findDictDetailDTOByTypeCode(InstructionConstants.FIRE_FIGHTING_ACTION); + //第一条指令单独操作 + executeInstructionReplaceParamsValue(instructions.get(0), rgvNo, compartmentId); + //清除第一条指令 + instructions.remove(0); + //轮询后续指令 + executeInstructions(instructions, rgvNo, null, false); + } + + /** RGV重置 + */ + public static void rgvReset(String rgvNo) throws Exception { + executeInstructions(runningInstructionsDetailService.findDictDetailDTOByTypeCode(InstructionConstants.RGV_RESET), rgvNo, null, false); + } + /*** 设置仓位最大功率 + * @param compartmentId 仓位号 + * @param maxPower 最大功率 + */ + public static void settingMaxPower(Integer compartmentId, Integer maxPower) throws Exception { + ParamsDTO paramsDTO = ParamUtils.getParamsDTO(ParamSysConstants.BSL_CHG_POW_CFG); + InstructionUtils.initParamValue(paramsDTO, String.valueOf(compartmentId)); + InstructionUtils.writeInteger(paramsDTO.getName(), paramsDTO.getParamValue(), maxPower); + + } + /*** 开始充电 + * @param compartmentId + */ + public static void startCharging(Integer compartmentId) throws Exception { + executeInstructions(runningInstructionsDetailService.findDictDetailDTOByTypeCode(InstructionConstants.START_CHARGING), String.valueOf(compartmentId), null); + } + /*** 结束充电 + * @param compartmentId + */ + public static void stopCharging(Integer compartmentId) throws Exception { + executeInstructions(runningInstructionsDetailService.findDictDetailDTOByTypeCode(InstructionConstants.STOP_CHARGING), String.valueOf(compartmentId), null); + } + /*** BMS上电 + * @param compartmentId + */ + public static void BMSPowerOn(Integer compartmentId) throws Exception { + executeInstructions(runningInstructionsDetailService.findDictDetailDTOByTypeCode(InstructionConstants.BMS_POWER_ON), String.valueOf(compartmentId), null); + } + /*** BMS下电 + * @param compartmentId + */ + public static void BMSPowerOff(Integer compartmentId) throws Exception { + executeInstructions(runningInstructionsDetailService.findDictDetailDTOByTypeCode(InstructionConstants.BMS_POWER_OFF), String.valueOf(compartmentId), null); + } + /*** 空调打开 + */ + public static void airDeviceOn() throws Exception { + executeInstructions(runningInstructionsDetailService.findDictDetailDTOByTypeCode(InstructionConstants.AIR_DEVICE_ON), null, null, false); + } + /*** 空调关闭 + */ + public static void airDeviceOff() throws Exception { + executeInstructions(runningInstructionsDetailService.findDictDetailDTOByTypeCode(InstructionConstants.AIR_DEVICE_OFF), null, null, false); + } + /*** 空调自检打开 + */ + public static void airSelfCheckOn() throws Exception { + executeInstructions(runningInstructionsDetailService.findDictDetailDTOByTypeCode(InstructionConstants.AIR_SELF_CHECK_ON), null, null, false); + } + /*** 空调自检关闭 + */ + public static void airSelfCheckOff() throws Exception { + executeInstructions(runningInstructionsDetailService.findDictDetailDTOByTypeCode(InstructionConstants.AIR_SELF_CHECK_OFF), null, null, false); + } + /*** 轴流风机打开 + */ + public static void axialFlowFanOn() throws Exception { + executeInstructions(runningInstructionsDetailService.findDictDetailDTOByTypeCode(InstructionConstants.AXIAL_FLOW_FAN_ON), null, null, false); + } + /*** 轴流风机关闭 + */ + public static void axialFlowFanOff() throws Exception { + executeInstructions(runningInstructionsDetailService.findDictDetailDTOByTypeCode(InstructionConstants.AXIAL_FLOW_FAN_OFF), null, null, false); + } + + /*** 内风机待机开启温度 + * @param temperature + */ + public static void airConIntFanStartTemp(Integer temperature) throws Exception { + executeInstructions(runningInstructionsDetailService.findDictDetailDTOByTypeCode(InstructionConstants.AIR_CON_INT_FAN_START_TEMP), null, temperature, false); + } + /*** 内风机待机停止回差温度 + * @param temperature + */ + public static void airConIntFanStopTemp(Integer temperature) throws Exception { + executeInstructions(runningInstructionsDetailService.findDictDetailDTOByTypeCode(InstructionConstants.AIR_CON_INT_FAN_STOP_TEMP), null, temperature, false); + } + + /*** 压缩机启动温度 + * @param temperature + */ + public static void airConCmpStartTemp(Integer temperature) throws Exception { + executeInstructions(runningInstructionsDetailService.findDictDetailDTOByTypeCode(InstructionConstants.AIR_CON_CMP_START_TEMP), null, temperature, false); + } + /*** 压缩机停止回差值 + * @param temperature + */ + public static void airConCmpStopTemp(Integer temperature) throws Exception { + executeInstructions(runningInstructionsDetailService.findDictDetailDTOByTypeCode(InstructionConstants.AIR_CON_CMP_STOP_TEMP), null, temperature, false); + } + + + /*** 加热器启动温度 + * @param temperature + */ + public static void airConHeatStartTemp(Integer temperature) throws Exception { + executeInstructions(runningInstructionsDetailService.findDictDetailDTOByTypeCode(InstructionConstants.AIR_CON_HEAT_START_TEMP), null, temperature, false); + } + /*** 加热器启动温度 + * @param temperature + */ + public static void airConHeatStopTemp(Integer temperature) throws Exception { + executeInstructions(runningInstructionsDetailService.findDictDetailDTOByTypeCode(InstructionConstants.AIR_CON_HEAT_STOP_TEMP), null, temperature, false); + } + + /*** 柜内高温告警温度值 + * @param temperature + */ + public static void airConOverAlmTemp(Integer temperature) throws Exception { + executeInstructions(runningInstructionsDetailService.findDictDetailDTOByTypeCode(InstructionConstants.AIR_CON_OVER_ALM_TEMP), null, temperature, false); + } + /*** 柜内低温告警温度值 + * @param temperature + */ + public static void airConLowAlmTemp(Integer temperature) throws Exception { + executeInstructions(runningInstructionsDetailService.findDictDetailDTOByTypeCode(InstructionConstants.AIR_CON_LOW_ALM_TEMP), null, temperature, false); + } + + + +private static void executeInstructions(List instructions, String replaceParamsValue, Integer replaceDefaultValue) throws Exception { + executeInstructions(instructions,replaceParamsValue, replaceDefaultValue, true); +} + + private static void executeInstructions(List instructions, final String replaceParamsValue, final Integer replaceDefaultValue, Boolean isSleep) throws Exception { + for (RunningInstructionsDetailDTO data : instructions){ + executeInstructionReplaceParamsValue(data, replaceParamsValue, replaceDefaultValue); + } + if(isSleep){ + //线程睡眠, 防止指令下发后, 立即执行, 导致设备还没有运作,返回的运作结束 + Thread.sleep(ParamUtils.getInstructionIntervalRequestMilliseconds()); + } + } + + private static void executeInstructionReplaceParamsValue(RunningInstructionsDetailDTO instruction, String replaceParamsValue, Integer replaceDefaultValue) throws InstructionException { + ParamsDTO paramsDTO = paramsService.getParamsDTOByCode(instruction.getDictValue()); + if(ObjectUtils.isNotEmpty(paramsDTO)){ + if(replaceDefaultValue != null){ + instruction.setDefaultValue(replaceDefaultValue); + } + if(StringUtils.isNotEmpty(replaceParamsValue)){ + InstructionUtils.initParamValue(paramsDTO, replaceParamsValue); + } + log.info("下发的ioName======>{}, 下发的ioValue=======>{}",paramsDTO.getParamValue(), instruction.getDefaultValue()); + InstructionUtils.writeInteger(paramsDTO.getName(), paramsDTO.getParamValue(), instruction.getDefaultValue()); + } + } + + + + +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/utils/query/QueryWrapperGenerator.java b/web-server/src/main/java/com/evotech/hd/webserver/utils/query/QueryWrapperGenerator.java new file mode 100644 index 0000000..85ecdda --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/utils/query/QueryWrapperGenerator.java @@ -0,0 +1,101 @@ +package com.evotech.hd.webserver.utils.query; + +import cn.hutool.core.util.ReflectUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.evotech.hd.query.Query; +import com.evotech.hd.query.QueryType; +import com.evotech.hd.webserver.utils.ReflectionUtils; +import com.google.common.base.Splitter; +import org.apache.commons.beanutils2.PropertyUtils; + +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; + + +public class QueryWrapperGenerator { + + + public static QueryWrapper buildQueryCondition(T searchObj, Class entityClass) throws Exception { + QueryWrapper queryWrapper = new QueryWrapper(); + + Field[] fields = ReflectUtil.getFields(entityClass); + + for (int i = 0; i < fields.length; i++) { + // 这个是检查类中属性是否含有查询注解 + Field field = fields[i]; + if (field.isAnnotationPresent(Query.class)) { + // 获取查询注解 + Query annotation = field.getAnnotation(Query.class); + // 获取查询的字段,如果没设置就使用属性名 + String column = StrUtil.isNotBlank(annotation.tableColumn()) ? annotation.tableColumn() : StrUtil.toUnderlineCase(field.getName()); + String fieldName = StrUtil.isNotBlank(annotation.javaField()) ? annotation.javaField() : field.getName(); + // 属性可读且值不为空 + if ((ReflectionUtils.isFieldReadable(searchObj, field.getName()) && ReflectionUtils.hasFieldValue(searchObj, field.getName())) || annotation.type().equals(QueryType.BETWEEN)) { + Object value = ReflectionUtils.getFieldValue(searchObj, fieldName); + if (value != null && StrUtil.isNotBlank(value.toString()) || annotation.type().equals(QueryType.BETWEEN)) { + switch (annotation.type()) { + case EQ: + queryWrapper.eq(column, value); + break; + case NE: + queryWrapper.ne(column, value); + break; + case GT: + queryWrapper.gt(column, value); + break; + case GE: + queryWrapper.ge(column, value); + break; + case LT: + queryWrapper.lt(column, value); + break; + case LE: + queryWrapper.le(column, value); + break; + case BETWEEN: { + Object begin = RequestUtils.getRequest().getParameter("begin" + StrUtil.upperFirst(fieldName)); + Object end = RequestUtils.getRequest().getParameter("end" + StrUtil.upperFirst(fieldName)); + if (begin != null && StrUtil.isNotBlank(begin.toString()) && end != null && StrUtil.isNotBlank(end.toString())) { + queryWrapper.between(column, begin, end); + } + break; + } + case NOTBETWEEN: { + Object begin = RequestUtils.getRequest().getParameter("begin" + StrUtil.upperFirst(fieldName)); + Object end = RequestUtils.getRequest().getParameter("end" + StrUtil.upperFirst(fieldName)); + if (begin != null && StrUtil.isNotBlank(begin.toString()) && end != null && StrUtil.isNotBlank(end.toString())) { + queryWrapper.notBetween(column, begin, end); + } + break; + } + case LIKE: + queryWrapper.like(column, value); + break; + case NOTLIKE: + queryWrapper.notLike(column, value); + break; + case LIKELEFT: + queryWrapper.likeLeft(column, value); + break; + case LIKERIGHT: + queryWrapper.likeRight(column, value); + break; + case IN: + queryWrapper.in(column, Splitter.on(",").trimResults().splitToList(String.valueOf(value))); + break; + + default: + queryWrapper.like(column, value); + + } + } + } + } + } + return queryWrapper; + } + + + +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/utils/query/RequestUtils.java b/web-server/src/main/java/com/evotech/hd/webserver/utils/query/RequestUtils.java new file mode 100644 index 0000000..705c824 --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/utils/query/RequestUtils.java @@ -0,0 +1,66 @@ +package com.evotech.hd.webserver.utils.query; + +import cn.hutool.core.util.StrUtil; +import cn.hutool.extra.servlet.ServletUtil; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; + +public class RequestUtils { + + /** + * 获取请求Request + * + * @return + */ + public static HttpServletRequest getRequest() { + if ( RequestContextHolder.getRequestAttributes ( ) == null ) { + return null; + } + HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes ( )).getRequest ( ); + return request; + } + + /** + * 获取请求header中的值 + * + * @return + */ + public static String getHeader(String key) { + if ( RequestContextHolder.getRequestAttributes ( ) == null ) { + return null; + } + HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes ( )).getRequest ( ); + if ( request != null ) { + return request.getHeader ( key ); + } + return null; + } + + /** + * 获取token,支持三种方式, 请求参数、header、cookie, 优先级依次降低,以请求参数中的优先级最高。 + * + * @param httpServletRequest + * @return + */ + public static String resolveToken(HttpServletRequest httpServletRequest, String token) { + if ( httpServletRequest == null ) { + return null; + } + String token0 = httpServletRequest.getParameter ( token ); + String token1 = httpServletRequest.getHeader ( token ); + Cookie token2 = ServletUtil.getCookie ( httpServletRequest, token ); + if ( StrUtil.isNotBlank ( token0 ) ) { + return token0; + } + if ( StrUtil.isNotBlank ( token1 ) ) { + return token1; + } + if ( token2 != null && StrUtil.isNotBlank ( token2.getValue ( ) ) ) { + return token2.getValue ( ); + } + return null; + } +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/utils/sendCloud/CloudSendInfoUtils.java b/web-server/src/main/java/com/evotech/hd/webserver/utils/sendCloud/CloudSendInfoUtils.java new file mode 100644 index 0000000..e03e680 --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/utils/sendCloud/CloudSendInfoUtils.java @@ -0,0 +1,272 @@ +package com.evotech.hd.webserver.utils.sendCloud; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONArray; +import com.alibaba.fastjson2.JSONObject; +import com.evotech.hd.core.dtos.Result; +import com.evotech.hd.utils.Collections; +import com.evotech.hd.webserver.logging.AsyncLogService; +import com.evotech.hd.webserver.logging.LogUtils; +import com.evotech.hd.webserver.utils.ParamUtils; +import lombok.extern.slf4j.Slf4j; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.ContentType; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.DefaultHttpClient; +import org.apache.http.util.EntityUtils; +import org.springframework.stereotype.Component; + +import java.time.Instant; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * 推送云平台数据工具类 + * + * @ClassName:CloudSendInfoUtils + * @date: 2025年08月19日 11:38 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@Slf4j +@Component +public class CloudSendInfoUtils { + + private static AsyncLogService asyncLogService; + + public CloudSendInfoUtils(AsyncLogService asyncLogService) { + this.asyncLogService = asyncLogService; + } + + + + /*** + * 检查云平台链接状况 + * @return + */ + public static Result sendKeepalive(){ + try { + JSONObject result = send("心跳检测", JSONObject.parseObject(JSONObject.toJSONString(buildBaseInfo(Collections.emptyMap(),"KeepaliveProcessor","keepalive")))); + return JSONObject.parseObject(result.toString(),Result.class); + } catch (Exception e) { + return Result.getInstance().build(Boolean.class).success(false); + } + } + + /*** + * 推送站端状态 + * @param status + * @return + */ + public static Result sendStationStatus(Boolean status){ + JSONObject result = send("站端运营/维修推送", JSONObject.parseObject(JSONObject.toJSONString(buildBaseInfo(Collections.asMap("status",status),"StationRunningStatusProcessor","updateStationRunningStatus")))); + return JSONObject.parseObject(result.toString(),Result.class); + } + + /*** + * 站端推送预约单数据 + * @param map + * @return + */ + public static Result sendOrderReservation(Map map){ + JSONObject result = send("创建预约单", JSONObject.parseObject(JSONObject.toJSONString(buildBaseInfo(map,"orderSwapBatteryPreProcessor","orderSwapBatteryPreAdd")))); + return JSONObject.parseObject(result.toString(),Result.class); + } + /*** + * 站端推送预约单数据状态 + * @param map + * @return + */ + public static Result sendOrderReservationStatus(Map map){ + JSONObject result = send("修改预约单", JSONObject.parseObject(JSONObject.toJSONString(buildBaseInfo(map,"orderSwapBatteryPreProcessor","orderSwapBatteryPreUpdateStatus")))); + return JSONObject.parseObject(result.toString(),Result.class); + } + + /*** + * 站端获取预约单数据 + * @param map + * @return + */ + public static Result findOrderReservationList(Map map){ + JSONObject result = send("获取云端预约单数据", JSONObject.parseObject(JSONObject.toJSONString(buildBaseInfo(map,"orderSwapBatteryPreProcessor","findOrderSwapBatteryPre")))); + return JSONObject.parseObject(result.toString(),Result.class); + } + + /*** + * 站端推送预约单数据状态 + * @param map + * @return + */ + public static Result sendOrder(Map map){ + JSONObject result = send("创建订单", JSONObject.parseObject(JSONObject.toJSONString(buildBaseInfo(map,"orderSwapProcessor","orderSwapAdd")))); + return JSONObject.parseObject(result.toString(),Result.class); + } + + /*** + * 站端推送换电完成数据 + * @param map + * @return + */ + public static Result sendOrderCompleted(Map map){ + JSONObject result = send("换电完成数据", JSONObject.parseObject(JSONObject.toJSONString(buildBaseInfo(map,"orderSwapProcessor","orderCompleted")))); + return JSONObject.parseObject(result.toString(),Result.class); + } + + /*** + * 站端推送告警信息 + * @param map + * @return + */ + public static void sendAlarm(Map map){ + //结合云端接口增加站端运行状态 + map.put("state", (ParamUtils.findStationRunningStatus() ? 1 : 3)); + send("推送告警信息", JSONObject.parseObject(JSONObject.toJSONString(buildBaseInfo(map,"alarmProcessor","alarmMessage")))); + } + + /*** + * 站端推送车辆到位信息 + * @param map + * @return + */ + public static Result sendArrivalSignal(Map map){ + JSONObject result = send("创建订单", JSONObject.parseObject(JSONObject.toJSONString(buildBaseInfo(map,"orderSwapProcessor","arrivalSignal")))); + return JSONObject.parseObject(result.toString(),Result.class); + } + + /*** + * 站端推送策略信息 + * @param map + * @return + */ + public static Result sendStrategy(Map map){ + JSONObject result = send("推送充电策略", JSONObject.parseObject(JSONObject.toJSONString(buildBaseInfo(map,"BatteryStrategyProcessor","receiveStationStrategy")))); + return JSONObject.parseObject(result.toString(),Result.class); + } + + + + private static Map buildBaseInfo(Map map ,String className, String method){ + map.put("class",className); + map.put("method",method); + return map; + } + + private static JSONObject send(String functionName, JSONObject json) { + Long startTime = System.currentTimeMillis(); + try { + long timestamp = Instant.now().getEpochSecond(); + String stationCode = ParamUtils.findStationCode(); + String stationName = ParamUtils.findStationName(); + json.put("stationCode", stationCode); + json.put("stationName", stationName); + json.put("timestamp", timestamp); + String sign = null; + try { + sign = RsaEcbPkcsFullUtil.sign(json.toString()); + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException(e); + } + Map map = Collections.emptyMap(); + map.put("stationCode", stationCode); + map.put("stationName", stationName); + map.put("sign", sign); + map.put("data", json.toString()); + JSONObject result = doPost(ParamUtils.getCloudRequestUrl(), map, Collections.asMap("Content-Type", "application/json","Accept-Language", "zh-CN")); + asyncLogService.saveLog(LogUtils.getSendCloudLog(functionName, (System.currentTimeMillis()-startTime), json, (result == null ? "" : result.toString()), null)); + return result; + } catch (Exception e) { + e.printStackTrace(); + log.error("推送云端数据出现错误", e); + asyncLogService.saveLog(LogUtils.getSendCloudLog(functionName, (System.currentTimeMillis()-startTime), json, "", e)); + JSONObject result = new JSONObject(); + result.put("code", 0); + return result; + } + } + + public static void sendAlarmWx(Map map){ + JSONObject result = doPost("https://api.evo-techina.com/cloud/alarm/send/hq_web", map, Collections.asMap("Content-Type", "application/json","Accept-Language", "zh-CN")); + } + + private static JSONObject doPost(String url, Map map, Map headersMap) { + String json = JSON.toJSONString(map); + log.info("doPost请求== 请求接口 ===={}, 请求参数 ===={}, 请求headers ===={}", url, json, JSON.toJSONString(headersMap)); + HttpClient client = new DefaultHttpClient(); + HttpPost post = new HttpPost(url); + JSONObject jsonObject = new JSONObject(); + try { + StringEntity entity = new StringEntity(json, ContentType.APPLICATION_JSON); + post.setEntity(entity); + Iterator keyIterator = headersMap.keySet().iterator(); + while (keyIterator.hasNext()){ + String key = keyIterator.next();; + post.setHeader(key, headersMap.get(key)); + } + String result = EntityUtils.toString(client.execute(post).getEntity(), "UTF-8"); + jsonObject = JSONObject.parseObject(result); + } catch (Exception e) { + e.printStackTrace(); + } finally { + post.releaseConnection(); + } + return jsonObject; + } + + private static JSONObject doGet(String url, Map map, Map headersMap) { + log.info("doPost请求== 请求接口 ===={}", url); + String json = JSON.toJSONString(map); + log.info("doPost请求== 请求参数 ===={}", json); + log.info("doPost请求== 请求headers ===={}", JSON.toJSONString(headersMap)); + // 拼接参数请求 + if(Collections.isNotEmpty(map)){ + StringBuffer sb = new StringBuffer(url).append("?"); + Integer maxParams = map.size(); + Integer i = 1; + for (String key : map.keySet()) { + sb.append(key).append("=").append(map.get(key)); + if(i< maxParams){ + sb.append("&"); + } + } + //转换url + url = sb.toString(); + } + HttpClient client = new DefaultHttpClient(); + HttpGet get = new HttpGet(url); + JSONObject jsonObject = new JSONObject(); + try { + Iterator keyIterator = headersMap.keySet().iterator(); + while (keyIterator.hasNext()){ + String key = keyIterator.next();; + get.setHeader(key, headersMap.get(key)); + } + + String result = EntityUtils.toString(client.execute(get).getEntity(), "UTF-8"); + jsonObject = JSONObject.parseObject(result); + } catch (Exception e) { + e.printStackTrace(); + } finally { + get.releaseConnection(); + } + return jsonObject; + } + + public static void main(String[] args) { + JSONObject res = doGet("https://www.cwl.gov.cn/cwl_admin/front/cwlkj/search/kjxx/findDrawNotice?name=ssq&issueCount=&issueStart=&issueEnd=&dayStart=&dayEnd=&pageNo=1&pageSize=136&week=&systemType=PC", Collections.emptyMap(), Collections.emptyMap()); + JSONArray arr= res.getJSONArray("result"); + Map> resMap = Collections.emptyMap(); + for (int i = 0; i< arr.size(); i++){ + JSONObject d = arr.getJSONObject(i); + resMap.put(d.getLong("code"), Collections.asMap("red", d.getString("red").replaceAll(",", " "), "blue", d.getString("blue"))); + } + List sortList = resMap.keySet().stream().sorted((x, y)-> {return x.compareTo(y);}).collect(Collectors.toList()); + for (Long code: sortList){ + System.out.println("INSERT INTO `cp`.`lottery` (`issue_number`, `lottery_red_number`, `lottery_blue_number`, `create_time`) VALUES ('"+code+"', '"+resMap.get(code).get("red")+"', '"+resMap.get(code).get("blue")+"', now());"); + } + } +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/utils/sendCloud/RsaEcbPkcsFullUtil.java b/web-server/src/main/java/com/evotech/hd/webserver/utils/sendCloud/RsaEcbPkcsFullUtil.java new file mode 100644 index 0000000..6d8f745 --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/utils/sendCloud/RsaEcbPkcsFullUtil.java @@ -0,0 +1,143 @@ +package com.evotech.hd.webserver.utils.sendCloud; + +import cn.hutool.crypto.SecureUtil; +import cn.hutool.crypto.asymmetric.AsymmetricAlgorithm; +import cn.hutool.crypto.asymmetric.Sign; +import cn.hutool.crypto.asymmetric.SignAlgorithm; +import com.evotech.hd.webserver.utils.ParamUtils; + +import javax.crypto.Cipher; +import java.security.KeyFactory; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.Signature; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; +import java.util.Base64; + +/** + * 加密解密类 + * + * @ClassName:RsaEcbPkcs1FullUtil + * @date: 2025年08月19日 13:43 + * @author: andy.shi + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +public class RsaEcbPkcsFullUtil { + + // 签名算法:SHA256哈希 + RSA/ECB/PKCS1Padding + private static final String SIGNATURE_ALGORITHM = "SHA256withRSA/ECB/PKCS1Padding"; + // RSA密钥算法 + private static final String KEY_ALGORITHM = "RSA"; + private static byte[] privateKeyStr = null; + private static PrivateKey privateKey = null; + private static PublicKey publicKey = null; +// static{ +// try { +// privateKeyStr = Base64.getDecoder().decode(ParamUtils.findStationSecretKey()); +// privateKey = base64ToPrivateKey(); +// publicKey = base64ToPublicKey(); +// } catch (Exception e) { +// throw new RuntimeException(e); +// } +// } + + /** + * 用私钥对消息签名(SHA256withRSA/ECB/PKCS1Padding) + * @param message 原始消息 + * @return 签名(Base64编码) + */ + public static String sign(String message) throws Exception { + Sign sign = SecureUtil.sign(SignAlgorithm.SHA256withRSA, ParamUtils.findStationSecretKey(), null); + return Base64.getEncoder().encodeToString(sign.sign(message)); + } + + /*** + * 私钥消息加密 + * @param message + * @return + * @throws Exception + */ + public static String encrypt(String message) throws Exception { +// e.getBytes("UTF-8")); + return Base64.getEncoder().encodeToString(SecureUtil.desede(privateKeyStr).encrypt(message)); + } + + + /** + * 用公钥验证签名(SHA256withRSA/ECB/PKCS1Padding) + * @param message 解密后的消息 + * @param signature 接收的签名(Base64编码) + * @return 验证是否通过 + */ + public static boolean verify(String message, String signature) throws Exception { + Signature verifier = Signature.getInstance(AsymmetricAlgorithm.RSA_ECB_PKCS1.getValue()); + verifier.initVerify(publicKey); // 公钥验证模式 + verifier.update(message.getBytes("UTF-8")); + byte[] signBytes = Base64.getDecoder().decode(signature); + return verifier.verify(signBytes); + } + + /** + * 用私钥解密消息(RSA/ECB/PKCS1Padding) + * @param encryptedMessage 加密后的消息(Base64编码) + * @return 解密后的原始消息 + */ + public static String decrypt(String encryptedMessage) throws Exception { + Cipher cipher = Cipher.getInstance(AsymmetricAlgorithm.RSA_ECB_PKCS1.getValue()); + cipher.init(Cipher.DECRYPT_MODE, privateKey); // 私钥解密模式 + byte[] encryptedBytes = Base64.getDecoder().decode(encryptedMessage); + byte[] decryptedBytes = cipher.doFinal(encryptedBytes); + return new String(decryptedBytes, "UTF-8"); + } + + + /*** + * 私钥消息加密 + * @param message + * @return + * @throws Exception + */ + public static String encryptWithPrivateKey(String message) throws Exception { +// Cipher cipher = Cipher.getInstance(AsymmetricAlgorithm.RSA_ECB_PKCS1.getValue()); +// cipher.init(Cipher.ENCRYPT_MODE, privateKey); // 私钥加密模式 +// // 3. 加密并返回Base64结果 +// byte[] encryptedBytes = cipher.doFinal(message.getBytes("UTF-8")); + return Base64.getEncoder().encodeToString(SecureUtil.desede(privateKeyStr).encrypt(message)); + } + + /** + * 用公钥解密(私钥加密的数据必须用对应公钥解密) + * @param encryptedMessage 加密后的消息(Base64) + * @return 解密后的原始消息 + */ + public static String decryptWithPublicKey(String encryptedMessage) throws Exception { + Cipher cipher = Cipher.getInstance(AsymmetricAlgorithm.RSA_ECB_PKCS1.getValue()); + cipher.init(Cipher.DECRYPT_MODE, publicKey); // 公钥解密模式 + byte[] encryptedBytes = Base64.getDecoder().decode(encryptedMessage); + byte[] decryptedBytes = cipher.doFinal(encryptedBytes); + return new String(decryptedBytes, "UTF-8"); + } + + + /** + * Base64字符串私钥转为PrivateKey对象(PKCS#8格式) + */ + private static PrivateKey base64ToPrivateKey() throws Exception { + byte[] keyBytes = Base64.getDecoder().decode(ParamUtils.findStationSecretKey()); + PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes); // 私钥用PKCS8规范 + KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); + return keyFactory.generatePrivate(keySpec); + } + + /** + * Base64字符串转为PublicKey对象(X.509格式) + */ + private static PublicKey base64ToPublicKey() throws Exception { + byte[] keyBytes = Base64.getDecoder().decode(ParamUtils.findStationPublicKey()); + X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes); // 公钥用X.509规范 + KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); + return keyFactory.generatePublic(keySpec); + } + +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/websocket/config/WebSocketConfig.java b/web-server/src/main/java/com/evotech/hd/webserver/websocket/config/WebSocketConfig.java new file mode 100644 index 0000000..5adff8a --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/websocket/config/WebSocketConfig.java @@ -0,0 +1,22 @@ +package com.evotech.hd.webserver.websocket.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.stereotype.Component; +import org.springframework.web.socket.server.standard.ServerEndpointExporter; + +/** + * 类 + * + * @ClassName:WebSocketConfig + * @date: 2025年06月24日 9:58 + * @author: andy.shi + * @contact: 17330188597 + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ +@Component +public class WebSocketConfig { + @Bean + public ServerEndpointExporter serverEndpointExporter(){ + return new ServerEndpointExporter(); + } +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/websocket/controller/WebSocketService.java b/web-server/src/main/java/com/evotech/hd/webserver/websocket/controller/WebSocketService.java new file mode 100644 index 0000000..924141f --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/websocket/controller/WebSocketService.java @@ -0,0 +1,66 @@ +package com.evotech.hd.webserver.websocket.controller; + + +import io.swagger.annotations.Api; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import javax.websocket.OnClose; +import javax.websocket.OnError; +import javax.websocket.OnOpen; +import javax.websocket.Session; +import javax.websocket.server.ServerEndpoint; + + +/** + * websocket服务类 + * + * @ClassName:WebSocketService + * @date: 2025年06月24日 9:59 + * @author: andy.shi + * @contact: 17330188597 + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + * + * * @ServerEndpoint 注解是一个类层次的注解,它的功能主要是将目前的类定义成一个websocket服务器端, + * * 注解的值将被用于监听用户连接的终端访问URL地址,客户端可以通过这个URL来连接到WebSocket服务器端 + */ +@Component +@ServerEndpoint("/websocket") +@Slf4j +public class WebSocketService { + /** + * 建立连接 + * @param session 当前会话 + */ + @OnOpen + public void openConnection(Session session) { + WebSocketUtils.SESSION_POOLS.put(session.getId(), session); + WebSocketUtils.onlineNum.incrementAndGet(); + log.info("建立连接! 当前连接数为: {}", WebSocketUtils.onlineNum); + } + + /** + * 关闭连接 + * + */ + @OnClose + public void closeConnection(Session session) { + WebSocketUtils.SESSION_POOLS.remove(session.getId()); + int cnt = WebSocketUtils.onlineNum.decrementAndGet(); + log.info("断开连接, 当前连接数为:{}", cnt); + } + + /** + * 发生错误 + * + * @param throwable 错误信息 + */ + @OnError + public void errorConnection(Session session, Throwable throwable) throws Exception { + if (session.isOpen()){ + // 关闭连接 + session.close(); + } + log.error("sessionId: " + session.getId() + " 发生错误", throwable); + } +} diff --git a/web-server/src/main/java/com/evotech/hd/webserver/websocket/controller/WebSocketUtils.java b/web-server/src/main/java/com/evotech/hd/webserver/websocket/controller/WebSocketUtils.java new file mode 100644 index 0000000..ee4e11e --- /dev/null +++ b/web-server/src/main/java/com/evotech/hd/webserver/websocket/controller/WebSocketUtils.java @@ -0,0 +1,87 @@ +package com.evotech.hd.webserver.websocket.controller; + +import lombok.extern.slf4j.Slf4j; + +import javax.websocket.Session; +import java.io.IOException; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * websocket工具类 + * + * @ClassName:WebSocketUtils + * @date: 2025年06月24日 10:26 + * @author: andy.shi + * @contact: 17330188597 + * @remark: 开发人员联系方式 1042025947@qq.com/微信同步 + */ + +@Slf4j +public class WebSocketUtils { + + /** + * 当前在线连接数 + */ + public static final AtomicInteger onlineNum = new AtomicInteger(0); + /** + * 存放每个客户端对应的Session + */ + public static final Map SESSION_POOLS = new ConcurrentHashMap<>(); +// public static final List SESSION_POOLS = Collections.emptyList(); + + /** + * 消息发送 + * + * @param session 当前会话 + * @param message 消息内容 + * @throws IOException IO异常 + */ + public static void sendMessage(Session session, String message) throws IOException { + if (session != null && session.isOpen()) { + synchronized (session) { + session.getBasicRemote().sendText(message); + } + } + } + + /** + * 消息发送 + * + * @param wechatId 用户ID + * @param message 消息内容 + * @throws IOException IO异常 + */ +// public static void sendMessage(String message){ +// try { +//// //记录日志 +//// try { +//// SpringUtil.getBean(LogWebSocketDao.class).insert(new LogWebSocket(wechatId, message)); +//// } catch (Exception e) { +//// log.error("日志记录错误"+e.getMessage()); +//// } +// // sendMessage(WebSocketUtils.SESSION_POOLS.get(wechatId), message); +// } catch (IOException e) { +// log.error("信息推送失败, 用户的微信ID: {}, 推送消息是: {}, 异常原因是:{}", wechatId, message, e.getMessage()); +// } +// } + + /** + * 消息广播 + * + * @param message 消息内容 + */ + public static void broadCastMessage(String message) { + WebSocketUtils.SESSION_POOLS.forEach((k, v) -> { + if (v.isOpen()) { + try { + sendMessage(v, message); + } catch (IOException e) { + log.error("消息广播推送失败, 推送消息是: {}, 异常原因是:{}", message, e.getMessage()); + throw new RuntimeException(e); + } + } + }); + } +} diff --git a/web-server/src/main/resources/application-dev.yml b/web-server/src/main/resources/application-dev.yml new file mode 100644 index 0000000..107eca5 --- /dev/null +++ b/web-server/src/main/resources/application-dev.yml @@ -0,0 +1,25 @@ +spring: + redis: + host: 192.168.16.128 + port: 6379 + database: 13 + password: hbyt2025 + # 连接超时时间 + timeout: 10s + lettuce: + pool: + # 连接池中的最小空闲连接 + min-idle: 0 + # 连接池中的最大空闲连接 + max-idle: 8 + # 连接池的最大数据库连接数 + max-active: 8 + # #连接池最大阻塞等待时间(使用负值表示没有限制) + max-wait: -1ms +mqtt: + broker: tcp://192.168.16.128:1883 # MQTT服务器地址(如Mosquitto默认端口1883) + client-id: springboot-mqtt-${random.value} # 客户端ID(添加随机数确保唯一) + username: admin # 可选,MQTT服务器认证用户名 + password: hbyt123456 # 可选,MQTT服务器认证密码 + keep-alive: 60 # 心跳间隔(秒) + connection-timeout: 30 # 连接超时(秒) \ No newline at end of file diff --git a/web-server/src/main/resources/application-pro.yml b/web-server/src/main/resources/application-pro.yml new file mode 100644 index 0000000..f1b4ff2 --- /dev/null +++ b/web-server/src/main/resources/application-pro.yml @@ -0,0 +1,25 @@ +spring: + redis: + host: localhost + port: 6379 + database: 13 + password: + # 连接超时时间 + timeout: 10s + lettuce: + pool: + # 连接池中的最小空闲连接 + min-idle: 0 + # 连接池中的最大空闲连接 + max-idle: 8 + # 连接池的最大数据库连接数 + max-active: 8 + # #连接池最大阻塞等待时间(使用负值表示没有限制) + max-wait: -1ms +mqtt: + broker: tcp://192.168.5.210:1883 # MQTT服务器地址(如Mosquitto默认端口1883) + client-id: hq-web-${random.value} # 客户端ID(添加随机数确保唯一) + username: admin # 可选,MQTT服务器认证用户名 + password: hbyt!123 # 可选,MQTT服务器认证密码 + keep-alive: 60 # 心跳间隔(秒) + connection-timeout: 30 # 连接超时(秒) \ No newline at end of file diff --git a/web-server/src/main/resources/application-quartz.yml b/web-server/src/main/resources/application-quartz.yml new file mode 100644 index 0000000..68e6396 --- /dev/null +++ b/web-server/src/main/resources/application-quartz.yml @@ -0,0 +1,61 @@ +spring: + # Quartz配置 + quartz: + # 任务存储类型:jdbc表示持久化到数据库 + job-store-type: jdbc + + # JDBC相关配置 + jdbc: + # 初始化表结构:never表示不自动初始化,生产环境推荐手动执行SQL + initialize-schema: never + # 数据源名称,对应spring.datasource配置 + data-source-name: dataSource + + # 调度器属性 + properties: + org: + quartz: + scheduler: + instanceName: SpringBootQuartzScheduler + instanceId: AUTO # 自动生成实例ID + makeSchedulerThreadDaemon: true # 调度器线程作为守护线程 + + # 线程池配置 + threadPool: + class: org.quartz.simpl.SimpleThreadPool + threadCount: 5 # 线程数量 + threadPriority: 5 # 线程优先级 + threadNamePrefix: QuartzThread- + makeThreadsDaemons: true # 线程作为守护线程 + + # 持久化配置 + jobStore: + class: org.springframework.scheduling.quartz.LocalDataSourceJobStore + driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate + tablePrefix: QRTZ_ # 表前缀 + isClustered: false # 非集群模式 + useProperties: true # 使用Properties存储JobDataMap + misfireThreshold: 60000 # 任务错过执行的阈值(毫秒) + clusterCheckinInterval: 5000 # 集群节点检查间隔(毫秒) + + # 插件配置 + plugin: + shutdownhook: + class: org.quartz.plugins.management.ShutdownHookPlugin + cleanShutdown: true # 优雅关闭 + + # 调度器启动配置 + auto-startup: true # 自动启动 + startup-delay: 3000 # 启动延迟(毫秒) + overwrite-existing-jobs: false # 不覆盖已存在的任务 + wait-for-jobs-to-complete-on-shutdown: true # 关闭时等待任务完成 + wait-for-jobs-to-complete-on-shutdown-timeout: 60000 # 等待超时时间 + + # 日志配置 + logging: + level: + org.quartz: INFO # Quartz日志级别 + com.example: INFO + pattern: + console: "%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n" + diff --git a/web-server/src/main/resources/application.yml b/web-server/src/main/resources/application.yml new file mode 100644 index 0000000..02411e4 --- /dev/null +++ b/web-server/src/main/resources/application.yml @@ -0,0 +1,151 @@ +server: + port: 9808 + servlet: + context-path: + tomcat: + # tomcat的URI编码 + uri-encoding: UTF-8 + # 连接数满后的排队数,默认为100 + accept-count: 1000 + threads: + # tomcat最大线程数,默认为200 + max: 800 + # Tomcat启动初始化的线程数,默认值10 + min-spare: 100 +logging: + level: + root: INFO + com.evotech: DEBUG + org.springframework: WARN + org.springframework.security: debug + +spring: + jackson: + default-property-inclusion: always + quartz: + jdbc: + initialize-schema: always + job-store-type: jdbc + profiles: + active: ${profiles.active:pro},mongo${profiles.active:},quartz #development + # 服务模块 + devtools: + restart: + enabled: false +# redis: +# host: 192.168.16.128 +# port: 6379 +# database: 13 +# password: hbyt2025 +# # 连接超时时间 +# timeout: 10s +# lettuce: +# pool: +# # 连接池中的最小空闲连接 +# min-idle: 0 +# # 连接池中的最大空闲连接 +# max-idle: 8 +# # 连接池的最大数据库连接数 +# max-active: 8 +# # #连接池最大阻塞等待时间(使用负值表示没有限制) +# max-wait: -1ms + datasource: + type: com.alibaba.druid.pool.DruidDataSource + driverClassName: com.mysql.cj.jdbc.Driver + druid: + master: + url: jdbc:mysql://localhost:3306/zk?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 + username: root + password: hbyt123456 + # 从库数据源 + slave: + # 从数据源开关/默认关闭 + enabled: false + url: + username: + password: + # 初始连接数 + initialSize: 5 + # 最小连接池数量 + minIdle: 10 + # 最大连接池数量 + maxActive: 20 + # 配置获取连接等待超时的时间 + maxWait: 60000 + # 配置连接超时时间 + connectTimeout: 30000 + # 配置网络超时时间 + socketTimeout: 60000 + # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 + timeBetweenEvictionRunsMillis: 60000 + # 配置一个连接在池中最小生存的时间,单位是毫秒 + minEvictableIdleTimeMillis: 300000 + # 配置一个连接在池中最大生存的时间,单位是毫秒 + maxEvictableIdleTimeMillis: 900000 + # 配置检测连接是否有效 + validationQuery: SELECT 1 FROM DUAL + testWhileIdle: true + testOnBorrow: false + testOnReturn: false + webStatFilter: + enabled: true + statViewServlet: + enabled: true + # 设置白名单,不填则允许所有访问 + allow: + url-pattern: /druid/* + # 控制台管理用户名和密码 + login-username: evo + login-password: 123456 + filter: + stat: + enabled: true + # 慢SQL记录 + log-slow-sql: true + slow-sql-millis: 1000 + merge-sql: true + wall: + config: + multi-statement-allow: true + + +# MyBatis配置 +mybatis-plus: + # 搜索指定包别名 + # 配置mapper的扫描,找到所有的mapper.xml映射文件 + # 加载全局的配置文件 + # configuration: classpath:mybatis/mybatis-config.xml + configuration: + map-underscore-to-camel-case: true + log-impl: org.apache.ibatis.logging.stdout.StdOutImpl + type-aliases-package: com.evotech.** + mapper-locations: classpath:mapper/**/*Mapper.xml + global-config: + db-config: + id-type: auto + logic-delete-value: 1 + logic-not-delete-value: 0 + check-config-location: false + +# PageHelper分页插件 +pagehelper: + helperDialect: mysql + supportMethodsArguments: true + params: count=countSql + + +swagger: + enable: true + +jwt: + accessToken: + expireTime: 1000000 + +# +#mqtt: +# broker: tcp://192.168.16.128:1883 # MQTT服务器地址(如Mosquitto默认端口1883) +# client-id: springboot-mqtt-${random.value} # 客户端ID(添加随机数确保唯一) +# username: admin # 可选,MQTT服务器认证用户名 +# password: hbyt123456 # 可选,MQTT服务器认证密码 +# keep-alive: 60 # 心跳间隔(秒) +# connection-timeout: 30 # 连接超时(秒) \ No newline at end of file diff --git a/web-server/src/main/resources/mapper/AccessStrategyMapper.xml b/web-server/src/main/resources/mapper/AccessStrategyMapper.xml new file mode 100644 index 0000000..0a4027d --- /dev/null +++ b/web-server/src/main/resources/mapper/AccessStrategyMapper.xml @@ -0,0 +1,19 @@ + + + + + + id, `name`, create_time, extendeds, + strategy,power_strategy, charging_strategy, instant_charge_power, minimum_safety_stock, effective_rgv_no, minimum_safety_stock,lowest_battery_swap_soc,full_of_limit_soc, + valid + + + + + + + diff --git a/web-server/src/main/resources/mapper/BatteryCompartmentMapper.xml b/web-server/src/main/resources/mapper/BatteryCompartmentMapper.xml new file mode 100644 index 0000000..852415c --- /dev/null +++ b/web-server/src/main/resources/mapper/BatteryCompartmentMapper.xml @@ -0,0 +1,227 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + a.id, a.name, a.create_time,a.extendeds, a.battery_compartment_no,a.status, a.state, a.sort, a.exists_battery,a.charger_status, a.battery_place_status, + a.battery_communication_status, a.direct_current_communication_status, a.low_pressure_power_supply_status, a.auxiliary_power_supply_status, + a.maximum_charging_power, a.direct_current_meter_voltage, + b.id as batteryId, + b.battery_code as batteryCode, + b.battery_type as batteryType, + b.battery_mode as batteryMode, + b.extendeds as batteryExtendeds, + b.battery_compartment_id as batteryCompartmentId, + b.name as batteryName, + b.create_time as createTime, + b.storage_Status as storageStatus, + b.soh as soh, + b.rated_capacity as ratedCapacity, + b.monomer_quantity as monomerQuantity, + b.number_of_probes as numberOfProbes, + b.soc as soc + + + + + + + + + + + + + + + + + + + + + + + + + + + + INSERT INTO `zk`.`sc_battery_compartment` ( + `id`, + `battery_compartment_no`, + `status`, `state`, `sort`, + `create_time`, `extendeds`, + `exists_battery`, `charger_status`, + `battery_place_status`, + `battery_communication_status`, + `direct_current_communication_status`, + `low_pressure_power_supply_status`, + `auxiliary_power_supply_status`, + `direct_current_meter_voltage`, + `maximum_charging_power`, + `name`, + `deposit_time`, + `order_charging_id`, + `all_power` + ) VALUES ( + #{id}, + #{batteryCompartmentNo}, + #{status}, + #{state}, + #{sort}, + #{createTime}, + #{extendeds}, + #{existsBattery}, + #{chargerStatus,jdbcType=VARCHAR}, + #{batteryPlaceStatus}, + #{batteryCommunicationStatus}, + #{directCurrentCommunicationStatus}, + #{lowPressurePowerSupplyStatus}, + #{auxiliaryPowerSupplyStatus}, + #{directCurrentMeterVoltage}, + #{maximumChargingPower}, + #{name}, + #{depositTime}, + #{orderChargingId}, + #{allPower}); + + + + diff --git a/web-server/src/main/resources/mapper/BatteryStrategyMapper.xml b/web-server/src/main/resources/mapper/BatteryStrategyMapper.xml new file mode 100644 index 0000000..01be629 --- /dev/null +++ b/web-server/src/main/resources/mapper/BatteryStrategyMapper.xml @@ -0,0 +1,35 @@ + + + + + + a.id, a.name, a.create_time,a.extendeds, a.begin_time, a.end_time,a.charging_power, a.del + + + + + + + + + + + diff --git a/web-server/src/main/resources/mapper/BatterySwapStepMapper.xml b/web-server/src/main/resources/mapper/BatterySwapStepMapper.xml new file mode 100644 index 0000000..5b58a21 --- /dev/null +++ b/web-server/src/main/resources/mapper/BatterySwapStepMapper.xml @@ -0,0 +1,16 @@ + + + + + + id, `name`, order_id, step, step_time, human_operation, create_time, error + + + + + + diff --git a/web-server/src/main/resources/mapper/InstructionLogMapper.xml b/web-server/src/main/resources/mapper/InstructionLogMapper.xml new file mode 100644 index 0000000..870b157 --- /dev/null +++ b/web-server/src/main/resources/mapper/InstructionLogMapper.xml @@ -0,0 +1,17 @@ + + + + + + a.id, a.`name`, a.create_time, a.extendeds, a.`type`, a.request_uri, a.request_uri, a.method, a.`method`, a.params, a.result, + a.exception, a.record_time, a.remarks + + + + + diff --git a/web-server/src/main/resources/mapper/LogMapper.xml b/web-server/src/main/resources/mapper/LogMapper.xml new file mode 100644 index 0000000..5cf8e9d --- /dev/null +++ b/web-server/src/main/resources/mapper/LogMapper.xml @@ -0,0 +1,17 @@ + + + + + + a.id, a.`name`, a.`type`, a.remote_addr, a.request_uri, a.request_type, a.`method`, a.params, a.result, + a.user_agent, a.exception, a.record_time, a.remarks, a.create_time + + + + + diff --git a/web-server/src/main/resources/mapper/OrderChargingMapper.xml b/web-server/src/main/resources/mapper/OrderChargingMapper.xml new file mode 100644 index 0000000..d4e3ec8 --- /dev/null +++ b/web-server/src/main/resources/mapper/OrderChargingMapper.xml @@ -0,0 +1,28 @@ + + + + + + a.id, a.`name`, a.create_time, a.extendeds, + a.charging_no, +a.battery_code, +a.battery_compartment_id, +a.strategy_info, +a.status, +a.power_consumption, +a.start_electric, +a.end_electric, +a.begin_time, +a.end_time + + + + + + + diff --git a/web-server/src/main/resources/mapper/OrderReservationMapper.xml b/web-server/src/main/resources/mapper/OrderReservationMapper.xml new file mode 100644 index 0000000..c83cca0 --- /dev/null +++ b/web-server/src/main/resources/mapper/OrderReservationMapper.xml @@ -0,0 +1,18 @@ + + + + + + id, `name`, `source`, cloud_reservation_id, reservation_name, reservation_phone, plate_num, wechat_user_id, wechat_user_name, + reservation_time, swap_day, swap_duration, status, push_status, create_time + + + + + + diff --git a/web-server/src/main/resources/mapper/OrderSwapMapper.xml b/web-server/src/main/resources/mapper/OrderSwapMapper.xml new file mode 100644 index 0000000..60b81e4 --- /dev/null +++ b/web-server/src/main/resources/mapper/OrderSwapMapper.xml @@ -0,0 +1,46 @@ + + + + + + a.id, a.name, a.create_time,a.extendeds, a.cloud_order_id, +a.order_no, +a.order_pre_id, +a.pre_wechat_id, +a.order_pre_name, +a.order_type, +a.plate_num, +a.order_time, +a.order_amount, +a.status, +a.pay_type, +a.pay_status, +a.pay_time, +a.fee_type, +a.order_fee, +a.service_fee, +a.service_time_begin, +a.service_time_end, +a.electricity_quantity, +a.rent_bat_no, +a.rent_bat_code, +a.rent_bat_soc, +a.return_bat_no, +a.return_bat_code, +a.return_bat_soc, +a.change_mode, +a.change_lane, +a.push_status + + + + + + + + diff --git a/web-server/src/main/resources/mapper/ParamsMapper.xml b/web-server/src/main/resources/mapper/ParamsMapper.xml new file mode 100644 index 0000000..e2e57e3 --- /dev/null +++ b/web-server/src/main/resources/mapper/ParamsMapper.xml @@ -0,0 +1,34 @@ + + + + + + a.id, a.name, a.create_time,a.extendeds, +a.group_id, +a.param_value, +a.param_code, +a.description, +a.expected_results, +a.verify_result, +a.err_message + + + + + + + + + + + diff --git a/web-server/src/main/resources/mapper/RunningInstructionsDetailMapper.xml b/web-server/src/main/resources/mapper/RunningInstructionsDetailMapper.xml new file mode 100644 index 0000000..f51dd2e --- /dev/null +++ b/web-server/src/main/resources/mapper/RunningInstructionsDetailMapper.xml @@ -0,0 +1,24 @@ + + + + + + id, `name`, dict_value, type_code, extendeds, sort, default_value, create_time + + + + + + + + + diff --git a/web-server/src/test/java/com/evotech/hd/webserver/WebServerApplicationTests.java b/web-server/src/test/java/com/evotech/hd/webserver/WebServerApplicationTests.java new file mode 100644 index 0000000..236553b --- /dev/null +++ b/web-server/src/test/java/com/evotech/hd/webserver/WebServerApplicationTests.java @@ -0,0 +1,13 @@ +package com.evotech.hd.webserver; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class WebServerApplicationTests { + + @Test + void contextLoads() { + } + +}