From c6efc0865060d37f95417ac77dd64e703061b439 Mon Sep 17 00:00:00 2001 From: lcj <2331845269@qq.com> Date: Tue, 29 Jul 2025 08:56:19 +0800 Subject: [PATCH] =?UTF-8?q?[add]=20=E8=8E=B7=E5=8F=96=E8=BA=AB=E4=BB=BD?= =?UTF-8?q?=E8=AF=81=E3=80=81=E9=93=B6=E8=A1=8C=E5=8D=A1=E4=BF=A1=E6=81=AF?= =?UTF-8?q?=EF=BC=8C=E4=BA=BA=E5=91=98=E5=AE=9E=E5=90=8D=E3=80=81=E6=89=93?= =?UTF-8?q?=E5=8D=A1=EF=BC=8C=E8=8E=B7=E5=8F=96=E6=89=93=E5=8D=A1=E4=BF=A1?= =?UTF-8?q?=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../web/controller/AuthController.java | 4 +- .../src/main/resources/application.yml | 2 +- .../org/dromara/common/domain/GeoPoint.java | 18 ++ .../org/dromara/common/utils/JSTUtil.java | 48 +++- .../common/utils/baiduUtil/BaiDuCommon.java | 9 +- .../common/utils/baiduUtil/BaiDuFace.java | 2 +- .../common/utils/baiduUtil/BaiDuOCR.java | 119 ++++------ .../common/utils/baiduUtil/HttpUtil.java | 77 +++++++ .../baiduUtil/entity/AccessTokenResponse.java | 10 +- .../utils/baiduUtil/entity/ocr/BankData.java | 20 +- .../utils/baiduUtil/entity/ocr/Field.java | 3 + .../baiduUtil/entity/ocr/IDCardInfo.java | 2 +- .../utils/baiduUtil/entity/ocr/Location.java | 18 +- .../utils/baiduUtil/entity/ocr/OcrReq.java | 16 +- .../utils/baiduUtil/entity/ocr/Result.java | 2 +- .../baiduUtil/entity/ocr/WordsResult.java | 33 +-- .../app/SubConstructionUserAppController.java | 77 +++++++ .../domain/SubConstructionUser.java | 5 + .../SubConstructionUserAuthenticationReq.java | 150 +++++++++++++ .../service/ISubConstructionUserService.java | 46 ++++ .../impl/SubConstructionUserServiceImpl.java | 210 ++++++++++++++++++ .../app/BusAttendanceAppController.java | 69 ++++++ .../controller/app/BusLeaveAppController.java | 42 ++++ .../app/BusProjectAppController.java | 33 +++ .../app/BusReissueCardAppController.java | 41 ++++ .../org/dromara/project/domain/BusLeave.java | 5 + .../dto/attendance/BusAttendanceByDayReq.java | 25 +++ .../attendance/BusAttendanceByMonthReq.java | 22 ++ .../BusAttendancePunchCardByFaceReq.java | 28 +++ .../dto/leave/BusLeaveGangerReviewReq.java | 41 ++++ .../enums/SubConstructionUserRoleEnum.java | 24 ++ .../service/IBusAttendanceService.java | 21 ++ .../project/service/IBusLeaveService.java | 9 + .../impl/BusAttendanceServiceImpl.java | 160 ++++++++++++- .../service/impl/BusLeaveServiceImpl.java | 89 +++++++- 35 files changed, 1350 insertions(+), 130 deletions(-) create mode 100644 xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/domain/GeoPoint.java create mode 100644 xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/baiduUtil/HttpUtil.java create mode 100644 xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/contractor/controller/app/SubConstructionUserAppController.java create mode 100644 xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/contractor/domain/dto/constructionuser/SubConstructionUserAuthenticationReq.java create mode 100644 xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/controller/app/BusAttendanceAppController.java create mode 100644 xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/controller/app/BusLeaveAppController.java create mode 100644 xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/controller/app/BusProjectAppController.java create mode 100644 xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/controller/app/BusReissueCardAppController.java create mode 100644 xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/domain/dto/attendance/BusAttendanceByDayReq.java create mode 100644 xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/domain/dto/attendance/BusAttendanceByMonthReq.java create mode 100644 xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/domain/dto/attendance/BusAttendancePunchCardByFaceReq.java create mode 100644 xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/domain/dto/leave/BusLeaveGangerReviewReq.java create mode 100644 xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/domain/enums/SubConstructionUserRoleEnum.java diff --git a/xinnengyuan/ruoyi-admin/src/main/java/org/dromara/web/controller/AuthController.java b/xinnengyuan/ruoyi-admin/src/main/java/org/dromara/web/controller/AuthController.java index 06797a12..64c83754 100644 --- a/xinnengyuan/ruoyi-admin/src/main/java/org/dromara/web/controller/AuthController.java +++ b/xinnengyuan/ruoyi-admin/src/main/java/org/dromara/web/controller/AuthController.java @@ -80,7 +80,7 @@ public class AuthController { * @param body 登录信息 * @return 结果 */ - @ApiEncrypt +// @ApiEncrypt @PostMapping("/login") public R login(@RequestBody String body) { LoginBody loginBody = JsonUtils.parseObject(body, LoginBody.class); @@ -183,7 +183,7 @@ public class AuthController { /** * 用户注册 */ - @ApiEncrypt +// @ApiEncrypt @PostMapping("/register") public R register(@Validated @RequestBody RegisterBody user) { if (!configService.selectRegisterEnabled(user.getTenantId())) { diff --git a/xinnengyuan/ruoyi-admin/src/main/resources/application.yml b/xinnengyuan/ruoyi-admin/src/main/resources/application.yml index 253717a5..a033f316 100644 --- a/xinnengyuan/ruoyi-admin/src/main/resources/application.yml +++ b/xinnengyuan/ruoyi-admin/src/main/resources/application.yml @@ -8,7 +8,7 @@ ruoyi: copyrightYear: 2024 captcha: - enable: true + enable: false # 页面 <参数设置> 可开启关闭 验证码校验 # 验证码类型 math 数组计算 char 字符验证 type: MATH diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/domain/GeoPoint.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/domain/GeoPoint.java new file mode 100644 index 00000000..51252011 --- /dev/null +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/domain/GeoPoint.java @@ -0,0 +1,18 @@ +package org.dromara.common.domain; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @author lilemy + * @date 2025-07-28 20:07 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class GeoPoint { + private double lng; + private double lat; + private double alt; +} diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/JSTUtil.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/JSTUtil.java index 60515da5..b75253ff 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/JSTUtil.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/JSTUtil.java @@ -1,8 +1,10 @@ package org.dromara.common.utils; +import cn.hutool.json.JSONUtil; +import org.dromara.common.constant.GeoJsonConstant; import org.dromara.common.core.constant.HttpStatus; import org.dromara.common.core.exception.ServiceException; -import org.dromara.common.constant.GeoJsonConstant; +import org.dromara.common.domain.GeoPoint; import org.dromara.facility.domain.dto.geojson.FacFeatureByPlane; import org.dromara.facility.domain.dto.geojson.FacFeatureByPoint; import org.dromara.facility.domain.dto.geojson.FacGeometry; @@ -267,4 +269,48 @@ public class JSTUtil { return nearestPolygon; } + /** + * 判断一个点是否在多个区域中,返回第一个匹配区域的点集合(否则 null) + * + * @param lat 纬度 + * @param lng 经度 + * @param rangeListJson 多边形列表,每个为 JSON 数组(多边形的点数组) + * @return 匹配区域的 List,否则 null + */ + public static List findMatchingRange(String lat, String lng, List rangeListJson) { + double latitude = Double.parseDouble(lat); + double longitude = Double.parseDouble(lng); + Point point = geometryFactory.createPoint(new Coordinate(longitude, latitude)); + for (String rangeJson : rangeListJson) { + List polygonPoints = JSONUtil.toList(rangeJson, GeoPoint.class); + if (polygonPoints.size() < 3) continue; // 不是有效多边形 + + Polygon polygon = buildPolygon(polygonPoints); + if (polygon.contains(point)) { + return polygonPoints; // 找到匹配范围 + } + } + return null; + } + + /** + * 将点集合转换为 JTS 多边形 + */ + private static Polygon buildPolygon(List points) { + Coordinate[] coordinates = points.stream() + .map(p -> new Coordinate(p.getLng(), p.getLat())) + .toArray(Coordinate[]::new); + + // 需要闭合坐标环(首尾相连) + if (!coordinates[0].equals2D(coordinates[coordinates.length - 1])) { + Coordinate[] closed = new Coordinate[coordinates.length + 1]; + System.arraycopy(coordinates, 0, closed, 0, coordinates.length); + closed[closed.length - 1] = coordinates[0]; + coordinates = closed; + } + + LinearRing shell = geometryFactory.createLinearRing(coordinates); + return geometryFactory.createPolygon(shell); + } + } diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/baiduUtil/BaiDuCommon.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/baiduUtil/BaiDuCommon.java index d2e41c54..af959bb6 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/baiduUtil/BaiDuCommon.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/baiduUtil/BaiDuCommon.java @@ -1,12 +1,13 @@ package org.dromara.common.utils.baiduUtil; +import cn.hutool.json.JSONUtil; import com.google.gson.Gson; import org.dromara.common.utils.baiduUtil.entity.AccessTokenResponse; -import org.glassfish.jaxb.runtime.v2.runtime.reflect.opt.Const; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Service; -import org.springframework.beans.factory.annotation.Value; + import java.io.IOException; import java.net.URI; import java.net.http.HttpClient; @@ -20,7 +21,7 @@ import static org.dromara.common.constant.businessConstant.REDIS_BAIDU_KEY; * @Author 铁憨憨 * @Date 2025/7/18 9:46 * @Version 1.0 - * + *

* 获取百度AccessToken */ @Service @@ -88,7 +89,7 @@ public class BaiDuCommon { HttpResponse response = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); if (response.statusCode() == 200) { - AccessTokenResponse tokenResponse = gson.fromJson(response.body(), AccessTokenResponse.class); + AccessTokenResponse tokenResponse = JSONUtil.toBean(response.body(), AccessTokenResponse.class); return tokenResponse.getAccessToken(); } else { throw new IOException("获取AccessToken失败,状态码: " + response.statusCode()); diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/baiduUtil/BaiDuFace.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/baiduUtil/BaiDuFace.java index 733f0c17..3e305038 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/baiduUtil/BaiDuFace.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/baiduUtil/BaiDuFace.java @@ -1,12 +1,12 @@ package org.dromara.common.utils.baiduUtil; +import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import org.dromara.common.utils.baiduUtil.entity.face.ComparisonRes; import org.dromara.common.utils.baiduUtil.entity.face.HumanFaceReq; import org.dromara.common.utils.baiduUtil.entity.face.HumanFaceRes; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import com.fasterxml.jackson.core.JsonProcessingException; import java.io.IOException; import java.net.URI; diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/baiduUtil/BaiDuOCR.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/baiduUtil/BaiDuOCR.java index 65a45da6..49ce8439 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/baiduUtil/BaiDuOCR.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/baiduUtil/BaiDuOCR.java @@ -1,6 +1,8 @@ package org.dromara.common.utils.baiduUtil; +import com.alibaba.fastjson.JSON; import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.extern.slf4j.Slf4j; import org.dromara.common.utils.baiduUtil.entity.ocr.IDCardInfo; import org.dromara.common.utils.baiduUtil.entity.ocr.OcrReq; import org.dromara.common.utils.baiduUtil.entity.ocr.Result; @@ -9,20 +11,18 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.io.IOException; -import java.net.URI; import java.net.URLEncoder; import java.net.http.HttpClient; -import java.net.http.HttpRequest; -import java.net.http.HttpResponse; import java.nio.charset.StandardCharsets; /** * @Author 铁憨憨 * @Date 2025/7/18 10:45 * @Version 1.0 - * + *

* 处理百度OCR相关逻辑:身份证识别、银行卡识别 */ +@Slf4j @Service public class BaiDuOCR { @Autowired @@ -30,20 +30,19 @@ public class BaiDuOCR { @Autowired private HttpClient httpClient; @Autowired - private ObjectMapper objectMapper; //ObjectMapper 是 Java 处理 JSON 的核心工具,项目中使用它进行 JSON 与 Java 对象的相互转换。 + private ObjectMapper objectMapper; //ObjectMapper 是 Java 处理 JSON 的核心工具,项目中使用它进行 JSON 与 Java 对象的相互转换。 - String baseUrlTemplate = "https://aip.baidubce.com/rest/2.0/ocr/v1/bankcard?access_token=%s"; - String BASE_URL = "https://aip.baidubce.com/rest/2.0/ocr/v1/idcard?access_token=%s"; - + String baseUrlTemplate = "https://aip.baidubce.com/rest/2.0/ocr/v1/bankcard"; + String BASE_URL = "https://aip.baidubce.com/rest/2.0/ocr/v1/idcard"; /** + * @param vr 请求参数 + * @return 识别结果Result * @description 银行卡OCR识别 * @author 铁憨憨 * @date 2025/7/18 11:50 - * @param vr 请求参数 - * @return 识别结果Result **/ public Result bankCardOCRRecognition(OcrReq vr) { // 先从缓存里面捞取token,若为空直接抛出异常 @@ -53,62 +52,41 @@ public class BaiDuOCR { } try { - // 构建请求URL(包含token参数) - String requestUrl = String.format(baseUrlTemplate, URLEncoder.encode(atStr, StandardCharsets.UTF_8)); - - // 准备请求体(将请求参数转为JSON) - String requestBody = objectMapper.writeValueAsString(vr); - if (requestBody == null) { - throw new RuntimeException("请求参数序列化失败"); + String param; + if (vr.getImage() != null) { + String imgParam = URLEncoder.encode(vr.getImage(), StandardCharsets.UTF_8); + param = "image=" + imgParam; + } else if (vr.getUrl() != null) { + param = "url=" + vr.getUrl(); + } else { + throw new RuntimeException("请传入图片或图片URL"); } // 构建HTTP请求 - HttpRequest request = HttpRequest.newBuilder() - .uri(URI.create(requestUrl)) - .header("Content-Type", "application/x-www-form-urlencoded") - .POST(HttpRequest.BodyPublishers.ofString(requestBody)) - .build(); + String resultStr = HttpUtil.post(baseUrlTemplate, atStr, param); - // 发送请求并获取响应 - HttpResponse response = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); - - // 处理响应状态码(非200直接抛出异常) - if (response.statusCode() != 200) { - throw new RuntimeException("接口请求失败,状态码:" + response.statusCode() + ",响应信息:" + response.body()); - } - - // 解析响应结果(若解析失败抛出异常) - Result result = objectMapper.readValue(response.body(), Result.class); + log.info("百度OCR识别结果:{}", resultStr); + Result result = JSON.parseObject(resultStr, Result.class); if (result == null) { - throw new RuntimeException("响应结果解析失败"); + throw new RuntimeException("未识别到银行卡信息"); } - -// // 若接口返回错误码(如百度API的error_code),抛出异常 -// if (result.getErrorCode() != null && result.getErrorCode() != 0) { -// throw new RuntimeException("接口返回错误:" + result.getErrorMessage() + "(错误码:" + result.getErrorCode() + ")"); -// } - return result; } catch (IOException e) { // IO异常(如序列化失败、网络IO错误) throw new RuntimeException("IO处理异常:" + e.getMessage(), e); - } catch (InterruptedException e) { - // 线程中断异常 - Thread.currentThread().interrupt(); // 恢复中断状态 - throw new RuntimeException("请求被中断:" + e.getMessage(), e); + } catch (Exception e) { + throw new RuntimeException(e); } } - - /** + * @param request 请求参数 + * @return 识别结果WordsResult * @description 身份证OCR识别 * @author 铁憨憨 * @date 2025/7/18 16:53 - * @param request 请求参数 - * @return 识别结果WordsResult **/ public WordsResult idCardOCR(OcrReq request) { // 获取AccessToken,为空直接抛异常 @@ -117,41 +95,28 @@ public class BaiDuOCR { throw new RuntimeException("获取访问令牌失败:token为空"); } - // 构建请求URL - String requestUrl = String.format(BASE_URL, accessToken); - try { // 准备请求体(序列化失败抛异常) String requestBody = objectMapper.writeValueAsString(request); if (requestBody == null) { throw new RuntimeException("请求参数序列化失败"); } - - // 构建HTTP请求 - HttpRequest httpRequest = HttpRequest.newBuilder() - .uri(URI.create(requestUrl)) - .header("Content-Type", "application/x-www-form-urlencoded") - .POST(HttpRequest.BodyPublishers.ofString(requestBody)) - .build(); - - // 发送请求并获取响应 - HttpResponse response = httpClient.send( - httpRequest, - HttpResponse.BodyHandlers.ofString() - ); - - // 处理响应状态码(非200直接抛异常) - if (response.statusCode() != 200) { - throw new RuntimeException("接口请求失败,状态码:" + response.statusCode() + ",响应信息:" + response.body()); + String param = "id_card_side=" + request.getIdCardSide(); + if (request.getImage() != null) { + String imgParam = URLEncoder.encode(request.getImage(), StandardCharsets.UTF_8); + param = param + "&image=" + imgParam; + } else if (request.getUrl() != null) { + param = param + "&url=" + request.getUrl(); + } else { + throw new RuntimeException("请传入图片或图片URL"); } - - // 处理响应内容(去除空格并解析) - String responseBody = response.body().replaceAll("\\s+", ""); - IDCardInfo idCardInfo = objectMapper.readValue(responseBody, IDCardInfo.class); + // 构建HTTP请求 + String result = HttpUtil.post(BASE_URL, accessToken, param); + // 处理响应内容 + IDCardInfo idCardInfo = JSON.parseObject(result, IDCardInfo.class); if (idCardInfo == null) { throw new RuntimeException("响应结果解析失败"); } - // 检查身份证状态(状态异常由validateImageStatus抛异常) validateImageStatus(idCardInfo.getImageStatus()); @@ -166,20 +131,18 @@ public class BaiDuOCR { } catch (IOException e) { // IO异常(序列化、网络IO等) throw new RuntimeException("IO处理异常:" + e.getMessage(), e); - } catch (InterruptedException e) { - // 线程中断异常 - Thread.currentThread().interrupt(); // 恢复中断状态 - throw new RuntimeException("请求被中断:" + e.getMessage(), e); + } catch (Exception e) { + throw new RuntimeException(e); } } /** + * @param imageStatus 图片状态 + * @throws RuntimeException 当状态不正常时抛出运行时异常 * @description 验证身份证图片状态 * @author 铁憨憨 * @date 2025/7/18 17:08 - * @param imageStatus 图片状态 - * @throws RuntimeException 当状态不正常时抛出运行时异常 **/ private void validateImageStatus(String imageStatus) { if (imageStatus == null || "normal".equals(imageStatus)) { diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/baiduUtil/HttpUtil.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/baiduUtil/HttpUtil.java new file mode 100644 index 00000000..1ae094de --- /dev/null +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/baiduUtil/HttpUtil.java @@ -0,0 +1,77 @@ +package org.dromara.common.utils.baiduUtil; + +import java.io.BufferedReader; +import java.io.DataOutputStream; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.List; +import java.util.Map; + +/** + * http 工具类 + */ +public class HttpUtil { + + public static String post(String requestUrl, String accessToken, String params) + throws Exception { + String contentType = "application/x-www-form-urlencoded"; + return HttpUtil.post(requestUrl, accessToken, contentType, params); + } + + public static String post(String requestUrl, String accessToken, String contentType, String params) + throws Exception { + String encoding = "UTF-8"; + if (requestUrl.contains("nlp")) { + encoding = "GBK"; + } + return HttpUtil.post(requestUrl, accessToken, contentType, params, encoding); + } + + public static String post(String requestUrl, String accessToken, String contentType, String params, String encoding) + throws Exception { + String url = requestUrl + "?access_token=" + accessToken; + return HttpUtil.postGeneralUrl(url, contentType, params, encoding); + } + + public static String postGeneralUrl(String generalUrl, String contentType, String params, String encoding) + throws Exception { + URL url = new URL(generalUrl); + // 打开和URL之间的连接 + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setRequestMethod("POST"); + // 设置通用的请求属性 + connection.setRequestProperty("Content-Type", contentType); + connection.setRequestProperty("Connection", "Keep-Alive"); + connection.setUseCaches(false); + connection.setDoOutput(true); + connection.setDoInput(true); + + // 得到请求的输出流对象 + DataOutputStream out = new DataOutputStream(connection.getOutputStream()); + out.write(params.getBytes(encoding)); + out.flush(); + out.close(); + + // 建立实际的连接 + connection.connect(); + // 获取所有响应头字段 + Map> headers = connection.getHeaderFields(); + // 遍历所有的响应头字段 + for (String key : headers.keySet()) { + System.err.println(key + "--->" + headers.get(key)); + } + // 定义 BufferedReader输入流来读取URL的响应 + BufferedReader in = null; + in = new BufferedReader( + new InputStreamReader(connection.getInputStream(), encoding)); + String result = ""; + String getLine; + while ((getLine = in.readLine()) != null) { + result += getLine; + } + in.close(); + System.err.println("result:" + result); + return result; + } +} diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/baiduUtil/entity/AccessTokenResponse.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/baiduUtil/entity/AccessTokenResponse.java index d0fec39d..c17404bd 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/baiduUtil/entity/AccessTokenResponse.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/baiduUtil/entity/AccessTokenResponse.java @@ -1,10 +1,11 @@ package org.dromara.common.utils.baiduUtil.entity; -import lombok.AllArgsConstructor; import lombok.Data; -import lombok.NoArgsConstructor; import lombok.experimental.Accessors; +import java.io.Serial; +import java.io.Serializable; + /** * @Author 铁憨憨 * @Date 2025/7/18 9:56 @@ -12,7 +13,10 @@ import lombok.experimental.Accessors; */ @Data @Accessors(chain = true) -public class AccessTokenResponse { +public class AccessTokenResponse implements Serializable { + @Serial + private static final long serialVersionUID = 5429166046159819095L; + private String accessToken; private int expiresIn; private String scope; diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/baiduUtil/entity/ocr/BankData.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/baiduUtil/entity/ocr/BankData.java index e74f8461..8732c05d 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/baiduUtil/entity/ocr/BankData.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/baiduUtil/entity/ocr/BankData.java @@ -1,13 +1,14 @@ package org.dromara.common.utils.baiduUtil.entity.ocr; -import com.alibaba.fastjson2.annotation.JSONField; +import com.alibaba.fastjson.annotation.JSONField; import lombok.Data; +import org.dromara.system.domain.vo.SysOssVo; /** * @Author 铁憨憨 * @Date 2025/7/18 10:58 * @Version 1.0 - * + *

* 银行卡具体信息 */ @Data @@ -22,18 +23,23 @@ import lombok.Data; public class BankData { - @JSONField(name = "bank_card_number",label = "银行卡卡号") + @JSONField(name = "bank_card_number", label = "银行卡卡号") private String bankCardNumber; - @JSONField(name = "valid_date",label = "有效期") + @JSONField(name = "valid_date", label = "有效期") private String validDate; - @JSONField(name = "bank_card_type",label = "银行卡类型,0:不能识别; 1:借记卡; 2:贷记卡(原信用卡大部分为贷记卡); 3:准贷记卡; 4:预付费卡") + @JSONField(name = "bank_card_type", label = "银行卡类型,0:不能识别; 1:借记卡; 2:贷记卡(原信用卡大部分为贷记卡); 3:准贷记卡; 4:预付费卡") private int bankCardType; - @JSONField(name = "bank_name",label = "银行名,不能识别时为空") + @JSONField(name = "bank_name", label = "银行名,不能识别时为空") private String bankName; - @JSONField(name = "holder_name",label = "持卡人姓名,不能识别时为空") + @JSONField(name = "holder_name", label = "持卡人姓名,不能识别时为空") private String holderName; + + /** + * 银行卡图片信息 + */ + private SysOssVo image; } diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/baiduUtil/entity/ocr/Field.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/baiduUtil/entity/ocr/Field.java index ebc77a35..6a0b539d 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/baiduUtil/entity/ocr/Field.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/baiduUtil/entity/ocr/Field.java @@ -1,5 +1,6 @@ package org.dromara.common.utils.baiduUtil.entity.ocr; +import com.alibaba.fastjson.annotation.JSONField; import lombok.Data; /** @@ -11,6 +12,8 @@ import lombok.Data; */ @Data public class Field { + @JSONField(name = "location") private Location location; // 位置信息 + @JSONField(name = "words") private String words; // 识别文字 } diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/baiduUtil/entity/ocr/IDCardInfo.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/baiduUtil/entity/ocr/IDCardInfo.java index fbd30a2d..4ba8a1aa 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/baiduUtil/entity/ocr/IDCardInfo.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/baiduUtil/entity/ocr/IDCardInfo.java @@ -1,6 +1,6 @@ package org.dromara.common.utils.baiduUtil.entity.ocr; -import com.alibaba.fastjson2.annotation.JSONField; +import com.alibaba.fastjson.annotation.JSONField; import lombok.Data; /** diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/baiduUtil/entity/ocr/Location.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/baiduUtil/entity/ocr/Location.java index fc611511..d0fc6380 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/baiduUtil/entity/ocr/Location.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/baiduUtil/entity/ocr/Location.java @@ -1,18 +1,26 @@ package org.dromara.common.utils.baiduUtil.entity.ocr; +import com.alibaba.fastjson.annotation.JSONField; import lombok.Data; /** * @Author 铁憨憨 * @Date 2025/7/18 10:56 * @Version 1.0 - * + *

* 位置信息(坐标) */ @Data public class Location { - private int top; // 顶部坐标 - private int left; // 左侧坐标 - private int width; // 宽度 - private int height; // 高度 + @JSONField(name = "top") + private int top; + + @JSONField(name = "left") + private int left; + + @JSONField(name = "width") + private int width; + + @JSONField(name = "height") + private int height; } diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/baiduUtil/entity/ocr/OcrReq.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/baiduUtil/entity/ocr/OcrReq.java index a785503c..5bf4aff9 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/baiduUtil/entity/ocr/OcrReq.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/baiduUtil/entity/ocr/OcrReq.java @@ -1,24 +1,30 @@ package org.dromara.common.utils.baiduUtil.entity.ocr; -import com.alibaba.fastjson2.annotation.JSONField; +import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Data; +import java.io.Serial; +import java.io.Serializable; + /** * @Author 铁憨憨 * @Date 2025/7/18 10:47 * @Version 1.0 - * + *

* OCR请求参数(身份证/银行卡通用) */ @Data -public class OcrReq { +public class OcrReq implements Serializable { + @Serial + private static final long serialVersionUID = -8670697823104813789L; + private String image; // 图像base64(与url二选一) private String url; // 图像URL(与image二选一) - @JSONField(name = "id_card_side") + @JsonProperty("id_card_side") private String idCardSide; // 身份证正反面(front/back,银行卡无需) - @JSONField(name = "detect_photo") + @JsonProperty("detect_photo") private boolean detectPhoto;// 是是否开启银行卡质量类型(清晰模糊、边框/四角不完整)检测功能,默认不开启,即:false。 } diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/baiduUtil/entity/ocr/Result.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/baiduUtil/entity/ocr/Result.java index b92420c1..0ca7d5fc 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/baiduUtil/entity/ocr/Result.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/baiduUtil/entity/ocr/Result.java @@ -1,6 +1,6 @@ package org.dromara.common.utils.baiduUtil.entity.ocr; -import com.alibaba.fastjson2.annotation.JSONField; +import com.alibaba.fastjson.annotation.JSONField; import lombok.Data; /** diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/baiduUtil/entity/ocr/WordsResult.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/baiduUtil/entity/ocr/WordsResult.java index 43de74b4..482c2372 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/baiduUtil/entity/ocr/WordsResult.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/common/utils/baiduUtil/entity/ocr/WordsResult.java @@ -1,33 +1,38 @@ package org.dromara.common.utils.baiduUtil.entity.ocr; -import com.alibaba.fastjson2.annotation.JSONField; +import com.alibaba.fastjson.annotation.JSONField; import lombok.Data; +import org.dromara.system.domain.vo.SysOssVo; /** * @Author 铁憨憨 * @Date 2025/7/18 10:50 * @Version 1.0 - * + *

* 身份证识别字段集合 */ @Data public class WordsResult { @JSONField(name = "姓名") - private Field name; // 姓名 + private Field name; + @JSONField(name = "民族") - private Field nation; // 民族 + private Field nation; + @JSONField(name = "住址") - private Field address; // 住址 + private Field address; + @JSONField(name = "公民身份号码") - private Field citizenIdentification; // 身份证号 + private Field citizenIdentification; + @JSONField(name = "出生") - private Field birth; // 出生日期 + private Field birth; + @JSONField(name = "性别") - private Field gender; // 性别 - @JSONField(name = "失效日期") - private Field expirationDate; // 失效日期 - @JSONField(name = "签发机关") - private Field issuingAuthority; // 签发机关 - @JSONField(name = "签发日期") - private Field issueDate; // 签发日期 + private Field gender; + + /** + * 身份证图片信息 + */ + private SysOssVo image; } diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/contractor/controller/app/SubConstructionUserAppController.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/contractor/controller/app/SubConstructionUserAppController.java new file mode 100644 index 00000000..b6deda7d --- /dev/null +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/contractor/controller/app/SubConstructionUserAppController.java @@ -0,0 +1,77 @@ +package org.dromara.contractor.controller.app; + +import jakarta.annotation.Resource; +import org.dromara.common.core.domain.R; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.satoken.utils.LoginHelper; +import org.dromara.common.utils.baiduUtil.entity.ocr.BankData; +import org.dromara.common.utils.baiduUtil.entity.ocr.WordsResult; +import org.dromara.contractor.domain.SubConstructionUser; +import org.dromara.contractor.domain.dto.constructionuser.SubConstructionUserAuthenticationReq; +import org.dromara.contractor.domain.vo.constructionuser.SubConstructionUserVo; +import org.dromara.contractor.service.ISubConstructionUserService; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +/** + * 施工人员app + * + * @author lilemy + * @date 2025-07-23 18:44 + */ +@Validated +@RestController +@RequestMapping("/app/contractor/constructionUser") +public class SubConstructionUserAppController { + + @Resource + private ISubConstructionUserService constructionUserService; + + /** + * 获取当前登录用户实名信息 + */ + @GetMapping("/loginUser") + public R getLoginUserInfo() { + SubConstructionUser constructionUser = constructionUserService.getBySysUserId(LoginHelper.getUserId()); + return R.ok(constructionUserService.getVo(constructionUser)); + } + + /** + * 根据身份证图片获取身份证信息 + */ + @Log(title = "施工人员", businessType = BusinessType.OTHER) + @PostMapping("/idCard") + public R getIdCardMessage(@RequestParam("file") MultipartFile file, String idCardSide) { + return R.ok(constructionUserService.getIdCardMessageByPic(file, idCardSide)); + } + + /** + * 根据银行卡图片获取银行卡信息 + */ + @Log(title = "施工人员", businessType = BusinessType.OTHER) + @PostMapping("/bankCard") + public R getBankCardMessage(@RequestParam("file") MultipartFile file) { + return R.ok(constructionUserService.getBankCardMessageByPic(file)); + } + + /** + * 施工人员实名认证 + */ + @Log(title = "施工人员", businessType = BusinessType.INSERT) + @PostMapping("/authentication") + public R insertByAuthentication(@RequestParam("file") MultipartFile file, @Validated SubConstructionUserAuthenticationReq req) { + return R.ok(constructionUserService.insertByAuthentication(file, req)); + } + + /** + * 施工人员人脸比对 + */ + @Log(title = "施工人员", businessType = BusinessType.OTHER) + @PostMapping("/face/comparison") + public R faceComparison(@RequestParam("file") MultipartFile file) { + return R.ok(constructionUserService.faceComparison(file)); + } + +} diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/contractor/domain/SubConstructionUser.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/contractor/domain/SubConstructionUser.java index 6d73f449..50060f19 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/contractor/domain/SubConstructionUser.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/contractor/domain/SubConstructionUser.java @@ -34,6 +34,11 @@ public class SubConstructionUser extends BaseEntity { */ private String facePic; + /** + * 系统用户id + */ + private Long sysUserId; + /** * 人员姓名 */ diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/contractor/domain/dto/constructionuser/SubConstructionUserAuthenticationReq.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/contractor/domain/dto/constructionuser/SubConstructionUserAuthenticationReq.java new file mode 100644 index 00000000..60d72f19 --- /dev/null +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/contractor/domain/dto/constructionuser/SubConstructionUserAuthenticationReq.java @@ -0,0 +1,150 @@ +package org.dromara.contractor.domain.dto.constructionuser; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Pattern; +import lombok.Data; +import org.dromara.common.core.constant.RegexConstants; + +import java.io.Serial; +import java.io.Serializable; + +/** + * @author lilemy + * @date 2025-07-24 10:48 + */ +@Data +public class SubConstructionUserAuthenticationReq implements Serializable { + + @Serial + private static final long serialVersionUID = 1001024234468070802L; + + /** + * 人员姓名 + */ + @NotBlank(message = "人员姓名不能为空") + private String userName; + + /** + * 项目id + */ + @NotNull(message = "项目id不能为空") + private Long projectId; + + /** + * 分包公司id + */ + @NotNull(message = "分包公司id不能为空") + private Long contractorId; + + /** + * 联系电话 + */ + @NotBlank(message = "联系电话不能为空") + @Pattern(regexp = RegexConstants.MOBILE, message = "手机号格式不正确") + private String phone; + + /** + * 0:保密 1:男 2女 + */ + @NotBlank(message = "性别不能为空") + private String sex; + + /** + * 民族 + */ + @NotBlank(message = "民族不能为空") + private String nation; + + /** + * 身份证正面图片 + */ + @NotBlank(message = "身份证正面图片不能为空") + private String sfzFrontPic; + + /** + * 身份证反面图片 + */ + @NotBlank(message = "身份证反面图片不能为空") + private String sfzBackPic; + + /** + * 身份证号码 + */ + @NotBlank(message = "身份证号码不能为空") + private String sfzNumber; + + /** + * 身份证有效开始期 + */ + @NotBlank(message = "身份证有效开始期不能为空") + private String sfzStart; + + /** + * 身份证有效结束期 + */ + @NotBlank(message = "身份证有效结束期不能为空") + private String sfzEnd; + + /** + * 身份证地址 + */ + @NotBlank(message = "身份证地址不能为空") + private String sfzSite; + + /** + * 身份证出生日期 + */ + @NotBlank(message = "身份证出生日期不能为空") + private String sfzBirth; + + /** + * 籍贯 + */ + @NotBlank(message = "籍贯不能为空") + private String nativePlace; + + /** + * 银行卡图片 + */ + @NotBlank(message = "银行卡图片不能为空") + private String yhkPic; + + /** + * 银行卡号 + */ + @NotBlank(message = "银行卡号不能为空") + private String yhkNumber; + + /** + * 开户行 + */ + private String yhkOpeningBank; + + /** + * 持卡人 + */ + private String yhkCardholder; + + /** + * 工种 + */ + @NotBlank(message = "工种不能为空") + private String typeOfWork; + + /** + * 工资计量单位 + */ + private String wageMeasureUnit; + + /** + * 特种工作证图片 + */ + private String specialWorkPic; + + /** + * 备注 + */ + private String remark; + +} diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/contractor/service/ISubConstructionUserService.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/contractor/service/ISubConstructionUserService.java index 16487a59..b2a9d7f0 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/contractor/service/ISubConstructionUserService.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/contractor/service/ISubConstructionUserService.java @@ -5,6 +5,8 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.IService; import org.dromara.common.mybatis.core.page.PageQuery; import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.utils.baiduUtil.entity.ocr.BankData; +import org.dromara.common.utils.baiduUtil.entity.ocr.WordsResult; import org.dromara.contractor.domain.SubConstructionUser; import org.dromara.contractor.domain.dto.constructionuser.*; import org.dromara.contractor.domain.vo.constructionuser.SubConstructionUserVo; @@ -12,6 +14,7 @@ import org.dromara.contractor.domain.exportvo.BusConstructionUserExportVo; import org.dromara.contractor.domain.vo.constructionuser.SubConstructionUserAttendanceMonthVo; import org.dromara.contractor.domain.vo.constructionuser.SubConstructionUserAttendanceTotalVo; import org.dromara.contractor.domain.vo.constructionuser.SubConstructionUserGisVo; +import org.springframework.web.multipart.MultipartFile; import java.util.Collection; import java.util.List; @@ -172,4 +175,47 @@ public interface ISubConstructionUserService extends IService getAttendanceTotalVoPage(SubConstructionUserAttendanceQueryReq req, PageQuery pageQuery); + /** + * 获取施工人员身份证信息 + * + * @param file 图片文件 + * @param idCardSide 身份证正反面(front/back) + * @return 身份证信息 + */ + WordsResult getIdCardMessageByPic(MultipartFile file, String idCardSide); + + + /** + * 获取施工人员银行卡信息 + * + * @param file 图片文件 + * @return 银行卡信息 + */ + BankData getBankCardMessageByPic(MultipartFile file); + + /** + * 实名认证 + * + * @param file 人脸图片 + * @param req 身份信息认证对象 + * @return 是否认证成功 + */ + Long insertByAuthentication(MultipartFile file, SubConstructionUserAuthenticationReq req); + + /** + * 人脸识别 + * + * @param file 图片文件 + * @return 是否匹配成功 + */ + Boolean faceComparison(MultipartFile file); + + /** + * 根据系统用户id查询施工人员 + * + * @param sysUserId 系统用户id + * @return 施工人员 + */ + SubConstructionUser getBySysUserId(Long sysUserId); + } diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/contractor/service/impl/SubConstructionUserServiceImpl.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/contractor/service/impl/SubConstructionUserServiceImpl.java index 89d004ea..5d02acad 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/contractor/service/impl/SubConstructionUserServiceImpl.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/contractor/service/impl/SubConstructionUserServiceImpl.java @@ -1,6 +1,7 @@ package org.dromara.contractor.service.impl; import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.io.IoUtil; import cn.hutool.core.util.DesensitizedUtil; import cn.hutool.core.util.IdcardUtil; import cn.hutool.core.util.PhoneUtil; @@ -18,8 +19,17 @@ import org.dromara.common.core.utils.ObjectUtils; import org.dromara.common.core.utils.StringUtils; import org.dromara.common.mybatis.core.page.PageQuery; import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.oss.core.OssClient; +import org.dromara.common.oss.exception.OssException; +import org.dromara.common.oss.factory.OssFactory; import org.dromara.common.satoken.utils.LoginHelper; import org.dromara.common.utils.IdCardEncryptorUtil; +import org.dromara.common.utils.baiduUtil.BaiDuFace; +import org.dromara.common.utils.baiduUtil.BaiDuOCR; +import org.dromara.common.utils.baiduUtil.entity.face.HumanFaceReq; +import org.dromara.common.utils.baiduUtil.entity.ocr.BankData; +import org.dromara.common.utils.baiduUtil.entity.ocr.OcrReq; +import org.dromara.common.utils.baiduUtil.entity.ocr.WordsResult; import org.dromara.contractor.constant.SubConstructionUserConstant; import org.dromara.contractor.domain.SubConstructionUser; import org.dromara.contractor.domain.SubConstructionUserFile; @@ -48,7 +58,12 @@ import org.springframework.beans.BeanUtils; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; +import java.io.IOException; +import java.io.InputStream; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; import java.time.YearMonth; import java.util.*; import java.util.stream.Collectors; @@ -99,6 +114,12 @@ public class SubConstructionUserServiceImpl extends ServiceImpl 0) { + throw new ServiceException("当前用户已关联施工人员信息"); + } + user.setSysUserId(userId); + // 保存施工人员信息 + boolean save = this.save(user); + if (!save) { + throw new ServiceException("施工人员信息保存失败"); + } + return user.getId(); + } + + /** + * 人脸识别 + * + * @param file 图片文件 + * @return 是否匹配成功 + */ + @Override + public Boolean faceComparison(MultipartFile file) { + if (file == null) { + throw new ServiceException("请上传图片", HttpStatus.BAD_REQUEST); + } + String reqBase64; + try { + // 获取文件字节数组 + byte[] bytes = file.getBytes(); + // Base64 编码 + reqBase64 = URLEncoder.encode(Base64.getEncoder().encodeToString(bytes), StandardCharsets.UTF_8); + } catch (IOException e) { + throw new ServiceException("图片转换失败,请重新上传"); + } + HumanFaceReq request = new HumanFaceReq(); + request.setImage(reqBase64); + Long userId = LoginHelper.getUserId(); + SubConstructionUser constructionUser = this.getById(userId); + if (constructionUser == null || StringUtils.isBlank(constructionUser.getFacePic())) { + throw new ServiceException("未进行实名认证"); + } + String facePic = constructionUser.getFacePic(); + SysOssVo sysOssVo = ossService.getById(Long.parseLong(facePic)); + if (sysOssVo == null) { + throw new ServiceException("未进行实名认证"); + } + // 获取文件输入流 + OssClient storage = OssFactory.instance(sysOssVo.getService()); + String path = sysOssVo.getUrl(); + String faceBase64; + try (InputStream is = storage.getObjectContent(path)) { + byte[] bytes = IoUtil.readBytes(is); + // Base64 编码 + faceBase64 = URLEncoder.encode(Base64.getEncoder().encodeToString(bytes), StandardCharsets.UTF_8); + } catch (IOException e) { + // 针对单个文件处理异常,可以选择记录日志或终止处理 + throw new OssException("处理文件[" + path + "]失败,错误信息: " + e.getMessage()); + } + HumanFaceReq faceReq = new HumanFaceReq(); + faceReq.setImage(faceBase64); + List list = List.of(request, faceReq); + double comparison = baiDuFace.comparison(list); + return comparison > 80; + } + + /** + * 根据系统用户id查询施工人员 + * + * @param sysUserId 系统用户id + * @return 施工人员 + */ + @Override + public SubConstructionUser getBySysUserId(Long sysUserId) { + SubConstructionUser constructionUser = this.lambdaQuery() + .eq(SubConstructionUser::getSysUserId, sysUserId) + .one(); + if (constructionUser == null) { + throw new ServiceException("实名认证信息不存在", HttpStatus.NOT_FOUND); + } + return constructionUser; + } + } diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/controller/app/BusAttendanceAppController.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/controller/app/BusAttendanceAppController.java new file mode 100644 index 00000000..42c7e8fe --- /dev/null +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/controller/app/BusAttendanceAppController.java @@ -0,0 +1,69 @@ +package org.dromara.project.controller.app; + +import jakarta.annotation.Resource; +import org.dromara.common.core.domain.R; +import org.dromara.common.satoken.utils.LoginHelper; +import org.dromara.contractor.domain.SubConstructionUser; +import org.dromara.contractor.service.ISubConstructionUserService; +import org.dromara.project.domain.dto.attendance.BusAttendanceByDayReq; +import org.dromara.project.domain.dto.attendance.BusAttendanceByMonthReq; +import org.dromara.project.domain.dto.attendance.BusAttendanceMonthByUserIdReq; +import org.dromara.project.domain.dto.attendance.BusAttendancePunchCardByFaceReq; +import org.dromara.project.domain.vo.attendance.BusAttendanceMonthByUserIdVo; +import org.dromara.project.domain.vo.attendance.BusAttendanceVo; +import org.dromara.project.service.IBusAttendanceService; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import java.util.List; + +/** + * 考勤app + * + * @author lilemy + * @date 2025-07-24 11:53 + */ +@Validated +@RestController +@RequestMapping("/app/project/attendance") +public class BusAttendanceAppController { + + @Resource + private IBusAttendanceService attendanceService; + + @Resource + private ISubConstructionUserService constructionUserService; + + /** + * 获取当前登录用户的考勤列表 + * + * @param req 查询参数 + * @return 考勤列表 + */ + @GetMapping("/list/loginUser") + public R> listLoginUser(BusAttendanceByDayReq req) { + return R.ok(attendanceService.getDayByUserId(LoginHelper.getUserId(), req.getClockDate())); + } + + /** + * 获取当前登录用户月份考勤列表 + */ + @GetMapping("/list/month/loginUser") + public R> listAttendanceMonthListByUserId(BusAttendanceByMonthReq req) { + Long userId = LoginHelper.getUserId(); + SubConstructionUser constructionUser = constructionUserService.getBySysUserId(userId); + BusAttendanceMonthByUserIdReq dto = new BusAttendanceMonthByUserIdReq(); + dto.setUserId(constructionUser.getId()); + dto.setClockMonth(req.getClockMonth()); + return R.ok(attendanceService.listAttendanceMonthListByUserId(dto)); + } + + /** + * 人脸坐标打卡 + */ + @PostMapping("/punch/card/face") + public R punchCardByFace(@RequestPart("file") MultipartFile file, BusAttendancePunchCardByFaceReq req) { + return R.ok(attendanceService.punchCardByFace(file, req)); + } +} diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/controller/app/BusLeaveAppController.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/controller/app/BusLeaveAppController.java new file mode 100644 index 00000000..d0b48d45 --- /dev/null +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/controller/app/BusLeaveAppController.java @@ -0,0 +1,42 @@ +package org.dromara.project.controller.app; + +import jakarta.annotation.Resource; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.satoken.utils.LoginHelper; +import org.dromara.contractor.domain.SubConstructionUser; +import org.dromara.contractor.service.ISubConstructionUserService; +import org.dromara.project.domain.dto.leave.BusLeaveQueryReq; +import org.dromara.project.domain.vo.leave.BusLeaveVo; +import org.dromara.project.service.IBusLeaveService; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author lilemy + * @date 2025-07-24 14:51 + */ +@Validated +@RestController +@RequestMapping("/app/project/leave") +public class BusLeaveAppController { + + @Resource + private IBusLeaveService leaveService; + + @Resource + private ISubConstructionUserService constructionUserService; + + /** + * 查询当前登录用户请假申请列表 + */ + @GetMapping("/list/loginUser") + public TableDataInfo listByLoginUser(BusLeaveQueryReq req, PageQuery pageQuery) { + SubConstructionUser constructionUser = constructionUserService.getBySysUserId(LoginHelper.getUserId()); + req.setUserId(constructionUser.getId()); + return leaveService.queryPageList(req, pageQuery); + } + +} diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/controller/app/BusProjectAppController.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/controller/app/BusProjectAppController.java new file mode 100644 index 00000000..ded4be99 --- /dev/null +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/controller/app/BusProjectAppController.java @@ -0,0 +1,33 @@ +package org.dromara.project.controller.app; + +import jakarta.annotation.Resource; +import org.dromara.common.core.domain.R; +import org.dromara.project.domain.vo.project.BusProjectContractorListVo; +import org.dromara.project.service.IBusProjectService; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +/** + * @author lilemy + * @date 2025-07-24 15:14 + */ +@Validated +@RestController +@RequestMapping("/app/project/project") +public class BusProjectAppController { + + @Resource + private IBusProjectService projectService; + + /** + * 查询项目以及项目下的分包公司列表 + */ + @GetMapping("/list/project/contractorList") + public R> listProjectContractorList() { + return R.ok(projectService.queryProjectContractorList()); + } +} diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/controller/app/BusReissueCardAppController.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/controller/app/BusReissueCardAppController.java new file mode 100644 index 00000000..8eb3af59 --- /dev/null +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/controller/app/BusReissueCardAppController.java @@ -0,0 +1,41 @@ +package org.dromara.project.controller.app; + +import jakarta.annotation.Resource; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.satoken.utils.LoginHelper; +import org.dromara.contractor.domain.SubConstructionUser; +import org.dromara.contractor.service.ISubConstructionUserService; +import org.dromara.project.domain.dto.reissuecard.BusReissueCardQueryReq; +import org.dromara.project.domain.vo.reissuecard.BusReissueCardVo; +import org.dromara.project.service.IBusReissueCardService; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author lilemy + * @date 2025-07-24 14:51 + */ +@Validated +@RestController +@RequestMapping("/app/project/reissueCard") +public class BusReissueCardAppController { + + @Resource + private IBusReissueCardService reissueCardService; + + @Resource + private ISubConstructionUserService constructionUserService; + + /** + * 查询当前登录用户补卡申请列表 + */ + @GetMapping("/list/loginUser") + public TableDataInfo listByLoginUser(BusReissueCardQueryReq req, PageQuery pageQuery) { + SubConstructionUser constructionUser = constructionUserService.getBySysUserId(LoginHelper.getUserId()); + req.setUserId(constructionUser.getId()); + return reissueCardService.queryPageList(req, pageQuery); + } +} diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/domain/BusLeave.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/domain/BusLeave.java index fcab96a0..5a41103a 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/domain/BusLeave.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/domain/BusLeave.java @@ -124,6 +124,11 @@ public class BusLeave extends BaseEntity { */ private Long teamId; + /** + * 分包公司id + */ + private Long contractorId; + /** * 备注 */ diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/domain/dto/attendance/BusAttendanceByDayReq.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/domain/dto/attendance/BusAttendanceByDayReq.java new file mode 100644 index 00000000..ac9a9ffa --- /dev/null +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/domain/dto/attendance/BusAttendanceByDayReq.java @@ -0,0 +1,25 @@ +package org.dromara.project.domain.dto.attendance; + +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * @author lilemy + * @date 2025-07-24 12:28 + */ +@Data +public class BusAttendanceByDayReq implements Serializable { + + @Serial + private static final long serialVersionUID = 1800816047235448533L; + + /** + * 打卡日期 + */ + @DateTimeFormat(pattern = "yyyy-MM-dd") + private Date clockDate; +} diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/domain/dto/attendance/BusAttendanceByMonthReq.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/domain/dto/attendance/BusAttendanceByMonthReq.java new file mode 100644 index 00000000..bd63741c --- /dev/null +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/domain/dto/attendance/BusAttendanceByMonthReq.java @@ -0,0 +1,22 @@ +package org.dromara.project.domain.dto.attendance; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * @author lilemy + * @date 2025-07-24 13:51 + */ +@Data +public class BusAttendanceByMonthReq implements Serializable { + + @Serial + private static final long serialVersionUID = 4257724767091558549L; + + /** + * 打卡月份 + */ + private String clockMonth; +} diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/domain/dto/attendance/BusAttendancePunchCardByFaceReq.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/domain/dto/attendance/BusAttendancePunchCardByFaceReq.java new file mode 100644 index 00000000..7aa27822 --- /dev/null +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/domain/dto/attendance/BusAttendancePunchCardByFaceReq.java @@ -0,0 +1,28 @@ +package org.dromara.project.domain.dto.attendance; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * @author lilemy + * @date 2025-07-28 18:43 + */ +@Data +public class BusAttendancePunchCardByFaceReq implements Serializable { + + @Serial + private static final long serialVersionUID = -8906344299387307633L; + + /** + * 经度 + */ + private String lng; + + /** + * 纬度 + */ + private String lat; + +} diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/domain/dto/leave/BusLeaveGangerReviewReq.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/domain/dto/leave/BusLeaveGangerReviewReq.java new file mode 100644 index 00000000..7e74ff91 --- /dev/null +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/domain/dto/leave/BusLeaveGangerReviewReq.java @@ -0,0 +1,41 @@ +package org.dromara.project.domain.dto.leave; + +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * @author lilemy + * @date 2025-07-24 15:28 + */ +@Data +public class BusLeaveGangerReviewReq implements Serializable { + + @Serial + private static final long serialVersionUID = 6403690469809708710L; + + /** + * 主键id + */ + @NotNull(message = "主键不能为空") + private Long id; + + /** + * 管理员意见(1未读 2同意 3拒绝) + */ + @NotNull(message = "管理员意见不能为空") + private String gangerOpinion; + + /** + * 管理员说明 + */ + private String gangerExplain; + + /** + * 备注 + */ + private String remark; + +} diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/domain/enums/SubConstructionUserRoleEnum.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/domain/enums/SubConstructionUserRoleEnum.java new file mode 100644 index 00000000..87140862 --- /dev/null +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/domain/enums/SubConstructionUserRoleEnum.java @@ -0,0 +1,24 @@ +package org.dromara.project.domain.enums; + +import lombok.Getter; + +/** + * @author lilemy + * @date 2025-07-24 15:52 + */ +@Getter +public enum SubConstructionUserRoleEnum { + + NORMAL("普通用户", "1"), + ADMIN("管理员", "2"); + + private final String text; + + private final String value; + + SubConstructionUserRoleEnum(String text, String value) { + this.text = text; + this.value = value; + } + +} diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/service/IBusAttendanceService.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/service/IBusAttendanceService.java index c0a35a31..bd38ba02 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/service/IBusAttendanceService.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/service/IBusAttendanceService.java @@ -7,12 +7,15 @@ import org.dromara.common.mybatis.core.page.PageQuery; import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.project.domain.BusAttendance; import org.dromara.project.domain.dto.attendance.BusAttendanceMonthByUserIdReq; +import org.dromara.project.domain.dto.attendance.BusAttendancePunchCardByFaceReq; import org.dromara.project.domain.dto.attendance.BusAttendanceQueryReq; import org.dromara.project.domain.dto.attendance.BusAttendanceQueryTwoWeekReq; import org.dromara.project.domain.vo.attendance.BusAttendanceClockDateForTwoWeekVo; import org.dromara.project.domain.vo.attendance.BusAttendanceMonthByUserIdVo; import org.dromara.project.domain.vo.attendance.BusAttendanceVo; +import org.springframework.web.multipart.MultipartFile; +import java.util.Date; import java.util.List; /** @@ -96,4 +99,22 @@ public interface IBusAttendanceService extends IService { */ Page getVoPage(Page attendancePage); + /** + * 根据系统用户id和日期查询考勤 + * + * @param userId 用户id + * @param clockDate 日期 + * @return 考勤 + */ + List getDayByUserId(Long userId, Date clockDate); + + /** + * 人脸打卡 + * + * @param file 人脸图片 + * @param req 人脸打卡请求 + * @return 是否打卡成功 + */ + Boolean punchCardByFace(MultipartFile file, BusAttendancePunchCardByFaceReq req); + } diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/service/IBusLeaveService.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/service/IBusLeaveService.java index c6d4d00b..04620aed 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/service/IBusLeaveService.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/service/IBusLeaveService.java @@ -6,6 +6,7 @@ import com.baomidou.mybatisplus.extension.service.IService; import org.dromara.common.mybatis.core.page.PageQuery; import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.project.domain.BusLeave; +import org.dromara.project.domain.dto.leave.BusLeaveGangerReviewReq; import org.dromara.project.domain.dto.leave.BusLeaveManagerReviewReq; import org.dromara.project.domain.dto.leave.BusLeaveQueryReq; import org.dromara.project.domain.vo.leave.BusLeaveVo; @@ -46,6 +47,14 @@ public interface IBusLeaveService extends IService { */ List queryList(BusLeaveQueryReq req); + /** + * 班长审核施工人员请假申请 + * + * @param req 班长审核施工人员请假申请 + * @return 是否审核成功 + */ + Boolean gangerReview(BusLeaveGangerReviewReq req); + /** * 管理员审核施工人员请假申请 * diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/service/impl/BusAttendanceServiceImpl.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/service/impl/BusAttendanceServiceImpl.java index 2a792da8..e1f6aa81 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/service/impl/BusAttendanceServiceImpl.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/service/impl/BusAttendanceServiceImpl.java @@ -13,30 +13,41 @@ import org.dromara.common.core.exception.ServiceException; import org.dromara.common.core.utils.DateUtils; import org.dromara.common.core.utils.ObjectUtils; import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.domain.GeoPoint; import org.dromara.common.mybatis.core.page.PageQuery; import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.satoken.utils.LoginHelper; +import org.dromara.common.utils.JSTUtil; import org.dromara.contractor.domain.SubConstructionUser; import org.dromara.contractor.service.ISubConstructionUserService; import org.dromara.project.domain.BusAttendance; +import org.dromara.project.domain.BusProject; +import org.dromara.project.domain.BusProjectPunchrange; import org.dromara.project.domain.BusProjectTeamMember; +import org.dromara.project.domain.dto.attendance.BusAttendanceMonthByUserIdReq; +import org.dromara.project.domain.dto.attendance.BusAttendancePunchCardByFaceReq; +import org.dromara.project.domain.dto.attendance.BusAttendanceQueryReq; +import org.dromara.project.domain.dto.attendance.BusAttendanceQueryTwoWeekReq; import org.dromara.project.domain.enums.BusAttendanceClockStatusEnum; import org.dromara.project.domain.enums.BusAttendanceCommuterEnum; import org.dromara.project.domain.enums.BusAttendanceStatusEnum; -import org.dromara.project.domain.dto.attendance.BusAttendanceMonthByUserIdReq; -import org.dromara.project.domain.dto.attendance.BusAttendanceQueryReq; -import org.dromara.project.domain.dto.attendance.BusAttendanceQueryTwoWeekReq; import org.dromara.project.domain.vo.attendance.BusAttendanceClockDateForTwoWeekVo; import org.dromara.project.domain.vo.attendance.BusAttendanceListByDay; import org.dromara.project.domain.vo.attendance.BusAttendanceMonthByUserIdVo; import org.dromara.project.domain.vo.attendance.BusAttendanceVo; import org.dromara.project.mapper.BusAttendanceMapper; import org.dromara.project.service.*; +import org.dromara.system.domain.vo.SysOssVo; +import org.dromara.system.service.ISysOssService; import org.springframework.beans.BeanUtils; import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; import java.time.LocalDate; +import java.time.LocalTime; import java.time.YearMonth; import java.time.ZoneId; +import java.time.format.DateTimeFormatter; import java.util.*; import java.util.stream.Collectors; @@ -50,9 +61,15 @@ import java.util.stream.Collectors; public class BusAttendanceServiceImpl extends ServiceImpl implements IBusAttendanceService { + @Resource + private ISysOssService ossService; + @Resource private IBusProjectService projectService; + @Resource + private IBusProjectPunchrangeService projectPunchrangeService; + @Resource private IBusProjectTeamMemberService projectTeamMemberService; @@ -404,4 +421,141 @@ public class BusAttendanceServiceImpl extends ServiceImpl getDayByUserId(Long userId, Date clockDate) { + SubConstructionUser constructionUser = constructionUserService.getBySysUserId(userId); + // 当日期未指定时,默认为今天 + if (clockDate == null) { + clockDate = DateUtils.parseDateTime(FormatsType.YYYY_MM_DD, DateUtils.getDate()); + } + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(BusAttendance::getUserId, constructionUser.getId()) + .eq(BusAttendance::getClockDate, clockDate); + return this.list(queryWrapper).stream().map(this::getVo).toList(); + } + + /** + * 人脸打卡 + * + * @param file 人脸图片 + * @param req 人脸打卡请求 + * @return 是否打卡成功 + */ + @Override + public Boolean punchCardByFace(MultipartFile file, BusAttendancePunchCardByFaceReq req) { + // 获取当前用户 + Long userId = LoginHelper.getUserId(); + synchronized (userId.toString().intern()) { + // 记录当前打卡时间 + LocalTime now = LocalTime.now(); + Date nowDate = new Date(); + // 获取坐标 + String lat = req.getLat(); + String lng = req.getLng(); + // 校验用户是否合法 + SubConstructionUser constructionUser = constructionUserService.getBySysUserId(userId); + Long projectId = constructionUser.getProjectId(); + if (projectId == null) { + throw new ServiceException("当前用户未加入项目", HttpStatus.BAD_REQUEST); + } + BusProject project = projectService.getById(projectId); + Long teamId = constructionUser.getTeamId(); + if (teamId == null) { + throw new ServiceException("当前用户未加入班组", HttpStatus.BAD_REQUEST); + } + final String status = "1"; + if (constructionUser.getStatus().equals(status)) { + throw new ServiceException("当前用户已离职", HttpStatus.BAD_REQUEST); + } + final String noClock = "1"; + if (constructionUser.getClock().equals(noClock)) { + throw new ServiceException("当前用户已被禁止打卡", HttpStatus.BAD_REQUEST); + } + + // 判断用户是否已经被拉黑 + constructionBlacklistService.validUserInBlacklist(constructionUser.getId(), projectId); + // todo 地理位置校验 + List punchranges = projectPunchrangeService.lambdaQuery() + .eq(BusProjectPunchrange::getProjectId, projectId) + .list(); + if (CollUtil.isEmpty(punchranges)) { + throw new ServiceException("项目未配置考勤范围", HttpStatus.BAD_REQUEST); + } + List punchRangeList = punchranges.stream().map(BusProjectPunchrange::getPunchRange).toList(); + List matchingRange = JSTUtil.findMatchingRange(lat, lng, punchRangeList); + if (matchingRange == null) { + throw new ServiceException("打卡位置不在范围内", HttpStatus.BAD_REQUEST); + } + // 进行人脸比对 + Boolean result = constructionUserService.faceComparison(file); + if (!result) { + throw new ServiceException("人脸识别失败,请重新识别", HttpStatus.BAD_REQUEST); + } + // 判断打卡状态 + String punchRange = project.getPunchRange(); + if (punchRange == null) { + throw new ServiceException("未设置打卡时间范围", HttpStatus.BAD_REQUEST); + } + String[] time = punchRange.split(","); + // 解析字符串为 LocalTime + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HH:mm"); + LocalTime startTime = LocalTime.parse(time[0], formatter); + LocalTime endTime = LocalTime.parse(time[1], formatter); + Date date = Date.from(LocalDate.now().atStartOfDay(ZoneId.systemDefault()).toInstant()); + // 判断当前用户打卡状态 + List attendances = this.lambdaQuery() + .eq(BusAttendance::getUserId, userId) + .eq(BusAttendance::getClockDate, date) + .list(); + BusAttendance attendance = new BusAttendance(); + if (CollUtil.isEmpty(attendances)) { + // 上班打卡 + attendance.setCommuter(BusAttendanceCommuterEnum.CLOCKIN.getValue()); + // 上传人脸照 + SysOssVo upload = ossService.upload(file); + attendance.setFacePic(upload.getOssId().toString()); + // 判断是否为迟到 + if (now.isAfter(startTime)) { + attendance.setClockStatus(BusAttendanceClockStatusEnum.LATE.getValue()); + } else { + attendance.setClockStatus(BusAttendanceClockStatusEnum.NORMAL.getValue()); + } + } else if (attendances.size() == 1 && attendances.getFirst().getCommuter().equals(BusAttendanceCommuterEnum.CLOCKIN.getValue())) { + // 下班打卡 + attendance.setCommuter(BusAttendanceCommuterEnum.CLOCKOUT.getValue()); + // 判断是否为早退 + if (now.isBefore(endTime)) { + attendance.setClockStatus(BusAttendanceClockStatusEnum.LEAVEEARLY.getValue()); + } else { + attendance.setClockStatus(BusAttendanceClockStatusEnum.NORMAL.getValue()); + } + } else if (attendances.size() == 2) { + throw new ServiceException("当前已完成打卡,请勿重复提交", HttpStatus.BAD_REQUEST); + } else { + throw new ServiceException("打卡异常,请联系管理员", HttpStatus.ERROR); + } + // 填充信息 + attendance.setUserId(userId); + attendance.setUserName(constructionUser.getUserName()); + attendance.setProjectId(projectId); + attendance.setClockDate(date); + attendance.setClockTime(nowDate); + // 记录打卡坐标 + attendance.setLat(lat); + attendance.setLng(lng); + attendance.setPunchRange(matchingRange.toString()); + // 上传人脸照 + SysOssVo upload = ossService.upload(file); + attendance.setFacePic(upload.getOssId().toString()); + return true; + } + } + } diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/service/impl/BusLeaveServiceImpl.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/service/impl/BusLeaveServiceImpl.java index ceaff8cb..32681119 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/service/impl/BusLeaveServiceImpl.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/service/impl/BusLeaveServiceImpl.java @@ -12,19 +12,23 @@ import org.dromara.common.core.utils.ObjectUtils; import org.dromara.common.core.utils.StringUtils; import org.dromara.common.mybatis.core.page.PageQuery; import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.satoken.utils.LoginHelper; +import org.dromara.contractor.domain.SubConstructionUser; +import org.dromara.contractor.service.ISubConstructionUserService; +import org.dromara.contractor.service.ISubContractorService; import org.dromara.project.domain.BusAttendance; import org.dromara.project.domain.BusLeave; import org.dromara.project.domain.BusProjectTeam; -import org.dromara.project.domain.enums.BusAttendanceClockStatusEnum; -import org.dromara.project.domain.enums.BusAttendanceCommuterEnum; -import org.dromara.project.domain.enums.BusOpinionStatusEnum; -import org.dromara.project.domain.enums.BusReviewStatusEnum; +import org.dromara.project.domain.BusProjectTeamMember; +import org.dromara.project.domain.dto.leave.BusLeaveGangerReviewReq; +import org.dromara.project.domain.enums.*; import org.dromara.project.domain.dto.leave.BusLeaveManagerReviewReq; import org.dromara.project.domain.dto.leave.BusLeaveQueryReq; import org.dromara.project.domain.vo.leave.BusLeaveVo; import org.dromara.project.mapper.BusLeaveMapper; import org.dromara.project.service.IBusAttendanceService; import org.dromara.project.service.IBusLeaveService; +import org.dromara.project.service.IBusProjectTeamMemberService; import org.dromara.project.service.IBusProjectTeamService; import org.springframework.beans.BeanUtils; import org.springframework.context.annotation.Lazy; @@ -52,6 +56,15 @@ public class BusLeaveServiceImpl extends ServiceImpl @Resource private IBusAttendanceService attendanceService; + @Resource + private IBusProjectTeamMemberService projectTeamMemberService; + + @Resource + private ISubConstructionUserService constructionUserService; + + @Resource + private ISubContractorService contractorService; + /** * 查询施工人员请假申请 * @@ -88,6 +101,63 @@ public class BusLeaveServiceImpl extends ServiceImpl return baseMapper.selectVoList(lqw); } + /** + * 班长审核施工人员请假申请 + * + * @param req 班长审核施工人员请假申请 + * @return 是否审核成功 + */ + @Override + public Boolean gangerReview(BusLeaveGangerReviewReq req) { + Long id = req.getId(); + String gangerOpinion = req.getGangerOpinion(); + // 判断该请假记录是否存在 + BusLeave oldLeave = this.getById(id); + if (oldLeave == null) { + throw new ServiceException("施工人员请假申请不存在", HttpStatus.NOT_FOUND); + } + // 如果审核状态相同,则返回 + if (Objects.equals(gangerOpinion, oldLeave.getGangerOpinion())) { + throw new ServiceException("请勿重复操作", HttpStatus.BAD_REQUEST); + } + // 如果已经审核过,则返回 + if (BusOpinionStatusEnum.PASS.getValue().equals(oldLeave.getManagerOpinion())) { + throw new ServiceException("该请假已审核通过,请勿重复操作", HttpStatus.BAD_REQUEST); + } + // 获取当前用户实名信息 + SubConstructionUser constructionUser = constructionUserService.getBySysUserId(LoginHelper.getUserId()); + // 判断当前用户是否有权审核 + Long teamId = oldLeave.getTeamId(); + Long count = projectTeamMemberService.lambdaQuery() + .eq(BusProjectTeamMember::getProjectId, oldLeave.getProjectId()) + .eq(BusProjectTeamMember::getMemberId, constructionUser.getId()) + .eq(BusProjectTeamMember::getTeamId, teamId) + .eq(BusProjectTeamMember::getPostId, BusProjectTeamMemberPostEnum.FOREMAN.getValue()) + .count(); + if (count <= 0) { + throw new ServiceException("您无权审核该请假申请", HttpStatus.FORBIDDEN); + } + BusLeave newLeave = new BusLeave(); + newLeave.setId(id); + newLeave.setGangerOpinion(gangerOpinion); + newLeave.setGangerExplain(req.getGangerExplain()); + newLeave.setGangerTime(new Date()); + newLeave.setRemark(req.getRemark()); + boolean result = this.updateById(newLeave); + if (!result) { + throw new ServiceException("更新班长审核操作失败", HttpStatus.ERROR); + } + // 向分包管理员发送通知 + List constructionAdminUsers = constructionUserService.lambdaQuery() + .eq(SubConstructionUser::getContractorId, oldLeave.getContractorId()) + .eq(SubConstructionUser::getUserRole, SubConstructionUserRoleEnum.ADMIN.getValue()) + .list(); + if (CollUtil.isNotEmpty(constructionAdminUsers)){ + + } + return true; + } + /** * 管理员审核施工人员请假申请 * @@ -114,9 +184,20 @@ public class BusLeaveServiceImpl extends ServiceImpl throw new ServiceException("请等待班组长审核通过后再进行操作", HttpStatus.BAD_REQUEST); } // todo 判断当前用户是否为项目管理员 + // 判断是否为分包公司管理员 + Long contractorId = oldLeave.getContractorId(); + SubConstructionUser constructionUser = constructionUserService.lambdaQuery() + .eq(SubConstructionUser::getSysUserId, LoginHelper.getUserId()) + .eq(SubConstructionUser::getContractorId, contractorId) + .eq(SubConstructionUser::getUserRole, SubConstructionUserRoleEnum.ADMIN.getValue()) + .one(); + if (constructionUser == null) { + throw new ServiceException("您无权审核该请假申请", HttpStatus.FORBIDDEN); + } // 填充默认值,更新数据 BusLeave leave = new BusLeave(); leave.setId(id); + leave.setManagerId(constructionUser.getId()); leave.setManagerOpinion(managerOpinion); leave.setManagerExplain(req.getManagerExplain()); leave.setManagerTime(new Date());