From f557cca7cccdc1f7ae0cffc3ee4fcb10152fbf8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B0=A2=E6=B4=AA=E9=BE=99?= <599179587@qq.com> Date: Thu, 6 Jul 2017 18:12:01 +0800 Subject: [PATCH] =?UTF-8?q?Socket=E9=A1=B9=E7=9B=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + nb-configuration.xml | 19 + pom.xml | 227 +++++++ src/main/java/com/ifish/bean/LoginRecord.java | 49 ++ src/main/java/com/ifish/bean/Tbl_Device.java | 508 ++++++++++++++ .../com/ifish/bean/Tbl_Device_Statistics.java | 630 ++++++++++++++++++ .../java/com/ifish/bean/Tbl_Device_User.java | 293 ++++++++ .../java/com/ifish/bean/Tbl_Factory_List.java | 125 ++++ .../com/ifish/bean/Tbl_HardWare_Type.java | 437 ++++++++++++ .../java/com/ifish/bean/Tbl_Push_List.java | 465 +++++++++++++ src/main/java/com/ifish/bean/Tbl_User.java | 557 ++++++++++++++++ .../java/com/ifish/config/MinaConfig.java | 139 ++++ .../config/MyWSServletContextListener.java | 14 + .../java/com/ifish/config/MybatisConfig.java | 86 +++ .../java/com/ifish/config/RedisConfig.java | 58 ++ .../java/com/ifish/config/RootConfig.java | 26 + .../com/ifish/config/WebAppInitializer.java | 46 ++ src/main/java/com/ifish/config/WebConfig.java | 59 ++ .../java/com/ifish/enums/PushTypeEnum.java | 33 + .../com/ifish/enums/SubDirectoryEnum.java | 45 ++ .../java/com/ifish/helper/DeviceHelperI.java | 41 ++ .../java/com/ifish/helper/UserHelperI.java | 20 + .../com/ifish/socket/codeFactory/Context.java | 58 ++ .../codeFactory/ServerCodecFactory.java | 31 + .../socket/codeFactory/ServerDecode.java | 385 +++++++++++ .../socket/codeFactory/ServerEncode.java | 137 ++++ .../com/ifish/socket/config/ExecuteJob.java | 119 ++++ .../com/ifish/socket/config/JobFactory.java | 31 + .../socket/config/MinaServerHandler.java | 126 ++++ .../socket/config/MyInetSocketAddress.java | 53 ++ .../com/ifish/socket/config/ScheduleJob.java | 114 ++++ .../com/ifish/socket/config/SomeServer.java | 364 ++++++++++ .../socket/model/DeviceResponseLength99.java | 500 ++++++++++++++ .../com/ifish/socket/model/HeadModel.java | 80 +++ .../java/com/ifish/socket/model/JobGroup.java | 107 +++ .../com/ifish/socket/model/OrderModel.java | 101 +++ .../receive/APPLoginResponseLength26.java | 41 ++ .../receive/DeviceLoginResponseLength25.java | 31 + .../receive/ServerHeartbeatLength17.java | 21 + .../model/send/APPLoginContextLength25.java | 30 + .../send/DeviceLoginContextLength24.java | 61 ++ .../model/send/ModelVersionLength22.java | 60 ++ .../model/send/ModelVersionLength38.java | 111 +++ .../model/send/TemperatureAlarmLength22.java | 51 ++ .../model/send/TemperatureSwitchLength22.java | 51 ++ .../model/send/UpgradeVersionLength78.java | 41 ++ .../ifish/socket/webService/MyService.java | 54 ++ .../ifish/socket/webService/ServiceBean.java | 19 + src/main/java/com/ifish/util/ByteUtil.java | 196 ++++++ .../java/com/ifish/util/IfishFilePath.java | 113 ++++ src/main/java/com/ifish/util/IfishUtil.java | 454 +++++++++++++ .../java/com/ifish/util/JdbcProperties.java | 61 ++ src/main/java/com/ifish/util/RedisKey.java | 78 +++ src/main/resources/fdfs_client_ifish7.conf | 7 + .../resources/fdfs_client_zhangxinyanv5.conf | 8 + src/main/resources/jdbc.properties | 40 ++ src/main/resources/property.properties | 44 ++ src/main/webapp/META-INF/context.xml | 2 + .../WEB-INF/lib/fastdfs-client-java-1.25.jar | Bin 0 -> 91061 bytes src/main/webapp/index.jsp | 13 + 60 files changed, 7671 insertions(+) create mode 100644 .gitignore create mode 100644 nb-configuration.xml create mode 100644 pom.xml create mode 100644 src/main/java/com/ifish/bean/LoginRecord.java create mode 100644 src/main/java/com/ifish/bean/Tbl_Device.java create mode 100644 src/main/java/com/ifish/bean/Tbl_Device_Statistics.java create mode 100644 src/main/java/com/ifish/bean/Tbl_Device_User.java create mode 100644 src/main/java/com/ifish/bean/Tbl_Factory_List.java create mode 100644 src/main/java/com/ifish/bean/Tbl_HardWare_Type.java create mode 100644 src/main/java/com/ifish/bean/Tbl_Push_List.java create mode 100644 src/main/java/com/ifish/bean/Tbl_User.java create mode 100644 src/main/java/com/ifish/config/MinaConfig.java create mode 100644 src/main/java/com/ifish/config/MyWSServletContextListener.java create mode 100644 src/main/java/com/ifish/config/MybatisConfig.java create mode 100644 src/main/java/com/ifish/config/RedisConfig.java create mode 100644 src/main/java/com/ifish/config/RootConfig.java create mode 100644 src/main/java/com/ifish/config/WebAppInitializer.java create mode 100644 src/main/java/com/ifish/config/WebConfig.java create mode 100644 src/main/java/com/ifish/enums/PushTypeEnum.java create mode 100644 src/main/java/com/ifish/enums/SubDirectoryEnum.java create mode 100644 src/main/java/com/ifish/helper/DeviceHelperI.java create mode 100644 src/main/java/com/ifish/helper/UserHelperI.java create mode 100644 src/main/java/com/ifish/socket/codeFactory/Context.java create mode 100644 src/main/java/com/ifish/socket/codeFactory/ServerCodecFactory.java create mode 100644 src/main/java/com/ifish/socket/codeFactory/ServerDecode.java create mode 100644 src/main/java/com/ifish/socket/codeFactory/ServerEncode.java create mode 100644 src/main/java/com/ifish/socket/config/ExecuteJob.java create mode 100644 src/main/java/com/ifish/socket/config/JobFactory.java create mode 100644 src/main/java/com/ifish/socket/config/MinaServerHandler.java create mode 100644 src/main/java/com/ifish/socket/config/MyInetSocketAddress.java create mode 100644 src/main/java/com/ifish/socket/config/ScheduleJob.java create mode 100644 src/main/java/com/ifish/socket/config/SomeServer.java create mode 100644 src/main/java/com/ifish/socket/model/DeviceResponseLength99.java create mode 100644 src/main/java/com/ifish/socket/model/HeadModel.java create mode 100644 src/main/java/com/ifish/socket/model/JobGroup.java create mode 100644 src/main/java/com/ifish/socket/model/OrderModel.java create mode 100644 src/main/java/com/ifish/socket/model/receive/APPLoginResponseLength26.java create mode 100644 src/main/java/com/ifish/socket/model/receive/DeviceLoginResponseLength25.java create mode 100644 src/main/java/com/ifish/socket/model/receive/ServerHeartbeatLength17.java create mode 100644 src/main/java/com/ifish/socket/model/send/APPLoginContextLength25.java create mode 100644 src/main/java/com/ifish/socket/model/send/DeviceLoginContextLength24.java create mode 100644 src/main/java/com/ifish/socket/model/send/ModelVersionLength22.java create mode 100644 src/main/java/com/ifish/socket/model/send/ModelVersionLength38.java create mode 100644 src/main/java/com/ifish/socket/model/send/TemperatureAlarmLength22.java create mode 100644 src/main/java/com/ifish/socket/model/send/TemperatureSwitchLength22.java create mode 100644 src/main/java/com/ifish/socket/model/send/UpgradeVersionLength78.java create mode 100644 src/main/java/com/ifish/socket/webService/MyService.java create mode 100644 src/main/java/com/ifish/socket/webService/ServiceBean.java create mode 100644 src/main/java/com/ifish/util/ByteUtil.java create mode 100644 src/main/java/com/ifish/util/IfishFilePath.java create mode 100644 src/main/java/com/ifish/util/IfishUtil.java create mode 100644 src/main/java/com/ifish/util/JdbcProperties.java create mode 100644 src/main/java/com/ifish/util/RedisKey.java create mode 100644 src/main/resources/fdfs_client_ifish7.conf create mode 100644 src/main/resources/fdfs_client_zhangxinyanv5.conf create mode 100644 src/main/resources/jdbc.properties create mode 100644 src/main/resources/property.properties create mode 100644 src/main/webapp/META-INF/context.xml create mode 100644 src/main/webapp/WEB-INF/lib/fastdfs-client-java-1.25.jar create mode 100644 src/main/webapp/index.jsp diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9f97022 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +target/ \ No newline at end of file diff --git a/nb-configuration.xml b/nb-configuration.xml new file mode 100644 index 0000000..99174dc --- /dev/null +++ b/nb-configuration.xml @@ -0,0 +1,19 @@ + + + + + + 1.6-web + Tomcat + + diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..80dba0b --- /dev/null +++ b/pom.xml @@ -0,0 +1,227 @@ + + 4.0.0 + + com.ifish + IfishSocketEnglish + 1.0 + war + + IfishSocketEnglish + + + ${project.build.directory}/endorsed + UTF-8 + + + + + + com.sun.xml.ws + jaxws-tools + 2.2.10 + pom + + + + org.json + json + 20170516 + + + + org.apache.mina + mina-core + 2.0.16 + + + + org.quartz-scheduler + quartz + 2.3.0 + + + + org.csource + fastdfs-client-java + 1.25 + system + ${basedir}/src/main/webapp/WEB-INF/lib/fastdfs-client-java-1.25.jar + + + cn.jpush.api + jpush-client + 3.2.17 + + + org.apache.commons + commons-lang3 + 3.5 + jar + + + org.codehaus.jackson + jackson-mapper-asl + 1.9.13 + jar + + + com.alibaba + fastjson + 1.2.28 + jar + + + org.springframework.data + spring-data-redis + 1.8.4.RELEASE + jar + + + org.apache.commons + commons-pool2 + 2.4.2 + jar + + + redis.clients + jedis + 2.9.0 + jar + + + c3p0 + c3p0 + 0.9.1.2 + jar + + + javax + javaee-web-api + 6.0 + provided + + + + org.springframework + spring-context + 4.3.9.RELEASE + + + + org.springframework + spring-beans + 4.3.9.RELEASE + + + + org.springframework + spring-core + 4.3.9.RELEASE + + + + org.springframework + spring-web + 4.3.9.RELEASE + + + + org.springframework + spring-webmvc + 4.3.8.RELEASE + jar + + + + org.springframework + spring-jdbc + 4.3.9.RELEASE + + + + org.mybatis + mybatis + 3.4.4 + + + + org.mybatis + mybatis-spring + 1.3.1 + + + cn.jpush.api + jiguang-common + 1.0.3 + + + io.netty + netty-all + 4.1.6.Final + compile + + + com.google.code.gson + gson + 2.3 + + + org.slf4j + slf4j-api + 1.7.13 + jar + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 2.3.2 + + 1.6 + 1.6 + + ${endorsed.dir} + + + + + org.apache.maven.plugins + maven-war-plugin + 2.1.1 + + false + + + + org.apache.maven.plugins + maven-dependency-plugin + 2.1 + + + validate + + copy + + + ${endorsed.dir} + true + + + javax + javaee-endorsed-api + 6.0 + jar + + + + + + + + + + diff --git a/src/main/java/com/ifish/bean/LoginRecord.java b/src/main/java/com/ifish/bean/LoginRecord.java new file mode 100644 index 0000000..46f9eb2 --- /dev/null +++ b/src/main/java/com/ifish/bean/LoginRecord.java @@ -0,0 +1,49 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.ifish.bean; + +import java.io.Serializable; +import java.util.Date; + +/** + * + * @author Administrator + */ +public class LoginRecord implements Serializable { + + private static final long serialVersionUID = -5292867602998823697L; + //ID + private Integer recordId; + //mac地址 + private String macAddress; + //登录时间 + private Date loginTime; + + public Integer getRecordId() { + return recordId; + } + + public void setRecordId(Integer recordId) { + this.recordId = recordId; + } + + public String getMacAddress() { + return macAddress; + } + + public void setMacAddress(String macAddress) { + this.macAddress = macAddress; + } + + public Date getLoginTime() { + return loginTime; + } + + public void setLoginTime(Date loginTime) { + this.loginTime = loginTime; + } + +} diff --git a/src/main/java/com/ifish/bean/Tbl_Device.java b/src/main/java/com/ifish/bean/Tbl_Device.java new file mode 100644 index 0000000..ffcb840 --- /dev/null +++ b/src/main/java/com/ifish/bean/Tbl_Device.java @@ -0,0 +1,508 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.ifish.bean; + +import java.util.Date; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; + +/** + * 设备详情表(tbl_device) + * + * @author Administrator + */ +@Entity +@Table(name = "tbl_device") +public class Tbl_Device implements java.io.Serializable { + + /** + * 版本号 + */ + private static final long serialVersionUID = 3405700970095511149L; + + /** + * 设备Id + */ + @Id + @Column(name = "device_id", unique = true, nullable = false, length = 10) + private Integer deviceId; + + /** + * 服务器IP + */ + @Column(name = "server_ip", nullable = true, length = 50) + private String serverIp; + + /** + * 设备IP + */ + @Column(name = "device_ip", nullable = true, length = 50) + private String deviceIp; + + /** + * 是否为摄像头:1是,0否 + */ + @Column(name = "is_camera", nullable = true, length = 1) + private String isCamera; + + /** + * 设备Mac地址 + */ + @Column(name = "mac_address", unique = true, nullable = true, length = 50) + private String macAddress; + + /** + * 登陆时间 + */ + @Column(name = "login_time", nullable = true) + private Date loginTime; + + /** + * 是否在黑名单:1不在、0在 + */ + @Column(name = "is_blacklist", nullable = true, length = 1) + private String isBlacklist; + + /** + * 设备型号 + */ + @Column(name = "hardware_type", nullable = true, length = 10) + private String hardwareType; + + /** + * 是否推送 + */ + @Column(name = "on_off", nullable = true, length = 1) + private String onOff; + + /** + * 今天是否提醒:1提醒、0不提醒 + */ + @Column(name = "today_remind", nullable = true, length = 1) + private String todayRemind; + + /** + * 是否开启换水提醒:1开启、0不开启 + */ + @Column(name = "water_remind", nullable = true, length = 1) + private String waterRemind; + + /** + * 换水提醒日期 + */ + @Column(name = "remind_date", nullable = true) + private Date remindDate; + + /** + * 换水周期 + */ + @Column(name = "remind_cycle", nullable = true, length = 10) + private Integer remindCycle; + + /** + * 电子厂代码 + */ + @Column(name = "factory_code", nullable = true, length = 20) + private String factoryCode; + + /** + * 鱼缸场代码 + */ + @Column(name = "brand_code", nullable = true, length = 20) + private String brandCode; + + /** + * 摄像头ID + */ + @Column(name = "camera_id", nullable = true, length = 20) + private String cameraId; + + /** + * 创建时间 + */ + @Column(name = "create_time", nullable = true) + private Date createTime; + + /** + * 摄像头激活码 + */ + @Column(name = "active_code", nullable = true, length = 50) + private String activeCode; + + /** + * 摄像头激活时间 + */ + @Column(name = "active_time", nullable = true) + private Date activeTime; + + /** + * 修改时间 + */ + @Column(name = "update_time", nullable = true) + private Date updateTime; + + /** + * 获取设备Id + * + * @return 设备Id + */ + public Integer getDeviceId() { + return this.deviceId; + } + + /** + * 设置设备Id + * + * @param deviceId 设备Id + */ + public void setDeviceId(Integer deviceId) { + this.deviceId = deviceId; + } + + /** + * 获取服务器IP + * + * @return 服务器IP + */ + public String getServerIp() { + return this.serverIp; + } + + /** + * 设置服务器IP + * + * @param serverIp 服务器IP + */ + public void setServerIp(String serverIp) { + this.serverIp = serverIp; + } + + /** + * 获取设备IP + * + * @return 设备IP + */ + public String getDeviceIp() { + return this.deviceIp; + } + + /** + * 设置设备IP + * + * @param deviceIp 设备IP + */ + public void setDeviceIp(String deviceIp) { + this.deviceIp = deviceIp; + } + + /** + * 获取是否为摄像头:1是,0否 + * + * @return 是否为摄像头:1是 + */ + public String getIsCamera() { + return this.isCamera; + } + + /** + * 设置是否为摄像头:1是,0否 + * + * @param isCamera 是否为摄像头:1是,0否 + */ + public void setIsCamera(String isCamera) { + this.isCamera = isCamera; + } + + /** + * 获取设备Mac地址 + * + * @return 设备Mac地址 + */ + public String getMacAddress() { + return this.macAddress; + } + + /** + * 设置设备Mac地址 + * + * @param macAddress 设备Mac地址 + */ + public void setMacAddress(String macAddress) { + this.macAddress = macAddress; + } + + /** + * 获取登陆时间 + * + * @return 登陆时间 + */ + public Date getLoginTime() { + return this.loginTime; + } + + /** + * 设置登陆时间 + * + * @param loginTime 登陆时间 + */ + public void setLoginTime(Date loginTime) { + this.loginTime = loginTime; + } + + /** + * 获取是否在黑名单:1不在、0在 + * + * @return 是否在黑名单 + */ + public String getIsBlacklist() { + return this.isBlacklist; + } + + /** + * 设置是否在黑名单:1不在、0在 + * + * @param isBlacklist 是否在黑名单:1不在、0在 + */ + public void setIsBlacklist(String isBlacklist) { + this.isBlacklist = isBlacklist; + } + + /** + * 获取设备型号 + * + * @return 设备型号 + */ + public String getHardwareType() { + return this.hardwareType; + } + + /** + * 设置设备型号 + * + * @param hardwareType 设备型号 + */ + public void setHardwareType(String hardwareType) { + this.hardwareType = hardwareType; + } + + /** + * 获取是否推送 + * + * @return 是否推送 + */ + public String getOnOff() { + return this.onOff; + } + + /** + * 设置是否推送 + * + * @param onOff 是否推送 + */ + public void setOnOff(String onOff) { + this.onOff = onOff; + } + + /** + * 获取今天是否提醒:1提醒、0不提醒 + * + * @return 今天是否提醒 + */ + public String getTodayRemind() { + return this.todayRemind; + } + + /** + * 设置今天是否提醒:1提醒、0不提醒 + * + * @param todayRemind 今天是否提醒:1提醒、0不提醒 + */ + public void setTodayRemind(String todayRemind) { + this.todayRemind = todayRemind; + } + + /** + * 获取是否开启换水提醒:1开启、0不开启 + * + * @return 是否开启换水提醒 + */ + public String getWaterRemind() { + return this.waterRemind; + } + + /** + * 设置是否开启换水提醒:1开启、0不开启 + * + * @param waterRemind 是否开启换水提醒:1开启、0不开启 + */ + public void setWaterRemind(String waterRemind) { + this.waterRemind = waterRemind; + } + + /** + * 获取换水提醒日期 + * + * @return 换水提醒日期 + */ + public Date getRemindDate() { + return this.remindDate; + } + + /** + * 设置换水提醒日期 + * + * @param remindDate 换水提醒日期 + */ + public void setRemindDate(Date remindDate) { + this.remindDate = remindDate; + } + + /** + * 获取换水周期 + * + * @return 换水周期 + */ + public Integer getRemindCycle() { + return this.remindCycle; + } + + /** + * 设置换水周期 + * + * @param remindCycle 换水周期 + */ + public void setRemindCycle(Integer remindCycle) { + this.remindCycle = remindCycle; + } + + /** + * 获取电子厂代码 + * + * @return 电子厂代码 + */ + public String getFactoryCode() { + return this.factoryCode; + } + + /** + * 设置电子厂代码 + * + * @param factoryCode 电子厂代码 + */ + public void setFactoryCode(String factoryCode) { + this.factoryCode = factoryCode; + } + + /** + * 获取鱼缸场代码 + * + * @return 鱼缸场代码 + */ + public String getBrandCode() { + return this.brandCode; + } + + /** + * 设置鱼缸场代码 + * + * @param brandCode 鱼缸场代码 + */ + public void setBrandCode(String brandCode) { + this.brandCode = brandCode; + } + + /** + * 获取摄像头ID + * + * @return 摄像头ID + */ + public String getCameraId() { + return this.cameraId; + } + + /** + * 设置摄像头ID + * + * @param cameraId 摄像头ID + */ + public void setCameraId(String cameraId) { + this.cameraId = cameraId; + } + + /** + * 获取创建时间 + * + * @return 创建时间 + */ + public Date getCreateTime() { + return this.createTime; + } + + /** + * 设置创建时间 + * + * @param createTime 创建时间 + */ + public void setCreateTime(Date createTime) { + this.createTime = createTime; + } + + /** + * 获取摄像头激活码 + * + * @return 摄像头激活码 + */ + public String getActiveCode() { + return this.activeCode; + } + + /** + * 设置摄像头激活码 + * + * @param activeCode 摄像头激活码 + */ + public void setActiveCode(String activeCode) { + this.activeCode = activeCode; + } + + /** + * 获取摄像头激活时间 + * + * @return 摄像头激活时间 + */ + public Date getActiveTime() { + return this.activeTime; + } + + /** + * 设置摄像头激活时间 + * + * @param activeTime 摄像头激活时间 + */ + public void setActiveTime(Date activeTime) { + this.activeTime = activeTime; + } + + /** + * 获取修改时间 + * + * @return 修改时间 + */ + public Date getUpdateTime() { + return this.updateTime; + } + + /** + * 设置修改时间 + * + * @param updateTime 修改时间 + */ + public void setUpdateTime(Date updateTime) { + this.updateTime = updateTime; + } +} diff --git a/src/main/java/com/ifish/bean/Tbl_Device_Statistics.java b/src/main/java/com/ifish/bean/Tbl_Device_Statistics.java new file mode 100644 index 0000000..a5df6aa --- /dev/null +++ b/src/main/java/com/ifish/bean/Tbl_Device_Statistics.java @@ -0,0 +1,630 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.ifish.bean; + +import java.util.Date; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; + +/** + * 设备统计信息表(tbl_device_statistics) + * + * @author bianj + * @version 1.0.0 2017-06-29 + */ +@Entity +@Table(name = "tbl_device_statistics") +public class Tbl_Device_Statistics implements java.io.Serializable { + + /** + * 版本号 + */ + private static final long serialVersionUID = 1215383532220649875L; + + /** + * id + */ + @Id + @Column(name = "id", unique = true, nullable = false, length = 10) + private Integer id; + + /** + * 设备ID + */ + @Id + @Column(name = "device_id", unique = true, nullable = false, length = 10) + private Integer deviceId; + + /** + * 设备MAC地址 + */ + @Column(name = "mac_address", nullable = true, length = 50) + private String macAddress; + + /** + * 登陆次数 + */ + @Column(name = "login_count", nullable = true, length = 11) + private int loginCount; + + /** + * 最后登陆时间 + */ + @Column(name = "login_time", nullable = true) + private Date loginTime; + + /** + * 首次激活时间 + */ + @Column(name = "first_activate", nullable = true) + private Date firstActivate; + + /** + * 创建时间 + */ + @Column(name = "create_time", nullable = true) + private Date createTime; + + /** + * 是否收费 + */ + @Column(name = "is_charge", nullable = true, length = 1) + private String isCharge; + + /** + * sdk版本 + */ + @Column(name = "sdk_version", nullable = true, length = 10) + private String sdkVersion; + + /** + * sdk时间 + */ + @Column(name = "sdk_time", nullable = true) + private Date sdkTime; + + /** + * 分配日期 + */ + @Column(name = "create_date", nullable = true) + private Date createDate; + + /** + * 是否升级 + */ + @Column(name = "is_upgrade", nullable = true, length = 1) + private String isUpgrade; + + /** + * 更新版本 + */ + @Column(name = "upgrade_version", nullable = true, length = 10) + private String upgradeVersion; + + /** + * 更新时间 + */ + @Column(name = "upgrade_time", nullable = true) + private Date upgradeTime; + + /** + * 授权时间 + */ + @Column(name = "authorize_time", nullable = true) + private Date authorizeTime; + + /** + * 编号 + */ + @Column(name = "create_code", nullable = true, length = 50) + private String createCode; + + /** + * MCU启动次数 + */ + @Column(name = "mcu_count", nullable = true, length = 10) + private Integer mcuCount; + + /** + * 模块启动次数 + */ + @Column(name = "module_count", nullable = true, length = 10) + private Integer moduleCount; + + /** + * 连接路由器次数 + */ + @Column(name = "router_count", nullable = true, length = 10) + private Integer routerCount; + + /** + * 成功连接服务器次数 + */ + @Column(name = "server_count", nullable = true, length = 10) + private Integer serverCount; + + /** + * 尝试连接服务器次数 + */ + @Column(name = "server_try_count", nullable = true, length = 10) + private Integer serverTryCount; + + /** + * mcu版本 + */ + @Column(name = "software_version", nullable = true, length = 10) + private String softwareVersion; + + /** + * 硬件型号 + */ + @Column(name = "hardware_type", nullable = true, length = 10) + private String hardwareType; + + /** + * 电子厂代码 + */ + @Column(name = "factory_code", nullable = true, length = 20) + private String factoryCode; + + /** + * 鱼缸场代码 + */ + @Column(name = "brand_code", nullable = true, length = 20) + private String brandCode; + + /** + * 获取id + * + * @return id + */ + public Integer getId() { + return this.id; + } + + /** + * 设置id + * + * @param id id + */ + public void setId(Integer id) { + this.id = id; + } + + /** + * 获取设备ID + * + * @return 设备ID + */ + public Integer getDeviceId() { + return this.deviceId; + } + + /** + * 设置设备ID + * + * @param deviceId 设备ID + */ + public void setDeviceId(Integer deviceId) { + this.deviceId = deviceId; + } + + /** + * 获取设备MAC地址 + * + * @return 设备MAC地址 + */ + public String getMacAddress() { + return this.macAddress; + } + + /** + * 设置设备MAC地址 + * + * @param macAddress 设备MAC地址 + */ + public void setMacAddress(String macAddress) { + this.macAddress = macAddress; + } + + /** + * 获取登陆次数 + * + * @return 登陆次数 + */ + public Integer getLoginCount() { + return this.loginCount; + } + + /** + * 设置登陆次数 + * + * @param loginCount 登陆次数 + */ + public void setLoginCount(Integer loginCount) { + this.loginCount = loginCount; + } + + /** + * 获取最后登陆时间 + * + * @return 最后登陆时间 + */ + public Date getLoginTime() { + return this.loginTime; + } + + /** + * 设置最后登陆时间 + * + * @param loginTime 最后登陆时间 + */ + public void setLoginTime(Date loginTime) { + this.loginTime = loginTime; + } + + /** + * 获取首次激活时间 + * + * @return 首次激活时间 + */ + public Date getFirstActivate() { + return this.firstActivate; + } + + /** + * 设置首次激活时间 + * + * @param firstActivate 首次激活时间 + */ + public void setFirstActivate(Date firstActivate) { + this.firstActivate = firstActivate; + } + + /** + * 获取创建时间 + * + * @return 创建时间 + */ + public Date getCreateTime() { + return this.createTime; + } + + /** + * 设置创建时间 + * + * @param createTime 创建时间 + */ + public void setCreateTime(Date createTime) { + this.createTime = createTime; + } + + /** + * 获取是否收费 + * + * @return 是否收费 + */ + public String getIsCharge() { + return this.isCharge; + } + + /** + * 设置是否收费 + * + * @param isCharge 是否收费 + */ + public void setIsCharge(String isCharge) { + this.isCharge = isCharge; + } + + /** + * 获取sdk版本 + * + * @return sdk版本 + */ + public String getSdkVersion() { + return this.sdkVersion; + } + + /** + * 设置sdk版本 + * + * @param sdkVersion sdk版本 + */ + public void setSdkVersion(String sdkVersion) { + this.sdkVersion = sdkVersion; + } + + /** + * 获取sdk时间 + * + * @return sdk时间 + */ + public Date getSdkTime() { + return this.sdkTime; + } + + /** + * 设置sdk时间 + * + * @param sdkTime sdk时间 + */ + public void setSdkTime(Date sdkTime) { + this.sdkTime = sdkTime; + } + + /** + * 获取分配日期 + * + * @return 分配日期 + */ + public Date getCreateDate() { + return this.createDate; + } + + /** + * 设置分配日期 + * + * @param createDate 分配日期 + */ + public void setCreateDate(Date createDate) { + this.createDate = createDate; + } + + /** + * 获取是否升级 + * + * @return 是否升级 + */ + public String getIsUpgrade() { + return this.isUpgrade; + } + + /** + * 设置是否升级 + * + * @param isUpgrade 是否升级 + */ + public void setIsUpgrade(String isUpgrade) { + this.isUpgrade = isUpgrade; + } + + /** + * 获取更新版本 + * + * @return 更新版本 + */ + public String getUpgradeVersion() { + return this.upgradeVersion; + } + + /** + * 设置更新版本 + * + * @param upgradeVersion 更新版本 + */ + public void setUpgradeVersion(String upgradeVersion) { + this.upgradeVersion = upgradeVersion; + } + + /** + * 获取更新时间 + * + * @return 更新时间 + */ + public Date getUpgradeTime() { + return this.upgradeTime; + } + + /** + * 设置更新时间 + * + * @param upgradeTime 更新时间 + */ + public void setUpgradeTime(Date upgradeTime) { + this.upgradeTime = upgradeTime; + } + + /** + * 获取授权时间 + * + * @return 授权时间 + */ + public Date getAuthorizeTime() { + return this.authorizeTime; + } + + /** + * 设置授权时间 + * + * @param authorizeTime 授权时间 + */ + public void setAuthorizeTime(Date authorizeTime) { + this.authorizeTime = authorizeTime; + } + + /** + * 获取编号 + * + * @return 编号 + */ + public String getCreateCode() { + return this.createCode; + } + + /** + * 设置编号 + * + * @param createCode 编号 + */ + public void setCreateCode(String createCode) { + this.createCode = createCode; + } + + /** + * 获取MCU启动次数 + * + * @return MCU启动次数 + */ + public Integer getMcuCount() { + return this.mcuCount; + } + + /** + * 设置MCU启动次数 + * + * @param mcuCount MCU启动次数 + */ + public void setMcuCount(Integer mcuCount) { + this.mcuCount = mcuCount; + } + + /** + * 获取模块启动次数 + * + * @return 模块启动次数 + */ + public Integer getModuleCount() { + return this.moduleCount; + } + + /** + * 设置模块启动次数 + * + * @param moduleCount 模块启动次数 + */ + public void setModuleCount(Integer moduleCount) { + this.moduleCount = moduleCount; + } + + /** + * 获取连接路由器次数 + * + * @return 连接路由器次数 + */ + public Integer getRouterCount() { + return this.routerCount; + } + + /** + * 设置连接路由器次数 + * + * @param routerCount 连接路由器次数 + */ + public void setRouterCount(Integer routerCount) { + this.routerCount = routerCount; + } + + /** + * 获取成功连接服务器次数 + * + * @return 成功连接服务器次数 + */ + public Integer getServerCount() { + return this.serverCount; + } + + /** + * 设置成功连接服务器次数 + * + * @param serverCount 成功连接服务器次数 + */ + public void setServerCount(Integer serverCount) { + this.serverCount = serverCount; + } + + /** + * 获取尝试连接服务器次数 + * + * @return 尝试连接服务器次数 + */ + public Integer getServerTryCount() { + return this.serverTryCount; + } + + /** + * 设置尝试连接服务器次数 + * + * @param serverTryCount 尝试连接服务器次数 + */ + public void setServerTryCount(Integer serverTryCount) { + this.serverTryCount = serverTryCount; + } + + /** + * 获取mcu版本 + * + * @return mcu版本 + */ + public String getSoftwareVersion() { + return this.softwareVersion; + } + + /** + * 设置mcu版本 + * + * @param softwareVersion mcu版本 + */ + public void setSoftwareVersion(String softwareVersion) { + this.softwareVersion = softwareVersion; + } + + /** + * 获取硬件型号 + * + * @return 硬件型号 + */ + public String getHardwareType() { + return this.hardwareType; + } + + /** + * 设置硬件型号 + * + * @param hardwareType 硬件型号 + */ + public void setHardwareType(String hardwareType) { + this.hardwareType = hardwareType; + } + + /** + * 获取电子厂代码 + * + * @return 电子厂代码 + */ + public String getFactoryCode() { + return this.factoryCode; + } + + /** + * 设置电子厂代码 + * + * @param factoryCode 电子厂代码 + */ + public void setFactoryCode(String factoryCode) { + this.factoryCode = factoryCode; + } + + /** + * 获取鱼缸场代码 + * + * @return 鱼缸场代码 + */ + public String getBrandCode() { + return this.brandCode; + } + + /** + * 设置鱼缸场代码 + * + * @param brandCode 鱼缸场代码 + */ + public void setBrandCode(String brandCode) { + this.brandCode = brandCode; + } +} diff --git a/src/main/java/com/ifish/bean/Tbl_Device_User.java b/src/main/java/com/ifish/bean/Tbl_Device_User.java new file mode 100644 index 0000000..08184a0 --- /dev/null +++ b/src/main/java/com/ifish/bean/Tbl_Device_User.java @@ -0,0 +1,293 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.ifish.bean; + +import java.util.Date; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; + +/** + * 设备与用户关系表(tbl_device_user) + * + * @author bianj + * @version 1.0.0 2017-06-27 + */ +@Entity +@Table(name = "tbl_device_user") +public class Tbl_Device_User implements java.io.Serializable { + + /** + * 版本号 + */ + private static final long serialVersionUID = -4018159739383033383L; + + /** + * id + */ + @Id + @Column(name = "id", unique = true, nullable = false, length = 10) + private Integer id; + + /** + * 用户Id + */ + @Column(name = "user_id", unique = true, nullable = false, length = 10) + private Integer userId; + + /** + * 设备Id + */ + @Column(name = "device_id", unique = true, nullable = false, length = 10) + private Integer deviceId; + + /** + * 是否主控:1、主控制权,0、被分享者 + */ + @Column(name = "is_master", nullable = true, length = 1) + private String isMaster; + + /** + * 显示名称 + */ + @Column(name = "show_name", nullable = true, length = 50) + private String showName; + + /** + * 创建时间 + */ + @Column(name = "create_time", nullable = true) + private Date createTime; + + /** + * 修改时间 + */ + @Column(name = "update_time", nullable = true) + private Date updateTime; + + /** + * 保存的显示图标 + */ + @Column(name = "custom_icon_name", nullable = true, length = 100) + private String customIconName; + + /** + * 保存的显示名称 + */ + @Column(name = "custom_show_name", nullable = true, length = 100) + private String customShowName; + + /** + * 是否看护 + */ + @Column(name = "is_look", nullable = true, length = 1) + private String isLook; + + /** + * 是否直播 + */ + @Column(name = "is_live", nullable = true, length = 1) + private String isLive; + + /** + * 获取id + * + * @return id + */ + public Integer getId() { + return this.id; + } + + /** + * 设置id + * + * @param id id + */ + public void setId(Integer id) { + this.id = id; + } + + /** + * 获取用户Id + * + * @return 用户Id + */ + public Integer getUserId() { + return this.userId; + } + + /** + * 设置用户Id + * + * @param userId 用户Id + */ + public void setUserId(Integer userId) { + this.userId = userId; + } + + /** + * 获取设备Id + * + * @return 设备Id + */ + public Integer getDeviceId() { + return this.deviceId; + } + + /** + * 设置设备Id + * + * @param deviceId 设备Id + */ + public void setDeviceId(Integer deviceId) { + this.deviceId = deviceId; + } + + /** + * 获取是否主控:1、主控制权,0、被分享者 + * + * @return 是否主控:1、主控制权 + */ + public String getIsMaster() { + return this.isMaster; + } + + /** + * 设置是否主控:1、主控制权,0、被分享者 + * + * @param isMaster 是否主控:1、主控制权,0、被分享者 + */ + public void setIsMaster(String isMaster) { + this.isMaster = isMaster; + } + + /** + * 获取显示名称 + * + * @return 显示名称 + */ + public String getShowName() { + return this.showName; + } + + /** + * 设置显示名称 + * + * @param showName 显示名称 + */ + public void setShowName(String showName) { + this.showName = showName; + } + + /** + * 获取创建时间 + * + * @return 创建时间 + */ + public Date getCreateTime() { + return this.createTime; + } + + /** + * 设置创建时间 + * + * @param createTime 创建时间 + */ + public void setCreateTime(Date createTime) { + this.createTime = createTime; + } + + /** + * 获取修改时间 + * + * @return 修改时间 + */ + public Date getUpdateTime() { + return this.updateTime; + } + + /** + * 设置修改时间 + * + * @param updateTime 修改时间 + */ + public void setUpdateTime(Date updateTime) { + this.updateTime = updateTime; + } + + /** + * 获取保存的显示图标 + * + * @return 保存的显示图标 + */ + public String getCustomIconName() { + return this.customIconName; + } + + /** + * 设置保存的显示图标 + * + * @param customIconName 保存的显示图标 + */ + public void setCustomIconName(String customIconName) { + this.customIconName = customIconName; + } + + /** + * 获取保存的显示名称 + * + * @return 保存的显示名称 + */ + public String getCustomShowName() { + return this.customShowName; + } + + /** + * 设置保存的显示名称 + * + * @param customShowName 保存的显示名称 + */ + public void setCustomShowName(String customShowName) { + this.customShowName = customShowName; + } + + /** + * 获取是否看护 + * + * @return 是否看护 + */ + public String getIsLook() { + return this.isLook; + } + + /** + * 设置是否看护 + * + * @param isLook 是否看护 + */ + public void setIsLook(String isLook) { + this.isLook = isLook; + } + + /** + * 获取是否直播 + * + * @return 是否直播 + */ + public String getIsLive() { + return this.isLive; + } + + /** + * 设置是否直播 + * + * @param isLive 是否直播 + */ + public void setIsLive(String isLive) { + this.isLive = isLive; + } +} diff --git a/src/main/java/com/ifish/bean/Tbl_Factory_List.java b/src/main/java/com/ifish/bean/Tbl_Factory_List.java new file mode 100644 index 0000000..b03bf10 --- /dev/null +++ b/src/main/java/com/ifish/bean/Tbl_Factory_List.java @@ -0,0 +1,125 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.ifish.bean; + +import java.util.Date; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; + +/** + * 电子厂详情表(tbl_factory_list) + * + * @author bianj + * @version 1.0.0 2017-07-03 + */ +@Entity +@Table(name = "tbl_factory_list") +public class Tbl_Factory_List implements java.io.Serializable { + + /** + * 版本号 + */ + private static final long serialVersionUID = -8290320819344778436L; + + /** + * 电子厂代码 + */ + @Id + @Column(name = "factory_code", unique = true, nullable = false, length = 20) + private String factoryCode; + + /** + * 电子厂名称 + */ + @Column(name = "factory_name", nullable = false, length = 20) + private String factoryName; + + /** + * 创建时间 + */ + @Column(name = "create_time", nullable = true) + private Date createTime; + + /** + * 是否收税:1收、0不收 + */ + @Column(name = "is_tax", nullable = true, length = 1) + private String isTax; + + /** + * 获取电子厂代码 + * + * @return 电子厂代码 + */ + public String getFactoryCode() { + return this.factoryCode; + } + + /** + * 设置电子厂代码 + * + * @param factoryCode 电子厂代码 + */ + public void setFactoryCode(String factoryCode) { + this.factoryCode = factoryCode; + } + + /** + * 获取电子厂名称 + * + * @return 电子厂名称 + */ + public String getFactoryName() { + return this.factoryName; + } + + /** + * 设置电子厂名称 + * + * @param factoryName 电子厂名称 + */ + public void setFactoryName(String factoryName) { + this.factoryName = factoryName; + } + + /** + * 获取创建时间 + * + * @return 创建时间 + */ + public Date getCreateTime() { + return this.createTime; + } + + /** + * 设置创建时间 + * + * @param createTime 创建时间 + */ + public void setCreateTime(Date createTime) { + this.createTime = createTime; + } + + /** + * 获取是否收税:1收、0不收 + * + * @return 是否收税 + */ + public String getIsTax() { + return this.isTax; + } + + /** + * 设置是否收税:1收、0不收 + * + * @param isTax 是否收税:1收、0不收 + */ + public void setIsTax(String isTax) { + this.isTax = isTax; + } +} diff --git a/src/main/java/com/ifish/bean/Tbl_HardWare_Type.java b/src/main/java/com/ifish/bean/Tbl_HardWare_Type.java new file mode 100644 index 0000000..7760168 --- /dev/null +++ b/src/main/java/com/ifish/bean/Tbl_HardWare_Type.java @@ -0,0 +1,437 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.ifish.bean; + +import java.util.Date; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; + +/** + * 设备类型详情表(tbl_hardware_type) + * + * @author bianj + * @version 1.0.0 2017-06-27 + */ +@Entity +@Table(name = "tbl_hardware_type") +public class Tbl_HardWare_Type implements java.io.Serializable { + + /** + * 版本号 + */ + private static final long serialVersionUID = 7870932947017585134L; + + /** + * 设备型号代码 + */ + @Id + @Column(name = "hardware_type", unique = true, nullable = false, length = 10) + private String hardwareType; + + /** + * 电子厂代码 + */ + @Column(name = "factory_code", nullable = true, length = 20) + private String factoryCode; + + /** + * 设备名称 + */ + @Column(name = "hardware_name", nullable = false, length = 20) + private String hardwareName; + + /** + * 描述 + */ + @Column(name = "hardware_desc", nullable = true, length = 100) + private String hardwareDesc; + + /** + * 创建时间 + */ + @Column(name = "create_time", nullable = true) + private Date createTime; + + /** + * 控制个数 + */ + @Column(name = "control_amount", nullable = true, length = 10) + private Integer controlAmount; + + /** + * 定时器个数 + */ + @Column(name = "timer_amount", nullable = true, length = 10) + private Integer timerAmount; + + /** + * 是否自定义图标:1是、0否 + */ + @Column(name = "is_custom_icon", nullable = true, length = 1) + private String isCustomIcon; + + /** + * 自定义图标根地址 + */ + @Column(name = "icon_link", nullable = true, length = 200) + private String iconLink; + + /** + * 根地址下图标名称 + */ + @Column(name = "all_icon_name", nullable = true, length = 200) + private String allIconName; + + /** + * 修改时间 + */ + @Column(name = "update_time", nullable = true) + private Date updateTime; + + /** + * 是否有背光:1是、0否 + */ + @Column(name = "is_lightness", nullable = true, length = 1) + private String isLightness; + + /** + * 是否有柜灯:1是、0否 + */ + @Column(name = "is_sark_lamp", nullable = true, length = 1) + private String isSarkLamp; + + /** + * 是否有工作模式:1是、0否 + */ + @Column(name = "is_work_model", nullable = true, length = 1) + private String isWorkModel; + + /** + * 显示名称 + */ + @Column(name = "all_show_name", nullable = true, length = 200) + private String allShowName; + + /** + * 默认名称 + */ + @Column(name = "default_show_name", nullable = true, length = 200) + private String defaultShowName; + + /** + * 默认图标 + */ + @Column(name = "default_icon_name", nullable = true, length = 200) + private String defaultIconName; + + /** + * 获取设备型号代码 + * + * @return 设备型号代码 + */ + public String getHardwareType() { + return this.hardwareType; + } + + /** + * 设置设备型号代码 + * + * @param hardwareType 设备型号代码 + */ + public void setHardwareType(String hardwareType) { + this.hardwareType = hardwareType; + } + + /** + * 获取电子厂代码 + * + * @return 电子厂代码 + */ + public String getFactoryCode() { + return this.factoryCode; + } + + /** + * 设置电子厂代码 + * + * @param factoryCode 电子厂代码 + */ + public void setFactoryCode(String factoryCode) { + this.factoryCode = factoryCode; + } + + /** + * 获取设备名称 + * + * @return 设备名称 + */ + public String getHardwareName() { + return this.hardwareName; + } + + /** + * 设置设备名称 + * + * @param hardwareName 设备名称 + */ + public void setHardwareName(String hardwareName) { + this.hardwareName = hardwareName; + } + + /** + * 获取描述 + * + * @return 描述 + */ + public String getHardwareDesc() { + return this.hardwareDesc; + } + + /** + * 设置描述 + * + * @param hardwareDesc 描述 + */ + public void setHardwareDesc(String hardwareDesc) { + this.hardwareDesc = hardwareDesc; + } + + /** + * 获取创建时间 + * + * @return 创建时间 + */ + public Date getCreateTime() { + return this.createTime; + } + + /** + * 设置创建时间 + * + * @param createTime 创建时间 + */ + public void setCreateTime(Date createTime) { + this.createTime = createTime; + } + + /** + * 获取控制个数 + * + * @return 控制个数 + */ + public Integer getControlAmount() { + return this.controlAmount; + } + + /** + * 设置控制个数 + * + * @param controlAmount 控制个数 + */ + public void setControlAmount(Integer controlAmount) { + this.controlAmount = controlAmount; + } + + /** + * 获取定时器个数 + * + * @return 定时器个数 + */ + public Integer getTimerAmount() { + return this.timerAmount; + } + + /** + * 设置定时器个数 + * + * @param timerAmount 定时器个数 + */ + public void setTimerAmount(Integer timerAmount) { + this.timerAmount = timerAmount; + } + + /** + * 获取是否自定义图标:1是、0否 + * + * @return 是否自定义图标 + */ + public String getIsCustomIcon() { + return this.isCustomIcon; + } + + /** + * 设置是否自定义图标:1是、0否 + * + * @param isCustomIcon 是否自定义图标:1是、0否 + */ + public void setIsCustomIcon(String isCustomIcon) { + this.isCustomIcon = isCustomIcon; + } + + /** + * 获取自定义图标根地址 + * + * @return 自定义图标根地址 + */ + public String getIconLink() { + return this.iconLink; + } + + /** + * 设置自定义图标根地址 + * + * @param iconLink 自定义图标根地址 + */ + public void setIconLink(String iconLink) { + this.iconLink = iconLink; + } + + /** + * 获取根地址下图标名称 + * + * @return 根地址下图标名称 + */ + public String getAllIconName() { + return this.allIconName; + } + + /** + * 设置根地址下图标名称 + * + * @param allIconName 根地址下图标名称 + */ + public void setAllIconName(String allIconName) { + this.allIconName = allIconName; + } + + /** + * 获取修改时间 + * + * @return 修改时间 + */ + public Date getUpdateTime() { + return this.updateTime; + } + + /** + * 设置修改时间 + * + * @param updateTime 修改时间 + */ + public void setUpdateTime(Date updateTime) { + this.updateTime = updateTime; + } + + /** + * 获取是否有背光:1是、0否 + * + * @return 是否有背光 + */ + public String getIsLightness() { + return this.isLightness; + } + + /** + * 设置是否有背光:1是、0否 + * + * @param isLightness 是否有背光:1是、0否 + */ + public void setIsLightness(String isLightness) { + this.isLightness = isLightness; + } + + /** + * 获取是否有柜灯:1是、0否 + * + * @return 是否有柜灯 + */ + public String getIsSarkLamp() { + return this.isSarkLamp; + } + + /** + * 设置是否有柜灯:1是、0否 + * + * @param isSarkLamp 是否有柜灯:1是、0否 + */ + public void setIsSarkLamp(String isSarkLamp) { + this.isSarkLamp = isSarkLamp; + } + + /** + * 获取是否有工作模式:1是、0否 + * + * @return 是否有工作模式 + */ + public String getIsWorkModel() { + return this.isWorkModel; + } + + /** + * 设置是否有工作模式:1是、0否 + * + * @param isWorkModel 是否有工作模式:1是、0否 + */ + public void setIsWorkModel(String isWorkModel) { + this.isWorkModel = isWorkModel; + } + + /** + * 获取显示名称 + * + * @return 显示名称 + */ + public String getAllShowName() { + return this.allShowName; + } + + /** + * 设置显示名称 + * + * @param allShowName 显示名称 + */ + public void setAllShowName(String allShowName) { + this.allShowName = allShowName; + } + + /** + * 获取默认名称 + * + * @return 默认名称 + */ + public String getDefaultShowName() { + return this.defaultShowName; + } + + /** + * 设置默认名称 + * + * @param defaultShowName 默认名称 + */ + public void setDefaultShowName(String defaultShowName) { + this.defaultShowName = defaultShowName; + } + + /** + * 获取默认图标 + * + * @return 默认图标 + */ + public String getDefaultIconName() { + return this.defaultIconName; + } + + /** + * 设置默认图标 + * + * @param defaultIconName 默认图标 + */ + public void setDefaultIconName(String defaultIconName) { + this.defaultIconName = defaultIconName; + } +} diff --git a/src/main/java/com/ifish/bean/Tbl_Push_List.java b/src/main/java/com/ifish/bean/Tbl_Push_List.java new file mode 100644 index 0000000..8bfa849 --- /dev/null +++ b/src/main/java/com/ifish/bean/Tbl_Push_List.java @@ -0,0 +1,465 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.ifish.bean; + +import java.util.Date; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; + +/** + * 推送详情表(tbl_push_list) + * + * @author bianj + * @version 1.0.0 2017-07-03 + */ +@Entity +@Table(name = "tbl_push_list") +public class Tbl_Push_List implements java.io.Serializable { + + /** + * 版本号 + */ + private static final long serialVersionUID = -7760764975977010045L; + + /** + * ID + */ + @Id + @Column(name = "push_id", unique = true, nullable = false, length = 10) + private Integer pushId; + + /** + * 用户Id + */ + @Column(name = "user_id", nullable = true, length = 10) + private Integer userId; + + /** + * 设备Id + */ + @Column(name = "device_id", nullable = true, length = 10) + private Integer deviceId; + + /** + * 显示名称 + */ + @Column(name = "show_name", nullable = true, length = 20) + private String showName; + + /** + * 推送手机类型 + */ + @Column(name = "phone_type", nullable = true, length = 20) + private String phoneType; + + /** + * 推送消息类型 + * 1."remove_device","解绑通知";2."wendu_warn","温度预警;3."qu_reply","问题反馈";4."app_update","IOS更新推送";5."remind_water","换水提醒";6."offline_push","离线通知";7."send_report","看护报告";8."shops_push","看护通知";9."all_push","所有用户推送" + */ + @Column(name = "push_type", nullable = true, length = 20) + private String pushType; + + /** + * 推送标题 + */ + @Column(name = "push_title", nullable = true, length = 100) + private String pushTitle; + + /** + * 推送内容 + */ + @Column(name = "push_context", nullable = true, length = 200) + private String pushContext; + + /** + * 创建时间 + */ + @Column(name = "create_time", nullable = true) + private Date createTime; + + /** + * 链接 + */ + @Column(name = "push_link", nullable = false, length = 100) + private String pushLink; + + /** + * 极光推送状态 + */ + @Column(name = "jpush_status", nullable = true, length = 1) + private String jpushStatus; + + /** + * 谷歌推送状态 + */ + @Column(name = "google_status", nullable = true, length = 1) + private String googleStatus; + + /** + * 看护报告Id + */ + @Column(name = "report_id", nullable = true, length = 10) + private Integer reportId; + + /** + * MCU启动次数 + */ + @Column(name = "mcu_count", nullable = true, length = 10) + private Integer mcuCount; + + /** + * 模块启动次数 + */ + @Column(name = "modular_count", nullable = true, length = 10) + private Integer modularCount; + + /** + * 连接路由器次数 + */ + @Column(name = "router_count", nullable = true, length = 10) + private Integer routerCount; + + /** + * 成功连接服务器次数 + */ + @Column(name = "server_count", nullable = true, length = 10) + private Integer serverCount; + + /** + * 尝试连接服务器次数 + */ + @Column(name = "server_try_count", nullable = true, length = 10) + private Integer serverTryCount; + + /** + * 获取ID + * + * @return ID + */ + public Integer getPushId() { + return this.pushId; + } + + /** + * 设置ID + * + * @param pushId ID + */ + public void setPushId(Integer pushId) { + this.pushId = pushId; + } + + /** + * 获取用户Id + * + * @return 用户Id + */ + public Integer getUserId() { + return this.userId; + } + + /** + * 设置用户Id + * + * @param userId 用户Id + */ + public void setUserId(Integer userId) { + this.userId = userId; + } + + /** + * 获取设备Id + * + * @return 设备Id + */ + public Integer getDeviceId() { + return this.deviceId; + } + + /** + * 设置设备Id + * + * @param deviceId 设备Id + */ + public void setDeviceId(Integer deviceId) { + this.deviceId = deviceId; + } + + /** + * 获取显示名称 + * + * @return 显示名称 + */ + public String getShowName() { + return this.showName; + } + + /** + * 设置显示名称 + * + * @param showName 显示名称 + */ + public void setShowName(String showName) { + this.showName = showName; + } + + /** + * 获取推送手机类型 + * + * @return 推送手机类型 + */ + public String getPhoneType() { + return this.phoneType; + } + + /** + * 设置推送手机类型 + * + * @param phoneType 推送手机类型 + */ + public void setPhoneType(String phoneType) { + this.phoneType = phoneType; + } + + /** + * 获取推送消息类型 + * 1."remove_device","解绑通知";2."wendu_warn","温度预警;3."qu_reply","问题反馈";4."app_update","IOS更新推送";5."remind_water","换水提醒";6."offline_push","离线通知";7."send_report","看护报告";8."shops_push","看护通知";9."all_push","所有用户推送" + * + * @return 推送消息类型 1."remove_device" + */ + public String getPushType() { + return this.pushType; + } + + /** + * 设置推送消息类型 + * 1."remove_device","解绑通知";2."wendu_warn","温度预警;3."qu_reply","问题反馈";4."app_update","IOS更新推送";5."remind_water","换水提醒";6."offline_push","离线通知";7."send_report","看护报告";8."shops_push","看护通知";9."all_push","所有用户推送" + * + * @param pushType 推送消息类型 + * 1."remove_device","解绑通知";2."wendu_warn","温度预警;3."qu_reply","问题反馈";4."app_update","IOS更新推送";5."remind_water","换水提醒";6."offline_push","离线通知";7."send_report","看护报告";8."shops_push","看护通知";9."all_push","所有用户推送" + */ + public void setPushType(String pushType) { + this.pushType = pushType; + } + + /** + * 获取推送标题 + * + * @return 推送标题 + */ + public String getPushTitle() { + return this.pushTitle; + } + + /** + * 设置推送标题 + * + * @param pushTitle 推送标题 + */ + public void setPushTitle(String pushTitle) { + this.pushTitle = pushTitle; + } + + /** + * 获取推送内容 + * + * @return 推送内容 + */ + public String getPushContext() { + return this.pushContext; + } + + /** + * 设置推送内容 + * + * @param pushContext 推送内容 + */ + public void setPushContext(String pushContext) { + this.pushContext = pushContext; + } + + /** + * 获取创建时间 + * + * @return 创建时间 + */ + public Date getCreateTime() { + return this.createTime; + } + + /** + * 设置创建时间 + * + * @param createTime 创建时间 + */ + public void setCreateTime(Date createTime) { + this.createTime = createTime; + } + + /** + * 获取链接 + * + * @return 链接 + */ + public String getPushLink() { + return this.pushLink; + } + + /** + * 设置链接 + * + * @param pushLink 链接 + */ + public void setPushLink(String pushLink) { + this.pushLink = pushLink; + } + + /** + * 获取极光推送状态 + * + * @return 极光推送状态 + */ + public String getJpushStatus() { + return this.jpushStatus; + } + + /** + * 设置极光推送状态 + * + * @param jpushStatus 极光推送状态 + */ + public void setJpushStatus(String jpushStatus) { + this.jpushStatus = jpushStatus; + } + + /** + * 获取谷歌推送状态 + * + * @return 谷歌推送状态 + */ + public String getGoogleStatus() { + return this.googleStatus; + } + + /** + * 设置谷歌推送状态 + * + * @param googleStatus 谷歌推送状态 + */ + public void setGoogleStatus(String googleStatus) { + this.googleStatus = googleStatus; + } + + /** + * 获取看护报告Id + * + * @return 看护报告Id + */ + public Integer getReportId() { + return this.reportId; + } + + /** + * 设置看护报告Id + * + * @param reportId 看护报告Id + */ + public void setReportId(Integer reportId) { + this.reportId = reportId; + } + + /** + * 获取MCU启动次数 + * + * @return MCU启动次数 + */ + public Integer getMcuCount() { + return this.mcuCount; + } + + /** + * 设置MCU启动次数 + * + * @param mcuCount MCU启动次数 + */ + public void setMcuCount(Integer mcuCount) { + this.mcuCount = mcuCount; + } + + /** + * 获取模块启动次数 + * + * @return 模块启动次数 + */ + public Integer getModularCount() { + return this.modularCount; + } + + /** + * 设置模块启动次数 + * + * @param modularCount 模块启动次数 + */ + public void setModularCount(Integer modularCount) { + this.modularCount = modularCount; + } + + /** + * 获取连接路由器次数 + * + * @return 连接路由器次数 + */ + public Integer getRouterCount() { + return this.routerCount; + } + + /** + * 设置连接路由器次数 + * + * @param routerCount 连接路由器次数 + */ + public void setRouterCount(Integer routerCount) { + this.routerCount = routerCount; + } + + /** + * 获取成功连接服务器次数 + * + * @return 成功连接服务器次数 + */ + public Integer getServerCount() { + return this.serverCount; + } + + /** + * 设置成功连接服务器次数 + * + * @param serverCount 成功连接服务器次数 + */ + public void setServerCount(Integer serverCount) { + this.serverCount = serverCount; + } + + /** + * 获取尝试连接服务器次数 + * + * @return 尝试连接服务器次数 + */ + public Integer getServerTryCount() { + return this.serverTryCount; + } + + /** + * 设置尝试连接服务器次数 + * + * @param serverTryCount 尝试连接服务器次数 + */ + public void setServerTryCount(Integer serverTryCount) { + this.serverTryCount = serverTryCount; + } +} diff --git a/src/main/java/com/ifish/bean/Tbl_User.java b/src/main/java/com/ifish/bean/Tbl_User.java new file mode 100644 index 0000000..57cb9af --- /dev/null +++ b/src/main/java/com/ifish/bean/Tbl_User.java @@ -0,0 +1,557 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.ifish.bean; + +import java.util.Date; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; + +/** + * 用户详情表(tbl_user) + * + * @author bianj + * @version 1.0.0 2017-06-28 + */ +@Entity +@Table(name = "tbl_user") +public class Tbl_User implements java.io.Serializable { + + /** + * 版本号 + */ + private static final long serialVersionUID = -3293426131332327827L; + + /** + * 用户Id + */ + @Id + @Column(name = "user_id", unique = true, nullable = false, length = 10) + private Integer userId; + + /** + * 昵称 + */ + @Column(name = "nick_name", nullable = true, length = 20) + private String nickName; + + /** + * 密码 + */ + @Column(name = "user_password", nullable = true, length = 32) + private String userPassword; + + /** + * 手机 + */ + @Column(name = "phone_number", unique = true, nullable = true, length = 11) + private String phoneNumber; + + /** + * 邮箱 + */ + @Column(name = "user_email", unique = true, nullable = true, length = 50) + private String userEmail; + + /** + * 用户类型 + */ + @Column(name = "user_type", nullable = false, length = 1) + private String userType; + + /** + * 头像 + */ + @Column(name = "user_img", nullable = true, length = 100) + private String userImg; + + /** + * 性别 + */ + @Column(name = "user_sex", nullable = true, length = 1) + private String userSex; + + /** + * 登陆次数 + */ + @Column(name = "login_count", nullable = true, length = 10) + private Integer loginCount; + + /** + * 修改时间 + */ + @Column(name = "update_time", nullable = true) + private Date updateTime; + + /** + * 登陆时间 + */ + @Column(name = "login_time", nullable = true) + private Date loginTime; + + /** + * 注册时间 + */ + @Column(name = "create_time", nullable = true) + private Date createTime; + + /** + * 登陆手机类型 + */ + @Column(name = "login_type", nullable = true, length = 10) + private String loginType; + + /** + * 备注 + */ + @Column(name = "remarks", nullable = true, length = 100) + private String remarks; + + /** + * 地址 + */ + @Column(name = "address", nullable = true, length = 200) + private String address; + + /** + * 技威Code1 + */ + @Column(name = "p2pverify_code1", nullable = true, length = 50) + private String p2pverifyCode1; + + /** + * 技威Code2 + */ + @Column(name = "p2pverify_code2", nullable = true, length = 50) + private String p2pverifyCode2; + + /** + * 技威用户ID + */ + @Column(name = "gwell_userid", nullable = true, length = 50) + private String gwellUserid; + + /** + * 是否注册技威:1已注册、0未注册 + */ + @Column(name = "is_register_gwell", nullable = true, length = 1) + private String isRegisterGwell; + + /** + * 极光用户ID + */ + @Column(name = "jiguang_userid", nullable = true, length = 50) + private String jiguangUserid; + + /** + * 经度 + */ + @Column(name = "longitude", nullable = true) + private Double longitude; + + /** + * 纬度 + */ + @Column(name = "latitude", nullable = true) + private Double latitude; + + /** + * 获取用户Id + * + * @return 用户Id + */ + public Integer getUserId() { + return this.userId; + } + + /** + * 设置用户Id + * + * @param userId 用户Id + */ + public void setUserId(Integer userId) { + this.userId = userId; + } + + /** + * 获取昵称 + * + * @return 昵称 + */ + public String getNickName() { + return this.nickName; + } + + /** + * 设置昵称 + * + * @param nickName 昵称 + */ + public void setNickName(String nickName) { + this.nickName = nickName; + } + + /** + * 获取密码 + * + * @return 密码 + */ + public String getUserPassword() { + return this.userPassword; + } + + /** + * 设置密码 + * + * @param userPassword 密码 + */ + public void setUserPassword(String userPassword) { + this.userPassword = userPassword; + } + + /** + * 获取手机 + * + * @return 手机 + */ + public String getPhoneNumber() { + return this.phoneNumber; + } + + /** + * 设置手机 + * + * @param phoneNumber 手机 + */ + public void setPhoneNumber(String phoneNumber) { + this.phoneNumber = phoneNumber; + } + + /** + * 获取邮箱 + * + * @return 邮箱 + */ + public String getUserEmail() { + return this.userEmail; + } + + /** + * 设置邮箱 + * + * @param userEmail 邮箱 + */ + public void setUserEmail(String userEmail) { + this.userEmail = userEmail; + } + + /** + * 获取用户类型 + * + * @return 用户类型 + */ + public String getUserType() { + return this.userType; + } + + /** + * 设置用户类型 + * + * @param userType 用户类型 + */ + public void setUserType(String userType) { + this.userType = userType; + } + + /** + * 获取头像 + * + * @return 头像 + */ + public String getUserImg() { + return this.userImg; + } + + /** + * 设置头像 + * + * @param userImg 头像 + */ + public void setUserImg(String userImg) { + this.userImg = userImg; + } + + /** + * 获取性别 + * + * @return 性别 + */ + public String getUserSex() { + return this.userSex; + } + + /** + * 设置性别 + * + * @param userSex 性别 + */ + public void setUserSex(String userSex) { + this.userSex = userSex; + } + + /** + * 获取登陆次数 + * + * @return 登陆次数 + */ + public Integer getLoginCount() { + return this.loginCount; + } + + /** + * 设置登陆次数 + * + * @param loginCount 登陆次数 + */ + public void setLoginCount(Integer loginCount) { + this.loginCount = loginCount; + } + + /** + * 获取修改时间 + * + * @return 修改时间 + */ + public Date getUpdateTime() { + return this.updateTime; + } + + /** + * 设置修改时间 + * + * @param updateTime 修改时间 + */ + public void setUpdateTime(Date updateTime) { + this.updateTime = updateTime; + } + + /** + * 获取登陆时间 + * + * @return 登陆时间 + */ + public Date getLoginTime() { + return this.loginTime; + } + + /** + * 设置登陆时间 + * + * @param loginTime 登陆时间 + */ + public void setLoginTime(Date loginTime) { + this.loginTime = loginTime; + } + + /** + * 获取注册时间 + * + * @return 注册时间 + */ + public Date getCreateTime() { + return this.createTime; + } + + /** + * 设置注册时间 + * + * @param createTime 注册时间 + */ + public void setCreateTime(Date createTime) { + this.createTime = createTime; + } + + /** + * 获取登陆手机类型 + * + * @return 登陆手机类型 + */ + public String getLoginType() { + return this.loginType; + } + + /** + * 设置登陆手机类型 + * + * @param loginType 登陆手机类型 + */ + public void setLoginType(String loginType) { + this.loginType = loginType; + } + + /** + * 获取备注 + * + * @return 备注 + */ + public String getRemarks() { + return this.remarks; + } + + /** + * 设置备注 + * + * @param remarks 备注 + */ + public void setRemarks(String remarks) { + this.remarks = remarks; + } + + /** + * 获取地址 + * + * @return 地址 + */ + public String getAddress() { + return this.address; + } + + /** + * 设置地址 + * + * @param address 地址 + */ + public void setAddress(String address) { + this.address = address; + } + + /** + * 获取技威Code1 + * + * @return 技威Code1 + */ + public String getP2pverifyCode1() { + return this.p2pverifyCode1; + } + + /** + * 设置技威Code1 + * + * @param p2pverifyCode1 技威Code1 + */ + public void setP2pverifyCode1(String p2pverifyCode1) { + this.p2pverifyCode1 = p2pverifyCode1; + } + + /** + * 获取技威Code2 + * + * @return 技威Code2 + */ + public String getP2pverifyCode2() { + return this.p2pverifyCode2; + } + + /** + * 设置技威Code2 + * + * @param p2pverifyCode2 技威Code2 + */ + public void setP2pverifyCode2(String p2pverifyCode2) { + this.p2pverifyCode2 = p2pverifyCode2; + } + + /** + * 获取技威用户ID + * + * @return 技威用户ID + */ + public String getGwellUserid() { + return this.gwellUserid; + } + + /** + * 设置技威用户ID + * + * @param gwellUserid 技威用户ID + */ + public void setGwellUserid(String gwellUserid) { + this.gwellUserid = gwellUserid; + } + + /** + * 获取是否注册技威:1已注册、0未注册 + * + * @return 是否注册技威 + */ + public String getIsRegisterGwell() { + return this.isRegisterGwell; + } + + /** + * 设置是否注册技威:1已注册、0未注册 + * + * @param isRegisterGwell 是否注册技威:1已注册、0未注册 + */ + public void setIsRegisterGwell(String isRegisterGwell) { + this.isRegisterGwell = isRegisterGwell; + } + + /** + * 获取极光用户ID + * + * @return 极光用户ID + */ + public String getJiguangUserid() { + return this.jiguangUserid; + } + + /** + * 设置极光用户ID + * + * @param jiguangUserid 极光用户ID + */ + public void setJiguangUserid(String jiguangUserid) { + this.jiguangUserid = jiguangUserid; + } + + /** + * 获取经度 + * + * @return 经度 + */ + public Double getLongitude() { + return this.longitude; + } + + /** + * 设置经度 + * + * @param longitude 经度 + */ + public void setLongitude(Double longitude) { + this.longitude = longitude; + } + + /** + * 获取纬度 + * + * @return 纬度 + */ + public Double getLatitude() { + return this.latitude; + } + + /** + * 设置纬度 + * + * @param latitude 纬度 + */ + public void setLatitude(Double latitude) { + this.latitude = latitude; + } +} diff --git a/src/main/java/com/ifish/config/MinaConfig.java b/src/main/java/com/ifish/config/MinaConfig.java new file mode 100644 index 0000000..00f9d5b --- /dev/null +++ b/src/main/java/com/ifish/config/MinaConfig.java @@ -0,0 +1,139 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.ifish.config; + +import com.ifish.socket.codeFactory.ServerCodecFactory; +import com.ifish.socket.config.JobFactory; +import com.ifish.socket.config.MinaServerHandler; +import com.ifish.socket.config.MyInetSocketAddress; +import com.ifish.socket.config.ScheduleJob; +import com.ifish.socket.config.SomeServer; +import com.ifish.socket.webService.ServiceBean; +import com.ifish.util.IfishFilePath; +import java.util.HashMap; +import java.util.Map; +import org.apache.mina.core.filterchain.DefaultIoFilterChainBuilder; +import org.apache.mina.filter.codec.ProtocolCodecFilter; +import org.apache.mina.filter.logging.LoggingFilter; +import org.apache.mina.transport.socket.nio.NioSocketAcceptor; +import org.springframework.context.annotation.Bean; + +/** + * + * @author Administrator + */ +public class MinaConfig { + + /** + * webService + * + * @return + */ + @Bean + public ServiceBean serviceBean() { + return new ServiceBean(); + } + + /** + * 消息处理类 + * + * @return + */ + @Bean + public SomeServer someServer() { + return new SomeServer(); + } + + /** + * 实现job中注入bean + * + * @return + */ + @Bean + public JobFactory jobFactory() { + return new JobFactory(); + } + + /** + * 调度任务 + * + * @return + */ + @Bean + public ScheduleJob scheduleJob() { + return new ScheduleJob(); + } + + /** + * 自定义的serverHandler + * + * @return + */ + @Bean + public MinaServerHandler serverHandler() { + return new MinaServerHandler(); + } + + /** + * 指定服务端地址和端口 + * + * @return + */ + @Bean + public MyInetSocketAddress address() { + MyInetSocketAddress myInetSocketAddress = new MyInetSocketAddress(IfishFilePath.ifiship, IfishFilePath.ifishport); + return myInetSocketAddress; + } + + /** + * 编码filter + * + * @return + */ + @Bean + public ProtocolCodecFilter codecFilter() { + return new ProtocolCodecFilter(new ServerCodecFactory()); + } + + /** + * 日志filter + * + * @return + */ + @Bean + public LoggingFilter loggingFilter() { + return new LoggingFilter(); + } + + /** + * 过滤器链 + * + * @return + */ + @Bean + public DefaultIoFilterChainBuilder filterChainBuilder() { + DefaultIoFilterChainBuilder defaultIoFilterChainBuilder = new DefaultIoFilterChainBuilder(); + Map map = new HashMap(); + map.put("codecFilter", codecFilter()); + defaultIoFilterChainBuilder.setFilters(map); + return defaultIoFilterChainBuilder; + } + + /** + * 开始运行socket服务 + * @return + */ + @Bean(initMethod = "bind", destroyMethod = "unbind") + public NioSocketAcceptor ioAcceptor() { + NioSocketAcceptor nioSocketAcceptor = new NioSocketAcceptor(); + nioSocketAcceptor.setHandler(serverHandler()); + nioSocketAcceptor.setDefaultLocalAddress(address().getSocketAddress()); + nioSocketAcceptor.setFilterChainBuilder(filterChainBuilder()); + nioSocketAcceptor.setReuseAddress(true); + nioSocketAcceptor.getSessionConfig().setBothIdleTime(0); + return nioSocketAcceptor; + } +} diff --git a/src/main/java/com/ifish/config/MyWSServletContextListener.java b/src/main/java/com/ifish/config/MyWSServletContextListener.java new file mode 100644 index 0000000..d2da971 --- /dev/null +++ b/src/main/java/com/ifish/config/MyWSServletContextListener.java @@ -0,0 +1,14 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.ifish.config; + +/** + * + * @author Administrator + */ +public class MyWSServletContextListener { + +} diff --git a/src/main/java/com/ifish/config/MybatisConfig.java b/src/main/java/com/ifish/config/MybatisConfig.java new file mode 100644 index 0000000..235479f --- /dev/null +++ b/src/main/java/com/ifish/config/MybatisConfig.java @@ -0,0 +1,86 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.ifish.config; + +import javax.sql.DataSource; +import org.mybatis.spring.SqlSessionFactoryBean; +import org.mybatis.spring.mapper.MapperScannerConfigurer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.jdbc.datasource.DataSourceTransactionManager; +import org.springframework.jdbc.datasource.DriverManagerDataSource; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.annotation.EnableTransactionManagement; + +/** + * + * @author Administrator + */ +@Configuration +@EnableTransactionManagement +public class MybatisConfig { + + @Bean + public DataSource dataSource() { +// try { +// ComboPooledDataSource datasourc = new ComboPooledDataSource(); +// datasourc.setDriverClass(JdbcProperties.driverClassName); +// datasourc.setJdbcUrl(JdbcProperties.url); +// datasourc.setUser(JdbcProperties.username); +// datasourc.setPassword(JdbcProperties.password); +// datasourc.setAutoCommitOnClose(JdbcProperties.autoCommitOnClose); +// datasourc.setInitialPoolSize(JdbcProperties.initialPoolSize); +// datasourc.setMinPoolSize(JdbcProperties.minPoolSize); +// datasourc.setMaxPoolSize(JdbcProperties.maxPoolSize); +// datasourc.setAcquireIncrement(JdbcProperties.acquireIncrement); +// datasourc.setCheckoutTimeout(JdbcProperties.checkoutTimeout); +// datasourc.setMaxIdleTime(JdbcProperties.maxIdleTime); +// datasourc.setIdleConnectionTestPeriod(JdbcProperties.idleConnectionTestPeriod); +// return datasourc; +// } catch (Exception e) { +// +// } + DriverManagerDataSource dataSource = new DriverManagerDataSource(); + dataSource.setDriverClassName("com.mysql.jdbc.Driver"); + //线上生产数据库连接 + dataSource.setUsername("ifish"); + dataSource.setPassword("ifish7pwd"); + dataSource.setUrl("jdbc:mysql://localhost:3306/myfishdbenglish?characterEncoding=UTF-8"); + return dataSource; + } + + @Bean(name = "sqlSessionFactory") + public SqlSessionFactoryBean sqlSessionFactory() { + SqlSessionFactoryBean ssfb = new SqlSessionFactoryBean(); + ssfb.setDataSource(dataSource()); + ssfb.setTypeAliasesPackage("com.ifish.bean"); + try { + org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration(); + configuration.setMapUnderscoreToCamelCase(true); + ssfb.setConfiguration(configuration); + + } catch (Exception e) { + } + + return ssfb; + } + + @Bean + public MapperScannerConfigurer mpperScannnerConfigurer() { + MapperScannerConfigurer msc = new MapperScannerConfigurer(); + msc.setSqlSessionFactoryBeanName("sqlSessionFactory"); + msc.setBasePackage("com.ifish.mapper"); + return msc; + } + + @Bean + PlatformTransactionManager transactionManager() { + DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(); + transactionManager.setDataSource(dataSource()); + return transactionManager; + } + +} diff --git a/src/main/java/com/ifish/config/RedisConfig.java b/src/main/java/com/ifish/config/RedisConfig.java new file mode 100644 index 0000000..1d37213 --- /dev/null +++ b/src/main/java/com/ifish/config/RedisConfig.java @@ -0,0 +1,58 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.ifish.config; + +import com.ifish.util.IfishFilePath; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.StringRedisTemplate; + +/** + * + * @author Administrator + */ +@Configuration +public class RedisConfig { + + @Bean + public RedisConnectionFactory redisCF() { + JedisConnectionFactory cf = new JedisConnectionFactory(); + +// //获取当前操作系统(servers_os为服务器设置的属性JAVA_OPTS=%JAVA_OPTS% -Dservers_os=online84) +// String servers_os = System.getProperty("servers_os") == null ? "" : System.getProperty("servers_os"); + if (IfishFilePath.link_img_head.contains("ifish7")) { + //站点线上服务器 + cf.setHostName("120.55.190.56"); + cf.setPort(3796); + cf.setPassword("ifish7myredis"); + } else { + //站点测试服务器 + cf.setHostName("139.196.24.156"); + cf.setPort(3796); + cf.setPassword("ifish7myredis"); +// FileUtil.writeLinesByLog("测试缓存连接"); + } + + return cf; + } + + @Bean + public RedisTemplate redisTemplate(RedisConnectionFactory cf) { + RedisTemplate redis = new RedisTemplate(); + redis.setConnectionFactory(cf); + return redis; + } + + @Bean + public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory cf) { + StringRedisTemplate redis = new StringRedisTemplate(); + redis.setConnectionFactory(cf); + return redis; + } +} diff --git a/src/main/java/com/ifish/config/RootConfig.java b/src/main/java/com/ifish/config/RootConfig.java new file mode 100644 index 0000000..f2aaaa3 --- /dev/null +++ b/src/main/java/com/ifish/config/RootConfig.java @@ -0,0 +1,26 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.ifish.config; + +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.ComponentScan.Filter; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.FilterType; +import org.springframework.context.annotation.Import; +import org.springframework.web.servlet.config.annotation.EnableWebMvc; + +/** + * + * @author Administrator + */ +@Configuration +//@Import(MybatisConfig.class) //导入数据库文件配置 +@ComponentScan(basePackages = {"com.ifish.*"}, + excludeFilters = { + @Filter(type = FilterType.ANNOTATION, value = EnableWebMvc.class)}) +public class RootConfig { + +} diff --git a/src/main/java/com/ifish/config/WebAppInitializer.java b/src/main/java/com/ifish/config/WebAppInitializer.java new file mode 100644 index 0000000..af5c16c --- /dev/null +++ b/src/main/java/com/ifish/config/WebAppInitializer.java @@ -0,0 +1,46 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.ifish.config; + +import javax.servlet.Filter; +import org.springframework.context.annotation.Bean; +import org.springframework.web.filter.CharacterEncodingFilter; +import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer; + +/** + * + * @author Administrator + */ +public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { + + @Override + protected Class[] getRootConfigClasses() { + return new Class[]{RootConfig.class, MybatisConfig.class,MinaConfig.class}; + } + + @Override + protected Class[] getServletConfigClasses() { + return new Class[]{WebConfig.class}; + } + + @Override + protected String[] getServletMappings() { + return new String[]{"/api/*"}; + } + + @Override + protected Filter[] getServletFilters() { + return null; + } + + @Bean + public Filter characterEncodingFilter() { + CharacterEncodingFilter filter = new CharacterEncodingFilter(); + filter.setEncoding("UTF-8"); + filter.setForceEncoding(true); + return filter; + } +} diff --git a/src/main/java/com/ifish/config/WebConfig.java b/src/main/java/com/ifish/config/WebConfig.java new file mode 100644 index 0000000..9771e0a --- /dev/null +++ b/src/main/java/com/ifish/config/WebConfig.java @@ -0,0 +1,59 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.ifish.config; + +import com.ifish.util.IfishFilePath; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.ViewResolver; +import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer; +import org.springframework.web.servlet.config.annotation.EnableWebMvc; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; +import org.springframework.web.servlet.view.InternalResourceViewResolver; + +/** + * + * @author Administrator + */ +@Configuration +/** + * 启动SpringMVC + */ +@EnableWebMvc +/** + * 启动组件扫描器,com.mymvc.controller是包 + */ +@ComponentScan("com.ifish.controller") +public class WebConfig extends WebMvcConfigurerAdapter { + + /** + * 配置视图解析器 + * + * @return + */ + @Bean + public ViewResolver viewResolver() { + InternalResourceViewResolver resolver = new InternalResourceViewResolver(); + resolver.setPrefix("/WEB-INF/views/"); + resolver.setSuffix(".jsp"); + resolver.setExposeContextBeansAsAttributes(true); + return resolver; + } + + /** + * 配置静态资源的处理 + * + * @param configurer + */ + @Override + public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { + configurer.enable();; + } + + +} diff --git a/src/main/java/com/ifish/enums/PushTypeEnum.java b/src/main/java/com/ifish/enums/PushTypeEnum.java new file mode 100644 index 0000000..4ffa4e8 --- /dev/null +++ b/src/main/java/com/ifish/enums/PushTypeEnum.java @@ -0,0 +1,33 @@ +package com.ifish.enums; + +public enum PushTypeEnum { + remove_device("remove_device","解绑通知"), + wendu_warn("wendu_warn","温度报警"), + qu_reply("qu_reply","问题反馈"), + app_update("app_update","IOS更新推送"), + remind_water("remind_water","换水提醒"), + offline_push("offline_push","离线通知"); + + private PushTypeEnum(String key,String value){ + this.key = key; + this.value = value; + } + + private String key; + private String value; + + + public String getKey() { + return key; + } + public void setKey(String key) { + this.key = key; + } + public String getValue() { + return value; + } + public void setValue(String value) { + this.value = value; + } + +} diff --git a/src/main/java/com/ifish/enums/SubDirectoryEnum.java b/src/main/java/com/ifish/enums/SubDirectoryEnum.java new file mode 100644 index 0000000..2715c25 --- /dev/null +++ b/src/main/java/com/ifish/enums/SubDirectoryEnum.java @@ -0,0 +1,45 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.ifish.enums; + +/** + * + * @author Administrator + */ +public enum SubDirectoryEnum { + headImage("app/", "头像"), + banner("banner/", "banner图"), + look("look/", "看护报告"), + ueditorHtml("ueditorHtml/", "后台生成的网页"), + adInfo("Img/adInfo/", "广告图片"), + information("Img/information/", "资讯图片"), + ifishGoods("Img/goods/", "商品图片"), + ifishCommodity("Img/commodity/", "商家商品介绍图"); + + private SubDirectoryEnum(String key, String value) { + this.key = key; + this.value = value; + } + + private String key; + private String value; + + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } +} diff --git a/src/main/java/com/ifish/helper/DeviceHelperI.java b/src/main/java/com/ifish/helper/DeviceHelperI.java new file mode 100644 index 0000000..f3bf135 --- /dev/null +++ b/src/main/java/com/ifish/helper/DeviceHelperI.java @@ -0,0 +1,41 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.ifish.helper; + +import com.ifish.bean.LoginRecord; +import com.ifish.bean.Tbl_Device; +import com.ifish.bean.Tbl_Device_User; +import com.ifish.bean.Tbl_Factory_List; +import com.ifish.bean.Tbl_HardWare_Type; +import com.ifish.socket.model.send.DeviceLoginContextLength24; +import java.util.List; + +/** + * + * @author Administrator + */ +public interface DeviceHelperI { + + public Tbl_Device update(Tbl_Device device); + + public Tbl_Device update(DeviceLoginContextLength24 model); + + public void updateWarnOnoff(String macAddress, String onOff); + + public Tbl_Device getUniqueByProperty(String property, Object value); + + public List getByProperty(String property, Object value); + + public Tbl_Device_User getUniqueByProperty(String macAddress); + + public List getListByProperty(Integer deviceId); + + public LoginRecord save(LoginRecord loginRecord); + + public Tbl_Factory_List getFactoryListById(String factoryCode); + + public Tbl_HardWare_Type getHardwareTypeById(String typeCode); +} diff --git a/src/main/java/com/ifish/helper/UserHelperI.java b/src/main/java/com/ifish/helper/UserHelperI.java new file mode 100644 index 0000000..3470650 --- /dev/null +++ b/src/main/java/com/ifish/helper/UserHelperI.java @@ -0,0 +1,20 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.ifish.helper; + +import com.ifish.bean.Tbl_Push_List; +import com.ifish.bean.Tbl_User; + +/** + * + * @author Administrator + */ +public interface UserHelperI { + + public Tbl_User findById(Integer userId); + + public Tbl_Push_List save(Tbl_Push_List pushList); +} diff --git a/src/main/java/com/ifish/socket/codeFactory/Context.java b/src/main/java/com/ifish/socket/codeFactory/Context.java new file mode 100644 index 0000000..3d632d2 --- /dev/null +++ b/src/main/java/com/ifish/socket/codeFactory/Context.java @@ -0,0 +1,58 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.ifish.socket.codeFactory; + +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import org.apache.mina.core.buffer.IoBuffer; + +/** + * + * @author Administrator + */ +public class Context { + + private final CharsetDecoder decoder; + private IoBuffer buf; + private int matchCount = 0; + private int overflowPosition = 0; + + public Context(Charset charset) { + decoder = charset.newDecoder(); + buf = IoBuffer.allocate(100).setAutoExpand(true); + //buf.order(ByteOrder.LITTLE_ENDIAN); + } + + public CharsetDecoder getDecoder() { + return decoder; + } + + public IoBuffer getBuffer() { + return buf; + } + + public int getOverflowPosition() { + return overflowPosition; + } + + public int getMatchCount() { + return matchCount; + } + + public void setMatchCount(int matchCount) { + this.matchCount = matchCount; + } + + public void reset() { + overflowPosition = 0; + matchCount = 0; + decoder.reset(); + } + + public void append(IoBuffer in) { + getBuffer().put(in); + } +} diff --git a/src/main/java/com/ifish/socket/codeFactory/ServerCodecFactory.java b/src/main/java/com/ifish/socket/codeFactory/ServerCodecFactory.java new file mode 100644 index 0000000..897b763 --- /dev/null +++ b/src/main/java/com/ifish/socket/codeFactory/ServerCodecFactory.java @@ -0,0 +1,31 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.ifish.socket.codeFactory; + +import java.nio.charset.Charset; +import org.apache.mina.core.session.IoSession; +import org.apache.mina.filter.codec.ProtocolCodecFactory; +import org.apache.mina.filter.codec.ProtocolDecoder; +import org.apache.mina.filter.codec.ProtocolEncoder; + +/** + * + * @author Administrator + */ +public class ServerCodecFactory implements ProtocolCodecFactory { + + private static final Charset charset = Charset.forName("UTF-8"); + + @Override + public ProtocolDecoder getDecoder(IoSession session) throws Exception { + return new ServerDecode(charset); + } + + @Override + public ProtocolEncoder getEncoder(IoSession session) throws Exception { + return new ServerEncode(charset); + } +} diff --git a/src/main/java/com/ifish/socket/codeFactory/ServerDecode.java b/src/main/java/com/ifish/socket/codeFactory/ServerDecode.java new file mode 100644 index 0000000..5cc6dd2 --- /dev/null +++ b/src/main/java/com/ifish/socket/codeFactory/ServerDecode.java @@ -0,0 +1,385 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.ifish.socket.codeFactory; + +import com.ifish.socket.model.send.DeviceLoginContextLength24; +import com.ifish.socket.model.send.APPLoginContextLength25; +import com.ifish.socket.model.send.ModelVersionLength22; +import com.ifish.socket.model.send.ModelVersionLength38; +import com.ifish.socket.model.send.TemperatureAlarmLength22; +import com.ifish.socket.model.send.TemperatureSwitchLength22; +import java.nio.charset.Charset; +import org.apache.mina.core.buffer.IoBuffer; +import org.apache.mina.core.session.AttributeKey; +import org.apache.mina.core.session.IoSession; +import org.apache.mina.filter.codec.ProtocolDecoder; +import org.apache.mina.filter.codec.ProtocolDecoderOutput; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 解码 + * + * @author + */ +public class ServerDecode implements ProtocolDecoder { + + private final AttributeKey CONTEXT = new AttributeKey(getClass(), "context"); + private final Charset charset; + private static Logger log = LoggerFactory.getLogger(ServerDecode.class); + + public ServerDecode() { + this(Charset.defaultCharset()); + } + + public ServerDecode(Charset charset) { + this.charset = charset; + } + + private Context getContext(IoSession session) { + Context ctx = (Context) session.getAttribute(CONTEXT); + if (ctx == null) { + ctx = new Context(charset); + session.setAttribute(CONTEXT, ctx); + } + return ctx; + } + + @Override + public void decode(IoSession session, IoBuffer in, ProtocolDecoderOutput out) throws Exception { + // 小端模式 + //in.order(ByteOrder.LITTLE_ENDIAN); + // 先获取上次的处理上下文,其中可能有未处理完的数据 + Context ctx = getContext(session); + // 先把当前buffer中的数据追加到Context的buffer当中 + ctx.append(in); + + IoBuffer buf = ctx.getBuffer(); + buf.flip(); + //验证 + getValid(session, buf, out); + } + + /** + * 验证协议 + * + * @param session + * @param buf + * @param out + */ + private void getValid(IoSession session, IoBuffer buf, ProtocolDecoderOutput out) { + int limit = buf.remaining(); + int check_code = buf.position(1).get() & 0xff; + int length = buf.position(14).get() & 0xff; + buf.position(0); + //计算包长度 + if (buf.hasRemaining() && limit == length && length >= 17) { + //字节转成对象 + doDecode(buf, out, length, check_code); + } else { + log.info("length error:length:【{}】,limit:【{}】,HexDump:【{}】", length, limit, buf.getHexDump()); + } + buf.position(0); + buf.clear(); + } + + /** + * 解码成对象 + * + * @param buf + * @param out + * @param length + */ + private void doDecode(IoBuffer buf, ProtocolDecoderOutput out, int length, int check_code) { + //字节 + byte[] bytes = new byte[length]; + buf.get(bytes); + buf.flip(); + //log.info("length:【{}】;limit:【{}】,HexDump:【{}】",length,buf.limit(),buf.getHexDump()); + //crc16校验 + //boolean bln = CRC16.calcCrc16(ByteUtil.hexStringToBytes(hexDump)); + switch (length) { + //长度为22 + case 22: { + //设置报警温度开关 + if (check_code == 5) { + TemperatureSwitch(buf, out); + } //设备报警 + else if (check_code == 9) { + TemperatureAlarm(buf, out); + } //模块版本指令 + else if (check_code == 16) { + ModelVersion(buf, out); + } else { + out.write(bytes); + } + break; + } + //长度为24,设备发登录请求 + case 24: { + if (check_code == 1) { + DeviceLogin(buf, out); + } + break; + } + //长度为25,手机发登录请求 + case 25: { + if (check_code == 0) { + AppLogin(buf, out); + } + break; + } + //模块版本指令 + case 42: { + if (check_code == 17) { + ModelVersion17(buf, out); + } + break; + } + default: { + out.write(bytes); + } + } + } + + /** + * 设备登陆服务器 + * + * @param buf + * @param out + */ + private void DeviceLogin(IoBuffer buf, ProtocolDecoderOutput out) { + DeviceLoginContextLength24 model = new DeviceLoginContextLength24(); + //15字节数据包头 + model.setType(buf.get()); + model.setCheck_code(buf.get()); + byte[] src = model.getSrc(); + for (int i = 0; i < src.length; i++) { + src[i] = buf.get(); + } + model.setSrc(src); + byte[] dest = model.getDest(); + for (int i = 0; i < dest.length; i++) { + dest[i] = buf.get(); + } + model.setDest(dest); + model.setRemote_len(buf.get()); + //登陆信息 + model.setVendor(buf.get()); + model.setHardware_type(buf.get()); + model.setVersion(buf.get()); + byte[] login_ip = model.getLogin_ip(); + for (int i = 0; i < login_ip.length; i++) { + login_ip[i] = buf.get(); + } + model.setLogin_ip(login_ip); + byte[] crc16_code = model.getCrc16_code(); + for (int i = 0; i < crc16_code.length; i++) { + crc16_code[i] = buf.get(); + } + out.write(model); + } + + /** + * APP登陆服务器 + * + * @param buf + * @param out + */ + private void AppLogin(IoBuffer buf, ProtocolDecoderOutput out) { + APPLoginContextLength25 model = new APPLoginContextLength25(); + //15字节数据包头 + model.setType(buf.get()); + model.setCheck_code(buf.get()); + byte[] src = model.getSrc(); + for (int i = 0; i < src.length; i++) { + src[i] = buf.get(); + } + model.setSrc(src); + byte[] dest = model.getDest(); + for (int i = 0; i < dest.length; i++) { + dest[i] = buf.get(); + } + model.setDest(dest); + model.setRemote_len(buf.get()); + //时间 + model.setLongtime(buf.getLong()); + byte[] crc16_code = model.getCrc16_code(); + for (int i = 0; i < crc16_code.length; i++) { + crc16_code[i] = buf.get(); + } + out.write(model); + } + + /** + * 设置报警温度开关 + * + * @param buf + * @param out + */ + private void TemperatureSwitch(IoBuffer buf, ProtocolDecoderOutput out) { + TemperatureSwitchLength22 model = new TemperatureSwitchLength22(); + //15字节数据包头 + model.setType(buf.get()); + model.setCheck_code(buf.get()); + byte[] src = model.getSrc(); + for (int i = 0; i < src.length; i++) { + src[i] = buf.get(); + } + model.setSrc(src); + byte[] dest = model.getDest(); + for (int i = 0; i < dest.length; i++) { + dest[i] = buf.get(); + } + model.setDest(dest); + model.setRemote_len(buf.get()); + //开启关闭报警,00关,01开,02设置温度 + model.setOnoff(buf.get()); + //低温报警温度 + model.setLow_wendu(buf.getShort()); + //高温报警温度 + model.setHight_wendu(buf.getShort()); + byte[] crc16_code = model.getCrc16_code(); + for (int i = 0; i < crc16_code.length; i++) { + crc16_code[i] = buf.get(); + } + out.write(model); + } + + /** + * 模块版本号 + * + * @param buf + * @param out + */ + private void ModelVersion(IoBuffer buf, ProtocolDecoderOutput out) { + ModelVersionLength22 model = new ModelVersionLength22(); + //15字节数据包头 + model.setType(buf.get()); + model.setCheck_code(buf.get()); + byte[] src = model.getSrc(); + for (int i = 0; i < src.length; i++) { + src[i] = buf.get(); + } + model.setSrc(src); + byte[] dest = model.getDest(); + for (int i = 0; i < dest.length; i++) { + dest[i] = buf.get(); + } + model.setDest(dest); + model.setRemote_len(buf.get()); + //版本 + model.setVersion(buf.get()); + //年 + model.setYear(buf.getShort()); + //月 + model.setMonth(buf.get()); + //日 + model.setDay(buf.get()); + byte[] crc16_code = model.getCrc16_code(); + for (int i = 0; i < crc16_code.length; i++) { + crc16_code[i] = buf.get(); + } + out.write(model); + } + + /** + * 模块版本号 + * + * @param buf + * @param out + */ + private void ModelVersion17(IoBuffer buf, ProtocolDecoderOutput out) { + ModelVersionLength38 model = new ModelVersionLength38(); + //15字节数据包头 + model.setType(buf.get()); + model.setCheck_code(buf.get()); + byte[] src = model.getSrc(); + for (int i = 0; i < src.length; i++) { + src[i] = buf.get(); + } + model.setSrc(src); + byte[] dest = model.getDest(); + for (int i = 0; i < dest.length; i++) { + dest[i] = buf.get(); + } + model.setDest(dest); + model.setRemote_len(buf.get()); + //版本 + model.setVersion(buf.get()); + //年 + model.setYear(buf.getShort()); + //月 + model.setMonth(buf.get()); + //日 + model.setDay(buf.get()); + //MCU启动次数 + model.setNumber1(buf.getInt()); + //模块启动次数 + model.setNumber2(buf.getInt()); + //连接路由器次数 + model.setNumber3(buf.getInt()); + //成功连接服务器次数 + model.setNumber4(buf.getInt()); + //尝试连接服务器次数 + model.setNumber5(buf.getInt()); + byte[] crc16_code = model.getCrc16_code(); + for (int i = 0; i < crc16_code.length; i++) { + crc16_code[i] = buf.get(); + } + out.write(model); + } + + /** + * 设备温度报警 + * + * @param buf + * @param out + */ + private void TemperatureAlarm(IoBuffer buf, ProtocolDecoderOutput out) { + TemperatureAlarmLength22 model = new TemperatureAlarmLength22(); + //15字节数据包头 + model.setType(buf.get()); + model.setCheck_code(buf.get()); + byte[] src = model.getSrc(); + for (int i = 0; i < src.length; i++) { + src[i] = buf.get(); + } + model.setSrc(src); + byte[] dest = model.getDest(); + for (int i = 0; i < dest.length; i++) { + dest[i] = buf.get(); + } + model.setDest(dest); + model.setRemote_len(buf.get()); + //报警温度类型,0是低温报警,1是高温报警 + model.setWarn_type(buf.get()); + //当前设备温度 + model.setWendu(buf.getShort()); + //报警温度 + model.setWarn_wendu(buf.getShort()); + byte[] crc16_code = model.getCrc16_code(); + for (int i = 0; i < crc16_code.length; i++) { + crc16_code[i] = buf.get(); + } + out.write(model); + } + + @Override + public void dispose(IoSession session) throws Exception { + Context ctx = (Context) session.getAttribute(CONTEXT); + if (ctx != null) { + session.removeAttribute(CONTEXT); + } + } + + @Override + public void finishDecode(IoSession session, ProtocolDecoderOutput out) + throws Exception { + + } + +} diff --git a/src/main/java/com/ifish/socket/codeFactory/ServerEncode.java b/src/main/java/com/ifish/socket/codeFactory/ServerEncode.java new file mode 100644 index 0000000..37f4926 --- /dev/null +++ b/src/main/java/com/ifish/socket/codeFactory/ServerEncode.java @@ -0,0 +1,137 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.ifish.socket.codeFactory; + +import com.ifish.socket.model.receive.APPLoginResponseLength26; +import com.ifish.socket.model.receive.DeviceLoginResponseLength25; +import com.ifish.socket.model.receive.ServerHeartbeatLength17; +import com.ifish.socket.model.send.UpgradeVersionLength78; +import com.ifish.socket.model.send.TemperatureSwitchLength22; +import java.nio.charset.Charset; +import org.apache.mina.core.buffer.IoBuffer; +import org.apache.mina.core.session.IoSession; +import org.apache.mina.filter.codec.ProtocolEncoder; +import org.apache.mina.filter.codec.ProtocolEncoderOutput; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 编码 + * + * @author + */ +public class ServerEncode implements ProtocolEncoder { + + private final Charset charset; + private static Logger log = LoggerFactory.getLogger(ServerEncode.class); + + public ServerEncode() { + this(Charset.defaultCharset()); + } + + public ServerEncode(Charset charset) { + this.charset = charset; + } + + /* 这个message来自哪? */ + @Override + public void encode(IoSession session, Object message, ProtocolEncoderOutput out) throws Exception { + + IoBuffer buffer = IoBuffer.allocate(100).setAutoExpand(true); + //修改此缓冲区的字节顺序,小端模式 + //buffer.order(ByteOrder.LITTLE_ENDIAN); + //转发不做处理的数据 + if (message instanceof byte[]) { + byte[] bytes = (byte[]) message; + buffer.put(bytes); + } //响应心跳 + else if (message instanceof ServerHeartbeatLength17) { + ServerHeartbeatLength17 model = (ServerHeartbeatLength17) message; + //15字节数据包头 + buffer.put(model.getType()); + buffer.put(model.getCheck_code()); + buffer.put(model.getSrc()); + buffer.put(model.getDest()); + buffer.put(model.getRemote_len()); + //crc16校验码 + buffer.put(model.getCrc16_code()); + } //设置报警温度 + else if (message instanceof TemperatureSwitchLength22) { + TemperatureSwitchLength22 model = (TemperatureSwitchLength22) message; + //15字节数据包头 + buffer.put(model.getType()); + buffer.put(model.getCheck_code()); + buffer.put(model.getSrc()); + buffer.put(model.getDest()); + buffer.put(model.getRemote_len()); + //开关 + buffer.put(model.getOnoff()); + //温度类型 + buffer.putShort(model.getLow_wendu()); + //报警温度 + buffer.putShort(model.getHight_wendu()); + //crc16校验码 + buffer.put(model.getCrc16_code()); + } //APP登录响应 + else if (message instanceof APPLoginResponseLength26) { + APPLoginResponseLength26 model = (APPLoginResponseLength26) message; + //15字节数据包头 + buffer.put(model.getType()); + buffer.put(model.getCheck_code()); + buffer.put(model.getSrc()); + buffer.put(model.getDest()); + buffer.put(model.getRemote_len()); + //状态 + buffer.put(model.getLogin_status()); + //时间 + buffer.putLong(model.getSysTime()); + //crc16校验码 + buffer.put(model.getCrc16_code()); + } //设备登陆返回 + else if (message instanceof DeviceLoginResponseLength25) { + DeviceLoginResponseLength25 model = (DeviceLoginResponseLength25) message; + //15字节数据包头 + buffer.put(model.getType()); + buffer.put(model.getCheck_code()); + buffer.put(model.getSrc()); + buffer.put(model.getDest()); + buffer.put(model.getRemote_len()); + //系统时间 + buffer.put(model.getSysTime()); + //crc16校验码 + buffer.put(model.getCrc16_code()); + } //升级 + else if (message instanceof UpgradeVersionLength78) { + UpgradeVersionLength78 model = (UpgradeVersionLength78) message; + //15字节数据包头 + buffer.put(model.getType()); + buffer.put(model.getCheck_code()); + buffer.put(model.getSrc()); + buffer.put(model.getDest()); + buffer.put(model.getRemote_len()); + //版本 + buffer.put(model.getVersion()); + //地址 + buffer.put(model.getAddress()); + //crc16校验码 + buffer.put(model.getCrc16_code()); + } + buffer.flip(); + /* + * 编码的方式就是按照短信协议拼 装字符串到IoBuffer 缓冲区,然后调用ProtocolEncoderOutput + * 的write()方法输出字节 流 + */ + //log.info("limit:【{}】,HexDump:【{}】",buffer.limit(),buffer.getHexDump()); + out.write(buffer); + out.flush(); + buffer.free(); + } + + @Override + public void dispose(IoSession session) throws Exception { + + } +} diff --git a/src/main/java/com/ifish/socket/config/ExecuteJob.java b/src/main/java/com/ifish/socket/config/ExecuteJob.java new file mode 100644 index 0000000..81f2658 --- /dev/null +++ b/src/main/java/com/ifish/socket/config/ExecuteJob.java @@ -0,0 +1,119 @@ + +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.ifish.socket.config; + +import com.ifish.bean.Tbl_Device; +import com.ifish.bean.Tbl_Device_Statistics; +import com.ifish.bean.Tbl_Device_User; +import com.ifish.bean.Tbl_Push_List; +import com.ifish.bean.Tbl_User; +import com.ifish.enums.PushTypeEnum; +import com.ifish.helper.DeviceHelperI; +import com.ifish.helper.UserHelperI; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.apache.mina.core.session.IoSession; +import org.quartz.Job; +import org.quartz.JobDataMap; +import org.quartz.JobExecutionContext; +import org.quartz.JobExecutionException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * + * @author Administrator + */ +public class ExecuteJob implements Job { + + private static Logger log = LoggerFactory.getLogger(ExecuteJob.class); + //@Autowired + //private NeteaseIM neteaseIM; + @Autowired + private UserHelperI userHelperI; + @Autowired + private DeviceHelperI deviceHelperI; + + public void execute(JobExecutionContext context) throws JobExecutionException { + //任务传递的参数 + JobDataMap dataMap = context.getJobDetail().getJobDataMap(); + String macAddress = dataMap.getString("macAddress"); + String timestamp = dataMap.getString("timestamp"); + try { + //移除为null的连接 + IoSession session_cz = SomeServer.sessions_cz.get(macAddress); + if (session_cz == null || session_cz.getRemoteAddress() == null) { + SomeServer.sessions_cz.remove(macAddress); + } + //设备是否存在 + Tbl_Device device = deviceHelperI.getUniqueByProperty("macAddress", macAddress); + Tbl_Device_Statistics device_Statistics = null; + if (device != null) { + //记录次数 + Integer number1 = device_Statistics.getMcuCount(); + Integer number2 = device_Statistics.getModuleCount(); + Integer number3 = device_Statistics.getRouterCount(); + Integer number4 = device_Statistics.getServerCount(); + Integer number5 = device_Statistics.getServerTryCount(); + List list = deviceHelperI.getListByProperty(device.getDeviceId()); + //是否被绑定 + if (list != null) { + for (Tbl_Device_User deviceUser : list) { + Integer userId = deviceUser.getUserId(); + String showName = deviceUser.getShowName(); + String title = PushTypeEnum.offline_push.getValue(); + String msg = "【" + title + "】你的水族箱“" + showName + "”于" + timestamp + "离线,请及时查看!"; + //发送云信消息 + boolean neteaseBln = false; +// Map neteaseMap = neteaseIM.sendMsg("ifish", userId.toString(), msg); +// if (neteaseMap != null) { +// neteaseBln = neteaseMap.get("code").equals(NeteaseEnum.status200.getKey()); +// } + //IOS极光推送 + boolean jpushIosBln = false; + //推送记录 + Tbl_Push_List pushList = new Tbl_Push_List(); + Integer deviceId = deviceUser.getDeviceId(); + Tbl_User user = userHelperI.findById(userId); + if (user != null) { + String loginType = user.getLoginType(); + if (loginType != null) { + Map map = new HashMap(); + map.put("device_id", deviceId.toString()); + map.put("device_name", showName); + map.put("timestamp", timestamp); + map.put("msg_type", PushTypeEnum.offline_push.getKey()); + //推送记录 + pushList.setUserId(userId); + pushList.setDeviceId(deviceId); + pushList.setPhoneType(loginType.toLowerCase()); + pushList.setShowName(showName); + pushList.setPushType(PushTypeEnum.offline_push.getKey()); + pushList.setJpushStatus(neteaseBln ? "1" : "0"); + pushList.setPushTitle(title); + pushList.setPushContext(msg); + pushList.setCreateTime(new Date()); + pushList.setMcuCount(number1); + pushList.setModularCount(number2); + pushList.setRouterCount(number3); + pushList.setServerCount(number4); + pushList.setServerTryCount(number5); + userHelperI.save(pushList); + } + } + } + } + } + } catch (Exception e) { + e.printStackTrace(); + log.error("error msg:{}", e.toString()); + } + } +} diff --git a/src/main/java/com/ifish/socket/config/JobFactory.java b/src/main/java/com/ifish/socket/config/JobFactory.java new file mode 100644 index 0000000..976a079 --- /dev/null +++ b/src/main/java/com/ifish/socket/config/JobFactory.java @@ -0,0 +1,31 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.ifish.socket.config; + +import org.quartz.spi.TriggerFiredBundle; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.config.AutowireCapableBeanFactory; +import org.springframework.scheduling.quartz.AdaptableJobFactory; + +/** + * + * @author Administrator + */ +public class JobFactory extends AdaptableJobFactory { + + @Autowired + private AutowireCapableBeanFactory capableBeanFactory; + + @Override + protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception { + //调用父类的方法 + Object jobInstance = super.createJobInstance(bundle); + //进行注入 + capableBeanFactory.autowireBean(jobInstance); + return jobInstance; + } + +} diff --git a/src/main/java/com/ifish/socket/config/MinaServerHandler.java b/src/main/java/com/ifish/socket/config/MinaServerHandler.java new file mode 100644 index 0000000..e5824ce --- /dev/null +++ b/src/main/java/com/ifish/socket/config/MinaServerHandler.java @@ -0,0 +1,126 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.ifish.socket.config; + +import com.ifish.socket.model.JobGroup; +import com.ifish.util.IfishUtil; +import java.util.Date; +import java.util.concurrent.atomic.AtomicInteger; +import org.apache.mina.core.service.IoHandlerAdapter; +import org.apache.mina.core.session.IdleStatus; +import org.apache.mina.core.session.IoSession; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * + * @author Administrator + */ +public class MinaServerHandler extends IoHandlerAdapter { + + private final AtomicInteger atomicInt = new AtomicInteger(); + + private static Logger log = LoggerFactory.getLogger(MinaServerHandler.class); + + @Autowired + private SomeServer someServer; + @Autowired + private ScheduleJob scheduleJob; + + @Override + public void exceptionCaught(IoSession session, Throwable cause) throws Exception { + cause.printStackTrace(); + log.error("{}服务器-客户端session异常:{}", session.getRemoteAddress(), cause.toString()); + } + + /** + * 服务端接收消息处理 + */ + @Override + public void messageReceived(IoSession session, Object message) throws Exception { + someServer.doSome(session, message); + } + + /** + * 客户端连接的会话创建 + */ + @Override + public void sessionCreated(IoSession session) throws Exception { + log.info("{}:会话创建,在线数:{}", session.getRemoteAddress(), atomicInt.incrementAndGet()); + } + + /** + * 客户端连接关闭 + */ + @Override + public void sessionClosed(IoSession session) throws Exception { + log.warn("{}:会话关闭,连接数:{}", session.getRemoteAddress(), atomicInt.decrementAndGet()); + //旧连接关闭,获取设备mac地址 + String oldConnect = session.getRemoteAddress().toString(); + String macAddress = SomeServer.remoteAddress.get(oldConnect); + if (macAddress != null) { + //移除关闭的连接 + SomeServer.remoteAddress.remove(oldConnect); + //设备当前连接 + IoSession nowIoSession = SomeServer.sessions_cz.get(macAddress); + if (nowIoSession != null && nowIoSession.getRemoteAddress() != null) { + String nowConnect = nowIoSession.getRemoteAddress().toString(); + //如果当前连接和关闭的连接是相同的,说明设备没有重连,10分钟内还未连接,则推送通知用户 + if (nowConnect.equals(oldConnect)) { + offlinePush(macAddress); + } + } //当前没有连接,10分钟内还未连接,则推送通知用户 + else { + offlinePush(macAddress); + } + } + } + + /** + * 延时10分钟推送离线通知,10分钟内重新连接上则移除推送,详见SomeServer + * + * @param macAddress + */ + public void offlinePush(String macAddress) { + JobGroup jobGroup = new JobGroup(); + jobGroup.setJobName(macAddress); + jobGroup.setTriggerName(macAddress); + jobGroup.setMacAddress(macAddress); + //离线时间 + jobGroup.setTimestamp(IfishUtil.format(new Date())); + //10分钟后推送 + jobGroup.setStartTime(new Date(new Date().getTime() + 600000L)); + scheduleJob.addJob(jobGroup); + } + + /** + * 给客户端发送消息 + */ + @Override + public void messageSent(IoSession session, Object message) { + //System.out.println("messageSent"); + } + + /** + * 空闲超时,主动关闭客户端连接 + */ + @Override + public void sessionIdle(IoSession session, IdleStatus status) + throws Exception { + log.warn("空闲超时,服务器主动关闭连接{}", session.getRemoteAddress()); + session.close(true); + } + + /** + * 客户端连接打开 + */ + @Override + public void sessionOpened(IoSession session) throws Exception { + //log.info("session open!!!"); + } + +} diff --git a/src/main/java/com/ifish/socket/config/MyInetSocketAddress.java b/src/main/java/com/ifish/socket/config/MyInetSocketAddress.java new file mode 100644 index 0000000..2260d5e --- /dev/null +++ b/src/main/java/com/ifish/socket/config/MyInetSocketAddress.java @@ -0,0 +1,53 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.ifish.socket.config; + +import java.net.InetSocketAddress; + +/** + * + * @author Administrator + */ +public class MyInetSocketAddress { + + private InetSocketAddress socketAddress = null; + private String ip = ""; + private int serverPort = 9955; + + public MyInetSocketAddress(String ip, int serverPort) { + try { + this.ip = ip; + this.serverPort = serverPort; + socketAddress = new InetSocketAddress(ip, serverPort); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public InetSocketAddress getSocketAddress() { + return socketAddress; + } + + public void setSocketAddress(InetSocketAddress socketAddress) { + this.socketAddress = socketAddress; + } + + public String getIp() { + return ip; + } + + public void setIp(String ip) { + this.ip = ip; + } + + public int getServerPort() { + return serverPort; + } + + public void setServerPort(int serverPort) { + this.serverPort = serverPort; + } +} diff --git a/src/main/java/com/ifish/socket/config/ScheduleJob.java b/src/main/java/com/ifish/socket/config/ScheduleJob.java new file mode 100644 index 0000000..684dfaa --- /dev/null +++ b/src/main/java/com/ifish/socket/config/ScheduleJob.java @@ -0,0 +1,114 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.ifish.socket.config; + +import com.ifish.socket.model.JobGroup; +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; +import org.quartz.JobBuilder; +import org.quartz.JobDetail; +import org.quartz.JobKey; +import org.quartz.Scheduler; +import org.quartz.SchedulerException; +import org.quartz.SchedulerFactory; +import org.quartz.Trigger; +import org.quartz.TriggerBuilder; +import org.quartz.TriggerKey; +import org.quartz.impl.StdSchedulerFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * + * @author Administrator + */ +public class ScheduleJob { + + private static Logger log = LoggerFactory.getLogger(ScheduleJob.class); + //调度 + private Scheduler scheduler = null; + + @Autowired + private JobFactory jobFactory; + + @PostConstruct //初始化方法的注解方式 等同与init-method=init + public void init() { + try { + //调度任务开启 + SchedulerFactory sf = new StdSchedulerFactory(); + scheduler = sf.getScheduler(); + //实现job中注入bean + scheduler.setJobFactory(jobFactory); + scheduler.start(); + } catch (SchedulerException e) { + log.error("start scheduler error:{}", e.toString()); + } + } + + /** + * 添加任务 + * + * @param jobGroup + * @throws SchedulerException + */ + public void addJob(JobGroup jobGroup) { + try { + log.info("添加任务:{}.{}", jobGroup.getJobName(), jobGroup.getTriggerName()); + //任务 + JobDetail jobDetail = JobBuilder.newJob(ExecuteJob.class) + .usingJobData("macAddress", jobGroup.getMacAddress()) + .usingJobData("timestamp", jobGroup.getTimestamp()) + .withIdentity(jobGroup.getJobName(), jobGroup.getJobName()) + .build(); + //触发器 + Trigger trigger = TriggerBuilder.newTrigger() + .withIdentity(jobGroup.getTriggerName(), jobGroup.getTriggerGroup()) + .startAt(jobGroup.getStartTime()).build(); + //添加触发器 + scheduler.scheduleJob(jobDetail, trigger); + } catch (SchedulerException e) { + log.error("addJob error【macAddress:{},jobGroup:{},triggerGroup:{},errMsg:{}】", jobGroup.getMacAddress(), jobGroup.getJobName(), jobGroup.getTriggerGroup(), e.toString()); + } + } + + /** + * 移除任务 + * + * @param jobGroup + * @throws SchedulerException + */ + public void deleteJob(JobGroup jobGroup) { + try { + log.info("移除任务:{}.{}", jobGroup.getJobName(), jobGroup.getTriggerName()); + //触发器 + TriggerKey triggerKey = TriggerKey.triggerKey(jobGroup.getTriggerName(), jobGroup.getTriggerGroup()); + //停止触发器 + scheduler.pauseTrigger(triggerKey); + //移除触发器 + scheduler.unscheduleJob(triggerKey); + //任务 + JobKey jobKey = JobKey.jobKey(jobGroup.getJobName(), jobGroup.getJobGroup()); + //删除任务 + scheduler.deleteJob(jobKey); + } catch (SchedulerException e) { + log.error("deleteJob error【macAddress:{},jobGroup:{},triggerGroup:{},errMsg:{}】", jobGroup.getMacAddress(), jobGroup.getJobName(), jobGroup.getTriggerGroup(), e.toString()); + } + } + + /** + * 关闭调度任务 + */ + @PreDestroy //销毁方法的注解方式 等同于destory-method=destory222 + public void shutdown() { + try { + //关闭任务 + scheduler.shutdown(); + } catch (SchedulerException e) { + log.error("shutdown scheduler error:{}", e.toString()); + } + } +} diff --git a/src/main/java/com/ifish/socket/config/SomeServer.java b/src/main/java/com/ifish/socket/config/SomeServer.java new file mode 100644 index 0000000..3deb2eb --- /dev/null +++ b/src/main/java/com/ifish/socket/config/SomeServer.java @@ -0,0 +1,364 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.ifish.socket.config; + +import com.ifish.bean.LoginRecord; +import com.ifish.bean.Tbl_Device; +import com.ifish.bean.Tbl_Device_Statistics; +import com.ifish.bean.Tbl_Device_User; +import com.ifish.bean.Tbl_Push_List; +import com.ifish.bean.Tbl_User; +import com.ifish.enums.PushTypeEnum; +import com.ifish.helper.DeviceHelperI; +import com.ifish.helper.UserHelperI; +import com.ifish.socket.model.JobGroup; +import com.ifish.socket.model.OrderModel; +import com.ifish.socket.model.receive.APPLoginResponseLength26; +import com.ifish.socket.model.receive.DeviceLoginResponseLength25; +import com.ifish.socket.model.receive.ServerHeartbeatLength17; +import com.ifish.socket.model.send.DeviceLoginContextLength24; +import com.ifish.socket.model.send.ModelVersionLength22; +import com.ifish.socket.model.send.ModelVersionLength38; +import com.ifish.socket.model.send.TemperatureAlarmLength22; +import com.ifish.socket.model.send.TemperatureSwitchLength22; +import com.ifish.socket.model.send.UpgradeVersionLength78; +import com.ifish.util.ByteUtil; +import com.ifish.util.IfishUtil; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArraySet; +import org.apache.mina.core.session.IoSession; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * + * @author Administrator + */ +public class SomeServer { + + @Autowired + private UserHelperI userHelperI; + @Autowired + private DeviceHelperI deviceHelperI; + @Autowired + private ScheduleJob scheduleJob; +// @Autowired +// private NeteaseIM neteaseIM; + + //是否回复心跳 + public static boolean isReplay = true; + + private static Logger log = LoggerFactory.getLogger(SomeServer.class); + public static ConcurrentHashMap remoteAddress = new ConcurrentHashMap(); + public static ConcurrentHashMap sessions_cz = new ConcurrentHashMap(); + private ConcurrentHashMap> sessions_sjs = new ConcurrentHashMap>(); + + /** + * 过滤收到的数据 服务器收到数据,进行协议类型匹配,如果不匹配,则不执行相应操作。 + * + * @param session + * @param message + */ + public void doSome(IoSession session, Object message) { + //通配字符串 + if (message instanceof byte[]) { + byte[] bytes = (byte[]) message; + //长度 + int length = bytes[14] & 0xff; + //mac地址 + byte[] srcByte = new byte[6]; + for (int i = 2; i < 8; i++) { + srcByte[i - 2] = bytes[i]; + } + String strSrc = ByteUtil.bytesToHexString(srcByte); + //心跳 + if (length >= 28) { + sendToApp(session, strSrc, message); + } else { + sendToDevice(session, strSrc, message); + } + } //设置设备温度报警 + else if (message instanceof TemperatureSwitchLength22) { + TemperatureSwitchLength22 receive = (TemperatureSwitchLength22) message; + String strDest = ByteUtil.bytesToHexString(receive.getDest()); + sendToDevice(session, strDest, message); + try { + Integer Onoff = receive.getOnoff() & 0xff; + if (Onoff == 1 || Onoff == 0) { + deviceHelperI.updateWarnOnoff(strDest, Onoff.toString()); + } else { + log.error("error Onoff:{},macAddress:{}", Onoff, strDest); + } + } catch (Exception e) { + log.warn("set OrderSetWarnModel error:macAddress:{},error msg:{}", strDest, e.toString()); + } + } //设备温度报警 + else if (message instanceof TemperatureAlarmLength22) { + TemperatureAlarmLength22 receive = (TemperatureAlarmLength22) message; + String strSrc = ByteUtil.bytesToHexString(receive.getSrc()); + //推送类型 + Integer warnType = receive.getWarn_type() & 0xff; + if (warnType == 0) { + pushNotifcation(strSrc, PushTypeEnum.wendu_warn.getValue(), "温度达到" + (float) receive.getWendu() / 10f + "℃,已低于" + (float) receive.getWarn_wendu() / 10f + "℃,请及时查看!"); + } else if (warnType == 1) { + pushNotifcation(strSrc, PushTypeEnum.wendu_warn.getValue(), "温度达到" + (float) receive.getWendu() / 10f + "℃,已高于" + (float) receive.getWarn_wendu() / 10f + "℃,请及时查看!"); + } else { + log.error("error warn_type:{},macAddress:{}", warnType, strSrc); + } + } //设备登录服务器 + else if (message instanceof DeviceLoginContextLength24) { + DeviceLoginContextLength24 receive = (DeviceLoginContextLength24) message; + byte[] srcBytes = receive.getSrc(); + byte[] destBytes = receive.getDest(); + String strSrc = ByteUtil.bytesToHexString(srcBytes); + //服务器回复设备登录请求 + DeviceLoginResponseLength25 model = OrderModel.DeviceLoginResponseLength25(destBytes, srcBytes); + //直接往本次session写回去 + session.write(model); + //保存连接 + sessions_cz.put(strSrc, session); + remoteAddress.put(session.getRemoteAddress().toString(), strSrc); + //设备重新连接上,则移除延时推送的任务 + JobGroup jobGroup = new JobGroup(); + jobGroup.setJobName(strSrc); + jobGroup.setTriggerName(strSrc); + scheduleJob.deleteJob(jobGroup); + try { + //更新设备信息 + deviceHelperI.update(receive); +// if(device==null){ +// String HexDump = ByteUtil.toHex(receive.getType())+ByteUtil.toHex(receive.getCheck_code())+ByteUtil.bytesToHexString(receive.getSrc())+ByteUtil.bytesToHexString(destBytes)+ByteUtil.toHex(receive.getRemote_len())+ByteUtil.toHex(receive.getVendor())+ByteUtil.toHex(receive.getHardware_type())+ByteUtil.toHex(receive.getVersion())+ByteUtil.bytesToHexString(receive.getLogin_ip())+ByteUtil.bytesToHexString(receive.getCrc16_code()); +// log.error("HexDump:{}",HexDump); +// log.error("factoryCode or hardwareType not exist:macAddress:{},factoryCode:{},hardwareType:{}",strSrc,ByteUtil.toHex(receive.getVendor()),ByteUtil.toHex(receive.getHardware_type())); +// } +// else{ + //记录登录时间 + LoginRecord loginRecord = new LoginRecord(); + loginRecord.setMacAddress(strSrc); + loginRecord.setLoginTime(new Date()); + deviceHelperI.save(loginRecord); +// } + } catch (Exception e) { + log.warn("save device login error:macAddress:{},error msg:{}", strSrc, e.toString()); + } + } //APP登录服务器 + else if (message instanceof APPLoginResponseLength26) { + APPLoginResponseLength26 receive = (APPLoginResponseLength26) message; + byte[] srcBytes = receive.getSrc(); + byte[] destBytes = receive.getDest(); + String strDest = ByteUtil.bytesToHexString(receive.getDest()); + //设备是否在线 + IoSession tmpSession = sessions_cz.get(strDest); + //1表示在线,0表示离线 + byte login_status = 0; + if (tmpSession != null && tmpSession.getRemoteAddress() != null) { + login_status = 1; + } else { + login_status = 0; + } + //服务器回复APP登录请求 + APPLoginResponseLength26 model = OrderModel.APPLoginResponseLength26(destBytes, srcBytes, login_status); + session.write(model); + //保存APP连接 + CopyOnWriteArraySet sessionSet = sessions_sjs.get(strDest); + if (sessionSet == null) { + sessionSet = new CopyOnWriteArraySet(); + sessionSet.add(session); + sessions_sjs.put(strDest, sessionSet); + } else { + for (IoSession ioSession : sessionSet) { + if (ioSession == null || ioSession.getRemoteAddress() == null) { + sessionSet.remove(ioSession); + } + } + sessionSet.add(session); + } + } //模块版本号 + else if (message instanceof ModelVersionLength22) { + ModelVersionLength22 receive = (ModelVersionLength22) message; + byte[] macBytes = receive.getSrc(); + String strSrc = ByteUtil.bytesToHexString(macBytes); + try { + //查找设备 + Tbl_Device device = deviceHelperI.getUniqueByProperty("macAddress", strSrc); + Tbl_Device_Statistics device_Statistics = null; + if (device != null) { + //是否升级 + if (device_Statistics.getIsUpgrade().equals("1")) { + device_Statistics.setIsUpgrade("0"); + device_Statistics.setUpgradeTime(new Date()); + //升级版本 + int upgradeVersion = Integer.valueOf(device_Statistics.getUpgradeVersion()); + UpgradeVersionLength78 model = OrderModel.UpgradeVersionLength78(macBytes, upgradeVersion); + session.write(model); + } + //记录参数 + device_Statistics.setSdkVersion(String.valueOf(receive.getVersion() & 0xff)); + device_Statistics.setSdkTime(IfishUtil.StrToDate(receive.getYear() + "-" + (receive.getMonth() & 0xff) + "-" + (receive.getDay() & 0xff))); + deviceHelperI.update(device); + } + } catch (Exception e) { + log.warn("save device sdk version16 error:macAddress:{},error msg:{}", strSrc, e.toString()); + } + } //模块版本号 + else if (message instanceof ModelVersionLength38) { + ModelVersionLength38 receive = (ModelVersionLength38) message; + byte[] macBytes = receive.getSrc(); + String strSrc = ByteUtil.bytesToHexString(macBytes); + try { + //查找设备 + Tbl_Device device = deviceHelperI.getUniqueByProperty("macAddress", strSrc); + Tbl_Device_Statistics device_Statistics = null; + if (device != null) { + //是否升级 + if (device_Statistics.getIsUpgrade().equals("1")) { + device_Statistics.setIsUpgrade("0"); + device_Statistics.setUpgradeTime(new Date()); + //升级版本 + int upgradeVersion = Integer.parseInt(device_Statistics.getUpgradeVersion()); + UpgradeVersionLength78 model = OrderModel.UpgradeVersionLength78(macBytes, upgradeVersion); + session.write(model); + } + //记录参数 + device_Statistics.setSdkVersion(String.valueOf(receive.getVersion() & 0xff)); + device_Statistics.setSdkTime(IfishUtil.StrToDate(receive.getYear() + "-" + (receive.getMonth() & 0xff) + "-" + (receive.getDay() & 0xff))); + device_Statistics.setMcuCount(receive.getNumber1()); + device_Statistics.setModuleCount(receive.getNumber2()); + device_Statistics.setRouterCount(receive.getNumber3()); + device_Statistics.setServerCount(receive.getNumber4()); + device_Statistics.setServerTryCount(receive.getNumber5()); + deviceHelperI.update(device); + } + } catch (Exception e) { + log.warn("save device sdk version17 error:macAddress:{},error msg:{}", strSrc, e.toString()); + } + } + } + + /** + * 发送数据APP + * + * @param dest 目的地 + * @param message 发送对象 + * @return + */ + public void sendToApp(IoSession session, String strDest, Object message) { + //回复设备 + if (isReplay) { + ServerHeartbeatLength17 model = OrderModel.ServerHeartbeatLength17(strDest); + session.write(model); + } + //转发给APP + CopyOnWriteArraySet sessionSet = sessions_sjs.get(strDest); + if (sessionSet != null && sessionSet.size() > 0) { + for (IoSession sj_session : sessionSet) { + if (sj_session != null && sj_session.getRemoteAddress() != null) { + sj_session.write(message); + } else { + sessionSet.remove(sj_session); + } + } + } + } + + /** + * 发送数据给设备 + * + * @param dest 目的地 + * @param message 发送对象 + * @return + */ + public void sendToDevice(IoSession session, String strDest, Object message) { + IoSession cz_session = sessions_cz.get(strDest); + if (cz_session != null && cz_session.getRemoteAddress() != null) { + cz_session.write(message); + } else { + //设备离线 + byte[] bytes = ByteUtil.hexStringToBytes2(strDest); + APPLoginResponseLength26 model = OrderModel.APPLoginResponseLength26(bytes, bytes, (byte) 0); + session.write(model); + } + } + + /** + * 温度预警推送 + * + * @param strSrc + * @param title + * @param contont + */ + public void pushNotifcation(String strSrc, String title, String contont) { + try { + //Device device = deviceService.getUniqueByProperty("macAddress", strSrc); + Tbl_Device device = deviceHelperI.getUniqueByProperty("macAddress", strSrc); + Tbl_Device_Statistics device_Statistics = null; + if (device != null) { + Integer deviceId = device.getDeviceId(); + //是否开启预警 + String onOff = device.getOnOff(); + if (onOff != null && onOff.equals("1")) { + //绑定设备的用户 + List list = deviceHelperI.getListByProperty(device.getDeviceId()); + for (Tbl_Device_User deviceUser : list) { + Integer userId = deviceUser.getUserId(); + String showName = deviceUser.getShowName(); + String timestamp = IfishUtil.format(new Date()); + String msg = "【" + title + "】你的水族箱“" + showName + "”在" + timestamp + contont; + Tbl_User user = userHelperI.findById(userId); + if (user != null) { + //云信推送 + boolean neteaseBln = false; + //极光推送 + boolean jpushIosBln = false; + boolean jpushAndroidBln = false; + //发送云信消息 +// Map neteaseMap = neteaseIM.sendMsg("ifish", userId.toString(), msg); +// if (neteaseMap != null) { +// neteaseBln = neteaseMap.get("code").equals(NeteaseEnum.status200.getKey()); +// } + String loginType = user.getLoginType(); + if (loginType != null) { + //推送记录 + Tbl_Push_List pushList = new Tbl_Push_List(); + Map map = new HashMap(); + map.put("device_id", deviceId.toString()); + map.put("device_name", showName); + map.put("timestamp", timestamp); + map.put("msg_type", PushTypeEnum.wendu_warn.getKey()); + //记录推送 + if (jpushIosBln || jpushAndroidBln || neteaseBln) { + pushList.setUserId(userId); + pushList.setDeviceId(deviceId); + pushList.setPhoneType(loginType.toLowerCase()); + pushList.setShowName(showName); + pushList.setPushType(PushTypeEnum.wendu_warn.getKey()); + pushList.setPushTitle(title); + pushList.setPushContext(msg); + pushList.setJpushStatus(neteaseBln ? "1" : "0"); + pushList.setCreateTime(new Date()); + pushList.setMcuCount(device_Statistics.getMcuCount()); + pushList.setModularCount(device_Statistics.getModuleCount()); + pushList.setRouterCount(device_Statistics.getRouterCount()); + pushList.setServerCount(device_Statistics.getServerCount()); + pushList.setServerTryCount(device_Statistics.getServerTryCount()); + userHelperI.save(pushList); + } + } + } + } + + } + } + } catch (Exception e) { + log.warn("pushNotifcation error:macAddress:{},error msg:{}", strSrc, e.toString()); + } + } +} diff --git a/src/main/java/com/ifish/socket/model/DeviceResponseLength99.java b/src/main/java/com/ifish/socket/model/DeviceResponseLength99.java new file mode 100644 index 0000000..f83b8f8 --- /dev/null +++ b/src/main/java/com/ifish/socket/model/DeviceResponseLength99.java @@ -0,0 +1,500 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.ifish.socket.model; + +import java.io.Serializable; + +/** + * + * @author Administrator + */ +public class DeviceResponseLength99 extends HeadModel implements Serializable { + + private static final long serialVersionUID = -2334084294686976805L; + //工作模式 + private byte work_model; + /** + * *******灯和开关(10字节)********* + */ + //灯编号1 + private byte number1; + //灯编号2 + private byte number2; + //灯编号3 + private byte number3; + //灯编号4 + private byte number4; + //灯编号5 + private byte number5; + //灯1开关 + private byte onoff1; + //灯2开关 + private byte onoff2; + //灯3开关 + private byte onoff3; + //灯4开关 + private byte onoff4; + //灯5开关 + private byte onoff5; + /** + * *******定时器编号(10字节)灯1对应定时器1-2,灯2对应定时器3-4,灯3对应定时器5-6********* + */ + //定时器1编号 + private byte timer_number1; + //定时器2编号 + private byte timer_number2; + //定时器3编号 + private byte timer_number3; + //定时器4编号 + private byte timer_number4; + //定时器5编号 + private byte timer_number5; + //定时器6编号 + private byte timer_number6; + //定时器7编号 + private byte timer_number7; + //定时器8编号 + private byte timer_number8; + //定时器9编号 + private byte timer_number9; + //定时器10编号 + private byte timer_number10; + /** + * *******定时器开关(10字节)********* + */ + //定时器1开关 + private byte timer_onoff1; + //定时器2开关 + private byte timer_onoff2; + //定时器3开关 + private byte timer_onoff3; + //定时器4开关 + private byte timer_onoff4; + //定时器5开关 + private byte timer_onoff5; + //定时器6开关 + private byte timer_onoff6; + //定时器7开关 + private byte timer_onoff7; + //定时器8开关 + private byte timer_onoff8; + //定时器9开关 + private byte timer_onoff9; + //定时器10开关 + private byte timer_onoff10; + /** + * *******定时器时间(40字节)********* + */ + //定时器1时间 + private byte[] timer_time1 = new byte[4]; + //定时器2时间 + private byte[] timer_time2 = new byte[4]; + //定时器3时间 + private byte[] timer_time3 = new byte[4]; + //定时器4时间 + private byte[] timer_time4 = new byte[4]; + //定时器5时间 + private byte[] timer_time5 = new byte[4]; + //定时器6时间 + private byte[] timer_time6 = new byte[4]; + //定时器7时间 + private byte[] timer_time7 = new byte[4]; + //定时器8时间 + private byte[] timer_time8 = new byte[4]; + //定时器9时间 + private byte[] timer_time9 = new byte[4]; + //定时器10时间 + private byte[] timer_time10 = new byte[4]; + //温度 + private short wendu; + //ph + private short ph_value; + //湿度 + private short shidu; + //报警开关,0关,1开 + private byte warn_onoff; + //最低报警温度 + private short low_wendu; + //最高报警温度 + private short high_wendu; + + public short getWendu() { + return wendu; + } + + public void setWendu(short wendu) { + this.wendu = wendu; + } + + public byte getWork_model() { + return work_model; + } + + public void setWork_model(byte work_model) { + this.work_model = work_model; + } + + public byte getNumber1() { + return number1; + } + + public void setNumber1(byte number1) { + this.number1 = number1; + } + + public byte getOnoff1() { + return onoff1; + } + + public void setOnoff1(byte onoff1) { + this.onoff1 = onoff1; + } + + public byte getNumber2() { + return number2; + } + + public void setNumber2(byte number2) { + this.number2 = number2; + } + + public byte getOnoff2() { + return onoff2; + } + + public void setOnoff2(byte onoff2) { + this.onoff2 = onoff2; + } + + public byte getNumber3() { + return number3; + } + + public void setNumber3(byte number3) { + this.number3 = number3; + } + + public byte getOnoff3() { + return onoff3; + } + + public void setOnoff3(byte onoff3) { + this.onoff3 = onoff3; + } + + public byte getTimer_number1() { + return timer_number1; + } + + public void setTimer_number1(byte timer_number1) { + this.timer_number1 = timer_number1; + } + + public byte getTimer_number2() { + return timer_number2; + } + + public void setTimer_number2(byte timer_number2) { + this.timer_number2 = timer_number2; + } + + public byte getTimer_number3() { + return timer_number3; + } + + public void setTimer_number3(byte timer_number3) { + this.timer_number3 = timer_number3; + } + + public byte getTimer_number4() { + return timer_number4; + } + + public void setTimer_number4(byte timer_number4) { + this.timer_number4 = timer_number4; + } + + public byte getTimer_number5() { + return timer_number5; + } + + public void setTimer_number5(byte timer_number5) { + this.timer_number5 = timer_number5; + } + + public byte getTimer_number6() { + return timer_number6; + } + + public void setTimer_number6(byte timer_number6) { + this.timer_number6 = timer_number6; + } + + public byte getTimer_number7() { + return timer_number7; + } + + public void setTimer_number7(byte timer_number7) { + this.timer_number7 = timer_number7; + } + + public byte getTimer_number8() { + return timer_number8; + } + + public void setTimer_number8(byte timer_number8) { + this.timer_number8 = timer_number8; + } + + public byte getTimer_number9() { + return timer_number9; + } + + public void setTimer_number9(byte timer_number9) { + this.timer_number9 = timer_number9; + } + + public byte[] getTimer_time1() { + return timer_time1; + } + + public void setTimer_time1(byte[] timer_time1) { + this.timer_time1 = timer_time1; + } + + public byte[] getTimer_time2() { + return timer_time2; + } + + public void setTimer_time2(byte[] timer_time2) { + this.timer_time2 = timer_time2; + } + + public byte[] getTimer_time3() { + return timer_time3; + } + + public void setTimer_time3(byte[] timer_time3) { + this.timer_time3 = timer_time3; + } + + public byte[] getTimer_time4() { + return timer_time4; + } + + public void setTimer_time4(byte[] timer_time4) { + this.timer_time4 = timer_time4; + } + + public byte[] getTimer_time5() { + return timer_time5; + } + + public void setTimer_time5(byte[] timer_time5) { + this.timer_time5 = timer_time5; + } + + public byte[] getTimer_time6() { + return timer_time6; + } + + public void setTimer_time6(byte[] timer_time6) { + this.timer_time6 = timer_time6; + } + + public byte[] getTimer_time7() { + return timer_time7; + } + + public void setTimer_time7(byte[] timer_time7) { + this.timer_time7 = timer_time7; + } + + public byte[] getTimer_time8() { + return timer_time8; + } + + public void setTimer_time8(byte[] timer_time8) { + this.timer_time8 = timer_time8; + } + + public byte[] getTimer_time9() { + return timer_time9; + } + + public void setTimer_time9(byte[] timer_time9) { + this.timer_time9 = timer_time9; + } + + public short getPh_value() { + return ph_value; + } + + public void setPh_value(short ph_value) { + this.ph_value = ph_value; + } + + public short getShidu() { + return shidu; + } + + public void setShidu(short shidu) { + this.shidu = shidu; + } + + public byte getWarn_onoff() { + return warn_onoff; + } + + public void setWarn_onoff(byte warn_onoff) { + this.warn_onoff = warn_onoff; + } + + public short getLow_wendu() { + return low_wendu; + } + + public void setLow_wendu(short low_wendu) { + this.low_wendu = low_wendu; + } + + public short getHigh_wendu() { + return high_wendu; + } + + public void setHigh_wendu(short high_wendu) { + this.high_wendu = high_wendu; + } + + public byte getOnoff4() { + return onoff4; + } + + public void setOnoff4(byte onoff4) { + this.onoff4 = onoff4; + } + + public byte getOnoff5() { + return onoff5; + } + + public void setOnoff5(byte onoff5) { + this.onoff5 = onoff5; + } + + public byte getTimer_number10() { + return timer_number10; + } + + public void setTimer_number10(byte timer_number10) { + this.timer_number10 = timer_number10; + } + + public byte[] getTimer_time10() { + return timer_time10; + } + + public void setTimer_time10(byte[] timer_time10) { + this.timer_time10 = timer_time10; + } + + public byte getNumber4() { + return number4; + } + + public void setNumber4(byte number4) { + this.number4 = number4; + } + + public byte getNumber5() { + return number5; + } + + public void setNumber5(byte number5) { + this.number5 = number5; + } + + public byte getTimer_onoff1() { + return timer_onoff1; + } + + public void setTimer_onoff1(byte timer_onoff1) { + this.timer_onoff1 = timer_onoff1; + } + + public byte getTimer_onoff2() { + return timer_onoff2; + } + + public void setTimer_onoff2(byte timer_onoff2) { + this.timer_onoff2 = timer_onoff2; + } + + public byte getTimer_onoff3() { + return timer_onoff3; + } + + public void setTimer_onoff3(byte timer_onoff3) { + this.timer_onoff3 = timer_onoff3; + } + + public byte getTimer_onoff4() { + return timer_onoff4; + } + + public void setTimer_onoff4(byte timer_onoff4) { + this.timer_onoff4 = timer_onoff4; + } + + public byte getTimer_onoff5() { + return timer_onoff5; + } + + public void setTimer_onoff5(byte timer_onoff5) { + this.timer_onoff5 = timer_onoff5; + } + + public byte getTimer_onoff6() { + return timer_onoff6; + } + + public void setTimer_onoff6(byte timer_onoff6) { + this.timer_onoff6 = timer_onoff6; + } + + public byte getTimer_onoff7() { + return timer_onoff7; + } + + public void setTimer_onoff7(byte timer_onoff7) { + this.timer_onoff7 = timer_onoff7; + } + + public byte getTimer_onoff8() { + return timer_onoff8; + } + + public void setTimer_onoff8(byte timer_onoff8) { + this.timer_onoff8 = timer_onoff8; + } + + public byte getTimer_onoff9() { + return timer_onoff9; + } + + public void setTimer_onoff9(byte timer_onoff9) { + this.timer_onoff9 = timer_onoff9; + } + + public byte getTimer_onoff10() { + return timer_onoff10; + } + + public void setTimer_onoff10(byte timer_onoff10) { + this.timer_onoff10 = timer_onoff10; + } + +} diff --git a/src/main/java/com/ifish/socket/model/HeadModel.java b/src/main/java/com/ifish/socket/model/HeadModel.java new file mode 100644 index 0000000..d0d0b44 --- /dev/null +++ b/src/main/java/com/ifish/socket/model/HeadModel.java @@ -0,0 +1,80 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.ifish.socket.model; + +import java.io.Serializable; + +/** + * 所有请求的数据包头,15字节数据包头+CRC16校验码2字节 + * + * @author guogw + * + */ +public class HeadModel implements Serializable { + + private static final long serialVersionUID = 7351497431272185642L; + //发送消息类型,1回复类型,0发送类型 + private byte type; + //功能码 + private byte check_code; + //6位为发送端的mac地址,高16位为0001 + private byte[] src = new byte[6]; + //6位为接收端的mac地址,高16位为0001 + private byte[] dest = new byte[6]; + //整个包长度 + private byte remote_len; + //CRC16校验码 + private byte[] crc16_code = new byte[2]; + + public byte getType() { + return type; + } + + public void setType(byte type) { + this.type = type; + } + + public byte getCheck_code() { + return check_code; + } + + public void setCheck_code(byte check_code) { + this.check_code = check_code; + } + + public void setRemote_len(byte remote_len) { + this.remote_len = remote_len; + } + + public byte[] getSrc() { + return src; + } + + public void setSrc(byte[] src) { + this.src = src; + } + + public byte[] getDest() { + return dest; + } + + public void setDest(byte[] dest) { + this.dest = dest; + } + + public byte getRemote_len() { + return remote_len; + } + + public byte[] getCrc16_code() { + return crc16_code; + } + + public void setCrc16_code(byte[] crc16_code) { + this.crc16_code = crc16_code; + } + +} diff --git a/src/main/java/com/ifish/socket/model/JobGroup.java b/src/main/java/com/ifish/socket/model/JobGroup.java new file mode 100644 index 0000000..35fbc72 --- /dev/null +++ b/src/main/java/com/ifish/socket/model/JobGroup.java @@ -0,0 +1,107 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.ifish.socket.model; + +import java.util.Date; + +/** + * + * @author Administrator + */ +public class JobGroup { + + /** + * 任务名称 * + */ + private String jobName; + + /** + * 任务分组 * + */ + private String jobGroup = "offlinePushGroup"; + + /** + * 触发器名称 * + */ + private String triggerName; + + /** + * 触发器分组 * + */ + private String triggerGroup = "offlinePushGroup"; + + /** + * mac地址 * + */ + private String macAddress; + + /** + * 执行时间 * + */ + private Date startTime; + + /** + * 离线时间 * + */ + private String timestamp; + + public String getJobName() { + return jobName; + } + + public void setJobName(String jobName) { + this.jobName = jobName; + } + + public String getJobGroup() { + return jobGroup; + } + + public void setJobGroup(String jobGroup) { + this.jobGroup = jobGroup; + } + + public String getTriggerName() { + return triggerName; + } + + public void setTriggerName(String triggerName) { + this.triggerName = triggerName; + } + + public String getTriggerGroup() { + return triggerGroup; + } + + public void setTriggerGroup(String triggerGroup) { + this.triggerGroup = triggerGroup; + } + + public String getMacAddress() { + return macAddress; + } + + public void setMacAddress(String macAddress) { + this.macAddress = macAddress; + } + + public Date getStartTime() { + return startTime; + } + + public void setStartTime(Date startTime) { + this.startTime = startTime; + } + + public String getTimestamp() { + return timestamp; + } + + public void setTimestamp(String timestamp) { + this.timestamp = timestamp; + } + +} diff --git a/src/main/java/com/ifish/socket/model/OrderModel.java b/src/main/java/com/ifish/socket/model/OrderModel.java new file mode 100644 index 0000000..178693a --- /dev/null +++ b/src/main/java/com/ifish/socket/model/OrderModel.java @@ -0,0 +1,101 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.ifish.socket.model; + +import com.ifish.socket.model.receive.APPLoginResponseLength26; +import com.ifish.socket.model.receive.DeviceLoginResponseLength25; +import com.ifish.socket.model.receive.ServerHeartbeatLength17; +import com.ifish.socket.model.send.UpgradeVersionLength78; +import com.ifish.util.ByteUtil; + +/** + * + * @author Administrator + */ +public class OrderModel { + + /** + * 服务器回复设备登录请求指令 + * + * @param src + * @param dest + * @return + */ + public final static DeviceLoginResponseLength25 DeviceLoginResponseLength25(byte[] src, byte[] dest) { + DeviceLoginResponseLength25 model = new DeviceLoginResponseLength25(); + model.setType((byte) 1); + model.setCheck_code((byte) 1); + model.setSrc(src); + model.setDest(dest); + model.setRemote_len((byte) 25); + model.setSysTime(ByteUtil.hexSysTime()); + return model; + } + + /** + * 服务器回复APP登录请求指令 + * + * @param src + * @param dest + * @return + */ + public final static APPLoginResponseLength26 APPLoginResponseLength26(byte[] src, byte[] dest, byte login_status) { + APPLoginResponseLength26 model = new APPLoginResponseLength26(); + model.setType((byte) 1); + model.setSrc(src); + model.setDest(dest); + model.setRemote_len((byte) 26); + model.setLogin_status(login_status); + model.setSysTime(System.currentTimeMillis()); + return model; + } + + /** + * 获取查询命令 + * + * @param type + * @param macAddress + * @return + */ + public final static ServerHeartbeatLength17 ServerHeartbeatLength17(String macAddress) { + ServerHeartbeatLength17 model = new ServerHeartbeatLength17(); + //15字节数据包头 + model.setType((byte) 1); + model.setCheck_code((byte) 8); + model.setSrc(ByteUtil.hexStringToBytes2(macAddress)); + model.setDest(ByteUtil.hexStringToBytes2(macAddress)); + model.setRemote_len((byte) 17); + return model; + } + + /** + * 升级 + * + * @param type + * @param macAddress + * @param timer_number + * @param time + * @return + */ + public final static UpgradeVersionLength78 UpgradeVersionLength78(byte[] macAddress, int version) { + UpgradeVersionLength78 model = new UpgradeVersionLength78(); + //15字节数据包头 + model.setType((byte) 0); + model.setCheck_code((byte) 15); + model.setSrc(macAddress); + model.setDest(macAddress); + model.setRemote_len((byte) 78); + model.setVersion((byte) version); + //地址 + byte[] addressByte = model.getAddress(); + byte[] bytes = "/download/sdk/".getBytes(); + for (int i = 0; i < bytes.length; i++) { + addressByte[i] = bytes[i]; + } + model.setAddress(addressByte); + return model; + } +} diff --git a/src/main/java/com/ifish/socket/model/receive/APPLoginResponseLength26.java b/src/main/java/com/ifish/socket/model/receive/APPLoginResponseLength26.java new file mode 100644 index 0000000..ce51e39 --- /dev/null +++ b/src/main/java/com/ifish/socket/model/receive/APPLoginResponseLength26.java @@ -0,0 +1,41 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.ifish.socket.model.receive; + +import com.ifish.socket.model.HeadModel; +import java.io.Serializable; + +/** + * 服务器回复手机端的登录请求26字节(17字节头+9字节) + * + * @author guogw + * + */ +public class APPLoginResponseLength26 extends HeadModel implements Serializable { + + private static final long serialVersionUID = 656305903606196100L; + //0表示设备还未连接服务器,1表示已连接 + private byte login_status; + //系统时间 + private long sysTime; + + public byte getLogin_status() { + return login_status; + } + + public void setLogin_status(byte login_status) { + this.login_status = login_status; + } + + public long getSysTime() { + return sysTime; + } + + public void setSysTime(long sysTime) { + this.sysTime = sysTime; + } + +} diff --git a/src/main/java/com/ifish/socket/model/receive/DeviceLoginResponseLength25.java b/src/main/java/com/ifish/socket/model/receive/DeviceLoginResponseLength25.java new file mode 100644 index 0000000..7f4d0bb --- /dev/null +++ b/src/main/java/com/ifish/socket/model/receive/DeviceLoginResponseLength25.java @@ -0,0 +1,31 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.ifish.socket.model.receive; + +import com.ifish.socket.model.HeadModel; +import java.io.Serializable; + +/** + * 服务器回复插座的登录请求25字节(15字节头+8字节时间+2字节) + * + * @author guogw + * + */ +public class DeviceLoginResponseLength25 extends HeadModel implements Serializable { + + private static final long serialVersionUID = 1L; + //系统时间 + private byte[] sysTime = new byte[8]; + + public byte[] getSysTime() { + return sysTime; + } + + public void setSysTime(byte[] sysTime) { + this.sysTime = sysTime; + } + +} diff --git a/src/main/java/com/ifish/socket/model/receive/ServerHeartbeatLength17.java b/src/main/java/com/ifish/socket/model/receive/ServerHeartbeatLength17.java new file mode 100644 index 0000000..32c19d4 --- /dev/null +++ b/src/main/java/com/ifish/socket/model/receive/ServerHeartbeatLength17.java @@ -0,0 +1,21 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.ifish.socket.model.receive; + +import com.ifish.socket.model.HeadModel; +import java.io.Serializable; + +/** + * 服务器响应心跳17字节(15字节头+2校验码) + * + * @author guogw + * + */ +public class ServerHeartbeatLength17 extends HeadModel implements Serializable { + + private static final long serialVersionUID = 8470317692571925603L; + +} diff --git a/src/main/java/com/ifish/socket/model/send/APPLoginContextLength25.java b/src/main/java/com/ifish/socket/model/send/APPLoginContextLength25.java new file mode 100644 index 0000000..1a05d53 --- /dev/null +++ b/src/main/java/com/ifish/socket/model/send/APPLoginContextLength25.java @@ -0,0 +1,30 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.ifish.socket.model.send; + +import com.ifish.socket.model.HeadModel; +import java.io.Serializable; + +/** + * 手机登录请求25字节(17字节头+8字节登陆信息) + * + * @author guogw + * + */ +public class APPLoginContextLength25 extends HeadModel implements Serializable { + + private static final long serialVersionUID = 5899193914651159511L; + //时间 + private long longtime; + + public long getLongtime() { + return longtime; + } + + public void setLongtime(long longtime) { + this.longtime = longtime; + } +} diff --git a/src/main/java/com/ifish/socket/model/send/DeviceLoginContextLength24.java b/src/main/java/com/ifish/socket/model/send/DeviceLoginContextLength24.java new file mode 100644 index 0000000..8adec41 --- /dev/null +++ b/src/main/java/com/ifish/socket/model/send/DeviceLoginContextLength24.java @@ -0,0 +1,61 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.ifish.socket.model.send; + +import com.ifish.socket.model.HeadModel; +import java.io.Serializable; + +/** + * 设备登录服务器(24字节=17+7字节登陆信息) + * + * @author guogw + * + */ +public class DeviceLoginContextLength24 extends HeadModel implements Serializable { + + private static final long serialVersionUID = 5329299292101065390L; + //厂商 + private byte vendor; + //硬件型号 + private byte hardware_type; + //版本 + private byte version; + //登陆IP + private byte[] login_ip = new byte[4]; + + public byte getVendor() { + return vendor; + } + + public void setVendor(byte vendor) { + this.vendor = vendor; + } + + public byte getHardware_type() { + return hardware_type; + } + + public void setHardware_type(byte hardware_type) { + this.hardware_type = hardware_type; + } + + public byte getVersion() { + return version; + } + + public void setVersion(byte version) { + this.version = version; + } + + public byte[] getLogin_ip() { + return login_ip; + } + + public void setLogin_ip(byte[] login_ip) { + this.login_ip = login_ip; + } + +} diff --git a/src/main/java/com/ifish/socket/model/send/ModelVersionLength22.java b/src/main/java/com/ifish/socket/model/send/ModelVersionLength22.java new file mode 100644 index 0000000..d21c083 --- /dev/null +++ b/src/main/java/com/ifish/socket/model/send/ModelVersionLength22.java @@ -0,0 +1,60 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.ifish.socket.model.send; + +import com.ifish.socket.model.HeadModel; +import java.io.Serializable; + +/** + * 模块版本号,日期 + * + * @author Administrator + */ +public class ModelVersionLength22 extends HeadModel implements Serializable { + + private static final long serialVersionUID = -6712376303551767331L; + //版本 + private byte version; + //年 + private short year; + //月 + private byte month; + //日 + private byte day; + + public byte getVersion() { + return version; + } + + public void setVersion(byte version) { + this.version = version; + } + + public short getYear() { + return year; + } + + public void setYear(short year) { + this.year = year; + } + + public byte getMonth() { + return month; + } + + public void setMonth(byte month) { + this.month = month; + } + + public byte getDay() { + return day; + } + + public void setDay(byte day) { + this.day = day; + } + +} diff --git a/src/main/java/com/ifish/socket/model/send/ModelVersionLength38.java b/src/main/java/com/ifish/socket/model/send/ModelVersionLength38.java new file mode 100644 index 0000000..a4fe84d --- /dev/null +++ b/src/main/java/com/ifish/socket/model/send/ModelVersionLength38.java @@ -0,0 +1,111 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.ifish.socket.model.send; + +import com.ifish.socket.model.HeadModel; +import java.io.Serializable; + +/** + * 模块版本号,日期,38字节(17字节头+21字节) + * + * @author guogw + * + */ +public class ModelVersionLength38 extends HeadModel implements Serializable { + + private static final long serialVersionUID = -6712376303551767331L; + //版本 + private byte version; + //年 + private short year; + //月 + private byte month; + //日 + private byte day; + //MCU启动次数 + private int number1; + //模块启动次数 + private int number2; + //连接路由器次数 + private int number3; + //成功连接服务器次数 + private int number4; + //尝试连接服务器次数 + private int number5; + + public byte getVersion() { + return version; + } + + public void setVersion(byte version) { + this.version = version; + } + + public short getYear() { + return year; + } + + public void setYear(short year) { + this.year = year; + } + + public byte getMonth() { + return month; + } + + public void setMonth(byte month) { + this.month = month; + } + + public byte getDay() { + return day; + } + + public void setDay(byte day) { + this.day = day; + } + + public int getNumber1() { + return number1; + } + + public void setNumber1(int number1) { + this.number1 = number1; + } + + public int getNumber2() { + return number2; + } + + public void setNumber2(int number2) { + this.number2 = number2; + } + + public int getNumber3() { + return number3; + } + + public void setNumber3(int number3) { + this.number3 = number3; + } + + public int getNumber4() { + return number4; + } + + public void setNumber4(int number4) { + this.number4 = number4; + } + + public int getNumber5() { + return number5; + } + + public void setNumber5(int number5) { + this.number5 = number5; + } + +} diff --git a/src/main/java/com/ifish/socket/model/send/TemperatureAlarmLength22.java b/src/main/java/com/ifish/socket/model/send/TemperatureAlarmLength22.java new file mode 100644 index 0000000..569b57c --- /dev/null +++ b/src/main/java/com/ifish/socket/model/send/TemperatureAlarmLength22.java @@ -0,0 +1,51 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.ifish.socket.model.send; + +import com.ifish.socket.model.HeadModel; +import java.io.Serializable; + +/** + * 温度报警工作模式和温度指令22字节(17字节头+5字节) + * + * @author guogw + * + */ +public class TemperatureAlarmLength22 extends HeadModel implements Serializable { + + private static final long serialVersionUID = -6712376303551767331L; + //报警温度类型,0是低温报警,1是高温报警 + private byte warn_type; + //当前设备温度 + private short wendu; + //报警温度 + private short warn_wendu; + + public byte getWarn_type() { + return warn_type; + } + + public void setWarn_type(byte warn_type) { + this.warn_type = warn_type; + } + + public short getWendu() { + return wendu; + } + + public void setWendu(short wendu) { + this.wendu = wendu; + } + + public short getWarn_wendu() { + return warn_wendu; + } + + public void setWarn_wendu(short warn_wendu) { + this.warn_wendu = warn_wendu; + } + +} diff --git a/src/main/java/com/ifish/socket/model/send/TemperatureSwitchLength22.java b/src/main/java/com/ifish/socket/model/send/TemperatureSwitchLength22.java new file mode 100644 index 0000000..aad3aa2 --- /dev/null +++ b/src/main/java/com/ifish/socket/model/send/TemperatureSwitchLength22.java @@ -0,0 +1,51 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.ifish.socket.model.send; + +import com.ifish.socket.model.HeadModel; +import java.io.Serializable; + +/** + * 设置报警温度开关指令22字节(17字节头+5字节) + * + * @author guogw + * + */ +public class TemperatureSwitchLength22 extends HeadModel implements Serializable { + + private static final long serialVersionUID = -6712376303551767331L; + //开启关闭报警,00关,01开,02设置温度 + private byte onoff; + //报警温度 + private short low_wendu; + //报警温度 + private short hight_wendu; + + public byte getOnoff() { + return onoff; + } + + public void setOnoff(byte onoff) { + this.onoff = onoff; + } + + public short getLow_wendu() { + return low_wendu; + } + + public void setLow_wendu(short low_wendu) { + this.low_wendu = low_wendu; + } + + public short getHight_wendu() { + return hight_wendu; + } + + public void setHight_wendu(short hight_wendu) { + this.hight_wendu = hight_wendu; + } + +} diff --git a/src/main/java/com/ifish/socket/model/send/UpgradeVersionLength78.java b/src/main/java/com/ifish/socket/model/send/UpgradeVersionLength78.java new file mode 100644 index 0000000..cf076d4 --- /dev/null +++ b/src/main/java/com/ifish/socket/model/send/UpgradeVersionLength78.java @@ -0,0 +1,41 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.ifish.socket.model.send; + +import com.ifish.socket.model.HeadModel; +import java.io.Serializable; + +/** + * 升级命令78字节(17字节头+61字节) + * + * @author guogw + * + */ +public class UpgradeVersionLength78 extends HeadModel implements Serializable { + + private static final long serialVersionUID = 3696291654504951296L; + //版本 + private byte version; + //地址 + private byte[] address = new byte[60]; + + public byte getVersion() { + return version; + } + + public void setVersion(byte version) { + this.version = version; + } + + public byte[] getAddress() { + return address; + } + + public void setAddress(byte[] address) { + this.address = address; + } + +} diff --git a/src/main/java/com/ifish/socket/webService/MyService.java b/src/main/java/com/ifish/socket/webService/MyService.java new file mode 100644 index 0000000..50dd40f --- /dev/null +++ b/src/main/java/com/ifish/socket/webService/MyService.java @@ -0,0 +1,54 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.ifish.socket.webService; + +import com.ifish.socket.config.SomeServer; +import com.ifish.socket.model.OrderModel; +import com.ifish.socket.model.send.UpgradeVersionLength78; +import com.ifish.util.ByteUtil; +import javax.jws.WebService; +import org.apache.mina.core.session.IoSession; + +/** + * + * @author Administrator + */ +@WebService +public class MyService { + + //查询设备是否在线 + public boolean deviceIsOnline(String macAddress) { + if (macAddress != null && macAddress.length() > 0) { + IoSession session = SomeServer.sessions_cz.get(macAddress); + if (session != null) { + if (session.getRemoteAddress() != null) { + return true; + } + } + } + return false; + } + + //查询在线设备数量 + public Integer getLineDeviceNumber() { + return SomeServer.sessions_cz.size(); + } + + //在线升级 + public boolean onlineUpgrade(String macAddress, Integer upgradeVersion) { + if (macAddress != null && macAddress.length() > 0 && upgradeVersion != null) { + IoSession session = SomeServer.sessions_cz.get(macAddress); + if (session != null) { + if (session.getRemoteAddress() != null) { + UpgradeVersionLength78 model = OrderModel.UpgradeVersionLength78(ByteUtil.hexStringToBytes2(macAddress), upgradeVersion); + session.write(model); + return true; + } + } + } + return false; + } +} diff --git a/src/main/java/com/ifish/socket/webService/ServiceBean.java b/src/main/java/com/ifish/socket/webService/ServiceBean.java new file mode 100644 index 0000000..d0b20d4 --- /dev/null +++ b/src/main/java/com/ifish/socket/webService/ServiceBean.java @@ -0,0 +1,19 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.ifish.socket.webService; + +import javax.xml.ws.Endpoint; + +/** + * + * @author Administrator + */ +public class ServiceBean { + + public ServiceBean() { + Endpoint.publish("http://localhost:9001/Service/webService", new MyService()); + } +} diff --git a/src/main/java/com/ifish/util/ByteUtil.java b/src/main/java/com/ifish/util/ByteUtil.java new file mode 100644 index 0000000..76f63c9 --- /dev/null +++ b/src/main/java/com/ifish/util/ByteUtil.java @@ -0,0 +1,196 @@ +package com.ifish.util; + +import com.ifish.socket.model.DeviceResponseLength99; +import java.util.Calendar; + +import org.apache.mina.core.buffer.IoBuffer; +import org.json.JSONException; +import org.json.JSONObject; + +public class ByteUtil { + + /** + * 将IoBuffer转换成byte + * + * @param str + */ + public static byte[] ioBufferToByte(IoBuffer ioBuffer) { + byte[] b = new byte[ioBuffer.limit()]; + ioBuffer.get(b, 0, b.length); + return b; + } + + /** + * 将byte[]转换成IoBuffer + * + * @param str + */ + public static IoBuffer byteToIoBuffer(byte[] bt, int length) { + IoBuffer ioBuffer = IoBuffer.allocate(100).setAutoExpand(true); + //ioBuffer.order(ByteOrder.LITTLE_ENDIAN);// 修改此缓冲区的字节顺大端模式 + ioBuffer.put(bt, 0, length); + ioBuffer.flip(); + return ioBuffer; + } + + /** + * 16进制字节数组转16进制字符 + * + * @param hexString + * @return byte[] + */ + public static String bytesToHexString(byte[] src) { + StringBuilder stringBuilder = new StringBuilder(); + if (src == null || src.length <= 0) { + return null; + } + for (int i = 0; i < src.length; i++) { + int v = src[i] & 0xFF; + String hv = Integer.toHexString(v); + if (hv.length() < 2) { + stringBuilder.append(0); + } + stringBuilder.append(hv); + } + return stringBuilder.toString(); + } + + /** + * 16进制字符串转换成字节数组(带空格) + * + * @param hexString + * @return byte[] + */ + public static byte[] hexStringToBytes(String hexString) { + hexString = hexString.toLowerCase(); + String[] hexStrings = hexString.split(" "); + byte[] bytes = new byte[hexStrings.length]; + for (int i = 0; i < hexStrings.length; i++) { + char[] hexChars = hexStrings[i].toCharArray(); + bytes[i] = (byte) (charToByte(hexChars[0]) << 4 | charToByte(hexChars[1])); + } + return bytes; + } + + private static byte charToByte(char c) { + return (byte) "0123456789abcdef".indexOf(c); + } + + /** + * 16进制字符串转换成字节数组(不带空格) + * + * @param hexString + * @return byte[] + */ + public static byte[] hexStringToBytes2(String hex) { + hex = hex.toLowerCase(); + int len = (hex.length() / 2); + byte[] result = new byte[len]; + char[] achar = hex.toCharArray(); + for (int i = 0; i < len; i++) { + int pos = i * 2; + result[i] = (byte) (charToByte(achar[pos]) << 4 | charToByte(achar[pos + 1])); + } + return result; + } + + /** + * 把byte 转化为两位十六进制数 + * + * @param b + * @return + */ + public static String toHex(byte b) { + String result = Integer.toHexString(b & 0xFF); + if (result.length() == 1) { + result = '0' + result; + } + return result; + } + + /** + * 当前16进制时间 + * + * @return + */ + public static byte[] hexSysTime() { + Calendar calendar = Calendar.getInstance(); + String hexYear = String.format("%04x", calendar.get(Calendar.YEAR)); + String hexMonth = String.format("%02x", (calendar.get(Calendar.MONTH) + 1)); + String hexDate = String.format("%02x", calendar.get(Calendar.DATE)); + String hexHours = String.format("%02x", calendar.get(Calendar.HOUR_OF_DAY)); + String hexMinutes = String.format("%02x", calendar.get(Calendar.MINUTE)); + String hexSeconds = String.format("%02x", calendar.get(Calendar.SECOND)); + int hexDay = calendar.get(Calendar.DAY_OF_WEEK); + String dayOfWeek = String.format("%02x", hexDay == 1 ? 7 : hexDay - 1); + byte[] sysTime = hexStringToBytes2(hexYear + hexMonth + hexDate + hexHours + hexMinutes + hexSeconds + dayOfWeek); + return sysTime; + } + + /** + * 将byte定时器转换成String + * + * @param str + */ + public static String BytesToTimer(byte[] bys) { + StringBuilder s = new StringBuilder(); + for (int i = 0; i < bys.length; i++) { + if (i == 1) { + s.append(String.format("%02d", (int) bys[i]) + "-"); + } else if (i == 3) { + s.append(String.format("%02d", (int) bys[i])); + } else { + s.append(String.format("%02d", (int) bys[i]) + ":"); + } + } + return s.toString(); + } + + /** + * 设备信息 + * + * @return + */ + public static String formatDeviceInfo(DeviceResponseLength99 model) { + String macAddress = ByteUtil.bytesToHexString(model.getSrc()); + String wendu = (float) model.getWendu() / 10f + "℃"; + String low_wendu = (float) model.getLow_wendu() / 10f + "℃"; + String height_wendu = (float) model.getHigh_wendu() / 10f + "℃"; + String warn_onoff = String.valueOf(model.getWarn_onoff()); + String onff1 = String.valueOf(model.getOnoff1()); + String onff2 = String.valueOf(model.getOnoff2()); + String onff3 = String.valueOf(model.getOnoff3()); + String timer_time1 = BytesToTimer(model.getTimer_time1()); + String timer_time2 = BytesToTimer(model.getTimer_time2()); + String timer_time3 = BytesToTimer(model.getTimer_time3()); + String timer_time4 = BytesToTimer(model.getTimer_time4()); + String timer_time5 = BytesToTimer(model.getTimer_time5()); + String timer_time6 = BytesToTimer(model.getTimer_time6()); + String timer_time7 = BytesToTimer(model.getTimer_time7()); + String timer_time8 = BytesToTimer(model.getTimer_time8()); + String timer_time9 = BytesToTimer(model.getTimer_time9()); + JSONObject json = new JSONObject(); + try { + json.put("macAddress", macAddress); + json.put("wendu", wendu); + json.put("low_wendu", low_wendu); + json.put("height_wendu", height_wendu); + json.put("warn_onoff", warn_onoff); + json.put("onff1", onff1); + json.put("onff2", onff2); + json.put("onff3", onff3); + json.put("timer_time1", timer_time1); + json.put("timer_time2", timer_time2); + json.put("timer_time3", timer_time3); + json.put("timer_time4", timer_time4); + json.put("timer_time5", timer_time5); + json.put("timer_time6", timer_time6); + json.put("timer_time7", timer_time7); + json.put("timer_time8", timer_time8); + json.put("timer_time9", timer_time9); + } catch (JSONException e) { + e.printStackTrace(); + } + return json.toString(); + } +} diff --git a/src/main/java/com/ifish/util/IfishFilePath.java b/src/main/java/com/ifish/util/IfishFilePath.java new file mode 100644 index 0000000..65f72ed --- /dev/null +++ b/src/main/java/com/ifish/util/IfishFilePath.java @@ -0,0 +1,113 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.ifish.util; + +import com.ifish.enums.SubDirectoryEnum; +import java.io.InputStream; +import java.lang.reflect.Field; +import java.util.Properties; + +/** + * + * @author Administrator + */ +public class IfishFilePath { + + //头像地址 + public static String path_img; + /** + * 图片头地址 + */ + public static String link_img_head; + //商家图片地址 + public static String path_shops; + //商品介绍图片 + public static String path_commodity; + //微信分享页面 + public static String path_share_html; + //微信分享页图片 + public static String path_share_img; + //看护报告页面 + public static String path_look_html; + //看护报告图片 + public static String path_look_img; + //html名字 + public static String html_name; + //图片上传格式 + public static String check_style; + //云信爱鱼奇官方帐号 + public static String ifish_account; + //IM官方手机号 + public static String netease_phone; + //直播间封面 + public static String path_room_img; + //FastDFS商家视频上传URL访问路径(本地和测试环境) + public static String fastDFS_url_local; + //FastDFS商家视频上传URL访问路径(正式环境) + public static String fastDFS_url_app; + //开发模式 + public static boolean devModel; + //本机IP + public static String ifiship; + //本机端口 + public static int ifishport; + + static { + Properties prop = new Properties(); + InputStream in = IfishFilePath.class.getResourceAsStream("/property.properties"); + try { + prop.load(in); + //利用反射机制给属性赋值 + Field[] fields = IfishFilePath.class.getDeclaredFields(); + for (Field field : fields) { + String name = field.getName(); + //获取property.properties的值 + String value = prop.getProperty(name).trim(); + //判断这个值希望塞入的变量类型,并进行转换 + Object value1 = getValue(value, field); + //转换完成后塞入此变量 + field.set(IfishFilePath.class, value1); + + } + in.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * 验证图片格式 + */ + public static String checkImgName(String fileName) { + //对格式进行验证 + int index = fileName.lastIndexOf("."); + String extEndName = fileName.substring(++index, fileName.length()); + if (check_style.indexOf(extEndName.toUpperCase()) == -1) { + return null; + } + //返回格式.png + return "." + extEndName; + } + + /** + * 获取图片链接地址 + * + * @param type + * @return + */ + public static String getPath(SubDirectoryEnum pictureEnum, String fileName) { + return link_img_head + pictureEnum.getKey() + fileName; + } + + private static Object getValue(String value, Field field) { + if (field.getType().equals(int.class)) { + return Integer.valueOf(value); + } else if (field.getType().equals(boolean.class)) { + return Boolean.valueOf(value); + } + return value; + } +} diff --git a/src/main/java/com/ifish/util/IfishUtil.java b/src/main/java/com/ifish/util/IfishUtil.java new file mode 100644 index 0000000..3ea006d --- /dev/null +++ b/src/main/java/com/ifish/util/IfishUtil.java @@ -0,0 +1,454 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.ifish.util; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.UnsupportedEncodingException; +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import org.codehaus.jackson.map.ObjectMapper; +import org.codehaus.jackson.type.JavaType; +import org.springframework.web.multipart.MultipartFile; + +/** + * + * @author Administrator + */ +public class IfishUtil { + + private static final String formatDate = "yyyy-MM-dd HH:mm:ss"; + + private static final String formatDate3 = "MM-dd HH:mm"; + + private static final String formatDate2 = "yyyy-MM-dd HH:mm"; + + private static final String formatDate1 = "yyyy-MM-dd"; + + /** + * 测试缓存时间:一分钟;单位:秒 + */ + public static final int CacheTime_Test_SECONDS = 60; + /** + * 缓存时间:一秒;单位:秒 + */ + public static final int CacheTime_SECONDS = 1; + /** + * 缓存时间:一分钟;单位:分 + */ + public static final int CacheTime_MINUTES = 1; + /** + * 缓存时间:一小时;单位:小时 + */ + public static final int CacheTime_HOURS = 1; + /** + * 缓存时间:一天;单位:天 + */ + public static final int CacheTime_DAYS = 1; + + /** + * Object转Json + */ + public static String ObjectToJson(Object value) { + try { + if (value == null) { + return null; + } + ObjectMapper mapper = new ObjectMapper(); + String js = mapper.writeValueAsString(value); + return js; + } catch (Exception ex) { + System.out.println("【异常信息】 >>> " + ex.toString()); + return "Error"; + } + } + + /** + * Json转Object(List) + * + * @param json 需要转换的JSON字符串 + * @param bean JavaBean, + * @return 拿到结果需要强转一次,因为你拿到的是Object, 例如这样调用和强转: List<School> lst + * =(List<School>)StringUtil.JsonToObjectList(value, School.class); + */ + public static Object JsonToList(String json, Class bean) throws Exception { + ObjectMapper mapper = new ObjectMapper(); + JavaType javaType = mapper.getTypeFactory().constructParametricType(List.class, bean); + return mapper.readValue(json, javaType); + } + + /** + * Json转Object(JavaBean) + * + * @param json 需要转换的JSON字符串 + * @param bean JavaBean, + * @return 拿到结果需要强转一次,因为你拿到的是Object, 例如这样调用和强转: School lst + * =(School)StringUtil.JsonToObjectList(value, School.class); + */ + public static Object JsonToBean(String json, Class bean) throws Exception { + ObjectMapper mapper = new ObjectMapper(); + JavaType javaType = mapper.getTypeFactory().uncheckedSimpleType(bean); + return mapper.readValue(json, javaType); + } + + /** + * 将json格式的字符串解析成Map对象 + */ + public static Map> JsonToMap(String json) { + try { + ObjectMapper mapper = new ObjectMapper(); + Map> maps = mapper.readValue(json, Map.class); + return maps; + } catch (Exception e) { + System.out.println("【异常信息】 >>> " + e.toString()); + return null; + } + } + + public static Map JsonToMapContainMap(String json) { + try { + ObjectMapper mapper = new ObjectMapper(); + Map maps = mapper.readValue(json, Map.class); + return maps; + } catch (Exception e) { + System.out.println("【异常信息】 >>> " + e.toString()); + return null; + } + } + + public static Map JsonToRealMap(String json) { + try { + ObjectMapper mapper = new ObjectMapper(); + Map maps = mapper.readValue(json, Map.class); + return maps; + } catch (Exception e) { + System.out.println("【异常信息】 >>> " + e.toString()); + return null; + } + } + + + //返回时分秒的时间字符串 + public static String TimestampToStringAndSecond(Object timestamp) { + DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + try { + return (format.format(timestamp)); + } catch (Exception e) { + System.out.println("【isGaokaoYearExpire 异常】:" + e); + return ""; + } + } + + //返回时分秒的时间字符串 + public static String TimestampToStringAndSecond(Object timestamp, String geshi) { + DateFormat format = new SimpleDateFormat(geshi); + try { + return (format.format(timestamp)); + } catch (Exception e) { + System.out.println("【isGaokaoYearExpire 异常】:" + e); + return ""; + } + } + + /** + * 返回当前日期yyyy-MM-dd + * + * @return + * @throws ParseException + */ + public static Date getCurDate() { + DateFormat df = DateFormat.getDateInstance(); + try { + return df.parse(df.format(new Date())); + } catch (ParseException e) { + } + return null; + } + + /** + * 格式化日期yyyy-MM-dd HH:mm:ss + * + * @param date + * @return + */ + public static String format(Date date) { + SimpleDateFormat format = new SimpleDateFormat(formatDate); + return format.format(date); + } + + /** + * 格式化日期 + * + * @param date + * @return + */ + public static String format1(Date date) { + SimpleDateFormat format = new SimpleDateFormat(formatDate1); + return format.format(date); + } + + /** + * 格式化日期 + * + * @param date + * @return + */ + public static String format2(Date date) { + SimpleDateFormat format = new SimpleDateFormat(formatDate2); + return format.format(date); + } + + /** + * 格式化日期 + * + * @param date + * @return + */ + public static String format3(Date date) { + SimpleDateFormat format = new SimpleDateFormat(formatDate3); + return format.format(date); + } + + /** + * 字符串转换成日期 + * + * @param str + * @return date + */ + public static Date StrToDate(String str) { + SimpleDateFormat format = new SimpleDateFormat(formatDate1); + Date date = null; + try { + date = format.parse(str); + } catch (ParseException e) { + e.printStackTrace(); + } + return date; + } + + /** + * 随机生成字母与数字组合 + * + * @param length + * @return + */ + public static String getCharAndNumr(int length) { + String val = ""; + Random random = new Random(); + for (int i = 0; i < length; i++) { + // 输出字母还是数字 + String charOrNum = random.nextInt(2) % 2 == 0 ? "char" : "num"; + // 字符串 + if ("char".equalsIgnoreCase(charOrNum)) { + // 取得大写字母还是小写字母 + int choice = random.nextInt(2) % 2 == 0 ? 65 : 97; + val += (char) (choice + random.nextInt(26)); + // 数字 + } else if ("num".equalsIgnoreCase(charOrNum)) { + val += String.valueOf(random.nextInt(10)); + } + } + return val; + } + + /** + * 返回json数据 + * + * @param str + * @return + */ + public static Map returnJson(String key, Object value) { + Map map = new HashMap(); + map.put("result", key); + if (value.equals("")) { + map.put("data", null); + } else { + map.put("data", value); + } + return map; + } + + /** + * 返回json数据 + * + * @param str + * @return + */ + public static Map toJson(String key, Object value) { + Map map = new HashMap(); + map.put("result", key); + if (value.equals("")) { + map.put("data", null); + } else { + map.put("data", value); + } + return map; + } + + /** + * 上传图片文件 + * + * @param fileUpload + * @param path + * @return + */ + public static String uploadFile(MultipartFile fileUpload, String path) { + //根据时间得文件名 + Calendar calendar = Calendar.getInstance(); + String fileName = String.valueOf(calendar.getTimeInMillis()); + //保存文件 + if (fileUpload == null) { + return "base/default.png"; + } + if (!fileUpload.isEmpty()) { + try { + if (!fileUpload.getOriginalFilename().equals("")) { + File picFile = new File(path, fileName + ".png"); + //不存在则创建 + if (!picFile.exists()) { + picFile.mkdirs(); + } + //保存 + fileUpload.transferTo(picFile); + } + return "share/" + fileName + ".png"; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + return ""; + } + + /** + * 生成微信分享页面 + * + * @param title + * @param base64 + * @return + */ + public static String getHtmlFile(String title, String pngName, String modelFile, String filePath) { + try { + if (pngName != null && !pngName.equals("")) { + //读取模板文件 + FileInputStream fileinputstream = new FileInputStream(filePath + modelFile); + int lenght = fileinputstream.available(); + byte bytes[] = new byte[lenght]; + fileinputstream.read(bytes); + fileinputstream.close(); + String templateContent = new String(bytes); + //替换掉模板中相应的地方 + templateContent = templateContent.replaceAll("###title###", title); + templateContent = templateContent.replaceAll("###fileName###", pngName); + //根据时间得文件名 + Calendar calendar = Calendar.getInstance(); + String fileName = String.valueOf(calendar.getTimeInMillis()); + fileName = fileName + ".html"; + //建立文件输出流 + FileOutputStream fileoutputstream = new FileOutputStream(filePath + fileName); + byte tag_bytes[] = templateContent.getBytes(); + fileoutputstream.write(tag_bytes); + fileoutputstream.close(); + return fileName; + } + } catch (Exception e) { + throw new RuntimeException(e); + } + return ""; + } + + /** + * 随即区间整数 + * + * @param min + * @param max + * @return + */ + public static int getRandom(int min, int max) { + int r = new Random().nextInt(max - min); + return r + min; + } + + /** + * URL 解码 + * + * @return String + * @author lifq + * @date 2015-3-17 下午04:09:51 + */ + public static String getURLDecoderString(String str) { + String result = ""; + if (null == str) { + return ""; + } + try { + result = java.net.URLDecoder.decode(str, "GBK"); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + return result; + } + + /** + * URL 转码 + * + * @return String + * @author lifq + * @date 2015-3-17 下午04:10:28 + */ + public static String getURLEncoderString(String str) { + String result = ""; + if (null == str) { + return ""; + } + try { + result = java.net.URLEncoder.encode(str, "GBK"); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + return result; + } + + /** + * 正则表达式截取字符串 + * + * @param str + * @param regex + * @return + */ + public static String regex(String str, String regex) { + Matcher matcher = Pattern.compile(regex).matcher(str); + if (matcher.find()) { + return matcher.group(0); + } else { + return ""; + } + } + + public static String regexToChinese(String str) { + String regex = "([\\u4e00-\\u9fa5]+)"; + return regex(str, regex); + } + + /** + * 验证当前系统是windows(本地,返回true),还是unix(服务器,返回false) + * + * @return + */ + public static boolean IsWinOrUnix() { + String os = System.getProperty("os.name");//获取当前操作系统 + return os.toLowerCase().startsWith("win"); + } +} diff --git a/src/main/java/com/ifish/util/JdbcProperties.java b/src/main/java/com/ifish/util/JdbcProperties.java new file mode 100644 index 0000000..9260ea9 --- /dev/null +++ b/src/main/java/com/ifish/util/JdbcProperties.java @@ -0,0 +1,61 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.ifish.util; + +import java.io.InputStream; +import java.lang.reflect.Field; +import java.util.Properties; + +/** + * + * @author Administrator + */ +public class JdbcProperties { + + public static String driverClassName; + public static String url; + public static String username; + public static String password; + public static boolean autoCommitOnClose; + public static int initialPoolSize; + public static int minPoolSize; + public static int maxPoolSize; + public static int acquireIncrement; + public static int checkoutTimeout; + public static int maxIdleTime; + public static int idleConnectionTestPeriod; + + static { + Properties prop = new Properties(); + InputStream in = IfishFilePath.class.getResourceAsStream("/jdbc.properties"); + try { + prop.load(in); + //利用反射机制给属性赋值 + Field[] fields = JdbcProperties.class.getDeclaredFields(); + for (Field field : fields) { + String name = field.getName(); + //获取property.properties的值 + String value = prop.getProperty(name).trim(); + //判断这个值希望塞入的变量类型,并进行转换 + Object value1 = getValue(value, field); + //转换完成后塞入此变量 + field.set(IfishFilePath.class, value1); + } + in.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + private static Object getValue(String value, Field field) { + if (field.getType().equals(int.class)) { + return Integer.valueOf(value); + } else if (field.getType().equals(boolean.class)) { + return Boolean.valueOf(value); + } + return value; + } +} diff --git a/src/main/java/com/ifish/util/RedisKey.java b/src/main/java/com/ifish/util/RedisKey.java new file mode 100644 index 0000000..331f03a --- /dev/null +++ b/src/main/java/com/ifish/util/RedisKey.java @@ -0,0 +1,78 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.ifish.util; + +/** + * + * @author Administrator + */ +public class RedisKey { + + /** + * 用户缓存前缀 + */ + public static final String User_Key = "userE:"; + + /** + * 商家信息缓存前缀 + */ + public static final String ShopsInfo_Key = "shopsInfoE:"; + + /** + * 用户绑定设备缓存前缀 + */ + public static final String DeviceUser_Key = "deviceUserE:"; + + /** + * 设备绑定摄像头缓存前缀 + */ + public static final String DeviceCamera_key = "deviceCameraE:"; + + /** + * 设备详情缓存前缀 + */ + public static final String Device_key = "deviceE:"; + + /** + * 设备类型缓存前缀 + */ + public static final String HardWareType_key = "hardwaretypeE:"; + + /** + * 厂家列表缓存前缀 + */ + public static final String VenderList_key = "venderlistE:"; + + /** + * 用户和摄像头的绑定关系存前缀 + */ + public static final String CameraUser_key = "cameraUserE:"; + + /** + * 摄像头详情缓存前缀 + */ + public static final String Camera_key = "cameraE:"; + + /** + * 设备统计信息表缓存前缀 + */ + public static final String DeviceStatistic_key = "deviceStaticticsE:"; + + /** + * 直播间详情表缓存前缀 + */ + public static final String LiveRoom_key = "liveroomE:"; + + /** + * 直播间数量缓存前缀 + */ + public static final String LIVEROOM_COUNT_KEY = "liveroomcountE:"; + + /** + * 推送消息缓存前缀 + */ + public static final String PUSHLIST_KEY = "pushlistE:"; +} diff --git a/src/main/resources/fdfs_client_ifish7.conf b/src/main/resources/fdfs_client_ifish7.conf new file mode 100644 index 0000000..9661bfb --- /dev/null +++ b/src/main/resources/fdfs_client_ifish7.conf @@ -0,0 +1,7 @@ +connect_timeout = 2 +network_timeout = 30 +charset = UTF-8 +http.tracker_http_port = 85 +http.anti_steal_token = no +http.secret_key = FastDFS1234567890 +tracker_server = 120.55.190.56:22131 \ No newline at end of file diff --git a/src/main/resources/fdfs_client_zhangxinyanv5.conf b/src/main/resources/fdfs_client_zhangxinyanv5.conf new file mode 100644 index 0000000..6a88c9e --- /dev/null +++ b/src/main/resources/fdfs_client_zhangxinyanv5.conf @@ -0,0 +1,8 @@ +connect_timeout = 2 +network_timeout = 30 +charset = UTF-8 +http.tracker_http_port = 8017 +http.anti_steal_token = no +http.secret_key = FastDFS1234567890 +tracker_server = 139.196.24.156:22122 + diff --git a/src/main/resources/jdbc.properties b/src/main/resources/jdbc.properties new file mode 100644 index 0000000..1b38a52 --- /dev/null +++ b/src/main/resources/jdbc.properties @@ -0,0 +1,40 @@ +#c3p0.driverClassName=com.mysql.jdbc.Driver +#c3p0.url=jdbc\:mysql\://localhost\:3306/myfishdbenglish?characterEncoding\=UTF-8 +#c3p0.username=ifish +#c3p0.password=ifish7pwd +#c3p0.autoCommitOnClose=true +#c3p0.initialPoolSize=20 +#c3p0.minPoolSize=20 +#c3p0.maxPoolSize=50 +#c3p0.acquireIncrement=3 + +#c3p0.checkoutTimeout=5000 +#c3p0.maxIdleTime=7200 +#c3p0.idleConnectionTestPeriod=18000 +#c3p0.maxIdleTimeExcessConnections=1800 + +#c3p0.automaticTestTable=C3P0TestTable +#c3p0.testConnectionOnCheckout=false +#c3p0.testConnectionOnCheckin=false + +#\u8fd9\u91cc\u662f\u4e0a\u9762\u53d6\u6d88\u524d\u7f00\u7684\u5f15\u7528,\u5982\u679c\u4fee\u6539\uff0c\u8bf7\u4fee\u6539\u8fd9\u91cc +driverClassName=com.mysql.jdbc.Driver +url=jdbc\:mysql\://localhost\:3306/myfishdbenglish?characterEncoding\=UTF-8 +username=ifish +password=ifish7pwd +autoCommitOnClose=true +initialPoolSize=20 +minPoolSize=20 +maxPoolSize=50 +acquireIncrement=3 +checkoutTimeout=5000 +maxIdleTime=7200 +idleConnectionTestPeriod=18000 + +#org.hibernate.dialect.MySQLInnoDBDialect +hibernate.dialect=org.hibernate.dialect.MySQLDialect +hibernate.show_sql=false +hibernate.format_sql=true +hibernate.hbm2ddl.auto=false +hibernate.jdbc.batch_size=50 +hibernate.query.substitutions=true 1,false 0 diff --git a/src/main/resources/property.properties b/src/main/resources/property.properties new file mode 100644 index 0000000..6fa1e49 --- /dev/null +++ b/src/main/resources/property.properties @@ -0,0 +1,44 @@ +#\u5934\u50cf\u5b58\u50a8\u8def\u5f84 +path_img=/mydata/tmp/Img/app +#\u5546\u5bb6\u8ba4\u8bc1\u56fe\u7247 +path_shops=/mydata/tmp/Img/shops +#\u5546\u54c1\u4ecb\u7ecd\u56fe\u7247 +path_commodity=/mydata/tmp/Img/commodity +#\u76f4\u64ad\u95f4\u5c01\u9762 +path_room_img=/mydata/tmp/Img/room +#\u5fae\u4fe1\u5206\u4eab\u9875\u9762 +path_share_html=/mydata/tmp/html/ +#\u5fae\u4fe1\u5206\u4eab\u9875\u56fe\u7247 +path_share_img=/mydata/tmp/html/images/share +#\u770b\u62a4\u62a5\u544a\u9875\u9762 +path_look_html=/mydata/tmp/look/ +#\u770b\u62a4\u62a5\u544a\u56fe\u7247 +path_look_img=/mydata/tmp/look/images/share +#\u770b\u62a4\u62a5\u544a\u6a21\u7248html\u540d\u5b57 +html_name=index.html +#\u4e91\u4fe1\u7231\u9c7c\u5947\u5b98\u65b9\u5e10\u53f7 +ifish_account=ifish +#IM\u5b98\u65b9\u624b\u673a\u53f7 +netease_phone=18501773036 +#FastDFS\u6587\u4ef6URL\u8bbf\u95ee\u8def\u5f84(\u672c\u5730\u548c\u6d4b\u8bd5\u73af\u5883) +fastDFS_url_local=https://app.zhangxinyanv5.top/ +#FastDFS\u6587\u4ef6URL\u8bbf\u95ee\u8def\u5f84(\u6b63\u5f0f\u73af\u5883) +fastDFS_url_app=http://app.ifish7.com/ +#\u672c\u5730 +#link_img_head=http://192.168.61.128:81/ +#\u6d4b\u8bd5\u73af\u5883 +#link_img_head=https://app.zhangxinyanv5.top/ +#\u6b63\u5f0f\u73af\u5883 +link_img_head=https://app.ifish7.com/ +#\u56fe\u7247\u683c\u5f0f\u9a8c\u8bc1 +check_style=GIF,PNG,BMP,JPG,JPEG +#\u662f\u5426\u5f00\u53d1\u6a21\u5f0f\uff0cfalse\u5219\u4ee3\u8868\u8981\u53d1\u5e03\u5230\u6d4b\u8bd5\u6216\u8005\u6b63\u5f0f\u73af\u5883\uff0c\u4f1a\u8fdb\u884c\u5168\u5c40\u5934\u90e8\u9a8c\u8bc1\uff0ctrue\u4ee3\u8868\u5728\u672c\u5730\u8fdb\u884c\u4ee3\u7801\u7f16\u5199\uff0c\u4e0d\u4f1a\u8fdb\u884c\u9a8c\u8bc1 +devModel=true +#\u672c\u5730 +ifiship=192.168.199.129 +#\u6d4b\u8bd5\u4e91 +#ifiship=139.196.24.156 +#\u6b63\u5f0f\u4e91 +#ifiship=120.55.190.56 +#\u670d\u52a1\u5668\u7aef\u53e3 +ifishport=9956 \ No newline at end of file diff --git a/src/main/webapp/META-INF/context.xml b/src/main/webapp/META-INF/context.xml new file mode 100644 index 0000000..a800ff1 --- /dev/null +++ b/src/main/webapp/META-INF/context.xml @@ -0,0 +1,2 @@ + + diff --git a/src/main/webapp/WEB-INF/lib/fastdfs-client-java-1.25.jar b/src/main/webapp/WEB-INF/lib/fastdfs-client-java-1.25.jar new file mode 100644 index 0000000000000000000000000000000000000000..74adb9294c5580275b3cd37fefcb9311db2073bf GIT binary patch literal 91061 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 + + + + + + Socket + + +

访问正常!

+ +