关于前后端时间(LocalDateTime)对接问题

首先时间格式很多

具体有多少种 可参考:cn.hutool.core.date.DatePattern hutool工具类定义的常量

两个注解的注意事项


  // 前端  -> 后端时间处理。  请求体传 2021-10-26 15:12:49 。不可传:2021-10-26T15:12:49 。请求头不可以
        @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private LocalDateTime startTime;
    
 // 前端  -> 后端时间处理。请求头可以,请求体不可以 (意思指:json入参)
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") 
     private LocalDateTime startTime;

我测试的时候,心情异常复杂。你说两个注解,在闹呢?

常见格式

// yyyy-MM-dd HH:mm:ss    2021-09-08 21:21:36
// yyyy-MM-dd'T'HH:mm:ss  2021-09-08T21:21:36
// EEE MMM dd HH:mm:ss zzz yyyy   jdk   Wed Sep 08 21:21:36 CST 2021

一般数据库是 dateTime或者bigint(个人更加推荐使用bigint),不管用什么样的,最重要的是统一!统一!统一!

若mysql使用 DateTime java可能用LocalDateTime(2021-09-08T21:21:36) 去接,
1-前端传参:2021-09-08T21:21:36
2-前端传参 :2021-09-08 21:21:36 对应dto字段上使用

// import com.fasterxml.jackson.annotation.JsonFormat;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")

若是bigint可以写个统一转换过滤器,或者写个工具类等等吧

测试

入参是:

// 2021-09-16T09:24:20.620Z  可以
// 2021-09-16T09:24:20  可以 
// 2021-09-16 09:24:20  不可以  抛出 JSON parse error: Cannot deserialize value of type `java.time.LocalDateTime` from String "2021-09-16 09:24:20": Failed to deserialize java.time.LocalDateTime
private LocalDateTime createTime;


// 加上 
// import com.fasterxml.jackson.annotation.JsonFormat;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime createTime;

// 2021-09-16 09:24:20  可以
// 2021-09-16T09:24:20.620Z 不可以
// 2021-09-16T09:24:20  不可以




总结

vo处理

import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;

import java.time.LocalDateTime;

@Data
public class TestTimeVo {
     // 后台  -> 前台 时间格式处理
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
     private LocalDateTime startTime;
}

前端展示

{
  "startTime": "2021-10-26 15:18:43"
}

DTO 处理

@Data
public class TimeDTo {
    // 前端  -> 后端时间处理。  请求体传 2021-10-26 15:12:49 不可传:2021-10-26T15:12:49 请求头不可以
        @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private LocalDateTime startTime;
}

前端入参:

{
  "startTime": "2021-10-26 15:18:43"
}
<think>嗯,用户想用Java实现一个Excel考勤统计系统,涉及数据清洗、数据库存储、统计和UI界面,还要前后端结合。首先,我需要分步骤考虑这个问题。 首先,数据清洗和入库。用户提到要支持多次覆盖更新,所以可能需要先清空旧数据再插入新的。使用Apache POI读取Excel,然后处理数据,比如去重、格式转换。字段名称要和Excel一致,所以数据库表结构需要和Excel的列对应。可能需要处理日期时间格式,确保存入数据库的正确性。 下来是考勤统计。需要根据指定日期范围查询数据,计算每个人的出勤情况。每天可能有多次刷卡记录,所以需要按天分组,取最早和最晚时间作为开始和结束时间。这里需要SQL的GROUP BY和聚合函数MIN、MAX。然后,统计出勤天数、迟到、早退等情况,可能需要业务逻辑处理,比如判断迟到时间是否超过规定。 然后是UI界面,用户希望导出Excel,可能需要使用前端框架,比如Vue或React,但用户没具体说明,可能需要简化。后端用Spring Boot,提供REST API。前端用Thymeleaf模板或者简单的HTML+JS,实现日期选择和数据展示,导出功能可以用Apache POI生成Excel。 代码结构方面,后端分Controller、Service、DAO层。数据清洗服务处理Excel上传,考勤统计服务处理日期查询和计算。数据库使用JPA或MyBatis,这里可能用Spring Data JPA方便快速开发。 需要考虑的问题:Excel文件上传的处理,数据库事务管理,日期时间处理,前端界面的交互设计,导出Excel的格式是否符合用户提供的样例。此外,计算公式的正确性,比如迟到和早退的判断逻辑,需要明确规则,比如迟到是晚于9:00,早退是早于18:00等,用户可能需要自定义这些规则,但问题中没有具体说明,可能需要假设或让用户配置。 可能遇到的难点:处理不同Excel格式的兼容性,大数据量时的性能问题,前端导出功能与后端数据的对接。需要确保多次上传时覆盖更新,而不是追加,可能需要在插入前删除所有旧数据。 测试方面,需要验证数据清洗是否正确,统计结果是否准确,导出功能是否正常。可能还要处理异常情况,比如Excel文件损坏、日期格式错误等。 最后,用户可能需要完整的代码,包括前后端,所以需要提供关键代码示例,但完整代码可能太冗长,需要给出主要部分和思路,并建议使用哪些库和框架。可能还需要说明如何配置数据库,前端如何调用API等。</think>我将分步骤为您讲解实现方案,并提供关键代码示例(完整工程需结合前后端框架): 一、技术选型 1. 后端:Spring Boot + MyBatis Plus + Apache POI 2. 前端:Vue + ElementUI(或Thymeleaf模板引擎) 3. 数据库MySQL 5.7+ 二、数据库设计(DDL示例) ```sql CREATE TABLE attendance_raw ( id INT PRIMARY KEY AUTO_INCREMENT, employee_id VARCHAR(20) COMMENT '工号', employee_name VARCHAR(50) COMMENT '姓名', record_time DATETIME COMMENT '刷卡时间', device_id VARCHAR(50) COMMENT '设备编号', department VARCHAR(100) COMMENT '部门' ) COMMENT '原始考勤表'; CREATE TABLE attendance_stat ( id INT PRIMARY KEY AUTO_INCREMENT, employee_id VARCHAR(20), stat_date DATE, first_punch TIME, last_punch TIME, work_hours DECIMAL(5,2), status VARCHAR(20) COMMENT '正常/迟到/早退' ); ``` 三、核心实现步骤 1. 数据清洗模块 ```java @Service public class ExcelImportService { @Transactional public void importExcel(MultipartFile file) throws IOException { // 清空旧数据 attendanceRawMapper.delete(null); Workbook workbook = new XSSFWorkbook(file.getInputStream()); Sheet sheet = workbook.getSheetAt(0); for (Row row : sheet) { if(row.getRowNum() == 0) continue; // 跳过标题 AttendanceRaw record = new AttendanceRaw(); record.setEmployeeId(row.getCell(0).getStringCellValue()); record.setEmployeeName(row.getCell(1).getStringCellValue()); record.setRecordTime(row.getCell(2).getLocalDateTimeCellValue()); // 其他字段处理... attendanceRawMapper.insert(record); } } } ``` 2. 考勤统计核心逻辑 ```java public List<AttendanceStat> calculateStats(LocalDate start, LocalDate end) { // 获取原始数据 QueryWrapper<AttendanceRaw> query = new QueryWrapper<>(); query.between("record_time", start, end.plusDays(1)); List<AttendanceRaw> records = attendanceRawMapper.selectList(query); // 按员工和日期分组 Map<String, Map<LocalDate, List<LocalDateTime>>> groupData = records.stream() .collect(Collectors.groupingBy(AttendanceRaw::getEmployeeId, Collectors.groupingBy(r -> r.getRecordTime().toLocalDate(), Collectors.mapping(AttendanceRaw::getRecordTime, Collectors.toList())))); // 计算统计结果 List<AttendanceStat> result = new ArrayList<>(); groupData.forEach((empId, datesMap) -> { datesMap.forEach((date, times) -> { LocalDateTime first = Collections.min(times); LocalDateTime last = Collections.max(times); AttendanceStat stat = new AttendanceStat(); stat.setEmployeeId(empId); stat.setStatDate(date); stat.setFirstPunch(first.toLocalTime()); stat.setLastPunch(last.toLocalTime()); stat.setWorkHours(calculateWorkHours(first, last)); stat.setStatus(checkStatus(first, last)); result.add(stat); }); }); return result; } private double calculateWorkHours(LocalDateTime start, LocalDateTime end) { return Duration.between(start, end).toMinutes() / 60.0; } private String checkStatus(LocalDateTime first, LocalDateTime last) { // 根据业务规则判断状态 LocalTime startThreshold = LocalTime.of(9, 0); LocalTime endThreshold = LocalTime.of(18, 0); String status = "正常"; if(first.toLocalTime().isAfter(startThreshold)) { status = "迟到"; } if(last.toLocalTime().isBefore(endThreshold)) { status += status.equals("正常") ? "早退" : "+早退"; } return status; } ``` 3. 前端界面关键代码(Vue示例) ```vue <template> <div> <el-date-picker v-model="dateRange" type="daterange"></el-date-picker> <el-button @click="loadData">查询</el-button> <el-table :data="tableData"> <el-table-column prop="employeeName" label="姓名"></el-table-column> <el-table-column label="日期" prop="statDate"></el-table-column> <el-table-column label="上班时间" prop="firstPunch"></el-table-column> <el-table-column label="下班时间" prop="lastPunch"></el-table-column> <el-table-column label="工时" prop="workHours"></el-table-column> <el-table-column label="状态" prop="status"></el-table-column> </el-table> <el-button @click="exportExcel">导出Excel</el-button> </div> </template> <script> export default { data() { return { dateRange: [], tableData: [] } }, methods: { async loadData() { const res = await this.$http.post('/api/attendance/stats', { start: this.dateRange[0], end: this.dateRange[1] }); this.tableData = res.data; }, exportExcel() { this.$http.post('/api/attendance/export', {data: this.tableData}, { responseType: 'blob' }).then(res => { const url = URL.createObjectURL(new Blob([res.data])); const link = document.createElement('a'); link.href = url; link.download = '考勤统计.xlsx'; link.click(); }); } } } </script> ``` 四、注意事项 1. Excel处理建议使用Apache POI的SXSSFWorkbook处理大数据量 2. 日期格式需要统一处理(建议使用Java 8的DateTime API) 3. 前端需要处理跨域问题(配置CORS) 4. 数据库池推荐使用HikariCP 5. 需要添加异常处理(Excel格式校验、空数据校验等) 完整实现需要结合具体业务规则进行以下扩展: 1. 添加用户权限控制 2. 完善异常处理机制 3. 补充迟到早退的具体计算规则 4. 增加考勤异常标记功能 5. 实现排班规则配置模块 建议采用以下架构分层: ``` src/ ├── main/ │ ├── java/ │ │ └── com/attendance/ │ │ ├── controller/ │ │ ├── service/ │ │ ├── dao/ │ │ └── entity/ │ └── resources/ │ ├── mapper/ │ └── static/ # 前端静态资源 ``` 需要根据实际业务需求补充完整参数校验、日志记录、单元测试等模块。具体实现时建议采用Maven或Gradle进行依赖管理。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值