Compare commits

...

6 Commits

60 changed files with 2088 additions and 415 deletions

2
.gitignore vendored
View File

@ -51,6 +51,8 @@ logs
doc
bin
.github
protocolFile
nginx
ry.bat
/LICENSE

View File

@ -71,6 +71,7 @@
<version>2.5.15</version>
<configuration>
<fork>true</fork> <!-- 如果没有该配置devtools不会生效 -->
<includeSystemScope>true</includeSystemScope>
</configuration>
<executions>
<execution>

View File

@ -4,6 +4,7 @@ import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.scheduling.annotation.EnableAsync;
/**
* 启动程序
@ -12,21 +13,12 @@ import org.springframework.cache.annotation.EnableCaching;
*/
@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })
@EnableCaching
@EnableAsync
public class RuoYiApplication
{
public static void main(String[] args)
{
// System.setProperty("spring.devtools.restart.enabled", "false");
SpringApplication.run(RuoYiApplication.class, args);
// System.out.println("(♥◠‿◠)ノ゙ 若依启动成功 ლ(´ڡ`ლ)゙ \n" +
// " .-------. ____ __ \n" +
// " | _ _ \\ \\ \\ / / \n" +
// " | ( ' ) | \\ _. / ' \n" +
// " |(_ o _) / _( )_ .' \n" +
// " | (_,_).' __ ___(_ o _)' \n" +
// " | |\\ \\ | || |(_,_)' \n" +
// " | | \\ `' /| `-' / \n" +
// " | | \\ / \\ / \n" +
// " ''-' `'-' `-..-' ");
}
}

View File

@ -1,6 +1,7 @@
package com.ruoyi.web.controller.board;
import com.ruoyi.board.domain.AlertPlan;
import com.ruoyi.board.domain.dto.AlertPlanAndPlanTypeDTO;
import com.ruoyi.board.service.IAlertPlanService;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
@ -34,7 +35,7 @@ public class AlertPlanController extends BaseController {
@GetMapping("/list")
public TableDataInfo list(AlertPlan alertPlan) {
startPage();
List<AlertPlan> list = alertPlanService.selectList(alertPlan);
List<AlertPlanAndPlanTypeDTO> list = alertPlanService.listPage(alertPlan);
return getDataTable(list);
}
@ -56,7 +57,7 @@ public class AlertPlanController extends BaseController {
@PreAuthorize("@ss.hasPermi('alert:plan:query')")
@GetMapping(value = "/{id}")
public AjaxResult getInfo(@PathVariable("id") Long id) {
return success(alertPlanService.getById(id));
return success(alertPlanService.getDTOById(id));
}
/**

View File

@ -33,9 +33,9 @@ public class BoardInfoController extends BaseController {
*/
@PreAuthorize("@ss.hasPermi('board:info:list')")
@GetMapping("/list")
public TableDataInfo list(BoardInfo BoardInfo) {
public TableDataInfo list(BoardInfo boardInfo) {
startPage();
List<BoardInfo> list = boardInfoService.list();
List<BoardInfo> list = boardInfoService.listPage(boardInfo);
return getDataTable(list);
}

View File

@ -0,0 +1,99 @@
package com.ruoyi.web.controller.board;
import com.ruoyi.board.domain.PlanType;
import com.ruoyi.board.service.IPlanTypeService;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.poi.ExcelUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
/**
* 计划发布中的类型Controller
*
* @author fuhao
* @date 2024-09-02
*/
@RestController
@RequestMapping("/plan/type")
public class PlanTypeController extends BaseController {
@Autowired
private IPlanTypeService planTypeService;
/**
* 查询计划发布中的类型列表
*/
@PreAuthorize("@ss.hasPermi('plan:type:list')")
@GetMapping("/list")
public AjaxResult list(PlanType planType) {
List<PlanType> list = planTypeService.list();
return success(list);
}
/**
* 导出计划发布中的类型列表
*/
@PreAuthorize("@ss.hasPermi('plan:type:export')")
@Log(title = "计划发布中的类型", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(HttpServletResponse response, PlanType planType) {
List<PlanType> list = planTypeService.list();
ExcelUtil<PlanType> util = new ExcelUtil<PlanType>(PlanType.class);
util.exportExcel(response, list, "计划发布中的类型数据");
}
/**
* 获取计划发布中的类型详细信息
*/
@PreAuthorize("@ss.hasPermi('plan:type:query')")
@GetMapping(value = "/{id}")
public AjaxResult getInfo(@PathVariable("id") Long id) {
return success(planTypeService.getById(id));
}
/**
* 新增计划发布中的类型
*/
@PreAuthorize("@ss.hasPermi('plan:type:add')")
@Log(title = "计划发布中的类型", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@RequestBody PlanType planType) {
return toAjax(planTypeService.save(planType));
}
/**
* 修改计划发布中的类型
*/
@PreAuthorize("@ss.hasPermi('plan:type:edit')")
@Log(title = "计划发布中的类型", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@RequestBody PlanType planType) {
return toAjax(planTypeService.saveOrUpdate(planType));
}
/**
* 删除计划发布中的类型
*/
@PreAuthorize("@ss.hasPermi('plan:type:remove')")
@Log(title = "计划发布中的类型", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public AjaxResult remove(@PathVariable List<Integer> ids) {
return toAjax(planTypeService.removeByIds(ids));
}
/**
* 获取类型树列表
*/
@PreAuthorize("@ss.hasPermi('plan:type:list')")
@GetMapping("/planTree")
public AjaxResult deptTree(PlanType planType) {
return success(planTypeService.listPlanTree(planType));
}
}

View File

@ -37,7 +37,7 @@ public class PresetContentController extends BaseController
public TableDataInfo list(PresetContent presetContent)
{
startPage();
List<PresetContent> list = presetContentService.list();
List<PresetContent> list = presetContentService.listPage(presetContent);
return getDataTable(list);
}

View File

@ -1,24 +1,64 @@
package com.ruoyi.web.controller.sensor;
import com.ruoyi.common.annotation.Anonymous;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.sm4.SM4Utils;
import com.ruoyi.sensor.domain.PerceptionParams;
import com.ruoyi.sensor.service.ISensorDataService;
import org.apache.commons.codec.binary.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
@RestController
@RequestMapping("/sensor/data")
public class OpenSensorDataController {
private static final String SIGN_HEADER_NAME = "sign";
private static final String SIGN_KEY_BASE64 = "RkYnZj5lcChmMSZQQHJFUg==";
private static final Logger log = LoggerFactory.getLogger(OpenSensorDataController.class);
@Autowired
private ISensorDataService sensorDataService;
@Value("${remote.sign}")
private boolean signSwitch;
@Anonymous
@PostMapping("/judge")
public void list(PerceptionParams perceptionParams) {
sensorDataService.perceptionParamsHandler(perceptionParams);
public AjaxResult list(HttpServletRequest request, @RequestBody PerceptionParams perceptionParams) {
if (signSwitch) {
if (!verifyRequest(request, perceptionParams.getStr())) {
return AjaxResult.error("sign in error");
}
}
sensorDataService.putPerceptionParamsToMap(perceptionParams);
return AjaxResult.success();
}
private boolean verifyRequest(HttpServletRequest request, String str) {
byte[] keyByte = Base64.decodeBase64(SIGN_KEY_BASE64);
String sign = request.getHeader(SIGN_HEADER_NAME);
if (StringUtils.isEmpty(sign)) {
log.error("Header 中没有 sign");
return false;
}
// 验证是否正确
try {
byte[] bytes = SM4Utils.encryptData_ECB(str, keyByte);
String base64String = Base64.encodeBase64String(bytes);
log.info("sign:{}, base64String:{}", sign, base64String);
return sign.equals(base64String);
} catch (Exception e) {
log.error("{}", e.getMessage());
}
return false;
}
}

View File

@ -6,8 +6,8 @@ spring:
druid:
# 主库数据源
master:
# url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
url: jdbc:mysql://192.168.2.137:33306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
# url: jdbc:mysql://mdb:3306/ry_vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
url: jdbc:mysql://192.168.2.137:33306/ry_vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=GMT%2B8
username: root
password: 1qaz@WSX
# 从库数据源

View File

@ -107,15 +107,6 @@ token:
# 令牌有效期默认30分钟
expireTime: 30
## MyBatis配置
#mybatis:
# # 搜索指定包别名
# typeAliasesPackage: com.ruoyi.**.domain
# # 配置mapper的扫描找到所有的mapper.xml映射文件
# mapperLocations: classpath*:mapper/**/*Mapper.xml
# # 加载全局的配置文件
# configLocation: classpath:mybatis/mybatis-config.xml
# MyBatis Plus配置
mybatis-plus:
# 搜索指定包别名
@ -146,3 +137,11 @@ xss:
excludes: /system/notice
# 匹配链接
urlPatterns: /system/*,/monitor/*,/tool/*
remote:
# 签名验证开关
sign: false
# 状态上报地址
statusReport: "http://192.166.1.246:8080/status"
# 发布记录上报地址
logReport: "http://192.166.1.246:8080/publish_log"

View File

@ -148,7 +148,7 @@
<dependency>
<groupId>com.github.yulichang</groupId>
<artifactId>mybatis-plus-join-boot-starter</artifactId>
<version>1.4.5</version>
<version>1.4.13</version>
</dependency>
<dependency>
@ -172,4 +172,6 @@
</dependencies>
</project>

View File

@ -163,7 +163,7 @@ public class Constants
/**
* 定时任务白名单配置仅允许访问的包名如其他需要可以自行添加
*/
public static final String[] JOB_WHITELIST_STR = { "com.ruoyi.quartz.task" };
public static final String[] JOB_WHITELIST_STR = {"com.ruoyi.quartz.task", "com.ruoyi.sensor.task"};
/**
* 定时任务违规的字符

View File

@ -1,11 +1,12 @@
package com.ruoyi.common.core.domain;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.ruoyi.common.core.domain.entity.SysDept;
import com.ruoyi.common.core.domain.entity.SysMenu;
import java.io.Serializable;
import java.util.List;
import java.util.stream.Collectors;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.ruoyi.common.core.domain.entity.SysDept;
import com.ruoyi.common.core.domain.entity.SysMenu;
/**
* Treeselect树结构实体类

View File

@ -14,7 +14,7 @@ public class HttpConf
public static int CONNECT_TIMEOUT = 10000;
// 读取超时时间
public static int SO_TIMEOUT = 60000;
public static int SO_TIMEOUT = 3000;
// 最大连接数
public static int MAX_TOTAL_CONN = 200;

View File

@ -0,0 +1,114 @@
package com.ruoyi.common.utils.sm4;
import org.apache.commons.codec.binary.Base64;
import java.nio.charset.StandardCharsets;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* 国密SM4工具类自行实现国密SM4算法
* <p>有些项目中需要单独实现国密SM4算法
* <p>比如已经集成了老版本的bouncycastle但老版本的bouncycastle无法实现国密SM4算法但你又不能升级它而你又需要国密SM4算法
*
* <p>参考</p>
* <ol>
* <li><a href="https://blog.csdn.net/sunxmwebstudy/article/details/114699704">SM4对称加密算法及Java实现</a></li>
* <li><a href="https://www.jianshu.com/p/5ec8464b0a1b">SM4对称加密算法</a></li>
* </ol>
*
* <p>另外有开源项目实现了国密的全部算法</p>
* <a href="https://github.com/GmSSL/GmSSL-Java">GmSSL-Java(基于JNI)</a>
* <a href="https://gitee.com/openeuler/bgmprovider">BGMProvider</a>
* <a href="https://github.com/Tencent/TencentKonaSMSuite">腾讯Kona国密套件</a>
*/
public class SM4Utils {
public static byte[] encryptData_ECB(String plainText, byte[] keyBytes) throws Exception {
SM4_Context ctx = new SM4_Context();
ctx.isPadding = true;
ctx.mode = SM4_Body.SM4_ENCRYPT;
SM4_Body sm4Body = new SM4_Body();
sm4Body.sm4_setkey_enc(ctx, keyBytes);
return sm4Body.sm4_crypt_ecb(ctx, plainText.getBytes(StandardCharsets.UTF_8));
}
public static String decryptData_ECB(String cipherText, byte[] keyBytes) throws Exception {
if (cipherText != null && !cipherText.trim().isEmpty()) {
Pattern p = Pattern.compile("\\s*|\t|\r|\n");
Matcher m = p.matcher(cipherText);
cipherText = m.replaceAll("");
}
return decryptData_ECB(Base64.decodeBase64(cipherText),keyBytes);
}
public static String decryptData_ECB(byte[] cipherByte, byte[] keyBytes) throws Exception {
SM4_Context ctx = new SM4_Context();
ctx.isPadding = true;
ctx.mode = SM4_Body.SM4_DECRYPT;
SM4_Body sm4Body = new SM4_Body();
sm4Body.sm4_setkey_dec(ctx, keyBytes);
byte[] decrypted = sm4Body.sm4_crypt_ecb(ctx, cipherByte);
return new String(decrypted, StandardCharsets.UTF_8);
}
public static byte[] encryptData_CBC(String plainText, byte[] keyBytes, byte[] ivBytes) throws Exception {
SM4_Context ctx = new SM4_Context();
ctx.isPadding = true;
ctx.mode = SM4_Body.SM4_ENCRYPT;
SM4_Body sm4Body = new SM4_Body();
sm4Body.sm4_setkey_enc(ctx, keyBytes);
return sm4Body.sm4_crypt_cbc(ctx, ivBytes, plainText.getBytes(StandardCharsets.UTF_8));
}
public static String decryptData_CBC(String cipherText, byte[] keyBytes, byte[] ivBytes) throws Exception {
if (cipherText != null && !cipherText.trim().isEmpty()) {
Pattern p = Pattern.compile("\\s*|\t|\r|\n");
Matcher m = p.matcher(cipherText);
cipherText = m.replaceAll("");
}
return decryptData_CBC(Base64.decodeBase64(cipherText),keyBytes,ivBytes);
}
public static String decryptData_CBC(byte[] cipherByte, byte[] keyBytes, byte[] ivBytes) throws Exception {
SM4_Context ctx = new SM4_Context();
ctx.isPadding = true;
ctx.mode = SM4_Body.SM4_DECRYPT;
SM4_Body sm4Body = new SM4_Body();
sm4Body.sm4_setkey_dec(ctx, keyBytes);
byte[] decrypted = sm4Body.sm4_crypt_cbc(ctx, ivBytes, cipherByte);
return new String(decrypted, StandardCharsets.UTF_8);
}
public static void main(String[] args) throws Exception {
String plainText = "abc";
// key 16 位字符串
String key = "UVMxWFBaZXVSNm9YVTU0dg==";
byte[] keyByte = Base64.decodeBase64(key);
System.out.println("ECB模式加密");
byte[] cipherTextByte = SM4Utils.encryptData_ECB(plainText, keyByte);
String cipherText = Base64.encodeBase64String(cipherTextByte);
System.out.println("密文: " + cipherText);//qtu7LHVpilXxaz8mPW1VjQ==
String plainText2 = SM4Utils.decryptData_ECB(cipherText, keyByte);
System.out.println("解密: " + plainText2);
System.out.println("CBC模式加密");
String iv = "3131313131313131";
//byte[] ivByte = Base64.decodeBase64(iv);
byte[] ivByte = iv.getBytes();
byte[] cipherText2Byte = SM4Utils.encryptData_CBC(plainText,keyByte,ivByte);
String cipherText2 = Base64.encodeBase64String(cipherText2Byte);
System.out.println("加密密文: " + cipherText2);
String plainText3 = SM4Utils.decryptData_CBC(cipherText2Byte,keyByte,ivByte);
System.out.println("解密明文: " + plainText3);
}
}

View File

@ -0,0 +1,338 @@
package com.ruoyi.common.utils.sm4;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Arrays;
/**
* 自行实现国密SM4算法
*/
public class SM4_Body {
public static final int SM4_ENCRYPT = 1;
public static final int SM4_DECRYPT = 0;
private long GET_ULONG_BE(byte[] b, int i) {
long n = (long) (b[i] & 0xff) << 24 | (long) ((b[i + 1] & 0xff) << 16) | (long) ((b[i + 2] & 0xff) << 8) | (long) (b[i + 3] & 0xff) & 0xffffffffL;
return n;
}
private void PUT_ULONG_BE(long n, byte[] b, int i) {
b[i] = (byte) (int) (0xFF & n >> 24);
b[i + 1] = (byte) (int) (0xFF & n >> 16);
b[i + 2] = (byte) (int) (0xFF & n >> 8);
b[i + 3] = (byte) (int) (0xFF & n);
}
private long SHL(long x, int n) {
return (x & 0xFFFFFFFF) << n;
}
private long ROTL(long x, int n) {
return SHL(x, n) | x >> (32 - n);
}
private void SWAP(long[] sk, int i) {
long t = sk[i];
sk[i] = sk[(31 - i)];
sk[(31 - i)] = t;
}
//S
public static final byte[] SboxTable = {(byte) 0xd6, (byte) 0x90, (byte) 0xe9, (byte) 0xfe,
(byte) 0xcc, (byte) 0xe1, 0x3d, (byte) 0xb7, 0x16, (byte) 0xb6,
0x14, (byte) 0xc2, 0x28, (byte) 0xfb, 0x2c, 0x05, 0x2b, 0x67,
(byte) 0x9a, 0x76, 0x2a, (byte) 0xbe, 0x04, (byte) 0xc3,
(byte) 0xaa, 0x44, 0x13, 0x26, 0x49, (byte) 0x86, 0x06,
(byte) 0x99, (byte) 0x9c, 0x42, 0x50, (byte) 0xf4, (byte) 0x91,
(byte) 0xef, (byte) 0x98, 0x7a, 0x33, 0x54, 0x0b, 0x43,
(byte) 0xed, (byte) 0xcf, (byte) 0xac, 0x62, (byte) 0xe4,
(byte) 0xb3, 0x1c, (byte) 0xa9, (byte) 0xc9, 0x08, (byte) 0xe8,
(byte) 0x95, (byte) 0x80, (byte) 0xdf, (byte) 0x94, (byte) 0xfa,
0x75, (byte) 0x8f, 0x3f, (byte) 0xa6, 0x47, 0x07, (byte) 0xa7,
(byte) 0xfc, (byte) 0xf3, 0x73, 0x17, (byte) 0xba, (byte) 0x83,
0x59, 0x3c, 0x19, (byte) 0xe6, (byte) 0x85, 0x4f, (byte) 0xa8,
0x68, 0x6b, (byte) 0x81, (byte) 0xb2, 0x71, 0x64, (byte) 0xda,
(byte) 0x8b, (byte) 0xf8, (byte) 0xeb, 0x0f, 0x4b, 0x70, 0x56,
(byte) 0x9d, 0x35, 0x1e, 0x24, 0x0e, 0x5e, 0x63, 0x58, (byte) 0xd1,
(byte) 0xa2, 0x25, 0x22, 0x7c, 0x3b, 0x01, 0x21, 0x78, (byte) 0x87,
(byte) 0xd4, 0x00, 0x46, 0x57, (byte) 0x9f, (byte) 0xd3, 0x27,
0x52, 0x4c, 0x36, 0x02, (byte) 0xe7, (byte) 0xa0, (byte) 0xc4,
(byte) 0xc8, (byte) 0x9e, (byte) 0xea, (byte) 0xbf, (byte) 0x8a,
(byte) 0xd2, 0x40, (byte) 0xc7, 0x38, (byte) 0xb5, (byte) 0xa3,
(byte) 0xf7, (byte) 0xf2, (byte) 0xce, (byte) 0xf9, 0x61, 0x15,
(byte) 0xa1, (byte) 0xe0, (byte) 0xae, 0x5d, (byte) 0xa4,
(byte) 0x9b, 0x34, 0x1a, 0x55, (byte) 0xad, (byte) 0x93, 0x32,
0x30, (byte) 0xf5, (byte) 0x8c, (byte) 0xb1, (byte) 0xe3, 0x1d,
(byte) 0xf6, (byte) 0xe2, 0x2e, (byte) 0x82, 0x66, (byte) 0xca,
0x60, (byte) 0xc0, 0x29, 0x23, (byte) 0xab, 0x0d, 0x53, 0x4e, 0x6f,
(byte) 0xd5, (byte) 0xdb, 0x37, 0x45, (byte) 0xde, (byte) 0xfd,
(byte) 0x8e, 0x2f, 0x03, (byte) 0xff, 0x6a, 0x72, 0x6d, 0x6c, 0x5b,
0x51, (byte) 0x8d, 0x1b, (byte) 0xaf, (byte) 0x92, (byte) 0xbb,
(byte) 0xdd, (byte) 0xbc, 0x7f, 0x11, (byte) 0xd9, 0x5c, 0x41,
0x1f, 0x10, 0x5a, (byte) 0xd8, 0x0a, (byte) 0xc1, 0x31,
(byte) 0x88, (byte) 0xa5, (byte) 0xcd, 0x7b, (byte) 0xbd, 0x2d,
0x74, (byte) 0xd0, 0x12, (byte) 0xb8, (byte) 0xe5, (byte) 0xb4,
(byte) 0xb0, (byte) 0x89, 0x69, (byte) 0x97, 0x4a, 0x0c,
(byte) 0x96, 0x77, 0x7e, 0x65, (byte) 0xb9, (byte) 0xf1, 0x09,
(byte) 0xc5, 0x6e, (byte) 0xc6, (byte) 0x84, 0x18, (byte) 0xf0,
0x7d, (byte) 0xec, 0x3a, (byte) 0xdc, 0x4d, 0x20, 0x79,
(byte) 0xee, 0x5f, 0x3e, (byte) 0xd7, (byte) 0xcb, 0x39, 0x48};
public static final int[] FK = {0xa3b1bac6, 0x56aa3350, 0x677d9197, 0xb27022dc};
public static final int[] CK = {0x00070e15, 0x1c232a31, 0x383f464d, 0x545b6269,
0x70777e85, 0x8c939aa1, 0xa8afb6bd, 0xc4cbd2d9,
0xe0e7eef5, 0xfc030a11, 0x181f262d, 0x343b4249,
0x50575e65, 0x6c737a81, 0x888f969d, 0xa4abb2b9,
0xc0c7ced5, 0xdce3eaf1, 0xf8ff060d, 0x141b2229,
0x30373e45, 0x4c535a61, 0x686f767d, 0x848b9299,
0xa0a7aeb5, 0xbcc3cad1, 0xd8dfe6ed, 0xf4fb0209,
0x10171e25, 0x2c333a41, 0x484f565d, 0x646b7279};
private byte sm4Sbox(byte inch) {
int i = inch & 0xFF;
return SboxTable[i];
}
private long sm4Lt(long ka) {
long bb = 0L;
long c = 0L;
byte[] a = new byte[4];
byte[] b = new byte[4];
PUT_ULONG_BE(ka, a, 0);
b[0] = sm4Sbox(a[0]);
b[1] = sm4Sbox(a[1]);
b[2] = sm4Sbox(a[2]);
b[3] = sm4Sbox(a[3]);
bb = GET_ULONG_BE(b, 0);
c = bb ^ ROTL(bb, 2) ^ ROTL(bb, 10) ^ ROTL(bb, 18) ^ ROTL(bb, 24);
return c;
}
private long sm4F(long x0, long x1, long x2, long x3, long rk) {
return x0 ^ sm4Lt(x1 ^ x2 ^ x3 ^ rk);
}
private long sm4CalciRK(long ka) {
long bb = 0L;
long rk = 0L;
byte[] a = new byte[4];
byte[] b = new byte[4];
PUT_ULONG_BE(ka, a, 0);
b[0] = sm4Sbox(a[0]);
b[1] = sm4Sbox(a[1]);
b[2] = sm4Sbox(a[2]);
b[3] = sm4Sbox(a[3]);
bb = GET_ULONG_BE(b, 0);
rk = bb ^ ROTL(bb, 13) ^ ROTL(bb, 23);
return rk;
}
private void sm4_setkey(long[] SK, byte[] key) {
long[] MK = new long[4];
long[] k = new long[36];
int i = 0;
MK[0] = GET_ULONG_BE(key, 0);
MK[1] = GET_ULONG_BE(key, 4);
MK[2] = GET_ULONG_BE(key, 8);
MK[3] = GET_ULONG_BE(key, 12);
k[0] = MK[0] ^ (long) FK[0];
k[1] = MK[1] ^ (long) FK[1];
k[2] = MK[2] ^ (long) FK[2];
k[3] = MK[3] ^ (long) FK[3];
for (; i < 32; i++) {
k[(i + 4)] = (k[i] ^ sm4CalciRK(k[(i + 1)] ^ k[(i + 2)] ^ k[(i + 3)] ^ (long) CK[i]));
SK[i] = k[(i + 4)];
}
}
private void sm4_one_round(long[] sk, byte[] input, byte[] output) {
int i = 0;
long[] ulbuf = new long[36];
ulbuf[0] = GET_ULONG_BE(input, 0);
ulbuf[1] = GET_ULONG_BE(input, 4);
ulbuf[2] = GET_ULONG_BE(input, 8);
ulbuf[3] = GET_ULONG_BE(input, 12);
while (i < 32) {
ulbuf[(i + 4)] = sm4F(ulbuf[i], ulbuf[(i + 1)], ulbuf[(i + 2)], ulbuf[(i + 3)], sk[i]);
i++;
}
PUT_ULONG_BE(ulbuf[35], output, 0);
PUT_ULONG_BE(ulbuf[34], output, 4);
PUT_ULONG_BE(ulbuf[33], output, 8);
PUT_ULONG_BE(ulbuf[32], output, 12);
}
public static byte[] PKCS5Padding(byte[] inputByte) {
// 获字节长度
int length = inputByte.length;
// 补齐位数
int leftLength = 16 - (length % 16);
// 定义新字节
byte[] arrayReturn = new byte[length + leftLength];
// 定义填充字节
byte[] plusbyte = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
0x0f,0x10 };
// 不满足16倍数自动填充
for (int i = 0; i < length + leftLength; i++) {
if (i < length) {
// 赋值
arrayReturn[i] = inputByte[i];
} else {
// 补齐位数
arrayReturn[i] = (byte)leftLength;
}
}
return arrayReturn;
}
public static byte[] PKCS7PaddingRestore(byte[] paramArray) {
//try {
// 定义是否需要消除填充数据
//boolean needRestore = true;
int length = paramArray.length;// 获取字节长度
// 拆分并获取最后一组16倍数数值
byte[] paramByteLast = Arrays.copyOfRange(paramArray, length - 16, length);
// 获取最后一个字节
byte byteLast = paramByteLast[paramByteLast.length - 1];
return Arrays.copyOfRange(paramArray,0,length-byteLast);
}
//修改了填充模式,为模式
private byte[] padding(byte[] input, int mode) {
byte[] ret = (byte[]) null;
if (mode == SM4_ENCRYPT) {
ret = PKCS5Padding(input);
} else {
ret = PKCS7PaddingRestore(input);
}
return ret;
}
public void sm4_setkey_enc(SM4_Context ctx, byte[] key) throws RuntimeException{
if (ctx == null) {
throw new RuntimeException("ctx is null!");
}
if (key == null || key.length != 16) {
throw new RuntimeException("key error!");
}
ctx.mode = SM4_ENCRYPT;
sm4_setkey(ctx.sk, key);
}
public void sm4_setkey_dec(SM4_Context ctx, byte[] key) throws RuntimeException {
if (ctx == null) {
throw new RuntimeException("ctx is null!");
}
if (key == null || key.length != 16) {
throw new RuntimeException("key error!");
}
int i = 0;
ctx.mode = SM4_DECRYPT;
sm4_setkey(ctx.sk, key);
for (i = 0; i < 16; i++) {
SWAP(ctx.sk, i);
}
}
public byte[] sm4_crypt_ecb(SM4_Context ctx, byte[] input) throws RuntimeException, IOException {
if (input == null) {
throw new RuntimeException("input is null!");
}
if ((ctx.isPadding) && (ctx.mode == SM4_ENCRYPT)) {
input = padding(input, SM4_ENCRYPT);
}
byte[] output = null;
try(ByteArrayInputStream bins = new ByteArrayInputStream(input);
ByteArrayOutputStream bous = new ByteArrayOutputStream();) {
int length = input.length;
for (; length > 0; length -= 16) {
byte[] in = new byte[16];
byte[] out = new byte[16];
bins.read(in);
sm4_one_round(ctx.sk, in, out);
bous.write(out);
}
output = bous.toByteArray();
if (ctx.isPadding && ctx.mode == SM4_DECRYPT) {
output = padding(output, SM4_DECRYPT);
}
}
return output;
}
public byte[] sm4_crypt_cbc(SM4_Context ctx, byte[] iv, byte[] input) throws RuntimeException, IOException {
if (iv == null || iv.length != 16) {
throw new RuntimeException("iv error!");
}
if (input == null) {
throw new RuntimeException("input is null!");
}
byte[] tempIv = new byte[iv.length];//防止iv被改变
System.arraycopy(iv, 0, tempIv, 0, iv.length);
if (ctx.isPadding && ctx.mode == SM4_ENCRYPT) {
input = padding(input, SM4_ENCRYPT);
}
byte[] output = null;
try(ByteArrayInputStream bins = new ByteArrayInputStream(input);
ByteArrayOutputStream bous = new ByteArrayOutputStream();) {
int i = 0;
int length = input.length;
if (ctx.mode == SM4_ENCRYPT) {
for (; length > 0; length -= 16) {
byte[] in = new byte[16];
byte[] out = new byte[16];
byte[] out1 = new byte[16];
bins.read(in);
for (i = 0; i < 16; i++) {
out[i] = ((byte) (in[i] ^ tempIv[i]));
}
sm4_one_round(ctx.sk, out, out1);
System.arraycopy(out1, 0, tempIv, 0, 16);
bous.write(out1);
}
} else {
byte[] temp = new byte[16];
for (; length > 0; length -= 16) {
byte[] in = new byte[16];
byte[] out = new byte[16];
byte[] out1 = new byte[16];
bins.read(in);
System.arraycopy(in, 0, temp, 0, 16);
sm4_one_round(ctx.sk, in, out);
for (i = 0; i < 16; i++) {
out1[i] = ((byte) (out[i] ^ tempIv[i]));
}
System.arraycopy(temp, 0, tempIv, 0, 16);
bous.write(out1);
}
}
output = bous.toByteArray();
if (ctx.isPadding && ctx.mode == SM4_DECRYPT) {
output = padding(output, SM4_DECRYPT);
}
}
return output;
}
}

View File

@ -0,0 +1,24 @@
package com.ruoyi.common.utils.sm4;
public class SM4_Context {
/**
* 模式0解密1加密
*/
public int mode;
/**
* 是否填充
*/
public boolean isPadding;
public long[] sk;
public SM4_Context(){
this.mode = 1;
this.isPadding = true;
this.sk = new long[32];//以字32位为单位进行加密运算
}
}

View File

@ -1,5 +1,9 @@
package com.ruoyi.framework.config;
import com.ruoyi.framework.config.properties.PermitAllUrlProperties;
import com.ruoyi.framework.security.filter.JwtAuthenticationTokenFilter;
import com.ruoyi.framework.security.handle.AuthenticationEntryPointImpl;
import com.ruoyi.framework.security.handle.LogoutSuccessHandlerImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@ -16,10 +20,6 @@ import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutFilter;
import org.springframework.web.filter.CorsFilter;
import com.ruoyi.framework.config.properties.PermitAllUrlProperties;
import com.ruoyi.framework.security.filter.JwtAuthenticationTokenFilter;
import com.ruoyi.framework.security.handle.AuthenticationEntryPointImpl;
import com.ruoyi.framework.security.handle.LogoutSuccessHandlerImpl;
/**
* spring security配置

View File

@ -0,0 +1,40 @@
package com.ruoyi.board.domain;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
/**
* 计划发布中的类型对象 pub_plan_type
*
* @author fuhao
* @date 2024-09-02
*/
@Data
@TableName(value = "pub_plan_type")
public class PlanType implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 唯一标识
*/
@TableId(value = "id", type = IdType.AUTO)
private Long id;
/** 类型名称 */
@TableField(value = "type_name")
private String typeName;
/** 类型父级id */
@TableField(value = "parent_id")
private Long parentId;
@TableField(select = false)
private List<PlanType> children = new ArrayList<>();
}

View File

@ -42,4 +42,10 @@ public class ReleaseRecord {
*/
@TableField(value = "created_by")
private Integer createdBy;
/**
* 预警计划级别
*/
@TableField(value = "plan_id")
private Integer planId;
}

View File

@ -0,0 +1,41 @@
package com.ruoyi.board.domain.dto;
import lombok.Data;
import java.math.BigDecimal;
@Data
public class AlertPlanAndPlanTypeDTO {
/**
* 主键自增
*/
private Integer id;
/**
* 类型
*/
private Integer type;
private String typeName;
/**
* 等级
*/
private Integer level;
/**
* 最大值
*/
private BigDecimal maxValue;
/**
* 最小值
*/
private BigDecimal minValue;
/**
* 显示内容
*/
private String displayContent;
}

View File

@ -0,0 +1,39 @@
package com.ruoyi.board.domain.dto;
import lombok.Data;
import java.util.Date;
@Data
public class ReleaseRecordAndPresetContentDTO {
/**
* 主键自增
*/
private Integer id;
/**
* 情报板ID
*/
private Integer boardId;
/**
* 预置信息ID
*/
private Integer presetContentId;
/**
* 创建时间
*/
private Date createTime;
/**
* 信息类型
*/
private Integer infoType;
/**
* 预警计划级别
*/
private Integer presetContentLevel;
}

View File

@ -0,0 +1,7 @@
package com.ruoyi.board.mapper;
import com.github.yulichang.base.MPJBaseMapper;
import com.ruoyi.board.domain.PlanType;
public interface PlanTypeMapper extends MPJBaseMapper<PlanType> {
}

View File

@ -2,12 +2,15 @@ package com.ruoyi.board.service;
import com.github.yulichang.base.MPJBaseService;
import com.ruoyi.board.domain.AlertPlan;
import com.ruoyi.board.domain.dto.AlertPlanAndPlanTypeDTO;
import java.util.List;
public interface IAlertPlanService extends MPJBaseService<AlertPlan> {
List<AlertPlan> selectList(AlertPlan alertPlan);
List<AlertPlanAndPlanTypeDTO> listPage(AlertPlan alertPlan);
AlertPlan getOneByTypeAndValue(Integer type, Double value);
AlertPlanAndPlanTypeDTO getDTOById(Long id);
}

View File

@ -3,7 +3,11 @@ package com.ruoyi.board.service;
import com.github.yulichang.base.MPJBaseService;
import com.ruoyi.board.domain.BoardInfo;
import java.util.List;
public interface IBoardInfoService extends MPJBaseService<BoardInfo> {
BoardInfo getOneByIP(String ip);
List<BoardInfo> listPage(BoardInfo boardInfo);
}

View File

@ -0,0 +1,12 @@
package com.ruoyi.board.service;
import com.github.yulichang.base.MPJBaseService;
import com.ruoyi.board.domain.PlanType;
import com.ruoyi.common.core.domain.TreeSelect;
import java.util.List;
public interface IPlanTypeService extends MPJBaseService<PlanType>{
List<TreeSelect> listPlanTree(PlanType planType);
}

View File

@ -3,7 +3,11 @@ package com.ruoyi.board.service;
import com.github.yulichang.base.MPJBaseService;
import com.ruoyi.board.domain.PresetContent;
import java.util.List;
public interface IPresetContentService extends MPJBaseService<PresetContent> {
PresetContent getOneByContentAndBoardSize(String content, String boardSize, Integer type);
List<PresetContent> listPage(PresetContent presetContent);
}

View File

@ -2,8 +2,13 @@ package com.ruoyi.board.service;
import com.github.yulichang.base.MPJBaseService;
import com.ruoyi.board.domain.ReleaseRecord;
import com.ruoyi.board.domain.dto.ReleaseRecordAndPresetContentDTO;
import java.util.List;
public interface IReleaseRecordService extends MPJBaseService<ReleaseRecord> {
ReleaseRecord getOneLatestByBoardId(String boardId, Integer type);
List<ReleaseRecordAndPresetContentDTO> listLatest2minRecordDtoByBoardId(Integer boardId);
List<ReleaseRecord> listLatest2minRecordByBoardId(Integer boardId);
}

View File

@ -2,23 +2,32 @@ package com.ruoyi.board.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.github.yulichang.base.MPJBaseServiceImpl;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.stereotype.Service;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import com.ruoyi.board.domain.AlertPlan;
import com.ruoyi.board.domain.PlanType;
import com.ruoyi.board.domain.dto.AlertPlanAndPlanTypeDTO;
import com.ruoyi.board.mapper.AlertPlanMapper;
import com.ruoyi.board.service.IAlertPlanService;
import org.apache.commons.lang3.ObjectUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class AlertPlanServiceImpl extends MPJBaseServiceImpl<AlertPlanMapper, AlertPlan> implements IAlertPlanService {
private static final Logger log = LoggerFactory.getLogger(AlertPlanServiceImpl.class);
@Override
public List<AlertPlan> selectList(AlertPlan alertPlan) {
LambdaQueryWrapper<AlertPlan> alertPlanLambdaQueryWrapper = new LambdaQueryWrapper<>();
alertPlanLambdaQueryWrapper.eq(ObjectUtils.isNotEmpty(alertPlan.getType()), AlertPlan::getType, alertPlan.getType());
alertPlanLambdaQueryWrapper.eq(ObjectUtils.isNotEmpty(alertPlan.getLevel()), AlertPlan::getLevel, alertPlan.getLevel());
return list(alertPlanLambdaQueryWrapper);
public List<AlertPlanAndPlanTypeDTO> listPage(AlertPlan alertPlan) {
MPJLambdaWrapper<AlertPlan> eq = new MPJLambdaWrapper<AlertPlan>()
.selectAll(AlertPlan.class)
.select(PlanType::getTypeName)
.leftJoin(PlanType.class, PlanType::getId, AlertPlan::getType)
.eq(ObjectUtils.isNotEmpty(alertPlan.getType()), AlertPlan::getType, alertPlan.getType());
return selectJoinList(AlertPlanAndPlanTypeDTO.class, eq);
}
@Override
@ -33,4 +42,15 @@ public class AlertPlanServiceImpl extends MPJBaseServiceImpl<AlertPlanMapper, Al
.ge(AlertPlan::getMaxValue, value);
return getOne(alertPlanLambdaQueryWrapper);
}
@Override
public AlertPlanAndPlanTypeDTO getDTOById(Long id) {
MPJLambdaWrapper<AlertPlan> eq = new MPJLambdaWrapper<AlertPlan>()
.selectAll(AlertPlan.class)
.select(PlanType::getTypeName)
.leftJoin(PlanType.class, PlanType::getId, AlertPlan::getType)
.eq(AlertPlan::getId, id);
return selectJoinOne(AlertPlanAndPlanTypeDTO.class, eq);
}
}

View File

@ -2,11 +2,14 @@ package com.ruoyi.board.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.github.yulichang.base.MPJBaseServiceImpl;
import com.ruoyi.common.utils.StringUtils;
import org.springframework.stereotype.Service;
import com.ruoyi.board.mapper.BoardInfoMapper;
import com.ruoyi.board.domain.BoardInfo;
import com.ruoyi.board.service.IBoardInfoService;
import java.util.List;
@Service
public class BoardInfoServiceImpl extends MPJBaseServiceImpl<BoardInfoMapper, BoardInfo> implements IBoardInfoService {
@ -16,4 +19,16 @@ public class BoardInfoServiceImpl extends MPJBaseServiceImpl<BoardInfoMapper, Bo
boardInfoLambdaQueryWrapper.eq(BoardInfo::getBoardIp, ip);
return getOne(boardInfoLambdaQueryWrapper);
}
@Override
public List<BoardInfo> listPage(BoardInfo boardInfo) {
LambdaQueryWrapper<BoardInfo> wrapper = new LambdaQueryWrapper<>();
wrapper.likeRight(StringUtils.isNotEmpty(boardInfo.getBoardName()), BoardInfo::getBoardName, boardInfo.getBoardName());
wrapper.likeRight(StringUtils.isNotEmpty(boardInfo.getBoardMileage()), BoardInfo::getBoardMileage, boardInfo.getBoardMileage());
wrapper.eq(StringUtils.isNotEmpty(boardInfo.getBoardSize()), BoardInfo::getBoardSize, boardInfo.getBoardSize());
wrapper.eq(StringUtils.isNotEmpty(boardInfo.getBoardBrand()), BoardInfo::getBoardBrand, boardInfo.getBoardBrand());
wrapper.eq(StringUtils.isNotEmpty(boardInfo.getBoardCommunicationProtocol()), BoardInfo::getBoardCommunicationProtocol, boardInfo.getBoardCommunicationProtocol());
wrapper.eq(StringUtils.isNotEmpty(boardInfo.getBoardRoadSection()), BoardInfo::getBoardRoadSection, boardInfo.getBoardRoadSection());
return list(wrapper);
}
}

View File

@ -0,0 +1,96 @@
package com.ruoyi.board.service.impl;
import com.github.yulichang.base.MPJBaseServiceImpl;
import com.ruoyi.board.domain.PlanType;
import com.ruoyi.board.mapper.PlanTypeMapper;
import com.ruoyi.board.service.IPlanTypeService;
import com.ruoyi.common.core.domain.TreeSelect;
import com.ruoyi.common.utils.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
@Service
public class PlanTypeServiceImpl extends MPJBaseServiceImpl<PlanTypeMapper, PlanType> implements IPlanTypeService {
private static final Logger log = LoggerFactory.getLogger(PlanTypeServiceImpl.class);
@Override
public List<TreeSelect> listPlanTree(PlanType planType) {
List<PlanType> list = list();
List<PlanType> planTypes = buildPlanTree(list);
return planTypes.stream().map(this::buildTreeSelect).collect(Collectors.toList());
}
private TreeSelect buildTreeSelect(PlanType planType){
List<TreeSelect> childTreeSelectList = planType.getChildren().stream().map(this::buildTreeSelect).collect(Collectors.toList());
TreeSelect treeSelect = new TreeSelect();
treeSelect.setId(planType.getId());
treeSelect.setLabel(planType.getTypeName());
treeSelect.setChildren(childTreeSelectList);
return treeSelect;
};
private List<PlanType> buildPlanTree(List<PlanType> list) {
List<PlanType> returnList = new ArrayList<>();
List<Long> tempList = list.stream().map(PlanType::getId).collect(Collectors.toList());
for (PlanType planType : list)
{
// 如果是顶级节点, 遍历该父节点的所有子节点
if (!tempList.contains(planType.getParentId()))
{
recursionFn(list, planType);
returnList.add(planType);
}
}
if (returnList.isEmpty())
{
returnList = list;
}
return returnList;
}
/**
* 递归列表
*/
private void recursionFn(List<PlanType> list, PlanType t)
{
// 得到子节点列表
List<PlanType> childList = getChildList(list, t);
t.setChildren(childList);
for (PlanType tChild : childList)
{
if (hasChild(list, tChild))
{
recursionFn(list, tChild);
}
}
}
/**
* 得到子节点列表
*/
private List<PlanType> getChildList(List<PlanType> list, PlanType t)
{
List<PlanType> tlist = new ArrayList<>();
for (PlanType n : list) {
if (StringUtils.isNotNull(n.getParentId()) && n.getParentId().longValue() == t.getId().longValue()) {
tlist.add(n);
}
}
return tlist;
}
/**
* 判断是否有子节点
*/
private boolean hasChild(List<PlanType> list, PlanType t)
{
return !getChildList(list, t).isEmpty();
}
}

View File

@ -2,11 +2,15 @@ package com.ruoyi.board.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.github.yulichang.base.MPJBaseServiceImpl;
import com.ruoyi.common.utils.StringUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.stereotype.Service;
import com.ruoyi.board.mapper.PresetContentMapper;
import com.ruoyi.board.domain.PresetContent;
import com.ruoyi.board.service.IPresetContentService;
import java.util.List;
@Service
public class PresetContentServiceImpl extends MPJBaseServiceImpl<PresetContentMapper, PresetContent> implements IPresetContentService {
@Override
@ -18,4 +22,13 @@ public class PresetContentServiceImpl extends MPJBaseServiceImpl<PresetContentMa
.eq(PresetContent::getInfoType, type);
return getOne(queryWrapper);
}
@Override
public List<PresetContent> listPage(PresetContent presetContent) {
LambdaQueryWrapper<PresetContent> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.likeRight(StringUtils.isNotEmpty(presetContent.getName()), PresetContent::getName, presetContent.getName());
queryWrapper.eq(StringUtils.isNotEmpty(presetContent.getBoardSize()), PresetContent::getBoardSize, presetContent.getBoardSize());
queryWrapper.eq(ObjectUtils.isNotEmpty(presetContent.getInfoType()), PresetContent::getInfoType, presetContent.getInfoType());
return list(queryWrapper);
}
}

View File

@ -4,36 +4,42 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.github.yulichang.base.MPJBaseServiceImpl;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import com.ruoyi.board.domain.PresetContent;
import com.ruoyi.board.domain.ReleaseRecordDto;
import lombok.Data;
import com.ruoyi.board.domain.dto.ReleaseRecordAndPresetContentDTO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import com.ruoyi.board.domain.ReleaseRecord;
import com.ruoyi.board.mapper.ReleaseRecordMapper;
import com.ruoyi.board.service.IReleaseRecordService;
import java.util.Date;
import java.util.List;
@Service
public class ReleaseRecordServiceImpl extends MPJBaseServiceImpl<ReleaseRecordMapper, ReleaseRecord> implements IReleaseRecordService {
private static final Logger log = LoggerFactory.getLogger(ReleaseRecordServiceImpl.class);
@Override
public ReleaseRecord getOneLatestByBoardId(String boardId, Integer type) {
// LambdaQueryWrapper<ReleaseRecord> releaseRecordLambdaQueryWrapper = new LambdaQueryWrapper<>();
// releaseRecordLambdaQueryWrapper
// .eq(ReleaseRecord::getBoardId, boardId)
// .orderByDesc(ReleaseRecord::getCreateTime)
// .last("LIMIT 1");
ReleaseRecordDto releaseRecord = selectJoinOne(ReleaseRecordDto.class,
public List<ReleaseRecordAndPresetContentDTO> listLatest2minRecordDtoByBoardId(Integer boardId) {
Date before2min = new Date(System.currentTimeMillis() - 30 * 60 * 1000);
return selectJoinList(ReleaseRecordAndPresetContentDTO.class,
new MPJLambdaWrapper<ReleaseRecord>()
.selectAll(ReleaseRecord.class)
.select(PresetContent::getInfoType)
.leftJoin(PresetContent.class, PresetContent::getId, ReleaseRecord::getPresetContentId)
.eq(ReleaseRecord::getBoardId, boardId)
.eq(PresetContent::getInfoType, type)
.orderByDesc(ReleaseRecord::getCreateTime)
.last("LIMIT 1")
.gt(ReleaseRecord::getCreateTime, before2min)
.eq(PresetContent::getPresetType, 0)
);
System.out.println(releaseRecord);
return null;
}
@Override
public List<ReleaseRecord> listLatest2minRecordByBoardId(Integer boardId) {
Date before2min = new Date(System.currentTimeMillis() - 30 * 60 * 1000);
LambdaQueryWrapper<ReleaseRecord> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(ReleaseRecord::getBoardId, boardId)
.gt(ReleaseRecord::getCreateTime, before2min);
return list(queryWrapper);
}
}

View File

@ -0,0 +1,17 @@
package com.ruoyi.protocol.enums;
import lombok.Getter;
@Getter
public enum ProtocolDefault {
SanSi(1, "SanSi", 2929),;
private final Integer id;
private final String name;
private final Integer port;
ProtocolDefault(Integer id, String name, Integer port) {
this.id = id;
this.name = name;
this.port = port;
}
}

View File

@ -1,12 +1,18 @@
package com.ruoyi.protocol.sansi;
import com.alibaba.fastjson2.JSON;
import com.ruoyi.board.domain.BoardInfo;
import com.ruoyi.board.domain.PresetContent;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.uuid.UUID;
import com.ruoyi.protocol.enums.ProtocolDefault;
import com.ruoyi.protocol.sansi.enums.SanSiFontColor;
import com.ruoyi.protocol.sansi.enums.SanSiFontStyle;
import com.ruoyi.protocol.service.IProtocolService;
import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import zhishuang.wang.sansi.fcms.devinfor.DeviceControl;
import zhishuang.wang.sansi.fcms.devinfor.DeviceVar;
import zhishuang.wang.sansi.playlist.AreaItem;
@ -17,38 +23,39 @@ import zhishuang.wang.sansi.playlist.fcms.PlayListFcms;
import zhishuang.wang.sansi.tools.ReturnData;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class SanSiProtocol {
@Service
public class SanSiProtocol implements IProtocolService {
private static final Logger log = LoggerFactory.getLogger(SanSiProtocol.class);
Integer deviceId = 0;
String deviceId = "1";
public String createTextItem(PresetContent presetContent, BoardInfo boardInfo) {
private String createTextItem(List<PresetContent> presetContentList, BoardInfo boardInfo) {
Animation animation = new Animation();
animation.setInAnimation(0);
animation.setInAnimationSpeed(200);
List<PlayItem> playItemList = new ArrayList<>();
TextBase textBase = new TextBase(0, presetContent.getContent());
textBase.setFontSize(presetContent.getFontSize()+","+presetContent.getFontSize());
textBase.setFontName(presetContent.getFontStyle());
textBase.setWordSpace(presetContent.getLetterSpacing());
BaseColour fontColour = SanSiFontColor.getBaseColourByColorName(presetContent.getFontColor());
int index = 1;
presetContentList.forEach(i -> {
TextBase textBase = new TextBase(0, i.getContent());
textBase.setFontSize(i.getFontSize()+","+i.getFontSize());
textBase.setFontName(SanSiFontStyle.getStyleValueByName(i.getFontStyle()));
textBase.setWordSpace(i.getLetterSpacing());
BaseColour fontColour = SanSiFontColor.getBaseColourByColorName(i.getFontColor());
if (null == fontColour) {
log.error("BaseColour fontColour = SanSiFontColor.getBaseColourByColorName(presetContent.getFontColor()); 没有匹配到相应的字体颜色");
return null;
}
textBase.setFontColour(fontColour);
PlayTimeBase textPlayTime = new PlayTimeBase(presetContent.getPlayTime());
PlayItem textPlayItem = new PlayItem(1, "1", "test", textBase, textPlayTime);
PlayTimeBase textPlayTime = new PlayTimeBase(i.getPlayTime() * 10);
PlayItem textPlayItem = new PlayItem(index, Integer.toString(index), "test" + index, textBase, textPlayTime);
textPlayItem.setAnimation(animation);
textPlayItem.setX(presetContent.getFontPositionX());
textPlayItem.setY(presetContent.getFontPositionY());
List<PlayItem> playItemList = new ArrayList<>();
textPlayItem.setX(i.getFontPositionX());
textPlayItem.setY(i.getFontPositionY());
playItemList.add(textPlayItem);
});
String boardSize = boardInfo.getBoardSize();
String[] split = StringUtils.split(boardSize, "*");
@ -68,45 +75,58 @@ public class SanSiProtocol {
PageItem pageItem = new PageItem("1", "pageItem", areaItemList);
PlayListFcms plf = new PlayListFcms();
String path = "./sansi/" + UUID.fastUUID() + "/play.lst";
File file = new File(path);
if (file.exists()) {
file.delete();
String path = "./protocolFile/SanSi/" + DateUtils.dateTimeNow();
String file = path + "/play.lst";
try {
FileUtils.forceMkdir(new File(path));
} catch (IOException e) {
log.error("创建文件夹失败");
return null;
}
ReturnData rd = plf.createFcmsPlayList(path, pageItem);
System.out.println("====" + rd.getCode());
System.out.println("====" + rd.getMessage());
ReturnData rd = plf.createFcmsPlayList(file, pageItem);
if (rd.getCode() != 0) {
log.error("生成三思的上传文件出错了");
return null;
}
return path;
return file;
}
private void loginDevice(BoardInfo boardInfo) {
DeviceVar.deviceInforInit(deviceId, 1, boardInfo.getBoardIp(), boardInfo.getBoardPort(), 2048);
@Override
public boolean protocolSupport(String protocolName) {
return StringUtils.equals(ProtocolDefault.SanSi.getName(), protocolName);
}
private boolean uploadAndStartPlayList(String path) {
ReturnData uploadPlayListRD = DeviceControl.fcmsUploadPlayList(deviceId, path);//上传播放表
if (uploadPlayListRD.getCode() != 0) {
log.error("上传错误");
return false;
@Override
public void loginDevice(BoardInfo boardInfo) {
if (deviceId > 10) {
deviceId = 0;
}
ReturnData activePlayListRD = DeviceControl.fcmsActivePlayList(deviceId, "play.lst");
if (activePlayListRD.getCode() != 0) {
log.error("播放错误");
return false;
deviceId++;
if (boardInfo.getBoardPort()<1) {
boardInfo.setBoardPort(ProtocolDefault.SanSi.getPort());
}
return true;
DeviceVar.deviceInforInit(Integer.toString(deviceId), 1, boardInfo.getBoardIp(), boardInfo.getBoardPort(), 2048);
}
@Override
public void logoutDevice() {
DeviceVar.deviceInforDel(Integer.toString(deviceId));
}
public boolean publishContent(BoardInfo boardInfo, PresetContent presetContent){
loginDevice(boardInfo);
String filePath = createTextItem(presetContent, boardInfo);
return uploadAndStartPlayList(filePath);
@Override
public void publishContent(BoardInfo boardInfo, List<PresetContent> presetContentList) {
String path = createTextItem(presetContentList, boardInfo);
uploadAndStartPlayList(path);
}
private void uploadAndStartPlayList(String path) {
ReturnData uploadPlayListRD = DeviceControl.fcmsUploadPlayList(Integer.toString(deviceId), path);//上传播放表
log.info("uploadPlayListRD:{}", JSON.toJSONString(uploadPlayListRD));
// ReturnData activePlayListRD = DeviceControl.fcmsActivePlayList(Integer.toString(deviceId), "000");
// if (activePlayListRD.getCode() != 0) {
// log.error("播放错误");
// return false;
// }
// return true;
}
}

View File

@ -0,0 +1,27 @@
package com.ruoyi.protocol.sansi.enums;
import lombok.Getter;
import java.util.stream.Stream;
@Getter
public enum SanSiFontStyle {
SongTi("宋体", "s");
private final String name;
private final String value;
SanSiFontStyle(String name, String value) {
this.name = name;
this.value = value;
}
public static String getStyleValueByName(String name) {
return Stream.of(SanSiFontStyle.values())
.filter(fontColor -> fontColor.getName().equals(name))
.map(SanSiFontStyle::getValue)
.findFirst()
.orElse(null);
}
}

View File

@ -1,4 +1,30 @@
package com.ruoyi.protocol.service;
import com.ruoyi.board.domain.BoardInfo;
import com.ruoyi.board.domain.PresetContent;
import java.util.List;
public interface IProtocolService {
boolean protocolSupport(String protocolName);
void loginDevice(BoardInfo boardInfo);
void logoutDevice();
void publishContent(BoardInfo boardInfo, List<PresetContent> presetContentList);
default void publishInformation(BoardInfo boardInfo, List<PresetContent> presetContentList){
// 登录
loginDevice(boardInfo);
// 发布
publishContent(boardInfo, presetContentList);
// 登出
logoutDevice();
};
}

View File

@ -0,0 +1,27 @@
package com.ruoyi.protocol.service.impl;
import com.ruoyi.board.domain.BoardInfo;
import com.ruoyi.board.domain.PresetContent;
import com.ruoyi.protocol.service.IProtocolService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class ContentPublishingService {
private static final Logger log = LoggerFactory.getLogger(ContentPublishingService.class);
@Autowired
List<IProtocolService> protocolServiceList;
public void contentPublish(BoardInfo boardInfo, List<PresetContent> presetContentList) {
IProtocolService iProtocolService = protocolServiceList.stream().filter(i -> i.protocolSupport(boardInfo.getBoardCommunicationProtocol())).findFirst().orElse(null);
if (iProtocolService ==null) {
log.error("协议不支持");
return;
}
iProtocolService.publishInformation(boardInfo, presetContentList);
}
}

View File

@ -1,6 +0,0 @@
package com.ruoyi.protocol.service.impl;
import com.ruoyi.protocol.service.IProtocolService;
public class ProtocolServiceImpl implements IProtocolService {
}

View File

@ -0,0 +1,9 @@
package com.ruoyi.sensor.common;
import com.ruoyi.sensor.domain.dto.PerceptionHandlerDTO;
import java.util.HashMap;
public class PerceptionHandlerMap {
public static final HashMap<String, PerceptionHandlerDTO> perceptionHandlerMap = new HashMap<>();
}

View File

@ -20,8 +20,14 @@ public class PerceptionParams {
*/
private String boardIp;
/**
* 情报板协议
*/
private String boardProtocol;
// /**
// * 情报板协议
// */
// private String boardProtocol;
public String getStr() {
return "perceptionType=" + perceptionType +
",perceptionValue=" + perceptionValue +
",boardIp='" + boardIp;
}
}

View File

@ -0,0 +1,18 @@
package com.ruoyi.sensor.domain;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import java.time.LocalDateTime;
@Data
public class PerceptionWarningStatusInfo {
private String boardIp;
private String perceptionType;
private Double perceptionValue;
private Integer perceptionLevel;
private String perceptionStatus;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime perceptionTime;
private String perceptionMsg;
}

View File

@ -0,0 +1,20 @@
package com.ruoyi.sensor.domain;
import lombok.Data;
@Data
public class PresetContentStyle {
private String areaType;
private String backgroundColor;
private String bmpFileName;
private String content;
private String font;
private String fontColor;
private Integer fontSize;
private Integer width;
private Integer height;
private String imageAddress;
private Integer startX;
private Integer startY;
private Integer stay;
}

View File

@ -0,0 +1,19 @@
package com.ruoyi.sensor.domain;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.List;
@Data
public class ReleaseLogReport {
private String boardIp;
private String perceptionType;
private int perceptionLevel;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime releaseTime;
private String releaseStatus;
private String releaseContent;
private List<PresetContentStyle> lines;
}

View File

@ -1,16 +0,0 @@
package com.ruoyi.sensor.domain;
import lombok.Data;
import java.util.Date;
@Data
public class WarningStatusInfo {
private String infoBoardIP;
private String warningType;
private Double warningValue;
private Integer currentWarningLevel;
private String status;
private Date triggerTime;
private String warningMessage;
}

View File

@ -0,0 +1,15 @@
package com.ruoyi.sensor.domain.dto;
import com.ruoyi.sensor.domain.PerceptionParams;
import lombok.Data;
import java.util.Date;
@Data
public class PerceptionHandlerDTO {
private Date createTime = new Date();
private Date updateTime = new Date();
private Integer allReceiveTimes = 0;
private Integer currentMinReceiveTimes = 0;
private PerceptionParams perceptionParams;
}

View File

@ -0,0 +1,26 @@
package com.ruoyi.sensor.enums;
import java.util.stream.Stream;
public enum PrintColor {
red("red", "255000000"),
yellow("yellow", "255255000"),
green("green", "000255000");
private final String colorName;
private final String colourBitStr;
PrintColor(String colorName, String colourBitStr) {
this.colorName = colorName;
this.colourBitStr = colourBitStr;
}
public static String getBitColor(String colorName) {
return Stream.of(PrintColor.values())
.filter(fontColor -> fontColor.colorName.equals(colorName))
.map(fontColor -> fontColor.colourBitStr)
.findFirst()
.orElse(null);
}
}

View File

@ -2,35 +2,54 @@ package com.ruoyi.sensor.merchants;
import com.alibaba.fastjson2.JSON;
import com.ruoyi.common.utils.http.HttpUtils;
import com.ruoyi.sensor.domain.WarningStatusInfo;
import com.ruoyi.sensor.domain.PerceptionWarningStatusInfo;
import com.ruoyi.sensor.domain.ReleaseLogReport;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
@Component
public class MerchantsHttp {
private static final Logger log = LoggerFactory.getLogger(MerchantsHttp.class);
private static final String uri = "http://localhost:8080/merchants";
private static String STATUS_URI;
private static String PUBLICATION_LOG_URI;
public static void sendWarning(WarningStatusInfo warningStatusInfo){
String jsonString = JSON.toJSONString(warningStatusInfo);
@Value("${remote.statusReport}")
public void setStatusUri(String statusUri) {
STATUS_URI = statusUri;
}
@Value("${remote.logReport}")
public void setPublicationLogUri(String publicationLogUri) {
PUBLICATION_LOG_URI = publicationLogUri;
}
public static void sendWarningStatus(PerceptionWarningStatusInfo perceptionWarningStatusInfo){
String jsonString = JSON.toJSONString(perceptionWarningStatusInfo);
send(STATUS_URI, jsonString);
}
public static void sendPublicationLog(ReleaseLogReport releaseLogReport){
String jsonString = JSON.toJSONString(releaseLogReport);
send(PUBLICATION_LOG_URI, jsonString);
}
private static void send(String URI, String jsonStr) {
String result = null;
Map<String, String> header = new HashMap<>();
try {
result = HttpUtils.postCall(uri, jsonString, header);
result = HttpUtils.postCall(URI, jsonStr);
} catch (Exception e) {
log.error("send warning error", e);
return;
}
if (StringUtils.isEmpty(result)) {
log.error("send warning error");
return;
}
// 记录上报结果
log.error("发送信息成功,但是没有接收到响应信息");
} else {
log.info(result);
}
}
}

View File

@ -6,4 +6,7 @@ public interface ISensorDataService {
void perceptionParamsHandler(PerceptionParams params);
void putPerceptionParamsToMap(PerceptionParams perceptionParams);
void handlerPerceptionMap();
}

View File

@ -9,23 +9,34 @@ import com.ruoyi.board.service.IAlertPlanService;
import com.ruoyi.board.service.IBoardInfoService;
import com.ruoyi.board.service.IPresetContentService;
import com.ruoyi.board.service.IReleaseRecordService;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.protocol.sansi.SanSiProtocol;
import com.ruoyi.protocol.service.impl.ContentPublishingService;
import com.ruoyi.sensor.common.PerceptionHandlerMap;
import com.ruoyi.sensor.domain.PerceptionParams;
import com.ruoyi.sensor.domain.WarningStatusInfo;
import com.ruoyi.sensor.domain.PerceptionWarningStatusInfo;
import com.ruoyi.sensor.domain.PresetContentStyle;
import com.ruoyi.sensor.domain.ReleaseLogReport;
import com.ruoyi.sensor.domain.dto.PerceptionHandlerDTO;
import com.ruoyi.sensor.enums.PrintColor;
import com.ruoyi.sensor.merchants.MerchantsHttp;
import com.ruoyi.sensor.service.ISensorDataService;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import java.util.Date;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;
@Service
public class SensorDataServiceImpl implements ISensorDataService {
private static final Logger log = LoggerFactory.getLogger(SensorDataServiceImpl.class);
private static final Integer ALL_TIMES_THRESHOLD = 5;
private static final Integer MIN_TIMES_THRESHOLD = 4;
@Autowired
private IBoardInfoService boardInfoService;
@Autowired
@ -34,55 +45,206 @@ public class SensorDataServiceImpl implements ISensorDataService {
private IPresetContentService presetContentService;
@Autowired
private IReleaseRecordService releaseRecordService;
@Autowired
private ContentPublishingService contentPublishingService;
@Override
@Async
public void perceptionParamsHandler(PerceptionParams params) {
int planTypeByName = AlertPlanType.getPlanTypeByName(params.getPerceptionType());
int currentContentPlanType = AlertPlanType.getPlanTypeByName(params.getPerceptionType());
// 找到符合计划的预警
AlertPlan oneByTypeAndValue = alertPlanService.getOneByTypeAndValue(planTypeByName, params.getPerceptionValue());
if (null == oneByTypeAndValue) {
AlertPlan currentAlertPlan = alertPlanService.getOneByTypeAndValue(currentContentPlanType, params.getPerceptionValue());
if (null == currentAlertPlan) {
log.error("找不到预警计划");
return;
}
// 情报板设备信息
BoardInfo oneByIP = boardInfoService.getOneByIP(params.getBoardIp());
if (null == oneByIP) {
BoardInfo boardInfo = boardInfoService.getOneByIP(params.getBoardIp());
if (null == boardInfo) {
log.error("找不到情报板信息");
return;
}
// 预置发布的信息
PresetContent oneByContentAndBoardSize = presetContentService.getOneByContentAndBoardSize(oneByTypeAndValue.getDisplayContent(), oneByIP.getBoardSize(), planTypeByName);
if (null == oneByContentAndBoardSize) {
PresetContent currentContent = presetContentService.getOneByContentAndBoardSize(currentAlertPlan.getDisplayContent(), boardInfo.getBoardSize(), currentContentPlanType);
if (null == currentContent) {
log.error("找不到预置发布的信息");
return;
}
// 获取同类型最新的一条发布记录
ReleaseRecord oneLatestByBoardId = releaseRecordService.getOneLatestByBoardId(oneByIP.getBoardIp(), 1);
if (null == oneLatestByBoardId) {
// 说明之前没有发布记录
// 预警信息发布流程
List<PresetContent> informationToBeReleasedList = new ArrayList<>();
String presetStatus = "预警开始";
// 查询这个情报板 2 分钟内所有发布内容
List<ReleaseRecord> releaseRecordList = releaseRecordService.listLatest2minRecordByBoardId(boardInfo.getId());
if (releaseRecordList.isEmpty()) {
// 如果查到为空说明 2 分钟内没有发布内容这里是第一次发布直接走发布逻辑
informationToBeReleasedList.add(currentContent);
} else {
// 如果不为空说明 2 分钟内是发布了内容的查询所有的发布内容
List<Integer> presetContentIdList = releaseRecordList.stream().map(ReleaseRecord::getPresetContentId).collect(Collectors.toList());
List<PresetContent> existPresetContentList = presetContentService.listByIds(presetContentIdList);
// 先把已经存在的都加入进去
informationToBeReleasedList.addAll(existPresetContentList);
// 判断所有发布内容中是否存在当前要发布的类型
PresetContent isExistsPresetContent = existPresetContentList.stream().filter(i -> i.getInfoType() == currentContentPlanType).findFirst().orElse(null);
if (isExistsPresetContent == null) {
// 说明这是一个新发布的预警类型
informationToBeReleasedList.add(currentContent);
} else {
// 说明这是一个已有的类型需要获取预警等级然后进行判断
// 判断已有类型是否和现在的一样然后再替换掉
// 现在需要知道这个同类型的属于是哪个级别的
int foundPlanId = releaseRecordList.stream()
.filter(i -> Objects.equals(i.getPresetContentId(), isExistsPresetContent.getId()))
.mapToInt(ReleaseRecord::getPlanId)
.findFirst().orElse(-1);
AlertPlan byId = alertPlanService.getById(foundPlanId);
int compare = NumberUtils.compare(byId.getLevel(),
currentAlertPlan.getLevel());
switch (compare) {
case 0:
// 两个一样等级
// 这时候就不需要替换也不需要重新发布
presetStatus = "预警持续";
break;
case 1:
// getPresetContentLevel > alertPlan.getLevel()
// 预警减弱
// 此时需要将同类型的信息给替换掉并重新发布
presetStatus = "预警减弱";
break;
case -1:
// getPresetContentLevel < alertPlan.getLevel()
// 预警加强
// 此时需要将同类型的信息给替换掉并重新发布
presetStatus = "预警加强";
break;
}
// todo 发布
boolean sanSiProtocol = new SanSiProtocol().publishContent(oneByIP, oneByContentAndBoardSize);
// 替换同类型的预警信息
for (int i = 0; i < existPresetContentList.size(); i++) {
if (existPresetContentList.get(i).getInfoType() == currentContentPlanType) {
existPresetContentList.set(i, currentContent);
}
}
}
}
// todo 发布
contentPublishingService.contentPublish(boardInfo, informationToBeReleasedList);
// 经过主动发布之后就变成预置信息
// 发布之后记录发布内容 ReleaseRecord
this.saveRecord(boardInfo.getId(), currentContent.getId(), currentAlertPlan.getId());
// 记录完了之后就需要向对方发送状态日志记录
this.sendStatus(presetStatus, boardInfo.getBoardIp(), params.getPerceptionType(), currentContent.getContent(), params.getPerceptionValue(), currentAlertPlan.getLevel());
// 发送情报板发布信息日志记录
this.sendPublicLog(boardInfo.getBoardIp(), params.getPerceptionType(), currentAlertPlan.getLevel(), currentContent.getContent(), informationToBeReleasedList);
}
@Override
public void putPerceptionParamsToMap(PerceptionParams perceptionParams) {
String key = perceptionParams.getPerceptionType() + perceptionParams.getBoardIp();
PerceptionHandlerDTO orDefault = PerceptionHandlerMap.perceptionHandlerMap.getOrDefault(key, new PerceptionHandlerDTO());
orDefault.setPerceptionParams(perceptionParams);
orDefault.setAllReceiveTimes(orDefault.getAllReceiveTimes() + 1);
orDefault.setCurrentMinReceiveTimes(orDefault.getCurrentMinReceiveTimes() + 1);
orDefault.setUpdateTime(new Date());
PerceptionHandlerMap.perceptionHandlerMap.put(key, orDefault);
}
@Override
public void handlerPerceptionMap(){
List<PerceptionParams> publishList = new ArrayList<>();
List<PerceptionParams> cancelList = new ArrayList<>();
List<String> delList = new ArrayList<>();
long now = new Date().getTime();
log.info("{}", PerceptionHandlerMap.perceptionHandlerMap);
PerceptionHandlerMap.perceptionHandlerMap.forEach((key, value) -> {
long lastUpdateTime = value.getUpdateTime().getTime();
// 最后的接收时间大于 1 分钟
if (now - lastUpdateTime > 60 * 1000) {
delList.add(key);
if (value.getAllReceiveTimes() > ALL_TIMES_THRESHOLD ) {
cancelList.add(value.getPerceptionParams());
}
} else {
// 接收时间 < 1 分钟 && 接收次数 > 5
// 要不要 + 个属性当前分钟内接收的次数以及接收的总次数
if (value.getAllReceiveTimes() > ALL_TIMES_THRESHOLD && value.getCurrentMinReceiveTimes() > MIN_TIMES_THRESHOLD) {
publishList.add(value.getPerceptionParams());
}
}
value.setCurrentMinReceiveTimes(0);
});
// 需要删除的内容有的是没有发布的浮动信息有的是已经显示过了需要取消显示的
cancelList.forEach(this::cancelPublication);
log.info("cancelPublication:{}", cancelList);
delList.forEach(PerceptionHandlerMap.perceptionHandlerMap::remove);
log.info("delList:{}", delList);
publishList.forEach(this::perceptionParamsHandler);
log.info("publishList:{}", publishList);
}
private void sendPublicLog(String ip, String type, Integer level, String content, List<PresetContent> informationToBeReleasedList) {
ReleaseLogReport releaseLogReport = new ReleaseLogReport();
releaseLogReport.setBoardIp(ip);
releaseLogReport.setPerceptionType(type);
releaseLogReport.setPerceptionLevel(level);
releaseLogReport.setReleaseContent(content);
releaseLogReport.setReleaseTime(LocalDateTime.now());
releaseLogReport.setReleaseStatus("发布成功");
List<PresetContentStyle> presetContentStyles = new ArrayList<>();
informationToBeReleasedList.forEach(i -> {
String[] split = StringUtils.split(i.getBoardSize(), "*");
PresetContentStyle p = new PresetContentStyle();
p.setFont(i.getFontStyle());
p.setFontSize(i.getFontSize());
p.setFontColor(PrintColor.getBitColor(i.getFontColor()));
p.setContent(i.getContent());
p.setStartX(i.getFontPositionX());
p.setStartY(i.getFontPositionY());
p.setWidth(NumberUtils.toInt(split[1]));
p.setHeight(NumberUtils.toInt(split[0]));
p.setStay(i.getPlayTime());
presetContentStyles.add(p);
});
releaseLogReport.setLines(presetContentStyles);
MerchantsHttp.sendPublicationLog(releaseLogReport);
}
private void sendStatus(String status, String ip, String type, String content, Double value, Integer level) {
PerceptionWarningStatusInfo statusInfo = new PerceptionWarningStatusInfo();
statusInfo.setPerceptionStatus(status);
statusInfo.setBoardIp(ip);
statusInfo.setPerceptionType(type);
statusInfo.setPerceptionValue(value);
statusInfo.setPerceptionMsg(content);
statusInfo.setPerceptionLevel(level);
statusInfo.setPerceptionTime(LocalDateTime.now());
// // 开始发送状态记录
MerchantsHttp.sendWarningStatus(statusInfo);
}
@Async
public void cancelPublication(PerceptionParams params) {
PerceptionWarningStatusInfo statusInfo = new PerceptionWarningStatusInfo();
statusInfo.setPerceptionStatus("预警结束");
statusInfo.setBoardIp(params.getBoardIp());
statusInfo.setPerceptionType(params.getPerceptionType());
statusInfo.setPerceptionValue(params.getPerceptionValue());
statusInfo.setPerceptionMsg("");
statusInfo.setPerceptionLevel(1);
statusInfo.setPerceptionTime(LocalDateTime.now());
// // 开始发送状态记录
MerchantsHttp.sendWarningStatus(statusInfo);
}
private void saveRecord(Integer boardId, Integer presetContentId, Integer planId) {
ReleaseRecord releaseRecord = new ReleaseRecord();
releaseRecord.setBoardId(oneByIP.getId());
releaseRecord.setPresetContentId(oneByContentAndBoardSize.getId());
releaseRecord.setBoardId(boardId);
releaseRecord.setPresetContentId(presetContentId);
releaseRecord.setPlanId(planId);
releaseRecord.setCreateTime(new Date());
releaseRecord.setCreatedBy(SecurityUtils.getUserId().intValue());
releaseRecord.setCreatedBy(1);
releaseRecordService.save(releaseRecord);
// 记录完了之后就需要向对方发送日志记录
WarningStatusInfo statusInfo = new WarningStatusInfo();
statusInfo.setStatus("");
statusInfo.setInfoBoardIP(oneByIP.getBoardIp());
statusInfo.setWarningType(params.getPerceptionType());
statusInfo.setWarningMessage(oneByContentAndBoardSize.getContent());
statusInfo.setWarningValue(params.getPerceptionValue());
statusInfo.setCurrentWarningLevel(oneByTypeAndValue.getLevel());
statusInfo.setTriggerTime(new Date());
// 开始发送状态记录
MerchantsHttp.sendWarning(new WarningStatusInfo());
}
}

View File

@ -0,0 +1,21 @@
package com.ruoyi.sensor.task;
import com.ruoyi.sensor.service.ISensorDataService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component("clearPerceptionTask")
public class ClearPerceptionTask {
@Autowired
private ISensorDataService sensorDataService;
private static final Logger log = LoggerFactory.getLogger(ClearPerceptionTask.class);
public void clearPerception(){
sensorDataService.handlerPerceptionMap();
log.info("handlerPerceptionMap 处理完了");
}
}

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.board.mapper.PlanTypeMapper">
</mapper>

View File

@ -0,0 +1,53 @@
import request from '@/utils/request'
// 查询计划发布中的类型列表
export function listType(query) {
return request({
url: '/plan/type/list',
method: 'get',
params: query
})
}
// 查询计划发布中的类型详细
export function getType(id) {
return request({
url: '/plan/type/' + id,
method: 'get'
})
}
// 新增计划发布中的类型
export function addType(data) {
return request({
url: '/plan/type',
method: 'post',
data: data
})
}
// 修改计划发布中的类型
export function updateType(data) {
return request({
url: '/plan/type',
method: 'put',
data: data
})
}
// 删除计划发布中的类型
export function delType(id) {
return request({
url: '/plan/type/' + id,
method: 'delete'
})
}
// 查询部门下拉树结构
export function planTypeTreeSelect() {
return request({
url: '/plan/type/planTree',
method: 'get'
})
}

View File

@ -0,0 +1,106 @@
<template>
<div>
<!-- 调整缩放比例的控件 -->
<el-form label-position="left" label-width="70px">
<el-form-item label="缩放比例">
<el-row :gutter="20" align="middle">
<el-col :span="10">
<el-slider
v-model="scale"
:min="0.5"
:max="3"
:step="0.5"
show-input
input-size="mini"
/>
</el-col>
</el-row>
</el-form-item>
<el-form-item label="文字预览">
<div :style="containerStyle">
<span :style="textStyle">
{{ content }}
</span>
</div>
</el-form-item>
</el-form>
</div>
</template>
<script>
export default {
data() {
return {
scale: 1, //
};
},
props: {
fontStyle: {
type: String,
default: 'normal',
},
fontColor: {
type: String,
default: '#ff0000', //
},
fontSize: {
type: Number,
default: 16,
},
letterSpacing: {
type: Number,
default: 0,
},
fontX: {
type: Number,
default: 0,
},
fontY: {
type: Number,
default: 0,
},
backgroundWidth: {
type: Number,
default: 320,
},
backgroundHeight: {
type: Number,
default: 160,
},
lineHeight: {
type: Number,
default: 1, // 1
},
content: {
type: String,
default: '这里是示例内容,哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈',
},
},
computed: {
containerStyle() {
return {
width: `${this.backgroundWidth * this.scale}px`,
height: `${this.backgroundHeight * this.scale}px`,
position: 'relative',
background: '#000000',
overflow: 'hidden',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
};
},
textStyle() {
return {
position: 'absolute',
top: `${-this.fontY * this.scale}px`,
left: `${this.fontX * this.scale}px`,
fontStyle: this.fontStyle,
color: this.fontColor,
fontSize: `${this.fontSize * this.scale}px`,
letterSpacing: `${this.letterSpacing * this.scale}px`,
lineHeight: `${this.lineHeight}`,
};
},
},
};
</script>

View File

@ -22,9 +22,9 @@
/>
</el-select>
</el-form-item>
<el-form-item label="类型" prop="type">
<el-form-item label="信息类型" prop="type">
<el-select
v-model="queryParams.type"
v-model="queryParams.infoType"
clearable
placeholder="全部"
>
@ -75,16 +75,16 @@
v-hasPermi="['board:content:remove']"
>删除</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="warning"
plain
icon="el-icon-download"
size="mini"
@click="handleExport"
v-hasPermi="['board:content:export']"
>导出</el-button>
</el-col>
<!-- <el-col :span="1.5">-->
<!-- <el-button-->
<!-- type="warning"-->
<!-- plain-->
<!-- icon="el-icon-download"-->
<!-- size="mini"-->
<!-- @click="handleExport"-->
<!-- v-hasPermi="['board:content:export']"-->
<!-- >导出</el-button>-->
<!-- </el-col>-->
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
@ -138,7 +138,7 @@
/>
<!-- 添加或修改预置信息及模版对话框 -->
<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
<el-dialog :title="title" :visible.sync="open" width="1000px" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="auto">
<el-form-item label="预置信息名称" prop="name">
<el-input v-model="form.name" placeholder="请输入预置信息名称" />
@ -199,7 +199,7 @@
</el-select>
</el-form-item>
<el-form-item label="字体大小" prop="fontSize">
<el-input-number :min="0" v-model="form.fontSize" placeholder="请输入字体大小" />
<el-input-number :min="20" :max="50" v-model="form.fontSize" placeholder="请输入字体大小" />
</el-form-item>
<el-form-item label="字体间距" prop="letterSpacing">
<el-input-number :min="0" v-model="form.letterSpacing" placeholder="请输入字体间距" />
@ -213,11 +213,18 @@
<el-form-item label="播放时间" prop="playTime">
<el-input-number :min="0" v-model="form.playTime" placeholder="请输入播放时间" />
</el-form-item>
<el-form-item label="预览路径" prop="previewPath">
<el-input v-model="form.previewPath" placeholder="请输入预览路径" />
</el-form-item>
<text-preview
fontStyle="SongTi"
:fontSize="form.fontSize"
:letterSpacing="form.letterSpacing"
:fontX="form.fontPositionX"
:fontY="form.fontPositionY"
:backgroundWidth="bgSize.width"
:backgroundHeight="bgSize.height"
:lineHeight="1"
:content="form.content" />
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
@ -228,9 +235,11 @@
<script>
import { listContent, getContent, delContent, addContent, updateContent } from "@/api/board/content";
import TextPreview from "@/views/board/component/TextPreview.vue";
export default {
name: "Content",
components: {TextPreview},
dicts: ['board_size','font_color','font_style', 'alert_type'],
data() {
return {
@ -334,6 +343,15 @@ export default {
created() {
this.getList();
},
computed:{
bgSize() {
if (!this.form.boardSize) {
return {width: 320, height:160}
}
let [height, width] = this.form.boardSize.split("*").map(Number);
return {width, height}
},
},
methods: {
/** 查询预置信息及模版列表 */
getList() {

View File

@ -98,17 +98,17 @@
>删除
</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="warning"
plain
icon="el-icon-download"
size="mini"
@click="handleExport"
v-hasPermi="['board:info:export']"
>导出
</el-button>
</el-col>
<!-- <el-col :span="1.5">-->
<!-- <el-button-->
<!-- type="warning"-->
<!-- plain-->
<!-- icon="el-icon-download"-->
<!-- size="mini"-->
<!-- @click="handleExport"-->
<!-- v-hasPermi="['board:info:export']"-->
<!-- >导出-->
<!-- </el-button>-->
<!-- </el-col>-->
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
@ -228,11 +228,12 @@ import {listInfo, getInfo, delInfo, addInfo, updateInfo} from "@/api/board/info"
import {deptTreeSelect} from "@/api/system/user";
import Treeselect from "@riophae/vue-treeselect";
import "@riophae/vue-treeselect/dist/vue-treeselect.css";
import TextPreview from "@/views/board/component/TextPreview.vue";
export default {
name: "Info",
dicts: ['board_size', 'board_protocol'],
components: {Treeselect},
components: {TextPreview, Treeselect},
data() {
return {
//

View File

@ -1,56 +1,22 @@
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="auto">
<el-form-item label="等级" prop="level">
<el-select
v-model="queryParams.level"
clearable
placeholder="全部"
>
<el-option
v-for="dict in dict.type.alert_level"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item label="类型" prop="type">
<el-select
v-model="queryParams.type"
clearable
placeholder="全部"
>
<el-option
v-for="dict in dict.type.alert_type"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item label="最大值" prop="maxValue">
<el-row :gutter="20">
<el-col :span="4" :xs="24">
<div class="head-container">
<el-input
v-model="queryParams.maxValue"
placeholder="请输入最大值"
v-model="searchValue"
placeholder="请输入名称"
clearable
@keyup.enter.native="handleQuery"
size="small"
prefix-icon="el-icon-search"
style="margin-bottom: 20px"
/>
</el-form-item>
<el-form-item label="最小值" prop="minValue">
<el-input
v-model="queryParams.minValue"
placeholder="请输入最小值"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
</div>
<div class="head-container">
<plan-type-tree @node-clicked="handleNodeClick" :search-value="searchValue" />
</div>
</el-col>
<el-col :span="20" :xs="24">
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button
@ -84,32 +50,23 @@
v-hasPermi="['board:plan:remove']"
>删除</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="warning"
plain
icon="el-icon-download"
size="mini"
@click="handleExport"
v-hasPermi="['board:plan:export']"
>导出</el-button>
</el-col>
<!-- <el-col :span="1.5">-->
<!-- <el-button-->
<!-- type="warning"-->
<!-- plain-->
<!-- icon="el-icon-download"-->
<!-- size="mini"-->
<!-- @click="handleExport"-->
<!-- v-hasPermi="['board:plan:export']"-->
<!-- >导出</el-button>-->
<!-- </el-col>-->
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table v-loading="loading" :data="planList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="唯一编号" align="center" prop="id" />
<el-table-column label="类型" align="center" prop="type">
<template slot-scope="scope">
<dict-tag :options="dict.type.alert_type" :value="scope.row.type"/>
</template>
</el-table-column>
<el-table-column label="等级" align="center" prop="level">
<template slot-scope="scope">
<dict-tag :options="dict.type.alert_level" :value="scope.row.level"/>
</template>
</el-table-column>
<el-table-column label="类型" align="center" prop="typeName" />
<el-table-column label="等级" align="center" prop="level" />
<el-table-column label="最大值" align="center" prop="maxValue" />
<el-table-column label="最小值" align="center" prop="minValue" />
<el-table-column label="显示内容" align="center" prop="displayContent" />
@ -139,36 +96,17 @@
:limit.sync="queryParams.pageSize"
@pagination="getList"
/>
</el-col>
</el-row>
<!-- 添加或修改警报计划对话框 -->
<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
<el-form-item label="等级" prop="level">
<el-select
v-model="form.level"
placeholder="全部"
:value="1"
>
<el-option
v-for="dict in dict.type.alert_level"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
<el-input v-model="form.level" placeholder="请输入等级" />
</el-form-item>
<el-form-item label="类型" prop="type">
<el-select
v-model="form.type"
placeholder="全部"
>
<el-option
v-for="dict in dict.type.alert_type"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
<el-form-item label="类型" prop="typeName">
<el-input v-model="clickNodeData.label" disabled />
</el-form-item>
<el-form-item label="最小值" prop="minValue">
<el-input-number :min="0" :precision="2" v-model="form.minValue" placeholder="请输入最小值" />
@ -190,12 +128,16 @@
<script>
import { listPlan, getPlan, delPlan, addPlan, updatePlan } from "@/api/board/plan";
import PlanTypeTree from "@/views/board/plan/planTypeTree.vue";
import {planTypeTreeSelect} from "@/api/board/plantype";
export default {
name: "Plan",
components: {PlanTypeTree},
dicts: ['alert_level','alert_type'],
data() {
return {
searchValue: "",
//
loading: true,
//
@ -224,6 +166,10 @@ export default {
minValue: null,
displayContent: null
},
clickNodeData: {
id: 0,
label: "",
},
//
form: {},
//
@ -247,7 +193,7 @@ export default {
};
},
created() {
this.getList();
this.getPlanTypeTree();
},
methods: {
/** 查询警报计划列表 */
@ -304,8 +250,6 @@ export default {
const id = row.id || this.ids
getPlan(id).then(response => {
this.form = response.data;
this.form.type += '';
this.form.level += '';
this.open = true;
this.title = "修改警报计划";
});
@ -321,6 +265,7 @@ export default {
this.getList();
});
} else {
this.form.type = this.clickNodeData.id;
addPlan(this.form).then(response => {
this.$modal.msgSuccess("新增成功");
this.open = false;
@ -346,10 +291,18 @@ export default {
...this.queryParams
}, `plan_${new Date().getTime()}.xlsx`)
},
/** 字典翻译 */
dictAlertPlanTypeFormat(){
}
}
handleNodeClick(data) {
this.clickNodeData = data;
this.queryParams.type = data.id;
this.handleQuery();
},
getPlanTypeTree() {
planTypeTreeSelect().then(response => {
this.clickNodeData = response.data[0].children[0];
this.queryParams.type = response.data[0].children[0].id;
this.getList();
});
},
},
};
</script>

View File

@ -0,0 +1,200 @@
<template>
<div>
<el-tree
:data="planTypeOptions"
node-key="id"
:props="defaultProps"
:filter-node-method="filterNode"
@node-click="handleNodeClick"
:expand-on-click-node="false"
ref="tree"
default-expand-all
highlight-current
>
<template #default="{ node, data }">
<div class="custom-tree-node">
<span>{{ data.label }}</span>
<span class="custom-tree-node-btns">
<el-button
type="text"
icon="el-icon-plus"
@click="handleAdd(node, data)"
></el-button>
<el-button
type="text"
icon="el-icon-minus"
@click="handleDelete(node, data)"
></el-button>
</span>
</div>
</template>
</el-tree>
<!-- 新增节点对话框 -->
<el-dialog
title="新增节点"
:visible.sync="dialogVisible">
<el-form :model="form" label-width="auto">
<el-form-item label="类型名称">
<el-row>
<el-col :span="8">
<el-input v-model="form.label"/>
</el-col>
</el-row>
</el-form-item>
<el-form-item label="节点类型">
<el-row>
<el-col :span="8">
<el-select v-model="form.addType">
<el-option
v-for="item in [{label: '同级',value: true},{label: '子级',value: false},]"
:key="item.value"
:label="item.label"
:value="item.value"/>
</el-select>
</el-col>
</el-row>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="confirmAdd">确定</el-button>
</div>
</el-dialog>
<!-- 删除确认框 -->
<el-dialog
title="确认删除"
:visible.sync="deleteDialogVisible"
>
<span>确定删除节点 "{{ selectedNodeLabel }}" </span>
<div slot="footer" class="dialog-footer">
<el-button @click="deleteDialogVisible = false">取消</el-button>
<el-button type="danger" @click="confirmDelete">删除</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import {addType, delType, planTypeTreeSelect} from "@/api/board/plantype";
export default {
name: "PlanTypeTree",
watch: {
//
searchValue(val) {
this.$refs.tree.filter(val);
},
},
props: {
searchValue: {
type: String,
default: "",
}
},
data() {
return {
planTypeOptions: undefined,
treeData: [],
dialogVisible: false,
deleteDialogVisible: false,
form: {
label: "",
addType: true,
selectedNodeId: 0,
selectedNodeLevel: 0,
selectedNodeParentId: 0,
},
parentNodeLabel: "",
selectedNode: null,
selectedNodeLabel: "",
defaultProps: {
children: "children",
label: "label",
},
};
},
methods: {
handleAdd(node, data) {
this.selectedNode = node;
this.parentNodeLabel = data.label;
this.dialogVisible = true;
},
confirmAdd() {
let addObj = {
typeName: this.form.label,
parentId: this.form.selectedNodeId,
}
if (this.form.addType === true) {
addObj.parentId = this.form.selectedNodeLevel === 1 ? 0 : this.form.selectedNodeParentId;
}
addType(addObj).then(resp => {
this.$modal.msgSuccess("新增成功");
this.getPlanTypeTree();
})
this.dialogVisible = false;
},
handleDelete(node, data) {
this.selectedNode = node;
this.selectedNodeLabel = data.label;
this.deleteDialogVisible = true;
},
confirmDelete() {
delType(this.form.selectedNodeId).then(resp => {
this.$modal.msgSuccess("删除成功");
this.getPlanTypeTree();
})
this.deleteDialogVisible = false;
},
handleNodeClick(data, node) {
if (node.level < 2) {
return;
}
this.$emit('node-clicked', data);
this.form.selectedNodeId = data.id;
this.form.selectedNodeLevel = node.level;
if (node.level === 1) {
this.form.selectedNodeParentId = 0;
} else {
this.form.selectedNodeParentId = node.parent.data.id;
}
},
/** 查询类型下拉树结构 */
getPlanTypeTree() {
planTypeTreeSelect().then(response => {
this.planTypeOptions = response.data;
});
},
//
filterNode(value, data) {
if (!value) return true;
return data.label.indexOf(value) !== -1;
},
},
created() {
this.getPlanTypeTree();
},
};
</script>
<style scoped>
.custom-tree-node {
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
position: relative;
}
.custom-tree-node-btns {
display: none;
position: absolute;
right: 0;
}
.custom-tree-node:hover .custom-tree-node-btns {
display: flex;
}
</style>

View File

@ -1,33 +1,34 @@
<template>
<div class="app-container">
<el-row :gutter="20">
<!--部门数据-->
<el-col :span="4" :xs="24">
<div class="head-container">
<el-input
v-model="deptName"
placeholder="请输入部门名称"
clearable
size="small"
prefix-icon="el-icon-search"
style="margin-bottom: 20px"
/>
</div>
<div class="head-container">
<el-tree
:data="deptOptions"
:props="defaultProps"
:expand-on-click-node="false"
:filter-node-method="filterNode"
ref="tree"
node-key="id"
default-expand-all
highlight-current
@node-click="handleNodeClick"
/>
</div>
</el-col>
</el-row>
<h1>瓷器活发布系统</h1>
<!-- <el-row :gutter="20">-->
<!-- &lt;!&ndash;部门数据&ndash;&gt;-->
<!-- <el-col :span="4" :xs="24">-->
<!-- <div class="head-container">-->
<!-- <el-input-->
<!-- v-model="deptName"-->
<!-- placeholder="请输入部门名称"-->
<!-- clearable-->
<!-- size="small"-->
<!-- prefix-icon="el-icon-search"-->
<!-- style="margin-bottom: 20px"-->
<!-- />-->
<!-- </div>-->
<!-- <div class="head-container">-->
<!-- <el-tree-->
<!-- :data="deptOptions"-->
<!-- :props="defaultProps"-->
<!-- :expand-on-click-node="false"-->
<!-- :filter-node-method="filterNode"-->
<!-- ref="tree"-->
<!-- node-key="id"-->
<!-- default-expand-all-->
<!-- highlight-current-->
<!-- @node-click="handleNodeClick"-->
<!-- />-->
<!-- </div>-->
<!-- </el-col>-->
<!-- </el-row>-->
</div>
</template>

View File

@ -9,7 +9,7 @@ const CompressionPlugin = require('compression-webpack-plugin')
const name = process.env.VUE_APP_TITLE || '瓷器活发布系统' // 网页标题
const port = process.env.port || process.env.npm_config_port || 80 // 端口
const port = process.env.port || process.env.npm_config_port || 8888 // 端口
// vue.config.js 配置说明
//官方vue.config.js 参考文档 https://cli.vuejs.org/zh/config/#css-loaderoptions