Browse Source

✨ feat: 新增人脸上传接口

Pchen. 7 months ago
parent
commit
e347d0b9f8
2 changed files with 171 additions and 0 deletions
  1. 81 0
      apis/Lepao/Face/BeginFaceReco.js
  2. 90 0
      apis/Upload/UploadFaceVideo.js

+ 81 - 0
apis/Lepao/Face/BeginFaceReco.js

@@ -0,0 +1,81 @@
+const API = require("../../../lib/API.js")
+const db = require("../../../plugin/DataBase/db.js")
+const axios = require('axios')
+const config = require('../../../config.json')
+const { BaseStdResponse } = require("../../../BaseStdResponse.js")
+
+class BeginFaceReco extends API {
+    constructor() {
+        super()
+
+        this.runpy = config.runpy
+
+        this.setPath('/Face/BeginFaceReco')
+        this.setMethod('POST')
+    }
+
+    /**
+     * 获取图片并转换为Base64
+     * @param {string} url - 图片链接
+     * @returns {Promise<string>} - Base64字符串
+     */
+    async getImageBase64(url) {
+        try {
+            const response = await axios.get(url, {
+                proxy: false,
+                responseType: "arraybuffer"
+            })
+            const base64 = Buffer.from(response.data, "binary").toString("base64")
+
+            // 获取图片MIME类型
+            const contentType = response.headers["content-type"]
+            return `data:${contentType};base64,${base64}`
+        } catch (error) {
+            throw new Error(`获取图片失败: ${error.message}`)
+        }
+    }
+
+    async onRequest(req, res) {
+        let { student_num, name } = req.body
+
+        if ([student_num, name].some(value => value === '' || value === null || value === undefined))
+            return res.json({
+                ...BaseStdResponse.MISSING_PARAMETER
+            })
+
+        try {
+            let sql = 'SELECT student_num, name, user_avatar, academy_name FROM lepao_account WHERE student_num = ? AND name = ?'
+            let rows = await db.query(sql, [student_num, name])
+            if (!rows)
+                return res.json({
+                    ...BaseStdResponse.DATABASE_ERR,
+                    msg: '获取用户人脸信息失败,请重试'
+                })
+            if (rows.length !== 1)
+                return res.json({
+                    ...BaseStdResponse.ERR,
+                    msg: '该用户尚未在RunForge系统中添加,请先添加账号'
+                })
+
+            const face_img = await this.getImageBase64('https://lepao-cloud.xxoo365.top/view.php/d0b85269c3683ed48da1fc5e468108c7.jpg')
+            // 此时应该从乐跑获取人脸照片
+            let resData = {
+                ...rows[0],
+                face_img
+            }
+
+            res.json({
+                ...BaseStdResponse.OK,
+                data: resData
+            })
+        } catch (error) {
+            this.logger.error(`获取用户人脸信息失败。${error.stack}`)
+            return res.json({
+                ...BaseStdResponse.ERR,
+                msg: '获取用户人脸信息失败,请重试'
+            })
+        }
+    }
+}
+
+module.exports.BeginFaceReco = BeginFaceReco

+ 90 - 0
apis/Upload/UploadFaceVideo.js

@@ -0,0 +1,90 @@
+const API = require("../../lib/API")
+const { v4: uuidv4 } = require('uuid')
+const { BaseStdResponse } = require("../../BaseStdResponse")
+const multer = require('multer')
+const path = require('path')
+const fs = require('fs')
+const { url } = require("../../config.json")
+
+// 配置 Multer 存储选项
+const storage = multer.diskStorage({
+    destination: (req, file, cb) => {
+        const { student_num } = req.body
+        if (!student_num) {
+            return cb(new Error('缺少 student_num'))
+        }
+        const destPath = path.join('uploads', 'faces', student_num)
+        fs.mkdirSync(destPath, { recursive: true }) // 确保目录存在
+        cb(null, destPath)
+    },
+    filename: (req, file, cb) => {
+        const randomName = uuidv4()
+        cb(null, `${randomName}.webm`)
+    }
+})
+
+// 限制文件类型为 webm
+const fileFilter = (req, file, cb) => {
+    const extname = path.extname(file.originalname).toLowerCase() === ".webm"
+    const mimetype = file.mimetype === "video/webm"
+    if (extname && mimetype) {
+        return cb(null, true)
+    } else {
+        cb(new Error('只允许上传 webm 格式的视频'))
+    }
+}
+
+// 初始化 Multer
+const upload = multer({
+    storage: storage,
+    fileFilter: fileFilter,
+    limits: { fileSize: 200 * 1024 * 1024 } // 最大 200MB
+}).single('upload')
+
+class UploadFaceVideo extends API {
+    constructor() {
+        super()
+
+        this.noEncrypt()
+        this.setMethod("POST")
+        this.setPath("/UploadFaceVideo")
+    }
+
+    async onRequest(req, res) {
+        upload(req, res, async (err) => {
+            if (err) {
+                this.logger.error(`视频上传失败!${err.stack || ''}`)
+                return res.json({
+                    ...BaseStdResponse.ERR,
+                    msg: '视频上传失败!'
+                })
+            }
+
+            const { student_num } = req.body
+
+            if ([student_num].some(value => value === '' || value === null || value === undefined)) {
+                return res.json({
+                    ...BaseStdResponse.MISSING_PARAMETER
+                })
+            }
+
+            if (!req.file) {
+                return res.json({
+                    ...BaseStdResponse.MISSING_PARAMETER,
+                    msg: '请上传 webm 视频文件'
+                })
+            }
+
+            const videoPath = `${url}/uploads/faces/${student_num}/${req.file.filename}`
+
+            res.json({
+                ...BaseStdResponse.OK,
+                data: {
+                    videoPath
+                }
+            })
+        })
+    }
+}
+
+module.exports.UploadFaceVideo = UploadFaceVideo