基于人脸识别的签到考勤APP设计与实现全解析

📅 发布时间:2026/9/14 17:42:48
基于人脸识别的签到考勤APP设计与实现全解析
最近这段时间来问我毕设选题意见的学弟学妹不少基于人脸识别的签到考勤APP的设计与实现这个题目出现的频率特别高。它确实是个好题目贴近日常场景、技术栈能有深度、答辩时有故事可讲而且不管是JAVA还是Python路线都能接得住。但别急着把它当成普通管理系统来做——真把这个项目从头到尾做完的人基本都有同感界面和CRUD只是皮身份认证才是骨最花时间的部分其实是如何让人脸识别在真实环境里稳定生效。这篇文章我打算完完整整地把这个题目的技术链条拆开讲一遍从需求分析、技术选型到人脸识别的核心链路、数据库和接口设计再到真实环境中踩过的坑和论文写法。适合准备拿这个题做毕设的在校生也适合想在公司内部快速搭一套考勤系统、但又不想直接买现成硬件的开发者。内容会比较长但每一步都是我实际做过的路子可以直接跟着往下走。1. 这题不简单人脸识别签到APP的需求边界到底在哪其实看到题目的时候大部分人的第一反应是这不就是个带人脸识别的打卡系统吗然后就开始写用户表、写考勤表。我不建议这么干因为需求没理清楚就动工后面大概率会反复改表结构。先花两三天把需求边界划清楚比多敲一万行业务代码都值。1.1 表面是考勤工具本质是身份认证系统签到考勤的痛点用过传统指纹机的人都懂指纹磨破皮了识别不出来、天冷干燥也识别不出来高峰期一群人在机器前面排队一边搓手指一边看表。后来有的公司换成IC卡又冒出代打卡的问题——一个人拿五张卡全勤纪录全靠人情操作。课堂点名更不用说大学教室里一百多号人老师一节课喊完名字半小时就过去了。人脸识别天然适合这个场景核心原因是它的非接触性和不可替代性。用户只需要站在手机摄像头前不需要接触任何设备识别过程又快又自然而且配合活体检测后照片和视频基本挡不住它。但这也决定了这个项目的重心不在考勤记录这些常规功能上而在身份认证的可靠性。所以做需求分析时我建议把整个系统当成一个身份认证平台来规划考勤只是它的第一个应用场景。用户注册、人脸录入、特征管理、身份校验、活体检测这些底子打好了后面要扩展签到之外的场景比如门禁、课堂专注度分析、会议签到都是顺理成章的事。这样想系统设计的格局就不一样了。1.2 把需求写成真正的需求文档别漏掉非功能要求很多毕设的需求分析章节写得很空,就是学生可以注册登录、教师可以管理课程、系统可以记录考勤,这哪叫需求分析,这叫功能介绍。真正动手前,要把角色、功能、约束条件逐条列清楚。最核心的两个角色是这样的:普通用户学生/员工注册登录、人脸录入支持重新录入、在线签到/签退、查看个人考勤记录、提交请假申请。管理员教师/HR用户管理、课程/部门管理、考勤规则配置签到时间窗口、迟到判定标准、考勤统计与导出、异常记录人工处理补卡、申诉。功能需求其实好列容易漏的是非功能需求。我自己总结下来有这几条是必须提前想清楚的识别速度单次签到的端到端耗时最好控制在1秒内超过2秒用户体感就很差了。识别准确率正确接受率至少要95%以上误识别率要低到可以接受否则会出现我明明站在镜头前却打不上卡的尴尬。防作弊能力必须能识别出照片、视频这类静态/动态攻击这就是活体检测存在的意义。环境适应性教室和办公室的光线条件差异巨大逆光、暗光、侧光都要能处理。并发处理上课前5分钟往往是签到高峰期上百人同时打开APP后端能不能扛住。这些需求里任何一条没考虑到到测试阶段都会被放大成灾难。我记得第一次现场测试时教室靠窗那排逆光严重识别率一度掉到六成以下后面专门做了图像预处理才压住这个问题。需求阶段多操一份心后面就少熬一次夜。2. 技术选型的逻辑Java打底、Python做人脸、算法服务独立部署毕设技术选型有一条隐性规则技术栈要能支撑你写出论文而不是写完代码就没事了。用人脸识别签到这个题目最稳妥的组合是Java Spring Boot做业务后端Python做算法服务中间通过网络接口通信。为什么这么选理由得说明白。2.1 双语言架构的真实理由市面上人脸识别相关的成熟库绝大多数是Python生态的。OpenCV、Dlib、Face_recognition、InsightFace官方文档和社区案例都以Python为主用起来最省事。而业务系统这边Spring Boot依然是国内中小型项目的绝对主流网上关于用户认证、权限管理、定时任务、导出报表的资料一搜一大把答辩的时候也更容易讲清楚。有人会问我能不能用Python全家桶把后端也写了可以但题目是APP的设计与实现APP的后端接口用Spring Boot实现在论文的相关技术介绍和系统实现两章里都能写出东西来。相反如果纯用Python写后端业务模块和算法模块混在一起代码结构容易乱答辩时评委问你的工程结构是怎么分层的会有点难答。那能不能纯Java做人脸识别能做Java有OpenCV的绑定也能调用深度学习框架导出的模型但问题在于(1) 模型转换和预训练模型获取的路径比Python曲折得多(2) 你需要处理大量图像数组操作代码写起来又长又绕(3) 社区方案少遇到问题很难搜到现成答案。所以我的建议是Java负责业务Python负责脸中间用HTTP接口通信各干各擅长的事。2.2 人脸识别方案选型别一上来就调云API人脸识别这块选型直接决定你后面开发体验和论文深度。我按从简单到复杂排个序方案准确率是否需要GPU离线可用上手难度适合场景OpenCV Haar级联低否是低纯入门demo不建议做毕设主体Dlib HOG特征中否是中小规模、光线较好的环境Face_recognition库中高否是低毕设快速开发几百人以内够用InsightFace(ArcFace)高推荐是高对精度要求高、论文学术感强的项目百度/Ali云人脸识别API高无需否低不想折腾算法、可以接受联网如果是毕设我建议优先在Face_recognition和InsightFace之间选。前者封装的API非常友好几行代码就能完成人脸编码和比对适合时间紧凑、把重心放在业务系统的同学后者是深度学习方案精度高但环境配置和模型推理要花更多时间适合想把算法部分写出深度、甚至对比多种模型的同学。云端API我反而不推荐作为主体方案因为论文里系统实现一章会很难写你总不能写我调用了百度的一个接口然后它返回了结果。但可以作为对比方案写进论文用来佐证你的方案在离线环境下的优势。2.3 算法服务化Flask包一层HTTP接口确定了算法用Python之后最直观的问题就是Java怎么调用Python。有的同学写Jython、有的用ProcessBuilder去启动Python脚本这两种我都试过维护性都很差。正确做法是把人脸识别算法封装成一个独立的算法微服务用Flask或FastAPI起一个HTTP接口Java后端通过HTTP调用。这样做有三个好处一是Java和Python进程完全解耦Python服务挂了不影响主业务处理二是算法可以单独部署在带GPU的机器上业务服务器不受影响三是以后想换算法模型只要保证接口输入输出不变内部随便改。我在项目里定义了这样两个核心接口# app.py - Flask算法服务 from flask import Flask, request, jsonify import face_recognition import numpy as np app Flask(__name__) app.route(/face/register, methods[POST]) def face_register(): # 接收图片提取特征向量返回给调用方存储 file request.files[image] image face_recognition.load_image_file(file) encodings face_recognition.face_encodings(image) if len(encodings) 0: return jsonify({code: 400, msg: 未检测到人脸}) return jsonify({code: 200, feature: encodings[0].tolist()}) app.route(/face/verify, methods[POST]) def face_verify(): # 接收当前拍摄的人脸特征与目标特征比对 data request.get_json() target np.array(data[target_feature]) candidates np.array(data[candidate_features]) # 库里候选特征列表 distances np.linalg.norm(candidates - target, axis1) min_idx int(np.argmin(distances)) min_dist float(distances[min_idx]) threshold data.get(threshold, 0.55) if min_dist threshold: return jsonify({code: 200, matched: True, distance: min_dist}) return jsonify({code: 200, matched: False, distance: min_dist}) if __name__ __main__: app.run(host0.0.0.0, port5001)Java这边用RestTemplate或者OpenFeign调用就行业务层不需要关心人脸怎么算出来的拿结果直接用。这个架构跑起来之后整个系统就清晰了Android端拍照或上传图片 - Java业务服务处理签到流程 - 需要比对时调Python算法服务 - 结果写回数据库。3. 核心链路拆解从摄像头一帧图像到签到成功现在到了整个项目最核心的部分——人脸识别链路。网上很多人把这部分当成一个黑盒封装好了直接用但论文里和答辩时都需要你讲清楚每一步在做什么。把链路拆开看其实就是一个固定的四步流程人脸检测、人脸对齐、特征提取、特征比对。3.1 四步走检测、对齐、提取、比对这四步我分别解释一下同时会带上代码思路。人脸检测是第一步解决的问题是画面里人脸在哪里。传统做法是Haar级联速度快但准确率一般大角度和暗光下容易漏检。深度学习方案里有MTCNN、RetinaFace这些效果明显更好。Face_recognition库底层用的就是HOG特征配合线性分类器在正面人脸场景下表现已经够用。人脸对齐解决的是人脸姿态不统一的问题。同样是正脸有人稍微歪头有人仰头特征提取前要把人脸校正到标准位置。方法是先定位眼睛、鼻子、嘴角这些关键点然后做仿射变换把人脸统一到一个标准尺寸和角度。这一步直接决定特征提取的稳定性。特征提取是把对齐后的人脸图像转换成一个固定维度的向量。传统方法提色彩、纹理特征就完事了深度学习模型则是通过训练好的卷积神经网络把人脸映射到一个向量空间同一个人的不同照片在这个空间里距离很近不同人的距离较远。Face_recognition库默认生成128维特征向量InsightFace的ArcFace模型通常生成512维向量。特征比对是在特征空间里算距离。常用的是欧氏距离或余弦相似度距离小于阈值就判定为同一个人。整个流程核心代码是这样的def recognize(frame, known_encodings, known_ids, tolerance0.55): face_locations face_recognition.face_locations(frame) face_encodings face_recognition.face_encodings(frame, face_locations) for encoding in face_encodings: distances face_recognition.face_distance(known_encodings, encoding) min_idx int(np.argmin(distances)) if distances[min_idx] tolerance: return known_ids[min_idx], float(distances[min_idx]) return None, None注意这个tolerance参数不是随便定的后面我会专门讲怎么调。3.2 人脸录入阶段的细节一张脸照片远远不够签到能不能成功一半的功劳在注册阶段。我见过不少项目注册时让用户拍一张正脸就完事了结果首次签到就失败——因为用户是侧着身子站的手机举的角度也不一样光线更是完全不同。正确做法是多角度、多帧采集。录入时让用户按照屏幕提示完成几个动作正对镜头、轻微左转、轻微右转、抬头、低头每个姿势采集一到两帧全部提特征后存成多条特征记录。签到比对时拿当前画面特征跟这个用户的全部特征挨个比只要有一条记录匹配成功就算通过。另外录入照片的质量检测也值得做进去。两件事一是模糊检测可以用Laplacian算子的方差来判断方差低于某个阈值就提示重新拍二是人脸完整度确保脸没有超出画面边界。这两行代码在论文里虽然不起眼但能体现你对真实环境可靠性的思考。3.3 识别阈值怎么定不是拍脑袋决定的阈值定得太严用户稍微换个角度就识别失败阈值定得太松相似脸会误判成同一个人。好的做法是拿一批测试数据画出阈值和误判率的关系再选一个平衡点。我当时准备了大概一百个人的照片集另外还有同一人的不同角度、不同光线下的照片用这批数据做了个简单测试表阈值欧氏距离正确接受率误接受率说明0.4592.1%0.0%偏严格容易拒识0.5096.5%0.3%平衡点推荐先从这里试0.5598.7%1.8%接受率高了但误识风险上升0.6099.2%5.6%不建议双胞胎和相似脸会出事建议把阈值做成配置项放到配置中心或数据库里线上发现问题可以动态调整而不是每次改代码重新部署。这个设计细节答辩时说出来很加分。3.4 活体检测挡住照片和视频攻击光有特征比对挡不住别人拿一张你正面照片去签到。于是就有了活体检测。常见的方案有几类动作指令活体后端随机下发眨眨眼张张嘴向左摇头指令用户照做通过关键点位置变化判断是否真人。优点是普通手机摄像头就能实现成本低缺点是用户配合成本略高。红外活体利用红外摄像头下皮肤和非皮肤材质的反光差异来区分真人脸和照片。精度高但需要特定硬件。深度学习活体用模型直接判断当前画面是真人还是照片/屏幕对硬件要求低但需要足够训练数据。毕设场景下我最推荐动作指令活体因为它逻辑不复杂、论文里容易写清楚而且视角效果好——用户看着是在和APP互动不是傻傻站那扫脸。具体实现就是在录入和签到时加一个动作验证步骤APP弹出指令时开始录像等用户完成动作后抽取关键帧送算法服务算法检测到眨眼或摇头动作就返回通过。4. 业务落地数据库表设计、接口约定与防重复签到算法链路通了大半但别忘了这毕竟是个考勤APP业务功能得撑起来。数据库设计和接口设计的好坏直接影响开发的顺畅程度和论文的系统设计章节。4.1 三张核心表用户、人脸特征、考勤记录我项目里最主要的表有用户表、人脸特征表、考勤记录表另外还有课程/部门、请假、考勤规则等相关表篇幅关系我先把三张核心表的结构写出来。用户表负责基础账号信息和角色区分CREATE TABLE sys_user ( id BIGINT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL, password VARCHAR(100) NOT NULL, real_name VARCHAR(50) NOT NULL, role TINYINT DEFAULT 0 COMMENT 0-普通用户 1-管理员, dept_id BIGINT COMMENT 部门或班级ID, status TINYINT DEFAULT 1 COMMENT 1-正常 0-禁用, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY uk_username (username) );人脸特征表单独抽出来而不是把特征向量直接塞用户表原因是一个人可以有多条特征记录而且特征数据本身是一个长度很大的数组放用户表会拖慢常规查询CREATE TABLE face_feature ( id BIGINT PRIMARY KEY AUTO_INCREMENT, user_id BIGINT NOT NULL, feature TEXT NOT NULL COMMENT 128维或512维特征向量JSON数组, feature_type TINYINT DEFAULT 0 COMMENT 0-普通 1-活体采集, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, KEY idx_user_id (user_id) );考勤记录表是业务核心设计时要注意唯一约束否则代码层面漏判一条数据就会产生重复签到CREATE TABLE attendance_record ( id BIGINT PRIMARY KEY AUTO_INCREMENT, user_id BIGINT NOT NULL, course_id BIGINT, check_date DATE NOT NULL, check_in_time DATETIME, check_out_time DATETIME, status TINYINT DEFAULT 1 COMMENT 1-正常 2-迟到 3-早退 4-缺勤 5-请假, UNIQUE KEY uk_user_date (user_id, check_date) );UK_KEY uk_user_date这个约束很重要它保证了同一天同一用户只能有一条考勤记录这是防重复签到的最后一道保险。4.2 核心接口怎么约Token鉴权与业务请求流APP和后端之间我用RESTful接口通信核心接口大概有这些。注意看人脸识别服务只负责生成特征和验证特征不参与考勤业务流程这样职责边界很清楚。接口方法说明/api/user/registerPOST用户注册/api/user/loginPOST登录返回Token/api/face/registerPOST上传人脸照片返回特征向量并绑定用户/api/attendance/check-inPOST签到上传当前人脸照片/api/attendance/recordsGET查询个人考勤记录/api/attendance/statisticsGET考勤统计管理员功能签到接口的完整调用链是APP拍照上传 - Java接口收到图片 - 转给Python算法服务提特征 - 算法服务返回特征向量 - Java再调比对接口或者直接传一个候选用户ID只跟这个人比- 比对通过后写考勤记录。为了减少算法服务调用次数我的做法是先按当前时间确定要签到的课程再只把该课程下所有已录脸用户的特征作为候选集这样比对范围通常只有几十到几百人速度非常快。接口请求时带上JWT Token登录后拿到Token刷脸签到前先验Token后面所有接口都走同一套鉴权逻辑。4.3 防止同一个人短时间重复打卡这是一个在普通管理系统里不会遇到的问题但在考勤系统里必须处理。场景是用户在8:59签了一次到发现签错了或者又退出去重新进了一次9:00又签一次结果库里出现两条记录。而且上课高峰期并发大代码里先查再插会有竞态问题。我的处理方案是双保险数据库层靠uk_user_date唯一索引兜底重复插入会直接报错应用层捕获后返回友好提示。应用层签到接口进入事务后先检查当天是否已有有效记录有就直接返回今日已签到没有才执行插入。有同学会问要不要用Redis做分布式锁其实考勤系统的并发量远没到需要分布式锁的程度数据库唯一索引已经能解决99%的问题加上代码逻辑判断就够了。分布式锁放在论文的性能优化部分提一嘴可以但别为了秀技术而滥用。5. 真实环境中踩过的坑光线、口罩、相似脸与识别速度这个项目最难的部分不在功能开发而在真实环境下的调试。算法模型在测试集上表现不错一放到教室里就各种翻车。下面这几个坑都是我在实测中真实遇到的按影响程度排序。5.1 逆光和暗光识别率从95%掉到60%的教训第一次在教室实测靠窗那排的学生反复签到失败后来发现是逆光问题——窗外阳光太强摄像头拍出来的人脸面部区域全是暗的特征提取出来跟注册时差很多。后来单独测暗光环境走廊灯光昏暗的地方检测不到人脸的情况也频繁出现。解决办法做了几个图像预处理把图片转成灰度图后做直方图均衡化增强暗部细节。这是OpenCV几行代码的事但对识别率的提升立竿见影。多帧采样取最优连续取5帧画面分别做人脸检测选择人脸区域最清晰的一帧去提取特征。前端提示APP端检测到环境亮度过低时主动提示当前光线不足请移步明亮处避免用户反复试错。还有一个细节是注册照片和签到照片的光线差异性。注册时在室内灯光下拍走廊灯光稍微不一样就容易失败。所以录入阶段我刻意在上传前同样做一遍预处理让注册和签到的图片处在同一处理条件下特征一致性会好很多。5.2 口罩绕不开的遮挡问题这几年做考勤系统口罩是绕不开的话题。口罩遮住鼻子和嘴巴之后上半张脸的特征信息大量缺失特征向量和注册时的差距会变大识别率明显下降。尤其是冬天用户戴着口罩来签到总不能让人家在门口摘口罩吧。我的处理思路是分模式的。系统增加一个口罩模式开关用户选择开启后算法比对的重点放到眼睛和额头区域。具体做法是先定位人脸关键点把鼻子以下的区域做掩膜处理后再提取特征。代码上看是用关键点坐标把下半张脸区域清零然后喂给特征提取模型。这个方案不是100%准确但相比直接拿全脸特征去比在戴口罩场景下的正确接受率能提升不少。如果业务上不允许戴口罩签到比如某些需要核验完整面孔的场景那么合理的设计是引导用户在临时摘口罩的瞬间完成活体检测签到一次也就几秒钟用户是能接受的。关键是系统要给出清晰的引导文案和足够的准备时间别让人在摄像头前手忙脚乱。5.3 双胞胎和相似脸阈值要设得恰到好处一开始我把阈值设得比较松结果测试时一对双胞胎互相签到了对方的考勤。研究了下两个人的特征向量距离确实非常接近比一般人的距离小得多。当时第一反应是把阈值调严但调严之后识别的失败率也跟着涨上去了经常有同学要在镜头前调整半天角度才打得上卡。最后的解决思路是这样的阈值保持在一个合理的平衡点上但对距离低于极相似区间的用户增加二次验证。就是说如果某次比对的结果和目标用户的距离在0.30以内正常通过如果在0.30到0.55这个可疑区间而且库里有其他用户特征距离也很近就触发一次活体动作验证确认是真人后再通过。这样既不牺牲普通用户的使用体验又增加了相似脸的防护强度。5.4 性能优化从秒级到毫秒级的三个关键改动最开始跑通全流程时一次签到要1.5到2秒响应慢得让人着急。后来做了三处改动基本稳定在300毫秒左右。第一处是底库特征常驻内存。一开始每次比对都从数据库读出候选用户的所有特征再转成numpy数组光IO和序列化就要花掉不少时间。后来在算法服务启动时就把底库特征加载进内存比对时直接查内存耗时直接降了一截。第二处是减少比对范围。前面提过根据当前时间和课程信息只取该课程的学生特征作为候选集而不是拿着全量几千人的底库算距离。这次优化把比对的计算量从十万次降到几百次。第三处是接入FAISS做向量检索。如果底库真的到了几万人级别线性扫描就有点吃力了这时可以用FAISS建立索引做近似最近邻搜索毫秒级返回topK结果。我在论文里把这部分作为性能对比数据写了进去答辩时评委很感兴趣。另外Android端也有一点技巧摄像头用CameraX而不是Camera2前者生命周期管理更简单内存分配更友好每次签到拍完照片及时回收Bitmap避免App内存持续上涨导致卡顿甚至OOM。6. 从代码到毕业设计论文毕设文档的写法参考代码写完只是完成了项目的一半对一个毕设题目来说论文才是最终呈现形式。很多同学代码能力很强一写文档就头疼这里我给一个可以直接参考的骨架。6.1 目录怎么设计最稳以基于人脸识别的签到考勤APP的设计与实现这个题目比较稳妥的目录结构是这样的第一章 绪论研究背景与意义、国内外研究现状、论文结构安排第二章 相关技术介绍Java/Spring Boot、Python、OpenCV、人脸识别相关算法第三章 系统分析可行性分析、需求分析、用例图用例描述第四章 系统设计总体架构设计、功能模块设计、数据库设计、接口设计第五章 系统实现环境搭建、核心模块实现重点写人脸识别模块和签到模块、界面展示第六章 系统测试测试环境、功能测试用例、性能测试结果与分析第七章 总结与展望项目完成情况总结、不足之处与后续改进方向这套结构的优点是逻辑顺先讲为什么做再讲用什么做再讲怎么做然后讲做成什么样最后讲还能怎么做更好。评委按目录就能把你的工作看明白。6.2 摘要和技术描述这样写不踩雷摘要最容易犯的毛病是写得像功能列表。给你一个参考框架针对传统考勤方式存在的代打卡、识别率不稳定、高峰期排队等问题设计并实现了一款基于人脸识别的签到考勤APP。系统采用Spring Boot搭建后端服务Android端负责用户交互与人脸图像采集Python算法服务完成人脸检测、特征提取与身份比对。文章详细阐述了人脸识别技术在考勤场景中的应用流程包括人脸录入、活体检测、特征匹配以及异常考勤处理等核心环节。测试结果表明系统在常规室内环境下能够高效、准确地完成签到任务具备较好的实际应用价值。注意这里有一个容易被破防的点研究方法和技术原理必须写实。把人脸检测-人脸对齐-特征提取-特征比对四步流程写成你能讲明白的原理而不是一句通过深度学习模型识别人脸就带过。评委一旦追问特征向量怎么生成的距离阈值怎么确定的你得能回答上来。6.3 测试用例与性能数据的组织方式测试章节有没有说服力直接影响论文质量。功能测试要覆盖核心流程格式清晰即可。用例编号测试模块测试步骤预期结果实际结果TC-01用户登录输入正确用户名密码登录成功并返回Token通过TC-02人脸录入上传正面清晰人脸照片特征提取成功并绑定用户通过TC-03在线签到用户对镜头完成活体动作签到成功生成考勤记录通过TC-04重复签到同一天再次签到提示今日已签到通过TC-05活体拦截使用手机照片挡住摄像头活体检测失败签到被拒通过性能测试建议给出真实测试数据。比如不同人脸底库规模下的识别耗时对比或者不同阈值下的准确率对比。这些数据不需要多华丽但必须看起来是做过实验的人才能写出来的。我当时就把内存底库和数据库直查对比了一下再附上一张耗时折线图这部分工作量不大却是论文里最扎实的实证支撑。最后再分享一个个人的体会这类身份识别项目做完之后最能沉淀下来的能力不是调包调参而是工程调试经验——你知道光线不对该怎么办知道相似脸怎么兜底知道性能瓶颈通常出现在哪一层。如果后续想在这个题目上继续加分可以扩展考勤大数据分析模块比如缺勤趋势可视化、课堂专注度分析、异常考勤预警或者把Android端改成微信小程序让校园场景下不用额外装APP。这些扩展方向跟原题贴合得很自然答辩时也有得聊。