最近为一个学校开发一个系统涉及到打卡。当时在网上找了很久都没有找到合适的文章,所以自己就硬着头皮慢慢摸索,最终写了出来。这里我将它贴出来,方便以后的开发,同时给需要的开发人员一个引路。该项目是用纯java开发的,我用的编辑器是intellij IDEA2016版的,数据库用msql,采用阿里巴巴的连接池。项目完成了两个基本的功能。1.和打卡硬件对接,录入打卡信息。2.每天定时计算,处理学生的到校情况。
废话不多说,现在贴出代码 点击下载
一;数据库说明
数据库表结构如图所示:
首先数据库名为example,里面有四张表,分别为学生表Students,设备表Device,打卡记录表Attendancesheet,和处理后的表Attendance。 里面的数据字段请看下载下来的sql脚本。
二:代码说明
代码结构如图所示:
ServerRun为服务器端的入口文件。
package socket;
import timer.TimerCore;
import java.io.FileWriter;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* Created by AcresJoe on 2016/9/28.
*/
public class ServerRun {
private static final int PORT = 9999;//端口自己定义
public static void main(String[] args) throws IOException {
try {
//服务器控制定时更新打卡记录表
TimerCore timerCore = new TimerCore();
timerCore.scheduleTest();
} catch (InterruptedException e) {
e.printStackTrace();
}
// 打卡服务器监听
int counter = 1;
ServerSocket ss = new ServerSocket(PORT);
while (true) {
Socket s = ss.accept();
System.out.println("第 " + (counter++) + " 个连接");
Thread t = new Thread(new ServerSockets(s));
t.start();
}
}
}
首先是定时更新数据的程序TimerCore:
package timer;
import controller.TimerController;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.StringTokenizer;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import static java.lang.Math.abs;
/**
* Created by AcresJoe on 2016/10/7.
*/
public class TimerCore {
private static final long INITIALDELAY = 60;//初始化延时60秒
private static final long INTERAL = 600;//间隔十分钟打判断一次
private TimerController timerController = new TimerController();
/**
* 通过ScheduledExecutorService设置每天13:00:00和19:00:00执行检查打卡操作(单位秒)
* @throws InterruptedException
*/
public void scheduleTest() throws InterruptedException {
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(3);
scheduledExecutorService.scheduleWithFixedDelay(new Runnable(){
@Override
public void run() {
System.out.println("start runn");
timerController.checkStudent();
}
},getInitialDelay(),INTERAL, TimeUnit.SECONDS);
}
/**
* 得到初始化的初始化延时
* @return
*/
public int getInitialDelay(){
int i = 0;//记录分
int m = 0;//记录秒
int count = 0;
StringTokenizer str;
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String t1 = simpleDateFormat.format(new Date());
str = new StringTokenizer(t1,":");
while(str.hasMoreTokens()) {
if(count == 0){
System.out.println(count+"----"+str.nextToken());
}
if(count == 1){
i = Integer.parseInt(str.nextToken())%10;
System.out.println("分:"+i);
}
if(count ==2){
m = Integer.parseInt(str.nextToken());
System.out.println("秒:"+m);
}
count++;
if(count >= 3){
count=0;
}
}
System.out.println("循环结束 " + (60 - m));
return (600-(i*60 + m));
}
}
然后是调用处理逻辑代码:
package controller;
import util.DBUtilss;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.StringTokenizer;
/**
* Created by AcresJoe on 2016/10/8.
*/
public class TimerController {
private static final int MORNINGLATE = 0;//早上迟到
private static final int MORNINGABSENCE = 1;//早上旷课
private static final int AFTERNOONLATE = 2;//下午迟到
private static final int AFTERNOONABSENCE = 3;//下午旷课
private static final int NOTCOMESCHOOL = 4;//未到校
private static final int MORNINGNORMALCOME = 5;//早上正常到校
private static final int MORNINGNORMALLEAVE = 6;//早上正常离校
private static final int MORNINGNOTLEAVESCHOOL = 7;//早上未离校
private static final int AFTERNOONCOME = 8;//下午正常到校
private static final int AFTERNOONNORMALLEAVE = 9;//下午正常离校
private static final int AFTERNOONNOTLEAVE = 10;//下午未离校
public Connection conn;
public PreparedStatement ptmt;
public ResultSet rs;
public void checkStudent(){
System.out.println("it is running" + getTimeNow());
//早上进校,六点之前将所有学生标记为 未到校(4)getTime6()- 600表示5:50
if(getTimeNow() >= (getTime6()- 600) && getTimeNow() < getTime6()){
//设置时间范围为早上5:40到早上6:00点的时查看学生,如果没到校则标记为未到校4,
// 执行两次5:50一次,6:00一次,(getTime8-8100为5:45的时间)(getTime8-7200为六点的时间)
System.out.println("早上打卡成功:" + getTimeNow());
insertAllForNotArriveShcool(getTimeNow(),NOTCOMESCHOOL);
}
if(getTimeNow() >= getTime6() && getTimeNow() < getTime8()){
//六点到八点到校的学生,正常到校 标记为 5
judgeAndChangeStudentInShcoolState(getTime6(),getTime8(),getTimeNow(),MORNINGNORMALCOME);
//及时更新未到校的学生的时间
updateNotShcoolStudentTime(getTimeNow());
}
if(getTimeNow() >= getTime8() && getTimeNow() < getTime12()){
//八点到十二点到校的学生,迟到 标记为 0
judgeAndChangeStudentInShcoolState(getTime8(),getTime12(),getTimeNow(),MORNINGLATE);
//及时更新未到校的学生的时间
updateNotShcoolStudentTime(getTimeNow());
}
if(getTimeNow() >= getTime12() && getTimeNow() < getTime12()+600 ){
//早上旷课的学生 标记为1
judgeStudentNotComeShcool(getTimeNow(),MORNINGABSENCE);
//早上所有到校的学生标记为 未离校(7)
insertAllMonningNotLeaveSchool(getTime6(),getTime12(),getTimeNow(),MORNINGNOTLEAVESCHOOL,MORNINGLATE,MORNINGNORMALCOME);
//中午的时候初始化下午的学生,将所有标记为 未到校 4
insertAllForNotArriveShcool(getTimeNow(),NOTCOMESCHOOL);
}
if(getTimeNow() >= getTime12() && getTimeNow() < getTime14_30()){
//早上正常离校的学生 标记为6
judgeStudentLeaveSchool(getTime12(),getTime14_30(),getTimeNow(),MORNINGNORMALLEAVE);
//下午正常上课的学生标记为8
judgeAndChangeStudentInShcoolState(getTime12(),getTime14_30(),getTimeNow(),AFTERNOONCOME);
//及时更新未到校的学生的时间
updateNotShcoolStudentTime(getTimeNow());
}
if(getTimeNow() >= getTime14_30() && getTimeNow() < getTime17()){
//将早上未离校的学生,下午标记为正常到校
judgeNotLeaveSchoolStudent(getTime6(),getTime14_30(),getTimeNow(),MORNINGNOTLEAVESCHOOL,AFTERNOONCOME);
//两点半点到五点点到校的学生,迟到 标记为 2
judgeAndChangeStudentInShcoolState(getTime14_30(),getTime17(),getTimeNow(),AFTERNOONLATE);
//及时更新未到校的学生的时间
updateNotShcoolStudentTime(getTimeNow());
}
if(getTimeNow() >= getTime17() && getTimeNow() < getTime17()+600){
//下午旷课的学生 标记为3
judgeStudentNotComeShcool(getTimeNow(),AFTERNOONABSENCE);
//下午所有到校的学生标记为 未离校(10)
insertAllMonningNotLeaveSchool(getTime12(),getTime17(),getTimeNow(),AFTERNOONNOTLEAVE,AFTERNOONLATE,AFTERNOONCOME);
}
if(getTimeNow() >= getTime17() && getTimeNow() < getTime20()){
//下午正常离校的学生 标记为9
judgeStudentLeaveSchool(getTime17(),getTime20(),getTimeNow(),AFTERNOONNORMALLEAVE);
//
}
}
/**
* 早上或下午 对Attendace表中所有学生数据新插入一行,状态:未到校(4)
* @param nowTime
* @param opType
*/
public void insertAllForNotArriveShcool(long nowTime,int opType){
String sqls;
// String sql = "SELECT studentNum FROM Students\n" +
// "WHERE studentNum NOT IN \n" +
// "(SELECT studentId FROM AttendanceSheet WHERE createTime BETWEEN "+startTime+" AND "+endTime+" and opType in ("+MORNINGLATE+","+AFTERNOONLATE+"))";
String sql = "SELECT studentNum FROM Students";
try {
conn = DBUtilss.getConnection();
ptmt = conn.prepareStatement(sql);
rs = ptmt.executeQuery();
while (rs.next()){
String studentId = rs.getString("studentNum");
sqls = "INSERT INTO Attendance(studentId,createTime,opType) VALUES('"+studentId+"',"+nowTime+","+opType+")";
insertDate(sqls);
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
if (rs != null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (ptmt != null){
try {
ptmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
/**
* 当十二点的时候插入一条数据并用作记录所有到校的学生标记为 未离校(7)
* @param startTime
* @param endTime
* @param nowTime
* @param opType
*/
public void insertAllMonningNotLeaveSchool(long startTime,long endTime,long nowTime,int opType,int comeLate,int come){
String sqls;
String sql = "SELECT studentId FROM Attendance WHERE punchCardTime BETWEEN "+startTime+" AND "+endTime+" and opType in ("+comeLate+","+come+")";
try {
conn = DBUtilss.getConnection();
ptmt = conn.prepareStatement(sql);
rs = ptmt.executeQuery();
while (rs.next()){
String studentId = rs.getString("studentId");
sqls = "INSERT INTO Attendance(studentId,createTime,opType) VALUES('"+ studentId +"',"+nowTime+","+opType+")";
insertDate(sqls);
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
if (rs != null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (ptmt != null){
try {
ptmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
/**
* 判断学生在校状态,正常到校或迟到
* @param startTime
* @param endTime
* @param nowTime
* @param opType
*/
public void judgeAndChangeStudentInShcoolState(long startTime,long endTime,long nowTime,int opType){
String sqls;
String sql = "SELECT studentId,createTime,opType FROM AttendanceSheet WHERE createTime BETWEEN "+startTime+" AND "+endTime+" and opType in ("+0+","+2+")";
try {
conn = DBUtilss.getConnection();
ptmt = conn.prepareStatement(sql);
rs = ptmt.executeQuery();
while (rs.next()){
String createTime = rs.getString("createTime");
String studentId = rs.getString("studentId");
sqls = "UPDATE Attendance SET createTime="+nowTime+",punchCardTime="+createTime+",opType="+opType+" WHERE studentId='"+studentId+"' AND opType="+NOTCOMESCHOOL;
insertDate(sqls);
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
if (rs != null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (ptmt != null){
try {
ptmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
/**
* 及时更新未到校的学生的时间
* @param nowTime
*/
public void updateNotShcoolStudentTime(long nowTime){
String sql = "UPDATE Attendance SET createTime="+nowTime+" WHERE optype = "+NOTCOMESCHOOL;
insertDate(sql);
}
/**
* 判断旷课的学生
* @param nowTime
* @param opType
*/
public void judgeStudentNotComeShcool(long nowTime,int opType){
String sql = "UPDATE Attendance SET createTime="+nowTime+",punchCardTime="+nowTime+",opType="+opType+" WHERE optype = "+NOTCOMESCHOOL;
insertDate(sql);
}
/**
* 判断学生在12:00-14:30 或 17:00-21:00之间正常离校
* @param startTime
* @param endTime
* @param nowTime
* @param opType
*/
public void judgeStudentLeaveSchool(long startTime,long endTime,long nowTime,int opType){
String sqls;
String sql = "SELECT studentId,createTime,opType FROM AttendanceSheet WHERE createTime BETWEEN "+startTime+" AND "+endTime+" and opType in ("+ 1 +","+ 3 +")";
try {
conn = DBUtilss.getConnection();
ptmt = conn.prepareStatement(sql);
rs = ptmt.executeQuery();
while (rs.next()){
String createTime = rs.getString("createTime");
String studentId = rs.getString("studentId");
sqls = "UPDATE Attendance SET createTime="+nowTime+",punchCardTime="+createTime+",opType="+opType+" WHERE studentId = '"+studentId+"' AND opType="+MORNINGNOTLEAVESCHOOL;
// sqls = "INSERT INTO Attendance(studentId,createTime,opType) VALUES("+rs.getString("studentId")+","+nowTime+","+opType+")";
insertDate(sqls);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
if (rs != null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (ptmt != null){
try {
ptmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
/**
*
* @param startTime
* @param endTime
* @param opType1 标记为未离校的学生
* @param opType2 改为正常到校的学生
*/
public void judgeNotLeaveSchoolStudent(long startTime,long endTime,long nowTime,int opType1,int opType2){
String sqls;
String sql = "SELECT studentId FROM Attendance WHERE punchCardTime BETWEEN "+startTime+"AND"+endTime+"AND opType="+opType1;
try {
conn = DBUtilss.getConnection();
ptmt = conn.prepareStatement(sql);
rs = ptmt.executeQuery();
while (rs.next()){
String studentId = rs.getString("studentId");
sqls = "UPDATE Attendance SET createTime="+nowTime+",punchCardTime="+nowTime+",opType="+opType2+" WHERE optype = "+opType1+"AND studentId='"+studentId+"'";
insertDate(sqls);
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
if (rs != null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (ptmt != null){
try {
ptmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
/**
* 插入数据
* @param sqls
*/
public void insertDate(String sqls){
Connection conn = null;
PreparedStatement ptmt = null;
ResultSet rs = null;
try {
conn = DBUtilss.getConnection();
ptmt = conn.prepareStatement(sqls);
ptmt.executeUpdate();
System.out.println(2);
} catch (SQLException e) {
e.printStackTrace();
}finally {
if (rs != null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (ptmt != null){
try {
ptmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
/**
* 得到当前时间戳(单位毫秒)
* @return
*/
public long getTimeNow(){
long t = 0;
try {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String time = simpleDateFormat.format(new Date());
t = simpleDateFormat.parse(time).getTime();
} catch (ParseException e) {
e.printStackTrace();
}
return t/1000;
}
/**
* 得到一天开始的时间戳(00:00:00单位秒)
* @return
*/
public long getDayStart(){
long t = 0;
try {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
t = simpleDateFormat.parse(simpleDateFormat.format(new Date())).getTime();
} catch (ParseException e) {
e.printStackTrace();
}
return t/1000;
//return 1478361600;
}
/**
* 得到一天的结束时间戳(24:00:00单位秒)
* @return
*/
public long getDayEnd(){
return getDayStart()+86400;
// return 1478448000;
}
/**
* 得到每天早上八点钟的时间戳(单位秒)
* @return
*/
public long getTime6(){
return getDayStart()+21600;
}
/**
* 得到每天早上八点钟的时间戳(单位秒)
* @return
*/
public long getTime8(){
return getDayStart()+28800;
}
/**
* 得到每天早上十二点钟的时间戳(单位秒)
* @return
*/
public long getTime12(){
return getDayStart()+43200;
}
/**
* 得到下午两点半
* @return
*/
public long getTime14_30(){
return getDayStart()+52200;
}
/**
* 得到下午五点
* @return
*/
public long getTime17(){
return getDayStart()+61200;
}
/**
* 得到晚上八点
* @return
*/
public long getTime20(){
return getDayStart()+72000;
}
/**
* 得到小时
* @return
*/
public long getInitialDelay(){
String s = null;//记录时
int i = 0;//记录分
int count = 0;
StringTokenizer str,st;
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String t1 = simpleDateFormat.format(new Date());
System.out.println("nowTime:"+t1);
str = new StringTokenizer(t1,":");
while(str.hasMoreTokens()) {
if(count == 0){
String s1 = str.nextToken();
System.out.println(count+"----"+s1);
st = new StringTokenizer(s1," ");
while (st.hasMoreTokens()){
if(count == 0){
st.nextToken();
count++;
}
if(count == 1){
s = st.nextToken();
System.out.println("小时:"+s);
count=0;
}
}
}
if(count == 1){
i = Integer.parseInt(str.nextToken())%10;
System.out.println("分中的个位数:"+i);
}
if(count ==2){
String time = str.nextToken();
System.out.println("秒:"+time);
}
count++;
if(count >= 3){
count=0;
}
}
// System.out.println("小时:"+s);
return Long.parseLong(s);
}
}
开启多线程监听打卡终端机:
package socket;
/**
* Created by AcresJoe on 2016/9/27.
*/
import util.DateAnalysis;
import util.MysqlOperation;
import java.io.*;
import java.net.Socket;
import java.util.List;
class ServerSockets implements Runnable {
private Socket server;
private int BUFFER_SIZE = 10240;
private String flag;
public ServerSockets(Socket i) {
// TODO Auto-generated constructor stub
server = i;
}
@Override
public void run() {
// TODO Auto-generated method stub
try {
InputStream in = server.getInputStream();
OutputStream out = server.getOutputStream();
byte[] recData = null;
while(true) {
recData = new byte[BUFFER_SIZE];
int r = in.read(recData);
System.out.println("这个R到底是多少: " + r);
if(r>-1) {
String data = new String(recData);
data = data.substring(0,r);
System.out.println("客户端发过来的信息:"+data + " data.size : " + data.length());
// String rebackStr = DateAnalysis.getInstance().rebackPublicSession(data);
List<String> rebackStrs = DateAnalysis.getInstance().processingCardData(data.substring(0,data.length()));
for (String rebackStr : rebackStrs) {
System.out.println("返回公话信息" + rebackStr);
logFile("发送过来的数据:" + data);
logFile("返回公话信息:" + rebackStr);
out.write(rebackStr.getBytes());
}
}else {
System.out.println("数据读取完毕!");
server.close();
break;
}
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
try {
server.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
private void logFile(String data) throws IOException {
FileOutputStream out = null;
FileOutputStream outSTr = null;
BufferedOutputStream Buff=null;
FileWriter fw = null;
try {
File file = new File("../log.txt");
if(!file.exists()) {
file.createNewFile();
}
outSTr = new FileOutputStream(file,true);
Buff=new BufferedOutputStream(outSTr);
long begin0 = System.currentTimeMillis();
String dataLog = data + "\r\n";
Buff.write("测试java 文件操作\r\n".getBytes());
Buff.write(dataLog.getBytes());
Buff.flush();
Buff.close();
} catch (Exception e) {
e.printStackTrace();
}
finally {
try {
Buff.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
还有一个攻击类这里就不贴出来了,有需要的请去下载源码。
下面说说模仿学生打卡的代码:
入口文件ClientRun:
package socket;
import java.io.IOException;
/**
* Created by AcresJoe on 2016/9/28.
*/
public class ClientRun {
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
int counter = 1;
while((counter--)>0) {
new Thread(new ClientSocket()).start();
}
}
}
开启套接字与服务器端进行通信:
package socket;
import util.MysqlOperation;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
/**
* Created by AcresJoe on 2016/9/27.
*/
class ClientSocket implements Runnable {
private static final String HOST = "127.0.0.1";
private static final int PORT = 9999;
private Socket client;
Map map = new HashMap<String,String>();
public ClientSocket() {
//new个Socket
client = new Socket();
try {
//建立连接,绑定ip和端口
client.connect(new InetSocketAddress(HOST, PORT), 500);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* 多线程
*/
@Override
public void run() {
// TODO Auto-generated method stub
try {
InputStream in = client.getInputStream();
OutputStream out = client.getOutputStream();
int counter = 0;
byte[] recData;
while (true) {
counter++;
String json = "00790805798980010051899001000542588367 1234567890 201611201745161";
byte[] bt = json.getBytes();
//向服务器发送信息
out.write(bt);
recData = new byte[1024];
//获取服务器端返回来的数据
int r = in.read(recData);
if(r>-1) {
String data = new String(recData);
System.out.println("服务端发过来的数据--:"+data);
if(data.trim().equals("over")) {
client.close();
break;
}
}
try {
// out.close();
Thread.sleep(5000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
重要说明:
这是打卡机传过来的数据包,当然不仅仅是这个还有其他的,根据打卡机的不同,要做相应的改变。
效果演示:首先启动服务器端。
再启动客服端: