docker-compose一键部署(仅需下载docker-compose文件夹运行即可)
用户在可靠验证下实现简化身份确认
在已有用户数据库基础上
传统校验方式:
- 直接选择 ==》 用户 【不安全/不可靠】
- 用户名 + 密码 查询数据库 ==》 用户 【麻烦/】
新型校验方式
- 第三方工具、qq/微信扫码 ==》 用户 【没手机时麻烦/不稳定】
- 生物特征人脸/指纹识别 ==》 用户 【方便/可靠】
因此基于各家人脸识别框架,选用百度作为接口,围绕其开发此系统;
这是一个前后端兼备的前后台分离全栈项目
- 前台上,PC端选择用主流Vue框架[另一位同学完成]、后台管理采用Erupt框架搭建、web显示采用百度Amis框架及bootstrap完成
- 后端选用主流的Java SpringBoot搭建,使用Schedule完成动态定时任务,使用AOP拦截消息通过pushPlus进行微信推送。
- 其他技术,后台管理依靠框架Erupt进行的快速构建,使用swagger集成api文档。采用百度云人脸识别接口基于百度人脸识别SDK完成本地数据库与云人脸库的对接。
【name404.study.face】文件结构
│ FaceApplication.java
│
├─aop 自定义注解实现标记切割
│ LogAspect.java
│ WxPush.java
│
├─common 公共包、存放公共文件
├─config 配置包
│ CompleteScheduleConfig.java
│ RedisConfig.java
│ Swagger2.java
│
├─controllers 控制层
│ FaceController.java
│ RouteController.java
│ SignInController.java
│ UserContorller.java
│ VisitorContorller.java
│
├─dao Dao层
│ GroupDao.java
│ SignLogDao.java
│ SignLogDetailDao.java
│ SystemVariablesDao.java
│ UserDao.java
│ UserDetailDao.java
│
├─entity 数据层
│ Group.java
│ SignLog.java
│ SignLogDetail.java
│ SystemVariables.java
│ User.java
│ UserDetail.java
│
├─handler handler层
│ FetchHandlerImpl.java
│ GlobalExceptionHandler.java
│
├─service 接口层
│ │ BaiduFaceService.java
│ │ GroupService.java
│ │ SignLogService.java
│ │ SystemVariablesService.java
│ │ UserService.java
│ │ VisitorService.java
│ │
│ └─impl 接口实现层
│ BaiduFaceServiceImpl.java
│ GroupServiceImpl.java
│ SignLogServiceImpl.java
│ SystemVariablesServiceImpl.java
│ UserServiceImpl.java
│ VisitorServiceImpl.java
│
└─utils 工具包
Base64Utils.java
OkHttpClientUtil.java
RedisUtil.java
Result.java
基础四个表:用户表、用户组别二级表、签到日志、系统变量(用户反馈可有可无)
视图部分:使用userDetail + signLogDetail减少后台对数据库的连表查询
数据库字段中存在大量引用[存在user_group二级分类菜单中],直接返回的内容不方便使用,使用时连表查询,多次太费空间,因此基于视图一次性创建好引用,方便后台直接调用,加速后台响应
SELECT
`sign_log`.`id` AS `id`,
`sign_log`.`user_name` AS `user_name`,
`sign_log`.`in_time` AS `in_time`,
`sign_log`.`out_time` AS `out_time`,
`sign_log`.`msg` AS `msg`,
`sign_log`.`date` AS `date`,
`a`.`name` AS `user_group`,
`d`.`name` AS `to_do`,
`b`.`name` AS `class_group`,
`c`.`name` AS `unit_group`,
`sign_log`.`sign_time` AS `sign_time`
FROM
((((
`sign_log`
LEFT JOIN `user_group` `a` ON ((
`sign_log`.`user_group` = `a`.`id`
)))
LEFT JOIN `user_group` `b` ON ((
`sign_log`.`class_group` = `b`.`id`
)))
LEFT JOIN `user_group` `c` ON ((
`sign_log`.`unit_group` = `c`.`id`
)))
LEFT JOIN `user_group` `d` ON ((
`sign_log`.`to_do` = `d`.`id`
)))
[UserService][UserDao]
用户基础服务 [Dao层接口定义]
数据库设计
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`real_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`status` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '0',
`face_id` int(11) NULL DEFAULT NULL,
`user_group` int(11) NULL DEFAULT 5,
`now_sign_id` int(11) NULL DEFAULT NULL,
`email` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`telphone` int(11) NULL DEFAULT NULL,
`expired_time` datetime NULL DEFAULT NULL,
`sign_state` bit(1) NULL DEFAULT b'0',
`class_group` bigint(20) NULL DEFAULT 0,
`unit_group` bigint(20) NULL DEFAULT 0,
PRIMARY KEY (`id`) USING BTREE,
INDEX `FK5ixd8ou7x5sln7b00u8qpf7il`(`group_id`) USING BTREE,
CONSTRAINT `FK5ixd8ou7x5sln7b00u8qpf7il` FOREIGN KEY (`group_id`) REFERENCES `user_group` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB AUTO_INCREMENT = 6 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = COMPACT;
[SystemVariablesService][SystemVariablesDao]
系统变量服务 [map存储复杂的系统变量] [通过key获取value的json] [通过对json修改实现变量增删改查] [简化数据库,方便统一处理常量]
数据库设计
-- ----------------------------
-- Table structure for system_variables
-- ----------------------------
DROP TABLE IF EXISTS `system_variables`;
CREATE TABLE `system_variables` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`my_key` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`my_value` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 13 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = COMPACT;
[SignLogService][SignLogDao]
签到日志服务 [基本增删改查] [签到逻辑处理] [Dao层接口]
数据库设计
-- ----------------------------
-- Table structure for sign_log
-- ----------------------------
DROP TABLE IF EXISTS `sign_log`;
CREATE TABLE `sign_log` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`user_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`in_time` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`msg` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`out_time` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`sign_time` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`to_do` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`user_group` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`date` datetime NULL DEFAULT NULL,
`from_user_id` bigint(20) NULL DEFAULT NULL,
`user_class` bit(1) NULL DEFAULT NULL,
`class_group` bigint(20) NULL DEFAULT NULL,
`unit_group` bigint(20) NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 90 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = COMPACT;
模块效果
人脸识别服务 [人脸库的增删改查] [人脸库对接本地数据库]
[GroupService]
对于一些杂乱的内容设置二级分类方便统一管理
数据库设计
-- ----------------------------
-- Table structure for user_group
-- ----------------------------
DROP TABLE IF EXISTS `user_group`;
CREATE TABLE `user_group` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`group_class` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`message` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 10 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = COMPACT;
注册百度云获取AppId ,API KEY Secret key作为密钥
下载SDK对接用户数据库 SDK和API区别 API:直接面向接口提交http请求 SDK:基于JAVA/PYTHON等语言集成一门API的通用函数库,方便直接调用对接
引入jar包,初始化client,开始使用
ConfigurationProperties(prefix = "baidu")
public class BaiduFaceServiceImpl implements BaiduFaceService {
private String app_id;
private String api_key;
private String secret_key;
private AipFace client = null;
void initClient(){
if(client == null){
System.out.println("初始化百度sdk");
client = new AipFace(app_id, api_key, secret_key);
}
}
...
}
怎么启动
创建配置类
@Configuration @EnableScheduling public class CompleteScheduleConfig implements SchedulingConfigurer
怎么动态:在执行定时任务下一次获取时间不是通过本地查询,而是通过数据库查询后后台拼接形成新的下一次时间
@Configuration
@EnableScheduling
public class CompleteScheduleConfig implements SchedulingConfigurer {
@Autowired
private SystemVariablesService systemVariablesService;
@Autowired
private SignLogService signLogService;
/**
* 执行定时任务.
*/
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.addTriggerTask(
//1.添加任务内容(Runnable)
() -> {
try {
//调用签到模块的定时执行任务
signLogService.scheduleTask();
} catch (ParseException e) {
e.printStackTrace();
}
},
//2.设置执行周期(Trigger)
triggerContext -> {
//2.1 从数据库获取执行周期
String time = systemVariablesService.getKey(SystemVariablesService.getEndTime);
String[] HMS = time.split(":");
String cron = HMS[2] + " " + HMS[1] +" " + HMS[0] +" * * ?";
System.out.println("【下一次执行时间】" + cron);
//2.2 合法性校验.
//2.3 返回执行周期(Date)
return new CronTrigger(cron).nextExecutionTime(triggerContext);
}
);
}
}
导入AOP相关的包
创建注解作为标记点
import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;//表示运行时开启@Retention(RetentionPolicy.RUNTIME)//表示定义在方法上@Target(ElementType.METHOD)public @interface WxPush {}
创建APO层去切割WxPush方法 微信推送:使用pushplus提供的接口直接通过http请求发送
- 获取自己微信token
- 使用pushplus提供的接口
- 通过http工具请求推送
@Aspect@Componentpublic class LogAspect { @Autowired private SystemVariablesService systemVariablesService; @Autowired private SignLogService signLogService; @Autowired private OkHttpClientUtil okHttpClientUtil; /** * 注解切入点 */ @Pointcut("@annotation(name404.study.face.aop.WxPush)") public void pointCut(){ } @AfterReturning(value = "pointCut()",returning = "result") public void wxPush(Object result){ Result res = (Result)result; //是成功的返回类型 if(res.getStatus() == 200){ // 根据返回结果进行微信推送 String title ="【每日打卡消息】 当前:" +toDayMsg.get("nowUser") + "人"; String content = (String)res.getData(); content += "<br/><br/>"; String startTime =systemVariablesService.getKey(SystemVariablesService.getStartTime); String endTime = systemVariablesService.getKey(SystemVariablesService.getEndTime); content += "[今日打卡时间段] " + startTime + "-" + endTime + "<br/><br/>"; content += "[今日总打卡数] " + toDayMsg.get("allUser") + "<br/>"; content += "[今日当前人数] " + toDayMsg.get("nowUser") + "<br/>"; content += "[今日签退人数] " + toDayMsg.get("leaveUser") + "<br/>"; String url = "http://pushplus.hxtrip.com/send?token="+token+"&title="+title+"&content="+content+"&template=html&topic="+groupId; okHttpClientUtil.getData(url); } }}
使用:直接在方法前加上@WxPush即可切割拦截进行结果集的推送了
导入包导入工具类,创建bean引入配置
<!-- redis --><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId></dependency>
当作map直接使用提供好的工具包
类比mysql数据库 100 访问同一个页面获取100跳信息,要查询100次 100数据的数据库库,数据库承受1w次查询 用redis 第一次查数据库 后面只要信息不变全部查缓存 ,数据库只承受100次查询
原理:
查:在查询数据库之前判断redisUtil里面存了首页信息没,有就直接返回
增:没有数据就查询数据库得到的信息存redis
改:每次数据库修改就清空redis缓存,下次查询就继续执行上面操作
前后台分离 多人合作:jsp不可行,要采用前后台分离 SSM:前台的数据一定要有后台先提供,导致开发工期延长 前后端分离:通过简单的数据包装,确保了视图和控制器的分离。只负责分发数据,谁拿不管。可以前后台同时进行,最后对接
统一的数据管理平台 ==》 swagger ==》 方便前台用户阅读 使用
引入jar包
<!-- swagger --><dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> <version>2.9.2</version></dependency><dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> <version>2.9.2</version></dependency>
配置config 配置扫描包指定扫描那些controller
@Configuration@EnableSwagger2//是否开启swagger,正式环境一般是需要关闭的(避免不必要的漏洞暴露!),可根据springboot的多环境配置进行设置@ConditionalOnProperty(name = "swagger.enable", havingValue = "true")public class Swagger2 { // swagger2的配置文件,这里可以配置swagger2的一些基本的内容,比如扫描的包等等 @Bean public Docket createRestApi() { return new Docket(DocumentationType.SWAGGER_2) .apiInfo(apiInfo()) .select() // 为当前包路径 .apis(RequestHandlerSelectors.basePackage("name404.study.face.controllers")).paths(PathSelectors.any()) .build(); } // 构建 api文档的详细信息函数,注意这里的注解引用的是哪个 private ApiInfo apiInfo() { return new ApiInfoBuilder() // 页面标题 .title("Spring Boot 测试使用 Swagger2 构建RESTful API") // 创建人信息 .contact(new Contact("404name", "yuque.com/404name", "1308964967@qq.com")) // 版本号 .version("1.0") // 描述 .description("API 描述") .build(); }
已完成基础框架,后台框架,swagger集成api 待开发如下。
- 动态增添单位、课题组、事由
- 事由的固定处理[实验默认3小时,其他超过30分钟处理为xxx,未达0.5小时按0.5记录]
- 常见权限失效时间[直接在用户字段设置freeTime]
- 可修改默认失效时间[在系统变量里面添加默认实现时间]
- 未开发