车车 před 4 měsíci
rodič
revize
634b6fe71d

+ 20 - 0
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/io/FileDriveLetterUtils.java

@@ -0,0 +1,20 @@
+package cn.iocoder.yudao.framework.common.util.io;
+
+/**
+ * 文件盘符
+ *
+ * @author cgj
+ */
+public class FileDriveLetterUtils {
+
+    public static String getDriveLetter() {
+        String path = System.getProperty("user.dir");
+        String driveLetter = path.substring(0, 2);
+        String os = System.getProperty("os.name").toLowerCase();
+        if (os.contains("linux")) {
+            driveLetter = "";
+        }
+        return driveLetter;
+    }
+
+}

+ 7 - 0
yudao-module-system/pom.xml

@@ -119,6 +119,13 @@
             <artifactId>hutool-extra</artifactId> <!-- 邮件 -->
         </dependency>
 
+        <!-- 指纹 -->
+        <dependency>
+            <groupId>com.machinezoo.sourceafis</groupId>
+            <artifactId>sourceafis</artifactId>
+            <version>3.13.0</version>
+        </dependency>
+
     </dependencies>
 
 </project>

+ 7 - 9
yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/usercharacteristic/UserCharacteristicController.java

@@ -3,10 +3,11 @@ package cn.iocoder.yudao.module.system.controller.admin.usercharacteristic;
 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
-import cn.iocoder.yudao.framework.redis.RedisCacheUtil;
 import cn.iocoder.yudao.module.system.controller.admin.usercharacteristic.vo.UserCharacteristicPageReqVO;
 import cn.iocoder.yudao.module.system.controller.admin.usercharacteristic.vo.UserCharacteristicRespVO;
+import cn.iocoder.yudao.module.system.dal.dataobject.attribute.AttributeDO;
 import cn.iocoder.yudao.module.system.dal.dataobject.user.UserCharacteristicDO;
+import cn.iocoder.yudao.module.system.service.attribute.AttributeService;
 import cn.iocoder.yudao.module.system.service.usercharacteristic.UserCharacteristicService;
 import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.Parameter;
@@ -18,7 +19,6 @@ import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.multipart.MultipartFile;
 
-import java.io.IOException;
 import java.util.List;
 
 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@@ -32,7 +32,7 @@ public class UserCharacteristicController {
     @Resource
     private UserCharacteristicService userCharacteristicService;
     @Resource
-    private RedisCacheUtil redisCacheUtil;
+    private AttributeService attributeService;
 
     @DeleteMapping("/deleteUserCharacteristicList")
     @Parameter(name = "ids", description = "编号", required = true)
@@ -62,16 +62,14 @@ public class UserCharacteristicController {
 
     @Operation(summary = "新增指纹录入-指纹图片转成dat存储")
     @PostMapping("/insertUserFingerprintDat")
-    public CommonResult<Boolean> insertUserFingerprintDat(MultipartFile file, String userName) throws IOException {
-       /* String sysAttrValue = String.valueOf(redisCacheUtil.getCacheObject("sys.fingerprint.limit"));
+    public CommonResult<Boolean> insertUserFingerprintDat(MultipartFile file, String userName) throws Exception {
+        userName = "admin";
+        AttributeDO one = attributeService.getAttributeByKey("sys.fingerprint.limit");
         String sysAttrValue = null;
         if (one != null) {
             sysAttrValue = one.getSysAttrValue();
         }
-        String url = serverConfig.getUrl();*/
-        //return CommonResult.success(sysUserCharacteristicService.insertUserFingerprintDat(file, userName, sysAttrValue, url));
-        // return CommonResult.success(sysUserCharacteristicService.insertUserFace(file, userName, sysAttrValue, url));
-        return null;
+        return CommonResult.success(userCharacteristicService.insertUserFingerprintDat(file, userName, sysAttrValue));
     }
 
 

+ 2 - 1
yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserService.java

@@ -9,6 +9,7 @@ import cn.iocoder.yudao.module.system.controller.admin.user.vo.profile.UserProfi
 import cn.iocoder.yudao.module.system.controller.admin.user.vo.user.*;
 import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO;
 import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
+import com.baomidou.mybatisplus.extension.service.IService;
 import jakarta.validation.Valid;
 
 import java.util.Collection;
@@ -21,7 +22,7 @@ import java.util.Map;
  *
  * @author 芋道源码
  */
-public interface AdminUserService {
+public interface AdminUserService extends IService<AdminUserDO> {
 
     /**
      * 创建用户

+ 2 - 1
yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImpl.java

@@ -35,6 +35,7 @@ import cn.iocoder.yudao.module.system.service.permission.RoleService;
 import cn.iocoder.yudao.module.system.service.tenant.TenantService;
 import cn.iocoder.yudao.module.system.service.userworkstation.UserWorkstationService;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.google.common.annotations.VisibleForTesting;
 import com.mzt.logapi.context.LogRecordContext;
 import com.mzt.logapi.service.impl.DiffParseFunction;
@@ -64,7 +65,7 @@ import static cn.iocoder.yudao.module.system.enums.LogRecordConstants.*;
  */
 @Service("adminUserService")
 @Slf4j
-public class AdminUserServiceImpl implements AdminUserService {
+public class AdminUserServiceImpl extends ServiceImpl<AdminUserMapper, AdminUserDO> implements AdminUserService {
 
     static final String USER_INIT_PASSWORD_KEY = "system.user.init-password";
 

+ 4 - 0
yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/usercharacteristic/UserCharacteristicService.java

@@ -4,6 +4,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.module.system.controller.admin.usercharacteristic.vo.UserCharacteristicPageReqVO;
 import cn.iocoder.yudao.module.system.dal.dataobject.user.UserCharacteristicDO;
 import com.baomidou.mybatisplus.extension.service.IService;
+import org.springframework.web.multipart.MultipartFile;
 
 import java.util.List;
 
@@ -37,4 +38,7 @@ public interface UserCharacteristicService extends IService<UserCharacteristicDO
      */
     PageResult<UserCharacteristicDO> getUserCharacteristicPage(UserCharacteristicPageReqVO pageReqVO);
 
+    Boolean insertUserFingerprintDat(MultipartFile file, String userName, String sysAttrValue) throws Exception;
+
+
 }

+ 138 - 0
yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/usercharacteristic/UserCharacteristicServiceImpl.java

@@ -1,14 +1,35 @@
 package cn.iocoder.yudao.module.system.service.usercharacteristic;
 
+import cn.hutool.core.lang.Assert;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.util.io.FileDriveLetterUtils;
+import cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.FileUploadReqVO;
+import cn.iocoder.yudao.module.infra.dal.dataobject.file.FileConfigDO;
+import cn.iocoder.yudao.module.infra.dal.dataobject.file.FileDO;
+import cn.iocoder.yudao.module.infra.framework.file.core.client.FileClient;
+import cn.iocoder.yudao.module.infra.framework.file.core.client.local.LocalFileClientConfig;
+import cn.iocoder.yudao.module.infra.service.file.FileConfigService;
+import cn.iocoder.yudao.module.infra.service.file.FileService;
 import cn.iocoder.yudao.module.system.controller.admin.usercharacteristic.vo.UserCharacteristicPageReqVO;
+import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
 import cn.iocoder.yudao.module.system.dal.dataobject.user.UserCharacteristicDO;
 import cn.iocoder.yudao.module.system.dal.mysql.user.UserCharacteristicMapper;
+import cn.iocoder.yudao.module.system.service.user.AdminUserService;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.machinezoo.sourceafis.FingerprintTemplate;
 import jakarta.annotation.Resource;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Service;
 import org.springframework.validation.annotation.Validated;
+import org.springframework.web.multipart.MultipartFile;
 
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
 import java.util.List;
 
 /**
@@ -22,6 +43,15 @@ public class UserCharacteristicServiceImpl extends ServiceImpl<UserCharacteristi
 
     @Resource
     private UserCharacteristicMapper userCharacteristicMapper;
+    @Resource
+    private AdminUserService adminUserService;
+    @Resource
+    private FileService fileService;
+    @Resource
+    private FileConfigService fileConfigService;
+
+    @Value("${local-file.finger-path}")
+    private String fingerPath;
 
     @Override
     public void deleteUserCharacteristicListByIds(List<Long> ids) {
@@ -40,4 +70,112 @@ public class UserCharacteristicServiceImpl extends ServiceImpl<UserCharacteristi
         return userCharacteristicMapper.selectPage(pageReqVO);
     }
 
+    @Override
+    public Boolean insertUserFingerprintDat(MultipartFile file, String userName, String sysAttrValue) throws Exception {
+        Assert.notBlank(userName, "请告知我这是哪个用户的指纹!");
+        Assert.isTrue(file.getSize() > 0, "指纹信息不能为空!");
+        AdminUserDO user = adminUserService.getOne(Wrappers.<AdminUserDO>lambdaQuery()
+                .eq(AdminUserDO::getUsername, userName));
+        Assert.isFalse(user == null, "系统中无该用户!");
+        Long userId = user.getId();
+        if (StringUtils.isNotBlank(sysAttrValue)) {
+            // 检查人员指纹上限
+            long count = count(Wrappers.<UserCharacteristicDO>lambdaQuery()
+                    .eq(UserCharacteristicDO::getType, "1")
+                    .eq(UserCharacteristicDO::getUserId, userId));
+            int i = Integer.parseInt(sysAttrValue);
+            Assert.isFalse(count >= i, "该人员的指纹录入已上限,最大" + i + "条!");
+        }
+        FileClient fileClient = fileConfigService.getFileClientByName("生物识别存储器");
+        FileConfigDO fileConfig = fileConfigService.getFileConfig(fileClient.getId());
+        LocalFileClientConfig config = (LocalFileClientConfig) fileConfig.getConfig();
+        String basePath = config.getBasePath();
+        // 上传文件
+        FileUploadReqVO fileUploadReqVO = new FileUploadReqVO();
+        fileUploadReqVO.setFile(file);
+        fileUploadReqVO.setDirectory(fingerPath + "/" + userId);
+        FileDO fileDO = fileService.uploadLocalFile(fileUploadReqVO);
+
+        // 开始生成dat文件
+        String imagePath = fileDO.getPath();
+        String imageUrl = fileDO.getUrl();
+        String datPath = null;
+        try {
+            // 盘符
+            String driveLetter = FileDriveLetterUtils.getDriveLetter();
+            // 时间戳
+            byte[] fingerprintData = extractFingerprintTemplate(driveLetter + basePath + "/" + fileDO.getPath());
+            // 保存到文件
+            datPath = driveLetter + basePath + "/" + getDirectoryPath(fileDO.getPath()) + extractFileName(fileDO.getPath()) + ".dat";
+            Files.write(Paths.get(datPath), fingerprintData);
+            System.out.println("指纹特征值已保存!");
+        } catch (IOException e) {
+            e.printStackTrace();
+            Assert.isFalse(true, "该文件指纹无法识别,请重新录入!");
+        }
+        UserCharacteristicDO sysUserCharacteristic = new UserCharacteristicDO();
+        sysUserCharacteristic.setUserId(userId);
+        sysUserCharacteristic.setType("1");
+        sysUserCharacteristic.setContent(datPath);
+        sysUserCharacteristic.setOrderNum(0);
+        sysUserCharacteristic.setImageUrl(imageUrl);
+        sysUserCharacteristic.setImagePath(imagePath);
+        save(sysUserCharacteristic);
+        return true;
+    }
+
+    public static byte[] extractFingerprintTemplate(String imagePath) throws IOException {
+        // 读取图像文件(灰度图像 PNG, BMP, JPEG 等)
+        byte[] imageBytes = Files.readAllBytes(Paths.get(imagePath));
+        try {
+            // 提取特征值
+            FingerprintTemplate template = new FingerprintTemplate()
+                    .dpi(500) // 指定 DPI (重要,通常为 500)
+                    .create(imageBytes);
+            // 导出为 byte[] 保存特征值
+            return template.serialize().getBytes();
+        } catch (Exception e) {
+            Assert.isFalse(true, "该文件指纹无法识别,请重新录入!");
+        }
+        return null;
+    }
+
+    /**
+     * 从URL中提取文件名主体部分(不含扩展名)
+     * @param urlStr 目标URL字符串
+     * @return 提取的文件名主体,例如"光猪_1751351611494"
+     * @throws URISyntaxException URL格式无效时抛出
+     */
+    public static String extractFileName(String urlStr) throws URISyntaxException {
+        // 解析URL获取路径
+        URI uri = new URI(urlStr);
+        String path = uri.getPath();
+
+        // 分割路径并获取最后一个有效段
+        String[] segments = path.split("/");
+        String fileName;
+        if (segments.length > 0 && !segments[segments.length - 1].isEmpty()) {
+            fileName = segments[segments.length - 1];
+        } else {
+            // 处理以斜杠结尾的特殊情况(视为前一个段)
+            fileName = segments.length >= 2 ? segments[segments.length - 2] : "";
+        }
+
+        // 去除扩展名
+        int dotIndex = fileName.lastIndexOf('.');
+        return (dotIndex == -1) ? fileName : fileName.substring(0, dotIndex);
+    }
+
+    public static String getDirectoryPath(String path) {
+        if (path == null || path.isEmpty()) {
+            return "";
+        }
+        int lastSlashIndex = path.lastIndexOf('/');
+        if (lastSlashIndex == -1) {
+            // 无斜杠时返回空(或根据需求调整)
+            return "";
+        }
+        return path.substring(0, lastSlashIndex + 1); // 包含斜杠
+    }
+
 }