移动考勤
This commit is contained in:
parent
af64fb3879
commit
78924866cb
@ -1,134 +0,0 @@
|
||||
package com.zhgd.netty.tcp.location;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Slf4j
|
||||
public class BCD {
|
||||
|
||||
public static long toLong(byte[] value) {
|
||||
long result = 0;
|
||||
int len = value.length;
|
||||
int temp;
|
||||
for (int i = 0; i < len; i++) {
|
||||
temp = (len - 1 - i) * 8;
|
||||
if (temp == 0) {
|
||||
result += (value[i] & 0x0ff);
|
||||
} else {
|
||||
result += (value[i] & 0x0ff) << temp;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static byte[] longToBytes(long value, int len) {
|
||||
byte[] result = new byte[len];
|
||||
int temp;
|
||||
for (int i = 0; i < len; i++) {
|
||||
temp = (len - 1 - i) * 8;
|
||||
if (temp == 0) {
|
||||
result[i] += (value & 0x0ff);
|
||||
} else {
|
||||
result[i] += (value >>> temp) & 0x0ff;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static long toDecimal(byte[] bcd) {
|
||||
return Long.valueOf(BCD.toString(bcd));
|
||||
}
|
||||
|
||||
public static int toInteger(byte[] bcd) {
|
||||
return Integer.parseInt(BCD.toString(bcd));
|
||||
}
|
||||
|
||||
public static String toString(byte bcd) {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
byte high = (byte) (bcd & 0xf0);
|
||||
high >>>= (byte) 4;
|
||||
high = (byte) (high & 0x0f);
|
||||
byte low = (byte) (bcd & 0x0f);
|
||||
sb.append(high);
|
||||
sb.append(low);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static String toString(byte[] bcd) {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
|
||||
for (int i = 0; i < bcd.length; i++) {
|
||||
sb.append(toString(bcd[i]));
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private static final String HEX = "0123456789ABCDEF";
|
||||
|
||||
private static byte toByte(char c) {
|
||||
byte b = (byte) HEX.indexOf(c);
|
||||
return b;
|
||||
}
|
||||
|
||||
public static byte[] toBcdBytes(String hex) {
|
||||
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) (toByte(achar[pos]) << 4 | toByte(achar[pos + 1]));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static String toBcdTimeString(byte[] bs) {
|
||||
int six = 6;
|
||||
int seven = 7;
|
||||
if (bs.length != six && bs.length != seven) {
|
||||
log.error("无效BCD时间");
|
||||
return "0000-00-00 00:00:00";
|
||||
}
|
||||
StringBuffer sb = new StringBuffer();
|
||||
int i = 0;
|
||||
if (bs.length == six) {
|
||||
sb.append("20");
|
||||
} else {
|
||||
sb.append(BCD.toString(bs[i++]));
|
||||
}
|
||||
sb.append(BCD.toString(bs[i++]));
|
||||
sb.append("-").append(BCD.toString(bs[i++]));
|
||||
sb.append("-").append(BCD.toString(bs[i++]));
|
||||
sb.append(" ").append(BCD.toString(bs[i++]));
|
||||
sb.append(":").append(BCD.toString(bs[i++]));
|
||||
sb.append(":").append(BCD.toString(bs[i]));
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据byteBuf的readerIndex和writerIndex计算校验码
|
||||
* 校验码规则:从消息头开始,同后一字节异或,直到校验码前一个字节,占用 1 个字节
|
||||
*
|
||||
* @param byteBuf
|
||||
* @return
|
||||
*/
|
||||
public static byte xorSumBytes(ByteBuf byteBuf) {
|
||||
byte sum = byteBuf.getByte(byteBuf.readerIndex());
|
||||
for (int i = byteBuf.readerIndex() + 1; i < byteBuf.writerIndex(); i++) {
|
||||
sum = (byte) (sum ^ byteBuf.getByte(i));
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
/**
|
||||
* 取num字节的第几位
|
||||
* @param num
|
||||
* @param i
|
||||
* @return
|
||||
*/
|
||||
public static int getBit(int num, int i) {
|
||||
//true 表示第i位为1,否则为0
|
||||
return ((num & (1 << i)) != 0) ? 1 : 0;
|
||||
|
||||
}
|
||||
}
|
||||
@ -1,69 +0,0 @@
|
||||
package com.zhgd.netty.tcp.location;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
import io.netty.handler.timeout.IdleState;
|
||||
import io.netty.handler.timeout.IdleStateEvent;
|
||||
import io.netty.util.Attribute;
|
||||
import io.netty.util.AttributeKey;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Slf4j
|
||||
public abstract class BaseHandler<T> extends SimpleChannelInboundHandler<T> {
|
||||
|
||||
/**
|
||||
* 消息流水号
|
||||
*/
|
||||
private static final AttributeKey<Short> SERIAL_NUMBER = AttributeKey.newInstance("serialNumber");
|
||||
|
||||
/**
|
||||
* 递增获取流水号
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public short getSerialNumber(Channel channel) {
|
||||
Attribute<Short> flowIdAttr = channel.attr(SERIAL_NUMBER);
|
||||
Short flowId = flowIdAttr.get();
|
||||
if (flowId == null) {
|
||||
flowId = 0;
|
||||
} else {
|
||||
flowId++;
|
||||
}
|
||||
flowIdAttr.set(flowId);
|
||||
return flowId;
|
||||
}
|
||||
|
||||
public void write(ChannelHandlerContext ctx, DataPacket msg) {
|
||||
ctx.writeAndFlush(msg).addListener(future -> {
|
||||
if (!future.isSuccess()) {
|
||||
log.error("发送失败", future.cause());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
|
||||
log.error("exceptionCaught", cause);
|
||||
ctx.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
|
||||
if (evt instanceof IdleStateEvent) {
|
||||
//此实例项目只设置了读取超时时间,可以通过state分别做处理,一般服务端在这里关闭连接节省资源,客户端发送心跳维持连接
|
||||
IdleState state = ((IdleStateEvent) evt).state();
|
||||
if (state == IdleState.READER_IDLE) {
|
||||
log.warn("客户端{}读取超时,关闭连接", ctx.channel().remoteAddress());
|
||||
ctx.close();
|
||||
} else if (state == IdleState.WRITER_IDLE) {
|
||||
log.warn("客户端{}写入超时", ctx.channel().remoteAddress());
|
||||
} else if (state == IdleState.ALL_IDLE) {
|
||||
log.warn("客户端{}读取写入超时", ctx.channel().remoteAddress());
|
||||
}
|
||||
} else {
|
||||
super.userEventTriggered(ctx, evt);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,76 +0,0 @@
|
||||
package com.zhgd.netty.tcp.location;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class CommonResponse extends DataPacket {
|
||||
|
||||
/**
|
||||
* 成功/确认
|
||||
*/
|
||||
public static final byte SUCCESS = 0;
|
||||
/**
|
||||
* 失败
|
||||
*/
|
||||
public static final byte FAILURE = 1;
|
||||
/**
|
||||
* 消息有误
|
||||
*/
|
||||
public static final byte MSG_ERROR = 2;
|
||||
/**
|
||||
* 不支持
|
||||
*/
|
||||
public static final byte UNSUPPORTED = 3;
|
||||
/**
|
||||
* 报警处理确认
|
||||
*/
|
||||
public static final byte ALARM_PROCESS_ACK = 4;
|
||||
|
||||
/**
|
||||
* 应答流水号 2字节
|
||||
*/
|
||||
private short replyFlowId;
|
||||
/**
|
||||
* 应答 ID 2字节
|
||||
*/
|
||||
private short replyId;
|
||||
/**
|
||||
* 结果 1字节
|
||||
*/
|
||||
private byte result;
|
||||
|
||||
public CommonResponse() {
|
||||
this.getHeader().setMsgId(Const.SERVER_RESP_COMMON);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuf toByteBufMsg() {
|
||||
ByteBuf bb = super.toByteBufMsg();
|
||||
bb.writeShort(replyFlowId);
|
||||
bb.writeShort(replyId);
|
||||
bb.writeByte(result);
|
||||
return bb;
|
||||
}
|
||||
|
||||
public static CommonResponse success(DataPacket msg, short flowId) {
|
||||
CommonResponse resp = new CommonResponse();
|
||||
resp.getHeader().setTerminalPhone(msg.getHeader().getTerminalPhone());
|
||||
resp.getHeader().setFlowId(flowId);
|
||||
resp.setReplyFlowId(msg.getHeader().getFlowId());
|
||||
resp.setReplyId(msg.getHeader().getMsgId());
|
||||
resp.setResult(SUCCESS);
|
||||
return resp;
|
||||
}
|
||||
|
||||
public static CommonResponse success(DataPacket msg, short flowId, byte result) {
|
||||
CommonResponse resp = new CommonResponse();
|
||||
resp.getHeader().setTerminalPhone(msg.getHeader().getTerminalPhone());
|
||||
resp.getHeader().setFlowId(flowId);
|
||||
resp.setReplyFlowId(msg.getHeader().getFlowId());
|
||||
resp.setReplyId(msg.getHeader().getMsgId());
|
||||
resp.setResult(result);
|
||||
return resp;
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,45 +0,0 @@
|
||||
package com.zhgd.netty.tcp.location;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
public class Const {
|
||||
|
||||
/**
|
||||
* 默认字符集为GBK
|
||||
*/
|
||||
public static final Charset DEFAULT_CHARSET = Charset.forName("GBK");
|
||||
|
||||
/**
|
||||
* 消息分隔符
|
||||
*/
|
||||
public static final byte PKG_DELIMITER = 0x7e;
|
||||
|
||||
// 终端消息分类
|
||||
/**
|
||||
* 心跳
|
||||
*/
|
||||
public static final short TERNIMAL_MSG_HEARTBEAT = 0x0002;
|
||||
/**
|
||||
* 注册
|
||||
*/
|
||||
public static final short TERNIMAL_MSG_REGISTER = 0x0100;
|
||||
/**
|
||||
* 鉴权
|
||||
*/
|
||||
public static final short TERNIMAL_MSG_AUTH = 0x0102;
|
||||
/**
|
||||
* 位置
|
||||
*/
|
||||
public static final short TERNIMAL_MSG_LOCATION = 0x0200;
|
||||
/**
|
||||
* 批量位置上报
|
||||
*/
|
||||
public static final short TERNIMAL_MSG_LOCATION_BATCH = 0x0704;
|
||||
|
||||
//服务器应答
|
||||
/**
|
||||
* 通用应答
|
||||
*/
|
||||
public static final short SERVER_RESP_COMMON = (short) 0x8001;
|
||||
|
||||
}
|
||||
@ -1,121 +0,0 @@
|
||||
package com.zhgd.netty.tcp.location;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufAllocator;
|
||||
import lombok.Data;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
@Data
|
||||
public class DataPacket {
|
||||
|
||||
/**
|
||||
* 消息头
|
||||
*/
|
||||
protected Header header = new Header();
|
||||
/**
|
||||
* 消息体
|
||||
*/
|
||||
protected ByteBuf body;
|
||||
|
||||
public DataPacket() {
|
||||
}
|
||||
|
||||
public DataPacket(ByteBuf body) {
|
||||
this.body = body;
|
||||
}
|
||||
|
||||
public void parse() {
|
||||
try {
|
||||
this.parseHead();
|
||||
//验证包体长度
|
||||
if (this.header.getMsgBodyLength() != this.body.readableBytes()) {
|
||||
throw new RuntimeException("包体长度有误");
|
||||
}
|
||||
this.parseBody();
|
||||
} finally {
|
||||
//ReferenceCountUtil.safeRelease(this.body);
|
||||
}
|
||||
}
|
||||
|
||||
public void parseHead() {
|
||||
header.setMsgId(body.readShort());
|
||||
header.setMsgBodyProps(body.readShort());
|
||||
header.setTerminalPhone(BCD.toString(readBytes(6)));
|
||||
header.setFlowId(body.readShort());
|
||||
if (header.hasSubPackage()) {
|
||||
//TODO 处理分包
|
||||
body.readInt();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求报文重写
|
||||
*/
|
||||
protected void parseBody() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 响应报文重写 并调用父类
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public ByteBuf toByteBufMsg() {
|
||||
//在JT808Encoder escape()方法处回收
|
||||
ByteBuf bb = ByteBufAllocator.DEFAULT.heapBuffer();
|
||||
//先占4字节用来写msgId和msgBodyProps,JT808Encoder中覆盖回来
|
||||
bb.writeInt(0);
|
||||
bb.writeBytes(BCD.toBcdBytes(StringUtils.leftPad(this.header.getTerminalPhone(), 12, "0")));
|
||||
bb.writeShort(this.header.getFlowId());
|
||||
//TODO 处理分包
|
||||
return bb;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从ByteBuf中read固定长度的数组,相当于ByteBuf.readBytes(byte[] dst)的简单封装
|
||||
*
|
||||
* @param length
|
||||
* @return
|
||||
*/
|
||||
public byte[] readBytes(int length) {
|
||||
byte[] bytes = new byte[length];
|
||||
this.body.readBytes(bytes);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从ByteBuf中读出固定长度的数组 ,根据808默认字符集构建字符串
|
||||
*
|
||||
* @param length
|
||||
* @return
|
||||
*/
|
||||
public String readString(int length) {
|
||||
return new String(readBytes(length), Const.DEFAULT_CHARSET);
|
||||
}
|
||||
|
||||
/**
|
||||
* 消息头对象
|
||||
*/
|
||||
@Data
|
||||
public static class Header {
|
||||
private short msgId;// 功能ID 2字节
|
||||
private short msgBodyProps;//消息属性 2字节
|
||||
private String terminalPhone; // 终端手机号 6字节
|
||||
private short flowId;// 流水号 2字节
|
||||
|
||||
//获取包体长度
|
||||
public short getMsgBodyLength() {
|
||||
return (short) (msgBodyProps & 0x3ff);
|
||||
}
|
||||
|
||||
//获取加密类型 3bits
|
||||
public byte getEncryptionType() {
|
||||
return (byte) ((msgBodyProps & 0x1c00) >> 10);
|
||||
}
|
||||
|
||||
//是否分包
|
||||
public boolean hasSubPackage() {
|
||||
return ((msgBodyProps & 0x2000) >> 13) == 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,60 +0,0 @@
|
||||
package com.zhgd.netty.tcp.location;
|
||||
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.util.concurrent.DefaultEventExecutorGroup;
|
||||
import io.netty.util.concurrent.EventExecutorGroup;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
|
||||
/**
|
||||
* @Author: Zpsw
|
||||
* @Date: 2019-05-16
|
||||
* @Description:
|
||||
* @Version: 1.0
|
||||
*/
|
||||
@Configuration
|
||||
public class EventLoopGroupConfig {
|
||||
|
||||
@Value("${netty.threads.boss:1}")
|
||||
private int bossThreadsNum;
|
||||
|
||||
@Value("${netty.threads.worker:6}")
|
||||
private int workerThreadsNum;
|
||||
|
||||
@Value("${netty.threads.business:6}")
|
||||
private int businessThreadsNum;
|
||||
|
||||
/**
|
||||
* 负责TCP连接建立操作 绝对不能阻塞
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Primary
|
||||
@Bean(name = "bossGroup")
|
||||
public NioEventLoopGroup bossGroup() {
|
||||
return new NioEventLoopGroup(bossThreadsNum);
|
||||
}
|
||||
|
||||
/**
|
||||
* 负责Socket读写操作 绝对不能阻塞
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Bean(name = "workerGroup")
|
||||
public NioEventLoopGroup workerGroup() {
|
||||
return new NioEventLoopGroup(workerThreadsNum);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler中出现IO操作(如数据库操作,网络操作)使用这个
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Bean(name = "businessGroup")
|
||||
public EventExecutorGroup businessGroup() {
|
||||
return new DefaultEventExecutorGroup(businessThreadsNum);
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,62 +0,0 @@
|
||||
package com.zhgd.netty.tcp.location;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
/**
|
||||
* @Author: Zpsw
|
||||
* @Date: 2019-05-15
|
||||
* @Description: JT808协议参数集合
|
||||
* @Version: 1.0
|
||||
*/
|
||||
public class JT808Const {
|
||||
|
||||
/**
|
||||
* 默认字符集为GBK
|
||||
*/
|
||||
public static final Charset DEFAULT_CHARSET = Charset.forName("GBK");
|
||||
|
||||
/**
|
||||
* 消息分隔符
|
||||
*/
|
||||
public static final byte PKG_DELIMITER = 0x7e;
|
||||
|
||||
//终端应答
|
||||
/**
|
||||
* 通用应答
|
||||
*/
|
||||
public static final short TERNIMAL_RESP_COMMON_ = 0x0001;
|
||||
|
||||
// 终端消息分类
|
||||
/**
|
||||
* 心跳
|
||||
*/
|
||||
public static final short TERNIMAL_MSG_HEARTBEAT = 0x0002;
|
||||
/**
|
||||
* 注册
|
||||
*/
|
||||
public static final short TERNIMAL_MSG_REGISTER = 0x0100;
|
||||
/**
|
||||
* 注销
|
||||
*/
|
||||
public static final short TERNIMAL_MSG_LOGOUT = 0x0003;
|
||||
/**
|
||||
* 鉴权
|
||||
*/
|
||||
public static final short TERNIMAL_MSG_AUTH = 0x0102;
|
||||
/**
|
||||
* 位置
|
||||
*/
|
||||
public static final short TERNIMAL_MSG_LOCATION = 0x0200;
|
||||
|
||||
|
||||
//服务器应答
|
||||
/**
|
||||
* 通用应答
|
||||
*/
|
||||
public static final short SERVER_RESP_COMMON = (short) 0x8001;
|
||||
/**
|
||||
* 注册应答
|
||||
*/
|
||||
public static final short SERVER_RESP_REGISTER = (short) 0x8100;
|
||||
|
||||
}
|
||||
@ -1,33 +0,0 @@
|
||||
package com.zhgd.netty.tcp.location;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
|
||||
@Data
|
||||
public class Location {
|
||||
|
||||
private String phone;
|
||||
private Integer alarm;
|
||||
private Integer statusField;
|
||||
private Float latitude;
|
||||
private Float longitude;
|
||||
private Short elevation;
|
||||
private Short speed;
|
||||
private Short direction;
|
||||
private String time;
|
||||
/**
|
||||
* 里程
|
||||
*/
|
||||
private Long mileage;
|
||||
/**
|
||||
* 主油箱高度
|
||||
*/
|
||||
private float mainFuelTankHeight;
|
||||
|
||||
public static Location parseFromLocationMsg(LocationMessage msg) {
|
||||
Location location = new Location();
|
||||
location.setPhone(msg.getHeader().getTerminalPhone());
|
||||
BeanUtils.copyProperties(msg, location);
|
||||
return location;
|
||||
}
|
||||
}
|
||||
@ -1,89 +0,0 @@
|
||||
package com.zhgd.netty.tcp.location;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@Data
|
||||
public class LocationMessage extends DataPacket {
|
||||
|
||||
/**
|
||||
* 告警信息 4字节
|
||||
*/
|
||||
private int alarm;
|
||||
/**
|
||||
* 状态 4字节
|
||||
*/
|
||||
private int statusField;
|
||||
/**
|
||||
* 纬度 4字节
|
||||
*/
|
||||
private float latitude;
|
||||
/**
|
||||
* 经度 4字节
|
||||
*/
|
||||
private float longitude;
|
||||
/**
|
||||
* 海拔高度 2字节
|
||||
*/
|
||||
private short elevation;
|
||||
/**
|
||||
* 速度 2字节
|
||||
*/
|
||||
private short speed;
|
||||
/**
|
||||
* 方向 2字节
|
||||
*/
|
||||
private short direction;
|
||||
/**
|
||||
* 时间 6字节BCD
|
||||
*/
|
||||
private String time;
|
||||
/**
|
||||
* 里程
|
||||
*/
|
||||
private Long mileage;
|
||||
/**
|
||||
* 主油箱高度 单位0.1mm
|
||||
*/
|
||||
private float mainFuelTankHeight;
|
||||
|
||||
public LocationMessage(ByteBuf byteBuf) {
|
||||
super(byteBuf);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void parseBody() {
|
||||
//解码器中的MessageDecoder.parse方法会把body消息体传到这里,在这里根据自己的业务进行解析数据
|
||||
ByteBuf bb = this.body;
|
||||
this.setAlarm(bb.readInt());
|
||||
this.setStatusField(bb.readInt());
|
||||
this.setLatitude(bb.readUnsignedInt() * 1.0F / 1000000);
|
||||
this.setLongitude(bb.readUnsignedInt() * 1.0F / 1000000);
|
||||
this.setElevation(bb.readShort());
|
||||
this.setSpeed(bb.readShort());
|
||||
this.setDirection(bb.readShort());
|
||||
this.setTime(BCD.toBcdTimeString(readBytes(6)));
|
||||
//读取其他数据
|
||||
while (bb.isReadable()) {
|
||||
short type = bb.readUnsignedByte();
|
||||
if (!bb.isReadable()) {
|
||||
break;
|
||||
}
|
||||
byte len = bb.readByte();
|
||||
if (type == 1) {
|
||||
if (len == 4) {
|
||||
this.setMileage(bb.readUnsignedInt());
|
||||
}
|
||||
} else if (type == 2) {
|
||||
if (len == 2) {
|
||||
this.setMainFuelTankHeight(bb.readShort());
|
||||
}
|
||||
} else {
|
||||
readBytes(len);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,206 +0,0 @@
|
||||
package com.zhgd.netty.tcp.location;
|
||||
|
||||
import cn.hutool.core.date.DateUnit;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.zhgd.xmgl.modules.vehicleposition.entity.VehiclePositionAlarm;
|
||||
import com.zhgd.xmgl.modules.vehicleposition.entity.VehiclePositionData;
|
||||
import com.zhgd.xmgl.modules.vehicleposition.entity.VehiclePositionDayRecord;
|
||||
import com.zhgd.xmgl.modules.vehicleposition.entity.VehiclePositionDev;
|
||||
import com.zhgd.xmgl.modules.vehicleposition.mapper.VehiclePositionAlarmMapper;
|
||||
import com.zhgd.xmgl.modules.vehicleposition.mapper.VehiclePositionDataMapper;
|
||||
import com.zhgd.xmgl.modules.vehicleposition.mapper.VehiclePositionDayRecordMapper;
|
||||
import com.zhgd.xmgl.modules.vehicleposition.mapper.VehiclePositionDevMapper;
|
||||
import com.zhgd.xmgl.modules.vehicleposition.service.IVehiclePositionAlarmService;
|
||||
import com.zhgd.xmgl.modules.vehicleposition.service.IVehiclePositionDataService;
|
||||
import io.netty.channel.ChannelHandler;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.util.ReferenceCountUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 具体处理类
|
||||
*/
|
||||
@Component
|
||||
@ChannelHandler.Sharable
|
||||
@Slf4j
|
||||
public class LocationMessageHandler extends BaseHandler<LocationMessage> {
|
||||
@Autowired
|
||||
VehiclePositionDataMapper vehiclePositionDataMapper;
|
||||
@Autowired
|
||||
VehiclePositionDevMapper vehiclePositionDevMapper;
|
||||
@Autowired
|
||||
VehiclePositionDayRecordMapper vehiclePositionDayRecordMapper;
|
||||
@Autowired
|
||||
VehiclePositionAlarmMapper vehiclePositionAlarmMapper;
|
||||
@Autowired
|
||||
IVehiclePositionAlarmService vehiclePositionAlarmService;
|
||||
@Autowired
|
||||
IVehiclePositionDataService vehiclePositionDataService;
|
||||
@Autowired
|
||||
@Qualifier("workerGroup")
|
||||
private NioEventLoopGroup workerGroup;
|
||||
|
||||
@Override
|
||||
protected void channelRead0(ChannelHandlerContext ctx, LocationMessage message) {
|
||||
try {
|
||||
//官方建议效验码判断通过后,应立刻给出应答,防止重复请求服务器
|
||||
CommonResponse response = CommonResponse.success(message, getSerialNumber(ctx.channel()), CommonResponse.SUCCESS);
|
||||
workerGroup.execute(() -> write(ctx, response));
|
||||
Location location = Location.parseFromLocationMsg(message);
|
||||
log.info("设备号/手机号:{}", location.getPhone());
|
||||
VehiclePositionDev dev = vehiclePositionDevMapper.selectOne(new LambdaQueryWrapper<VehiclePositionDev>()
|
||||
.eq(VehiclePositionDev::getDevSn, location.getPhone()));
|
||||
if (dev == null) {
|
||||
return;
|
||||
}
|
||||
VehiclePositionData firstData = vehiclePositionDataMapper.getTodayBeginData(dev.getDevSn());
|
||||
VehiclePositionData vehiclePositionData = new VehiclePositionData();
|
||||
vehiclePositionData.setDevSn(dev.getDevSn());
|
||||
//entity.setBatteryPercentage();
|
||||
double hourBt = DateUtil.between(DateUtil.beginOfDay(new Date()), new Date(), DateUnit.HOUR);
|
||||
vehiclePositionData.setMileage(location.getMileage());
|
||||
double trackDistanceDay = 0D;
|
||||
if (firstData != null) {
|
||||
trackDistanceDay = location.getMileage() - firstData.getMileage();
|
||||
vehiclePositionData.setTotalFuelConsumptionDay(location.getMainFuelTankHeight() - firstData.getTotalFuelConsumptionDay());
|
||||
vehiclePositionData.setCumulativeOperatingFuelConsumptionDay(location.getMainFuelTankHeight() - firstData.getTotalFuelConsumptionDay());
|
||||
vehiclePositionData.setTotalSleepTimeDay(firstData.getTotalSleepTimeDay());
|
||||
vehiclePositionData.setTotalWorkTimeDay(hourBt - firstData.getTotalSleepTimeDay());
|
||||
} else {
|
||||
trackDistanceDay = 0D;
|
||||
vehiclePositionData.setCumulativeOperatingFuelConsumptionDay(0D);
|
||||
vehiclePositionData.setTotalFuelConsumptionDay(0D);
|
||||
vehiclePositionData.setTotalSleepTimeDay(hourBt);
|
||||
vehiclePositionData.setTotalWorkTimeDay(0D);
|
||||
}
|
||||
vehiclePositionData.setLongitude(Double.valueOf(location.getLongitude()));
|
||||
vehiclePositionData.setLatitude(Double.valueOf(location.getLatitude()));
|
||||
vehiclePositionData.setProjectSn(dev.getProjectSn());
|
||||
vehiclePositionData.setSpeed(Double.valueOf(location.getSpeed()));
|
||||
vehiclePositionDataService.add(vehiclePositionData);
|
||||
|
||||
//日行数据
|
||||
VehiclePositionDayRecord dayRecordFirst = vehiclePositionDayRecordMapper.getTodayBeginData(dev.getDevSn());
|
||||
if (dayRecordFirst != null) {
|
||||
dayRecordFirst.setTrackDistanceDay(Double.valueOf(location.getMileage()));
|
||||
vehiclePositionDayRecordMapper.updateById(dayRecordFirst);
|
||||
} else {
|
||||
dayRecordFirst = new VehiclePositionDayRecord();
|
||||
dayRecordFirst.setDevSn(dev.getDevSn());
|
||||
dayRecordFirst.setTrackDistanceDay(0D);
|
||||
dayRecordFirst.setDay(DateUtil.today());
|
||||
dayRecordFirst.setProjectSn(dev.getProjectSn());
|
||||
vehiclePositionDayRecordMapper.insert(dayRecordFirst);
|
||||
}
|
||||
|
||||
//报警数据
|
||||
int al = location.getAlarm();
|
||||
if ((al & (1 << 0)) != 0) {
|
||||
addAlarm("紧急报警,触动报警开关后触发", dev);
|
||||
}
|
||||
if ((al & (1 << 1)) != 0) {
|
||||
addAlarm("超速报警", dev);
|
||||
}
|
||||
if ((al & (1 << 2)) != 0) {
|
||||
addAlarm("疲劳驾驶", dev);
|
||||
}
|
||||
if ((al & (1 << 3)) != 0) {
|
||||
addAlarm("危险预警", dev);
|
||||
}
|
||||
if ((al & (1 << 4)) != 0) {
|
||||
addAlarm("GNSS 模块发生故障", dev);
|
||||
}
|
||||
if ((al & (1 << 5)) != 0) {
|
||||
addAlarm("GNSS 天线未接或被剪断", dev);
|
||||
}
|
||||
if ((al & (1 << 6)) != 0) {
|
||||
addAlarm("GNSS 天线短路", dev);
|
||||
}
|
||||
if ((al & (1 << 7)) != 0) {
|
||||
addAlarm("终端主电源欠压", dev);
|
||||
}
|
||||
if ((al & (1 << 8)) != 0) {
|
||||
addAlarm("终端主电源掉电", dev);
|
||||
}
|
||||
if ((al & (1 << 9)) != 0) {
|
||||
addAlarm("终端 LCD 或显示器故障", dev);
|
||||
}
|
||||
if ((al & (1 << 10)) != 0) {
|
||||
addAlarm("TTS 模块故障", dev);
|
||||
}
|
||||
if ((al & (1 << 11)) != 0) {
|
||||
addAlarm("摄像头故障", dev);
|
||||
}
|
||||
if ((al & (1 << 12)) != 0) {
|
||||
addAlarm("道路运输证 IC 卡模块故障", dev);
|
||||
}
|
||||
if ((al & (1 << 13)) != 0) {
|
||||
addAlarm("超速预警", dev);
|
||||
}
|
||||
if ((al & (1 << 14)) != 0) {
|
||||
addAlarm("疲劳驾驶预警", dev);
|
||||
}
|
||||
if ((al & (1 << 18)) != 0) {
|
||||
addAlarm("当天累计驾驶超时", dev);
|
||||
}
|
||||
if ((al & (1 << 19)) != 0) {
|
||||
addAlarm("超时停车", dev);
|
||||
}
|
||||
if ((al & (1 << 20)) != 0) {
|
||||
addAlarm("进出区域", dev);
|
||||
}
|
||||
if ((al & (1 << 21)) != 0) {
|
||||
addAlarm("进出路线", dev);
|
||||
}
|
||||
if ((al & (1 << 22)) != 0) {
|
||||
addAlarm("路段行驶时间不足/过长", dev);
|
||||
}
|
||||
if ((al & (1 << 23)) != 0) {
|
||||
addAlarm("路线偏离报警", dev);
|
||||
}
|
||||
if ((al & (1 << 24)) != 0) {
|
||||
addAlarm("车辆 VSS 故障", dev);
|
||||
}
|
||||
if ((al & (1 << 25)) != 0) {
|
||||
addAlarm("车辆油量异常", dev);
|
||||
}
|
||||
if ((al & (1 << 26)) != 0) {
|
||||
addAlarm("车辆被盗(通过车辆防盗器)", dev);
|
||||
}
|
||||
if ((al & (1 << 27)) != 0) {
|
||||
addAlarm("车辆非法点火", dev);
|
||||
}
|
||||
if ((al & (1 << 28)) != 0) {
|
||||
addAlarm("车辆非法位移", dev);
|
||||
}
|
||||
if ((al & (1 << 29)) != 0) {
|
||||
addAlarm("碰撞预警", dev);
|
||||
}
|
||||
if ((al & (1 << 30)) != 0) {
|
||||
addAlarm("侧翻预警", dev);
|
||||
}
|
||||
if ((al & (1 << 31)) != 0) {
|
||||
addAlarm("非法开门报警(终端未设置区域时,不判断非法开门)", dev);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("LocationMessageHandler 解析报文信息发生错误", e);
|
||||
} finally {
|
||||
ReferenceCountUtil.release(message.getBody());
|
||||
}
|
||||
}
|
||||
|
||||
private void addAlarm(String alarmInformation, VehiclePositionDev dev) {
|
||||
VehiclePositionAlarm alarm = new VehiclePositionAlarm();
|
||||
alarm.setDevSn(dev.getDevSn());
|
||||
alarm.setAlarmInformation(alarmInformation);
|
||||
alarm.setProjectSn(dev.getProjectSn());
|
||||
vehiclePositionAlarmMapper.insert(alarm);
|
||||
}
|
||||
}
|
||||
@ -1,110 +0,0 @@
|
||||
package com.zhgd.netty.tcp.location;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufAllocator;
|
||||
import io.netty.buffer.ByteBufUtil;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.ByteToMessageDecoder;
|
||||
import io.netty.util.ReferenceCountUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
public class MessageDecoder extends ByteToMessageDecoder {
|
||||
|
||||
@Override
|
||||
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) {
|
||||
log.info("<<<<< ip:{},hex:{}", ctx.channel().remoteAddress(), ByteBufUtil.hexDump(in).toLowerCase());
|
||||
DataPacket msg = null;
|
||||
msg = decode(in);
|
||||
if (msg != null) {
|
||||
out.add(msg);
|
||||
}
|
||||
}
|
||||
|
||||
private DataPacket decode(ByteBuf in) {
|
||||
int i = 12;
|
||||
//包头最小长度
|
||||
if (in.readableBytes() < i) {
|
||||
return null;
|
||||
}
|
||||
//第一步 转义
|
||||
byte[] raw = new byte[in.readableBytes()];
|
||||
in.readBytes(raw);
|
||||
ByteBuf escape = revert(raw);
|
||||
//第二步 校验
|
||||
byte pkgCheckSum = escape.getByte(escape.writerIndex() - 1);
|
||||
//排除校验码
|
||||
escape.writerIndex(escape.writerIndex() - 1);
|
||||
byte calCheckSum = BCD.xorSumBytes(escape);
|
||||
if (pkgCheckSum != calCheckSum) {
|
||||
log.warn("校验码错误,pkgCheckSum:{},calCheckSum:{}", pkgCheckSum, calCheckSum);
|
||||
ReferenceCountUtil.safeRelease(escape);
|
||||
return null;
|
||||
}
|
||||
//第三步:解码
|
||||
return parse(escape);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将接收到的原始转义数据还原
|
||||
*
|
||||
* @param raw
|
||||
* @return
|
||||
*/
|
||||
public ByteBuf revert(byte[] raw) {
|
||||
int len = raw.length;
|
||||
//DataPacket parse方法回收
|
||||
ByteBuf buf = ByteBufAllocator.DEFAULT.heapBuffer(len);
|
||||
for (int i = 0; i < len; i++) {
|
||||
//这里如果最后一位是0x7d会导致index溢出,说明原始报文转义有误
|
||||
if (raw[i] == 0x7d && raw[i + 1] == 0x01) {
|
||||
buf.writeByte(0x7d);
|
||||
i++;
|
||||
} else if (raw[i] == 0x7d && raw[i + 1] == 0x02) {
|
||||
buf.writeByte(0x7e);
|
||||
i++;
|
||||
} else {
|
||||
buf.writeByte(raw[i]);
|
||||
}
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
public DataPacket parse(ByteBuf bb) {
|
||||
DataPacket packet = null;
|
||||
short msgId = bb.getShort(bb.readerIndex());
|
||||
switch (msgId) {
|
||||
case Const.TERNIMAL_MSG_HEARTBEAT:
|
||||
//packet = new HeartBeatMessage(bb);
|
||||
break;
|
||||
//0200
|
||||
case Const.TERNIMAL_MSG_LOCATION:
|
||||
packet = new LocationMessage(bb);
|
||||
break;
|
||||
//0704
|
||||
case Const.TERNIMAL_MSG_LOCATION_BATCH:
|
||||
//packet = new LocationBatchMessage(bb);
|
||||
break;
|
||||
case Const.TERNIMAL_MSG_REGISTER:
|
||||
//packet = new RegisterMessage(bb);
|
||||
break;
|
||||
case Const.TERNIMAL_MSG_AUTH:
|
||||
//packet = new AuthMessage(bb);
|
||||
break;
|
||||
// case Const.TERNIMAL_MSG_STATUS: 0900数据
|
||||
// packet = new StatusMessage(bb);
|
||||
// break;
|
||||
default:
|
||||
packet = new DataPacket(bb);
|
||||
break;
|
||||
}
|
||||
packet.parse();
|
||||
return packet;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -1,84 +0,0 @@
|
||||
package com.zhgd.netty.tcp.location;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufAllocator;
|
||||
import io.netty.channel.ChannelHandler;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.MessageToByteEncoder;
|
||||
import io.netty.util.ReferenceCountUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
@ChannelHandler.Sharable
|
||||
public class MessageEncoder extends MessageToByteEncoder<DataPacket> {
|
||||
|
||||
@Override
|
||||
protected void encode(ChannelHandlerContext ctx, DataPacket msg, ByteBuf out) throws Exception {
|
||||
log.debug(msg.toString());
|
||||
//第一步:转换
|
||||
ByteBuf bb = msg.toByteBufMsg();
|
||||
//标记一下,先到前面去写覆盖的,然后回到标记写校验码
|
||||
bb.markWriterIndex();
|
||||
//包体长度=总长度-头部长度
|
||||
short bodyLen = (short) (bb.readableBytes() - 12);
|
||||
short bodyProps = createDefaultMsgBodyProperty(bodyLen);
|
||||
//覆盖占用的4字节
|
||||
bb.writerIndex(0);
|
||||
bb.writeShort(msg.getHeader().getMsgId());
|
||||
bb.writeShort(bodyProps);
|
||||
bb.resetWriterIndex();
|
||||
bb.writeByte(BCD.xorSumBytes(bb));
|
||||
//log.info(">>>>> ip:{},hex:{}\n", ctx.channel().remoteAddress(), ByteBufUtil.hexDump(bb).toLowerCase());
|
||||
//第二步:转义
|
||||
ByteBuf escape = escape(bb);
|
||||
out.writeBytes(escape);
|
||||
ReferenceCountUtil.safeRelease(escape);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转义待发送数据
|
||||
*
|
||||
* @param raw
|
||||
* @return
|
||||
*/
|
||||
public ByteBuf escape(ByteBuf raw) {
|
||||
int len = raw.readableBytes();
|
||||
ByteBuf buf = ByteBufAllocator.DEFAULT.directBuffer(len + 12);
|
||||
buf.writeByte(Const.PKG_DELIMITER);
|
||||
while (len > 0) {
|
||||
byte b = raw.readByte();
|
||||
if (b == 0x7e) {
|
||||
buf.writeByte(0x7d);
|
||||
buf.writeByte(0x02);
|
||||
} else if (b == 0x7d) {
|
||||
buf.writeByte(0x7d);
|
||||
buf.writeByte(0x01);
|
||||
} else {
|
||||
buf.writeByte(b);
|
||||
}
|
||||
len--;
|
||||
}
|
||||
ReferenceCountUtil.safeRelease(raw);
|
||||
buf.writeByte(Const.PKG_DELIMITER);
|
||||
return buf;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成header中的消息体属性
|
||||
*
|
||||
* @param bodyLen
|
||||
* @return
|
||||
*/
|
||||
public static short createDefaultMsgBodyProperty(short bodyLen) {
|
||||
return createMsgBodyProperty(bodyLen, (byte) 0, false, (byte) 0);
|
||||
}
|
||||
|
||||
public static short createMsgBodyProperty(short bodyLen, byte encType, boolean isSubPackage, byte reversed) {
|
||||
int subPkg = isSubPackage ? 1 : 0;
|
||||
int ret = (bodyLen & 0x3FF) | ((encType << 10) & 0x1C00) | ((subPkg << 13) & 0x2000)
|
||||
| ((reversed << 14) & 0xC000);
|
||||
return (short) (ret & 0xffff);
|
||||
}
|
||||
}
|
||||
@ -1,77 +0,0 @@
|
||||
package com.zhgd.netty.tcp.location;
|
||||
|
||||
import com.zhgd.netty.tcp.listener.BindListener;
|
||||
import com.zhgd.netty.tcp.listener.CloseListener;
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.ChannelOption;
|
||||
import io.netty.channel.EventLoopGroup;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
||||
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
/**
|
||||
* @author jt808协议解析-人员车辆定位
|
||||
* @date 2020/03/17 15:45:35
|
||||
*/
|
||||
@Component
|
||||
@Slf4j
|
||||
public class TcpNettyServerJT808 {
|
||||
@Autowired
|
||||
private LocationMessageHandler locationMessageHandler;
|
||||
@Autowired
|
||||
private MessageEncoder messageEncoder;
|
||||
@Autowired
|
||||
@Qualifier("bossGroup")
|
||||
private NioEventLoopGroup bossGroup;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("workerGroup")
|
||||
private NioEventLoopGroup workerGroup;
|
||||
|
||||
@Value("${jt808.port:15003}")
|
||||
int port;
|
||||
|
||||
@PostConstruct
|
||||
private void startTcpServer() {
|
||||
try {
|
||||
ServerBootstrap serverBootstrap = new ServerBootstrap();
|
||||
serverBootstrap.group(bossGroup, workerGroup)
|
||||
.channel(NioServerSocketChannel.class)
|
||||
.option(ChannelOption.SO_BACKLOG, 1024)
|
||||
.childOption(ChannelOption.SO_KEEPALIVE, true)
|
||||
.childHandler(new ChannelInitializer<SocketChannel>() {
|
||||
@Override
|
||||
protected void initChannel(SocketChannel socketChannel) throws Exception {
|
||||
//添加自定义处理器
|
||||
socketChannel.pipeline()
|
||||
.addLast(new DelimiterBasedFrameDecoder(1100, Unpooled.copiedBuffer(new byte[]{JT808Const.PKG_DELIMITER}),
|
||||
Unpooled.copiedBuffer(new byte[]{JT808Const.PKG_DELIMITER, JT808Const.PKG_DELIMITER})))
|
||||
.addLast(new MessageDecoder())
|
||||
.addLast(messageEncoder)
|
||||
.addLast(locationMessageHandler);
|
||||
}
|
||||
});
|
||||
//监听器,当服务绑定成功后执行
|
||||
ChannelFuture channelFuture = serverBootstrap.bind(port).addListener(new BindListener());
|
||||
//监听器,当停止服务后执行。
|
||||
channelFuture.channel().closeFuture().addListener(new CloseListener());
|
||||
} catch (Exception e) {
|
||||
log.error("err:", e);
|
||||
bossGroup.shutdownGracefully();
|
||||
workerGroup.shutdownGracefully();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -122,7 +122,7 @@ public class LoginController {
|
||||
@ApiImplicitParam(name = "projectSn", value = "项目SN", paramType = "body", required = false, dataType = "String"),
|
||||
})
|
||||
@PostMapping(value = "/workerInfo/login")
|
||||
public Result<WorkerInfo> workerInfoLogin(@RequestBody Map<String, Object> map) {
|
||||
public Result<Map<String, Object>> workerInfoLogin(@RequestBody Map<String, Object> map) {
|
||||
return Result.success(systemUserService.workerInfoLogin(map));
|
||||
}
|
||||
|
||||
|
||||
@ -294,7 +294,7 @@ public interface ISystemUserService extends IService<SystemUser> {
|
||||
* @param map
|
||||
* @return
|
||||
*/
|
||||
WorkerInfo workerInfoLogin(Map<String, Object> map);
|
||||
Map<String, Object> workerInfoLogin(Map<String, Object> map);
|
||||
|
||||
/**
|
||||
* 是否项目子账号
|
||||
|
||||
@ -1767,7 +1767,7 @@ public class SystemUserServiceImpl extends ServiceImpl<SystemUserMapper, SystemU
|
||||
}
|
||||
|
||||
@Override
|
||||
public WorkerInfo workerInfoLogin(Map<String, Object> map) {
|
||||
public Map<String, Object> workerInfoLogin(Map<String, Object> map) {
|
||||
String idCard = MapUtils.getString(map, "idCard");
|
||||
String name = MapUtils.getString(map, "name");
|
||||
String projectSn = MapUtils.getString(map, "projectSn");
|
||||
@ -1789,7 +1789,10 @@ public class SystemUserServiceImpl extends ServiceImpl<SystemUserMapper, SystemU
|
||||
throw new OpenAlertException("身份证号和人员姓名不匹配");
|
||||
}
|
||||
}
|
||||
return workerInfos.get(0);
|
||||
WorkerInfo workerInfo = workerInfos.get(0);
|
||||
Map<String, Object> paramMap = new HashMap<>();
|
||||
paramMap.put("workerId", workerInfo.getId());
|
||||
return (Map<String, Object>) workerInfoService.viewWorkerInfoDetail(paramMap).get("workerInfo");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -105,6 +105,9 @@ public class ZtWorkerController {
|
||||
requestMap.put("project_id", "1833149331474493441");
|
||||
requestMap.put("device_numbers", "E03C1CB19B121805,E03C1CB19F7A1805");
|
||||
String post = HttpUtil.post("http://spm3.1357.cn/hewu-api/api/realtime-records", JSON.toJSONString(requestMap));
|
||||
if (!post.contains("[")) {
|
||||
return Result.success(new ArrayList<>());
|
||||
}
|
||||
return Result.success(JSONArray.parseArray(post));
|
||||
}
|
||||
|
||||
|
||||
@ -11,51 +11,67 @@ import lombok.Data;
|
||||
import org.jeecgframework.poi.excel.annotation.Excel;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
|
||||
/**
|
||||
* @Description: 项目配置
|
||||
* @author: pds
|
||||
* @date: 2020-08-17
|
||||
* @date: 2020-08-17
|
||||
* @version: V1.0
|
||||
*/
|
||||
@Data
|
||||
@TableName("project_config")
|
||||
@ApiModel(value="ProjectConfig实体类",description="ProjectConfig")
|
||||
@ApiModel(value = "ProjectConfig实体类", description = "ProjectConfig")
|
||||
public class ProjectConfig implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**id*/
|
||||
|
||||
/**
|
||||
* id
|
||||
*/
|
||||
@TableId(type = IdType.ASSIGN_ID)
|
||||
@ApiModelProperty(value="id")
|
||||
private java.lang.Long id ;
|
||||
/**项目SN*/
|
||||
@Excel(name = "项目SN", width = 15)
|
||||
@ApiModelProperty(value="项目SN")
|
||||
private java.lang.String projectSn ;
|
||||
/**人员定位类型,1卡片,2信标*/
|
||||
@Excel(name = "人员定位类型,1卡片,2信标", width = 15)
|
||||
@ApiModelProperty(value="人员定位类型,1卡片,2信标")
|
||||
private java.lang.Integer positionType ;
|
||||
/**手机打卡是否需要图片,0否,1是*/
|
||||
@Excel(name = "手机打卡是否需要图片,0否,1是", width = 15)
|
||||
@ApiModelProperty(value="手机打卡是否需要图片,0否,1是")
|
||||
private java.lang.Integer phoneClockImageType ;
|
||||
@ApiModelProperty(value = "id")
|
||||
private java.lang.Long id;
|
||||
/**
|
||||
* 项目SN
|
||||
*/
|
||||
@Excel(name = "项目SN", width = 15)
|
||||
@ApiModelProperty(value = "项目SN")
|
||||
private java.lang.String projectSn;
|
||||
/**
|
||||
* 人员定位类型,1卡片,2信标
|
||||
*/
|
||||
@Excel(name = "人员定位类型,1卡片,2信标", width = 15)
|
||||
@ApiModelProperty(value = "人员定位类型,1卡片,2信标")
|
||||
private java.lang.Integer positionType;
|
||||
/**
|
||||
* 手机打卡是否需要图片,0否,1是
|
||||
*/
|
||||
@Excel(name = "手机打卡是否需要图片,0否,1是", width = 15)
|
||||
@ApiModelProperty(value = "手机打卡是否需要图片,0否,1是")
|
||||
private java.lang.Integer phoneClockImageType;
|
||||
|
||||
/**人脸比对分数,0-100*/
|
||||
/**
|
||||
* 人脸比对分数,0-100
|
||||
*/
|
||||
@Excel(name = "人脸比对分数,0-100", width = 15)
|
||||
@ApiModelProperty(value="人脸比对分数,0-100")
|
||||
private java.lang.Integer faceScore ;
|
||||
@ApiModelProperty(value = "人脸比对分数,0-100")
|
||||
private java.lang.Integer faceScore;
|
||||
|
||||
/**手机录入人员是否需要审核,1是,0否*/
|
||||
/**
|
||||
* 手机录入人员是否需要审核,1是,0否
|
||||
*/
|
||||
@Excel(name = "手机录入人员是否需要审核,1是,0否", width = 15)
|
||||
@ApiModelProperty(value="手机录入人员是否需要审核,1是,0否")
|
||||
private java.lang.Integer workerAuditType ;
|
||||
@ApiModelProperty(value = "手机录入人员是否需要审核,1是,0否")
|
||||
private java.lang.Integer workerAuditType;
|
||||
|
||||
@Excel(name = "是否是深圳项目,1是,0否", width = 15)
|
||||
@ApiModelProperty(value="是否是深圳项目,1是,0否")
|
||||
private java.lang.Integer isSzProject ;
|
||||
@ApiModelProperty(value = "是否是深圳项目,1是,0否")
|
||||
private java.lang.Integer isSzProject;
|
||||
|
||||
@Excel(name = "是否是深圳项目,1是,0否", width = 15)
|
||||
@ApiModelProperty(value="外部平台项目审核状态,1待审核,2审核通过,3驳回")
|
||||
private java.lang.Integer projectAuditStatus ;
|
||||
@ApiModelProperty(value = "外部平台项目审核状态,1待审核,2审核通过,3驳回")
|
||||
private java.lang.Integer projectAuditStatus;
|
||||
|
||||
@Excel(name = "是否启用移动考勤,1是,0否", width = 15)
|
||||
@ApiModelProperty(value = "是否启用移动考勤,1是,0否")
|
||||
private java.lang.Integer isMobileAttendance;
|
||||
}
|
||||
|
||||
@ -119,5 +119,20 @@ public class WorkerAttendanceRuleController {
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 通过i查询
|
||||
* @param
|
||||
* @return
|
||||
*/
|
||||
@OperLog(operModul = "劳务管理",operType = "查询考勤规则信息",operDesc = "查询考勤规则信息")
|
||||
@ApiOperation(value = "查询考勤规则信息", notes = "查询考勤规则信息", httpMethod = "POST")
|
||||
@ApiImplicitParam(name = "id", value = "考勤规则ID", paramType = "body", required = true, dataType = "Integer")
|
||||
@PostMapping(value = "/queryById")
|
||||
public Result<WorkerAttendanceRule> queryById(@RequestBody Map<String,Object> map) {
|
||||
Result<WorkerAttendanceRule> result = new Result<WorkerAttendanceRule>();
|
||||
WorkerAttendanceRule workerAttendanceRule = workerAttendanceRuleService.getById(MapUtils.getString(map,"id"));
|
||||
result.setSuccess(true);
|
||||
result.setResult(workerAttendanceRule);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@ -60,6 +60,17 @@ public class WorkerMonthAttendanceStatisticsController {
|
||||
return Result.success(workerMonthAttendanceStatisticsService.selectMonthAttendanceByPage(map));
|
||||
}
|
||||
|
||||
@ApiOperation(value = "查询人员考勤统计列表", notes = "查询人员考勤统计列表", httpMethod = "POST")
|
||||
@ApiImplicitParams({
|
||||
@ApiImplicitParam(name = "workerId", value = "人员ID", paramType = "body", required = false, dataType = "String"),
|
||||
@ApiImplicitParam(name = "projectSn", value = "项目SN", paramType = "body", required = true, dataType = "String"),
|
||||
@ApiImplicitParam(name = "monthTime", value = "日期 格式2020-08", paramType = "body", required = true, dataType = "String"),
|
||||
})
|
||||
@PostMapping(value = "/selectMonthAttendanceByWorkerId")
|
||||
public Result<EntityMap> selectMonthAttendanceByWorkerId(@RequestBody Map<String,Object> map) {
|
||||
return Result.success(workerMonthAttendanceStatisticsService.selectMonthAttendanceByWorkerId(map));
|
||||
}
|
||||
|
||||
@ApiOperation(value = "查询人员指定月份考勤统计", notes = "查询人员指定月份考勤统计", httpMethod = "POST")
|
||||
@ApiImplicitParams({
|
||||
@ApiImplicitParam(name = "personSn", value = "人员唯一标识", paramType = "body", required = true, dataType = "String"),
|
||||
|
||||
@ -133,6 +133,11 @@ public class WorkerAttendance implements Serializable {
|
||||
*/
|
||||
@ApiModelProperty(value = "辅助考勤(补卡)原因")
|
||||
private java.lang.String reissueCardReason;
|
||||
/**
|
||||
* 打卡地址
|
||||
*/
|
||||
@ApiModelProperty(value = "打卡地址")
|
||||
private java.lang.String address;
|
||||
|
||||
/**
|
||||
* 出入时间的小时
|
||||
|
||||
@ -11,72 +11,108 @@ import lombok.Data;
|
||||
import org.jeecgframework.poi.excel.annotation.Excel;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
|
||||
/**
|
||||
* @Description: 考勤规则
|
||||
* @author: pds
|
||||
* @date: 2020-09-24
|
||||
* @date: 2020-09-24
|
||||
* @version: V1.0
|
||||
*/
|
||||
@Data
|
||||
@TableName("worker_attendance_rule")
|
||||
@ApiModel(value="WorkerAttendanceRule实体类",description="WorkerAttendanceRule")
|
||||
@ApiModel(value = "WorkerAttendanceRule实体类", description = "WorkerAttendanceRule")
|
||||
public class WorkerAttendanceRule implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**id*/
|
||||
|
||||
/**
|
||||
* id
|
||||
*/
|
||||
@TableId(type = IdType.ASSIGN_ID)
|
||||
@ApiModelProperty(value="id")
|
||||
private java.lang.Long id ;
|
||||
/**项目sn*/
|
||||
@Excel(name = "项目sn", width = 15)
|
||||
@ApiModelProperty(value="项目sn")
|
||||
private java.lang.String projectSn ;
|
||||
/**规则名称*/
|
||||
@Excel(name = "规则名称", width = 15)
|
||||
@ApiModelProperty(value="规则名称")
|
||||
private java.lang.String ruleName ;
|
||||
/**开始上班时间*/
|
||||
@Excel(name = "开始上班时间", width = 15)
|
||||
@ApiModelProperty(value="开始上班时间")
|
||||
private java.lang.String startTime ;
|
||||
/**结束上班时间*/
|
||||
@Excel(name = "结束上班时间", width = 15)
|
||||
@ApiModelProperty(value="结束上班时间")
|
||||
private java.lang.String endTime ;
|
||||
/**迟到多久不算迟到*/
|
||||
@Excel(name = "迟到多久不算迟到", width = 15)
|
||||
@ApiModelProperty(value="迟到多久不算迟到")
|
||||
private java.lang.Integer notLater ;
|
||||
/**迟到多久算旷工*/
|
||||
@Excel(name = "迟到多久算旷工", width = 15)
|
||||
@ApiModelProperty(value="迟到多久算旷工")
|
||||
private java.lang.Integer yesLater ;
|
||||
/**早退多久不算早退*/
|
||||
@Excel(name = "早退多久不算早退", width = 15)
|
||||
@ApiModelProperty(value="早退多久不算早退")
|
||||
private java.lang.Integer notAdvance ;
|
||||
/**早退多久算旷工*/
|
||||
@Excel(name = "早退多久算旷工", width = 15)
|
||||
@ApiModelProperty(value="早退多久算旷工")
|
||||
private java.lang.Integer yesAdvance ;
|
||||
/**是否启用加班*/
|
||||
@Excel(name = "是否启用加班", width = 15)
|
||||
@ApiModelProperty(value="是否启用加班")
|
||||
private java.lang.Integer yesOrNotOvertime ;
|
||||
/**几点之后算加班*/
|
||||
@Excel(name = "几点之后算加班", width = 15)
|
||||
@ApiModelProperty(value="几点之后算加班")
|
||||
private java.lang.String overtimeDate ;
|
||||
/**是否需要中午外出,0否,1是*/
|
||||
@Excel(name = "是否需要中午外出,0否,1是", width = 15)
|
||||
@ApiModelProperty(value="是否需要中午外出,0否,1是")
|
||||
private java.lang.Integer isRest ;
|
||||
/**上午下班时间*/
|
||||
@Excel(name = "上午下班时间", width = 15)
|
||||
@ApiModelProperty(value="上午下班时间")
|
||||
private java.lang.String morningEndTime ;
|
||||
/**下午上班时间*/
|
||||
@Excel(name = "下午上班时间", width = 15)
|
||||
@ApiModelProperty(value="下午上班时间")
|
||||
private java.lang.String afternoonStartTime ;
|
||||
@ApiModelProperty(value = "id")
|
||||
private java.lang.Long id;
|
||||
/**
|
||||
* 项目sn
|
||||
*/
|
||||
@Excel(name = "项目sn", width = 15)
|
||||
@ApiModelProperty(value = "项目sn")
|
||||
private java.lang.String projectSn;
|
||||
/**
|
||||
* 规则名称
|
||||
*/
|
||||
@Excel(name = "规则名称", width = 15)
|
||||
@ApiModelProperty(value = "规则名称")
|
||||
private java.lang.String ruleName;
|
||||
/**
|
||||
* 开始上班时间
|
||||
*/
|
||||
@Excel(name = "开始上班时间", width = 15)
|
||||
@ApiModelProperty(value = "开始上班时间")
|
||||
private java.lang.String startTime;
|
||||
/**
|
||||
* 结束上班时间
|
||||
*/
|
||||
@Excel(name = "结束上班时间", width = 15)
|
||||
@ApiModelProperty(value = "结束上班时间")
|
||||
private java.lang.String endTime;
|
||||
/**
|
||||
* 迟到多久不算迟到
|
||||
*/
|
||||
@Excel(name = "迟到多久不算迟到", width = 15)
|
||||
@ApiModelProperty(value = "迟到多久不算迟到")
|
||||
private java.lang.Integer notLater;
|
||||
/**
|
||||
* 迟到多久算旷工
|
||||
*/
|
||||
@Excel(name = "迟到多久算旷工", width = 15)
|
||||
@ApiModelProperty(value = "迟到多久算旷工")
|
||||
private java.lang.Integer yesLater;
|
||||
/**
|
||||
* 早退多久不算早退
|
||||
*/
|
||||
@Excel(name = "早退多久不算早退", width = 15)
|
||||
@ApiModelProperty(value = "早退多久不算早退")
|
||||
private java.lang.Integer notAdvance;
|
||||
/**
|
||||
* 早退多久算旷工
|
||||
*/
|
||||
@Excel(name = "早退多久算旷工", width = 15)
|
||||
@ApiModelProperty(value = "早退多久算旷工")
|
||||
private java.lang.Integer yesAdvance;
|
||||
/**
|
||||
* 是否启用加班
|
||||
*/
|
||||
@Excel(name = "是否启用加班", width = 15)
|
||||
@ApiModelProperty(value = "是否启用加班")
|
||||
private java.lang.Integer yesOrNotOvertime;
|
||||
/**
|
||||
* 几点之后算加班
|
||||
*/
|
||||
@Excel(name = "几点之后算加班", width = 15)
|
||||
@ApiModelProperty(value = "几点之后算加班")
|
||||
private java.lang.String overtimeDate;
|
||||
/**
|
||||
* 是否需要中午外出,0否,1是
|
||||
*/
|
||||
@Excel(name = "是否需要中午外出,0否,1是", width = 15)
|
||||
@ApiModelProperty(value = "是否需要中午外出,0否,1是")
|
||||
private java.lang.Integer isRest;
|
||||
/**
|
||||
* 上午下班时间
|
||||
*/
|
||||
@Excel(name = "上午下班时间", width = 15)
|
||||
@ApiModelProperty(value = "上午下班时间")
|
||||
private java.lang.String morningEndTime;
|
||||
/**
|
||||
* 下午上班时间
|
||||
*/
|
||||
@Excel(name = "下午上班时间", width = 15)
|
||||
@ApiModelProperty(value = "下午上班时间")
|
||||
private java.lang.String afternoonStartTime;
|
||||
|
||||
/**
|
||||
* 是否启用一天一打卡(1:启用;0:不启用;)
|
||||
*/
|
||||
@Excel(name = "是否启用一天一打卡(1:启用;0:不启用;)", width = 15)
|
||||
@ApiModelProperty(value = "是否启用一天一打卡(1:启用;0:不启用;)")
|
||||
private java.lang.Integer onceAttendance;
|
||||
}
|
||||
|
||||
@ -138,7 +138,7 @@ public interface WorkerAttendanceMapper extends BaseMapper<WorkerAttendance> {
|
||||
* @param workerId
|
||||
* @return
|
||||
*/
|
||||
String getWorkerAttendanceMinTime(@Param("workerId") Integer workerId);
|
||||
String getWorkerAttendanceMinTime(@Param("workerId") String workerId);
|
||||
|
||||
/**
|
||||
* 查询今日最晚通行的时间
|
||||
@ -146,7 +146,7 @@ public interface WorkerAttendanceMapper extends BaseMapper<WorkerAttendance> {
|
||||
* @param workerId
|
||||
* @return
|
||||
*/
|
||||
String getWorkerAttendanceMaxTime(@Param("workerId") Integer workerId);
|
||||
String getWorkerAttendanceMaxTime(@Param("workerId") String workerId);
|
||||
|
||||
/**
|
||||
* 统计某日出勤人数统计
|
||||
|
||||
@ -28,6 +28,8 @@ public interface WorkerMonthAttendanceStatisticsMapper extends BaseMapper<Worker
|
||||
*/
|
||||
List<EntityMap> selectMonthAttendanceByPage(Page<EntityMap> page, @Param("param") Map<String, Object> map);
|
||||
|
||||
EntityMap selectMonthAttendanceByWorkerId( @Param("param") Map<String, Object> map);
|
||||
|
||||
/**
|
||||
* 更新人员考勤统计
|
||||
*
|
||||
|
||||
@ -10,6 +10,7 @@
|
||||
a.pass_type,
|
||||
a.project_sn,
|
||||
a.temperature,
|
||||
a.address,
|
||||
IF(a.image_url IS NOT NULL, a.image_url, b.id_card_big_photo_url) image_url,
|
||||
b.worker_name,
|
||||
b.id_card
|
||||
@ -329,7 +330,7 @@
|
||||
INNER JOIN worker_info b ON a.person_sn = b.person_sn
|
||||
WHERE b.id = #{workerId}
|
||||
AND a.pass_type = 2
|
||||
AND a.is_statistics is null
|
||||
AND a.is_statistics = 0
|
||||
AND a.create_time >= CONCAT(DATE_FORMAT(NOW(), "%Y-%m-%d"), ' 00:00:00')
|
||||
and a.create_time <= CONCAT(DATE_FORMAT(NOW(), "%Y-%m-%d"), ' 23:59:59')
|
||||
</select>
|
||||
|
||||
@ -42,6 +42,24 @@
|
||||
</foreach>
|
||||
</if>
|
||||
</select>
|
||||
|
||||
<select id="selectMonthAttendanceByWorkerId" resultType="com.zhgd.jeecg.common.mybatis.EntityMap">
|
||||
select
|
||||
a.worker_name,a.id_card,a.project_sn,a.person_sn,a.person_type, c.query_time,a.inService_type,
|
||||
day1, day2, day3,
|
||||
day4, day5, day6, day7, day8, day9, day10, day11, day12, day13, day14, day15, day16,
|
||||
day17, day18, day19, day20, day21, day22, day23, day24, day25, day26, day27, day28,
|
||||
IF(29>DAY(last_day(str_to_date(CONCAT(#{param.monthTime},'-01'),'%Y-%m-%d'))),NULL,day29) day29,
|
||||
IF(30>DAY(last_day(str_to_date(CONCAT(#{param.monthTime},'-01'),'%Y-%m-%d'))),NULL,day30) day30,
|
||||
IF(31>DAY(last_day(str_to_date(CONCAT(#{param.monthTime},'-01'),'%Y-%m-%d'))),NULL,day31) day31
|
||||
from worker_info a
|
||||
LEFT JOIN worker_month_attendance_statistics c ON a.person_sn=c.person_sn AND c.query_time=#{param.monthTime}
|
||||
where a.project_sn=#{param.projectSn}
|
||||
<if test="param.workerId != null and param.workerId != ''">
|
||||
and a.id=#{param.workerId}
|
||||
</if>
|
||||
</select>
|
||||
|
||||
<select id="selectWorkerMonthAttendanceTotal" resultType="com.zhgd.jeecg.common.mybatis.EntityMap">
|
||||
select a.worker_name,
|
||||
a.id_card,
|
||||
|
||||
@ -26,6 +26,7 @@ public interface IWorkerMonthAttendanceStatisticsService extends IService<Worker
|
||||
*/
|
||||
IPage<EntityMap> selectMonthAttendanceByPage(Map<String, Object> map);
|
||||
|
||||
EntityMap selectMonthAttendanceByWorkerId(Map<String, Object> map);
|
||||
/**
|
||||
* 统计前一天的考勤状况
|
||||
*
|
||||
|
||||
@ -495,8 +495,8 @@ public class WorkerAttendanceServiceImpl extends ServiceImpl<WorkerAttendanceMap
|
||||
@Override
|
||||
public Map<String, Object> getPersonAttendanceTime(Map<String, Object> map) {
|
||||
Map<String, Object> data = new HashMap<>(16);
|
||||
String enterTime = workerAttendanceMapper.getWorkerAttendanceMinTime(MapUtils.getInteger(map, "workerId"));
|
||||
String outTime = workerAttendanceMapper.getWorkerAttendanceMaxTime(MapUtils.getInteger(map, "workerId"));
|
||||
String enterTime = workerAttendanceMapper.getWorkerAttendanceMinTime(MapUtils.getString(map, "workerId"));
|
||||
String outTime = workerAttendanceMapper.getWorkerAttendanceMaxTime(MapUtils.getString(map, "workerId"));
|
||||
data.put("enterTime", enterTime);
|
||||
data.put("outTime", outTime);
|
||||
return data;
|
||||
@ -515,6 +515,7 @@ public class WorkerAttendanceServiceImpl extends ServiceImpl<WorkerAttendanceMap
|
||||
workerAttendance.setAttendanceType(1);
|
||||
workerAttendance.setCardType(7);
|
||||
workerAttendance.setImageUrl(MapUtils.getString(map, "photoUrl"));
|
||||
workerAttendance.setAddress(MapUtils.getString(map, "address"));
|
||||
workerAttendanceMapper.insert(workerAttendance);
|
||||
if (environmentUtil.isJlwProd()) {
|
||||
// 向政务平台发送数据
|
||||
|
||||
@ -106,6 +106,27 @@ public class WorkerMonthAttendanceStatisticsServiceImpl extends ServiceImpl<Work
|
||||
return page.setRecords(list);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityMap selectMonthAttendanceByWorkerId(Map<String, Object> map) {
|
||||
EntityMap entityMap = workerMonthAttendanceStatisticsMapper.selectMonthAttendanceByWorkerId(map);
|
||||
Integer type = MapUtils.getInteger(map, "type");
|
||||
if (Objects.equals(type, 1)) {
|
||||
//1只显示出勤和未出勤(除了未出勤就是出勤),0无考勤,1、正常 2、迟到 3、早退 4、加班5、缺卡
|
||||
for (Map.Entry<String, Object> entry : entityMap.entrySet()) {
|
||||
String key = entry.getKey();
|
||||
if (StringUtils.startsWith(key, "day")) {
|
||||
if (entry.getValue() != null) {
|
||||
Integer value = (Integer) entry.getValue();
|
||||
if (!Objects.equals(value, 0)) {
|
||||
entityMap.put(key, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return entityMap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<EntityMap> selectProjectMonthAttendanceList(HashMap<String, Object> param) {
|
||||
return workerMonthAttendanceStatisticsMapper.selectProjectMonthAttendanceList(param);
|
||||
@ -562,13 +583,15 @@ public class WorkerMonthAttendanceStatisticsServiceImpl extends ServiceImpl<Work
|
||||
//如果中午有休息
|
||||
int notLater = 0; //迟到多久不算迟到
|
||||
int notAdvance = 0; //早退多久不算早退
|
||||
int yesAdvance = 0; //早退多久算旷工
|
||||
int yesLater = 0; //迟到多久算旷工
|
||||
WorkerAttendanceRule attendanceRule = null;
|
||||
if (workerAttendanceRuleId != null) {
|
||||
attendanceRule = workerAttendanceRuleMapper.selectById(workerAttendanceRuleId);
|
||||
}
|
||||
if (attendanceRule == null) {
|
||||
startTimes = "09:00";
|
||||
endTimes = "17:00";
|
||||
endTimes = "18:00";
|
||||
} else {
|
||||
startTimes = attendanceRule.getStartTime();
|
||||
endTimes = attendanceRule.getEndTime();
|
||||
@ -576,7 +599,7 @@ public class WorkerMonthAttendanceStatisticsServiceImpl extends ServiceImpl<Work
|
||||
startTimes = "09:00";
|
||||
}
|
||||
if (StringUtils.isEmpty(endTimes)) {
|
||||
endTimes = "09:00";
|
||||
endTimes = "18:00";
|
||||
}
|
||||
if (attendanceRule.getNotLater() != null) {
|
||||
notLater = attendanceRule.getNotLater();
|
||||
@ -584,34 +607,68 @@ public class WorkerMonthAttendanceStatisticsServiceImpl extends ServiceImpl<Work
|
||||
if (attendanceRule.getNotAdvance() != null) {
|
||||
notAdvance = attendanceRule.getNotAdvance();
|
||||
}
|
||||
if (attendanceRule.getYesAdvance() != null) {
|
||||
yesAdvance = attendanceRule.getYesAdvance();
|
||||
}
|
||||
}
|
||||
//判断进和出时间是否都有
|
||||
if (StringUtils.isEmpty(maxTime) || StringUtils.isEmpty(minTime)) {
|
||||
attendanceType = 5; //缺卡
|
||||
} else {
|
||||
Date minDate = sdf.parse(minTime);
|
||||
Date maxDate = sdf.parse(maxTime);
|
||||
//计算工作时长
|
||||
if (maxDate.getTime() > minDate.getTime()) {
|
||||
long diff = (maxDate.getTime() - minDate.getTime()) / 1000 / 60;
|
||||
durationNum = (int) diff;
|
||||
}
|
||||
|
||||
Date startDate = sdf.parse(new StringBuilder(passTime).append(" ").append(startTimes).append(":00").toString());
|
||||
if (notLater > 0) {
|
||||
startDate = org.apache.commons.lang.time.DateUtils.addMinutes(startDate, notLater);
|
||||
}
|
||||
Date endDate = sdf.parse(new StringBuilder(passTime).append(" ").append(endTimes).append(":00").toString());
|
||||
if (notAdvance > 0) {
|
||||
endDate = org.apache.commons.lang.time.DateUtils.addMinutes(endDate, -notAdvance);
|
||||
}
|
||||
|
||||
if (minDate.getTime() > startDate.getTime()) {
|
||||
attendanceType = 2; //迟到
|
||||
} else if (maxDate.getTime() < endDate.getTime()) {
|
||||
attendanceType = 3; //早退
|
||||
if (attendanceRule != null && attendanceRule.getOnceAttendance() != null && attendanceRule.getOnceAttendance() == 1) {
|
||||
Date attendanceTime = null;
|
||||
if (StringUtils.isEmpty(maxTime) && StringUtils.isEmpty(minTime)) {
|
||||
attendanceType = 5; //缺卡
|
||||
} else {
|
||||
attendanceType = 1; //正常
|
||||
Date startDate = sdf.parse(new StringBuilder(passTime).append(" ").append(startTimes).append(":00").toString());
|
||||
Date endDate = sdf.parse(new StringBuilder(passTime).append(" ").append(endTimes).append(":00").toString());
|
||||
attendanceTime = StringUtils.isEmpty(maxTime) ? sdf.parse(minTime) : sdf.parse(maxTime);
|
||||
if (attendanceTime.getTime() < startDate.getTime() && attendanceTime.getTime() > endDate.getTime()) {
|
||||
attendanceType = 1; //正常
|
||||
} else {
|
||||
attendanceType = 0; //缺勤
|
||||
}
|
||||
}
|
||||
} else {
|
||||
//判断进和出时间是否都有
|
||||
if (StringUtils.isEmpty(maxTime) || StringUtils.isEmpty(minTime)) {
|
||||
attendanceType = 5; //缺卡
|
||||
} else {
|
||||
Date minDate = sdf.parse(minTime);
|
||||
Date maxDate = sdf.parse(maxTime);
|
||||
//计算工作时长
|
||||
if (maxDate.getTime() > minDate.getTime()) {
|
||||
long diff = (maxDate.getTime() - minDate.getTime()) / 1000 / 60;
|
||||
durationNum = (int) diff;
|
||||
}
|
||||
|
||||
Date startDate = sdf.parse(new StringBuilder(passTime).append(" ").append(startTimes).append(":00").toString());
|
||||
Date yesLaterDate = null;
|
||||
if (yesLater > 0) {
|
||||
yesLaterDate = org.apache.commons.lang.time.DateUtils.addMinutes(startDate, yesLater);
|
||||
}
|
||||
if (notLater > 0) {
|
||||
startDate = org.apache.commons.lang.time.DateUtils.addMinutes(startDate, notLater);
|
||||
}
|
||||
Date endDate = sdf.parse(new StringBuilder(passTime).append(" ").append(endTimes).append(":00").toString());
|
||||
Date yesAdvanceDate = null;
|
||||
if (yesAdvance > 0) {
|
||||
yesAdvanceDate = org.apache.commons.lang.time.DateUtils.addMinutes(endDate, -yesAdvance);
|
||||
}
|
||||
if (notAdvance > 0) {
|
||||
endDate = org.apache.commons.lang.time.DateUtils.addMinutes(endDate, -notAdvance);
|
||||
}
|
||||
if (yesLaterDate != null && minDate.getTime() > yesLaterDate.getTime()) {
|
||||
attendanceType = 0; //缺勤
|
||||
} else if (minDate.getTime() > endDate.getTime() && maxDate.getTime() > endDate.getTime()) {
|
||||
attendanceType = 0; //缺勤
|
||||
} else if (minDate.getTime() > startDate.getTime()) {
|
||||
attendanceType = 2; //迟到
|
||||
} else if (maxDate.getTime() < endDate.getTime()) {
|
||||
if (yesAdvanceDate != null && maxDate.getTime() < yesAdvanceDate.getTime()) {
|
||||
attendanceType = 0; //缺勤
|
||||
} else {
|
||||
attendanceType = 3; //早退
|
||||
}
|
||||
} else {
|
||||
attendanceType = 1; //正常
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
@ -417,6 +417,15 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
.antMatchers("/xmgl/safetyHatData/allList").permitAll()
|
||||
.antMatchers("/xmgl/api/saveHardWareAlarm").permitAll()
|
||||
.antMatchers("/xmgl/safetyHatData/exportUseHistory").permitAll()
|
||||
.antMatchers("/xmgl/workerAttendance/getPersonAttendanceTime").permitAll()
|
||||
.antMatchers("/xmgl/projectConfig/getProjectConfigList").permitAll()
|
||||
.antMatchers("/xmgl/workerAttendance/addPhoneAttendance").permitAll()
|
||||
.antMatchers("/xmgl/electricFence/checkLocation").permitAll()
|
||||
.antMatchers("/xmgl/workerMonthAttendanceStatistics/selectWorkerMonthAttendanceTotal").permitAll()
|
||||
.antMatchers("/xmgl/workerMonthAttendanceStatistics/selectWorkerMonthAttendanceType").permitAll()
|
||||
.antMatchers("/xmgl/workerMonthAttendanceStatistics/selectMonthAttendanceByWorkerId").permitAll()
|
||||
.antMatchers("/xmgl/workerAttendance/viewDayAttendanceList").permitAll()
|
||||
.antMatchers("/xmgl/workerAttendanceRule/queryById").permitAll()
|
||||
.antMatchers(HttpMethod.OPTIONS, "/**").anonymous()
|
||||
.anyRequest().authenticated() // 剩下所有的验证都需要验证.
|
||||
.and()
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user