实验数据管理与分析系统【双库实现】

news2024/11/25 22:27:57

一、介绍

实验数据管理与分析系统,实现了对实验数据和统计数据的备份、统一管理、可视化分析展示、操作日志展示等功能。系统角色分为管理员与普通用户,普通用户可以上传实验数据到系统主库,将主库数据迁移到从库并进行操作,然后针对从库数据进行数据可视化展示。系统管理员可以直接操作主库实验数据,并对普通用户进行管理,查看系统用户操作日志。

二、架构

系统采用单体架构,后端使用SpringBoot,前端使用SpringBoot自带模板引擎Thymeleaf和Layui进行界面开发,通过Echarts进行图表展示,数据库使用MySQL数据库。

三、功能

1. 登录鉴权

界面

在这里插入图片描述

描述

加入了登录拦截、系统管理员页面拦截

核心代码

WebConfig.java

public class WebConfig implements WebMvcConfigurer {

    /**
     * 拦截器配置
     * @param registry
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginInterceptor())
                .addPathPatterns(
                        "/exp_page",
                        "/data_move_page",
                        "/sub_page",
                        "/data_visual_list",
                        "/data_visual",
                        "/dashboard"
                );  //拦截所有页面跳转请求

        registry.addInterceptor(new AuthInterceptor())
                .addPathPatterns(
                        "/user_page",
                        "/log_page",
                        "/admin_exp_page"
                );
    }
}

AuthInterceptor.java

public class AuthInterceptor extends HandlerInterceptorAdapter {

    //请求处理前调用的方法  执行
    @Override
    public boolean preHandle(HttpServletRequest request,
                             HttpServletResponse response,
                             Object handler) throws Exception {
        User user = (User) request.getSession().getAttribute("user");
        if (user == null) {//未登录
            response.sendRedirect("/login");//重定向到登录页面
            return false;
        } else {
            if (user.getRole() != 1) { // 非管理员
                response.sendRedirect("/dashboard");//没有管理员权限跳转到仪表盘
                return false;
            }
        }
        return true;
    }
}

2. 仪表盘

界面

在这里插入图片描述

描述

展示系统数据集总数,实验数据总数和可视化总数以及系统用户创建的可视化图表

3. 实验数据上传

界面

在这里插入图片描述

描述

上传实验数据到主库中,目前只支持csv格式的文件

核心代码
	@ExpLog(description = "上传实验数据")
    @Override
    public void uploadExpDataFile(MultipartFile file, ExpCreateReq req) throws Exception {
        BufferedReader reader = new BufferedReader(new InputStreamReader(file.getInputStream(), "UTF-8"));
        // 判断数据库表是否存在
        List<String> dbTables = expMapper.getDBTables(EXP_DB);
        if (dbTables.contains(req.getTableName())) {
            throw new Exception("数据库表已存在");
        }
        // 读取csv文件
        CSVReader csvReader = new CSVReader(reader);
        // 解析表头,动态创建数据库表
        String[] header = csvReader.readNext();
        // 1. 插入表字段关联表
        for (String field : header) {
            expMapper.insertExpTableFieldRel(req.getTableName(), field);
        }
        // 2. 创建表结构
        expMapper.createTable(SqlUtil.createTableSql(req.getTableName(), header));
        // 3. 添加实验信息
        List<String[]> cache = new ArrayList<>(); // 缓存空间
        long count = 0;
        String[] row;
        while ((row = csvReader.readNext()) != null) {
            if (count % Constant.EXP_MEM_CACHE == 0 && count != 0) {
                // 4. 读取缓存批量插入实验数据
                expMapper.insertExpData(SqlUtil.batchInsertDataSql(req.getTableName(), header, cache));
                cache.clear();
            }
            cache.add(row);
            count++;
        }
        // 在执行一次清空缓存
        if (cache.size() > 0) {
            expMapper.insertExpData(SqlUtil.batchInsertDataSql(req.getTableName(), header, cache));
        }
        System.out.println("导入实验数据条数: " + count);
        expMapper.insertExpInfo(req, count);
        // 备份本地文件
        MultipartFileUtil.saveMultipartFile(file, BACKUPS_PATH);
        csvReader.close();
        reader.close();
    }

4. 实验数据迁移

界面

在这里插入图片描述

描述

展示数据集主库和从库数据,支持选择指定数据迁移和所有数据迁移

核心代码
	@ExpLog(description = "实验数据迁移")
    @Override
    public int dataMove(DataMoveReq req) {
        System.out.println(req);
        // 1.查看主题数据库中是否存在该表
        List<String> dbTables = subMapper.getDBTables(SUB_DB);
        Experiment experiment = expMapper.queryExpById(req.getId());
        // 查询表头
        List<ExpTableFieldRel> list = expMapper.queryExpTableField(experiment.getTableName());
        String[] header = list.stream().map(ExpTableFieldRel::getFieldName).collect(Collectors.toList()).toArray(new String[list.size()]);
        if (!dbTables.contains(experiment.getTableName())) {
            // 2. 不存在则创建
            subMapper.createTable(SqlUtil.createTableSql(experiment.getTableName(), header));
        }

        // 3.插入迁移数据
        List<Map<String, Object>> datas = req.getList();
        List<String[]> rows = datas.stream().map(data -> {
            List<String> row = new ArrayList<>();
            for (String s : header) {
                Object o = data.get(s);
                row.add(String.valueOf(o));
            }
            return row.toArray(new String[row.size()]);
        }).collect(Collectors.toList());
        subMapper.insertExpData(SqlUtil.batchInsertDataSql(experiment.getTableName(), header, rows));
        return 1;
    }

    @ExpLog(description = "迁移所有数据")
    @Override
    public int dataMoveAll(int expId) {
        Experiment experiment = expMapper.queryExpById(expId);
        List<Map<String, Object>> datas = expMapper.queryExpData(experiment.getTableName());
        return dataMove(new DataMoveReq(expId, datas));
    }

5. 主题数据操作

界面

在这里插入图片描述

描述

展示数据集从库数据,提供对主题数据查询修改删除功能

核心代码
    @ExpLog(description = "删除主题数据")
    @Override
    public int batchDeleteData(int id, List<Integer> ids) {
        Experiment experiment = expMapper.queryExpById(id);
        ids.forEach(i -> subMapper.deleteData(experiment.getTableName(), i));
        return ids.size();
    }

    @ExpLog(description = "修改主题数据")
    @Override
    public int updateData(int id, Map<String, String> data) {
        Experiment experiment = expMapper.queryExpById(id);
        String sql = SqlUtil.updateDataSql(experiment.getTableName(), data);
        subMapper.updateData(sql);
        return 1;
    }

SqlUtil.java

    /**
     * 修改数据SQL
     */
    public static String updateDataSql(String tableName, Map<String, String> data) {
        StringBuilder sql = new StringBuilder("update ").append(tableName).append(" set ");
        boolean isFirst = true;
        for (String key : data.keySet()) {
            if (!key.equals("id")) {
                if (isFirst) {
                    isFirst = false;
                } else {
                    sql.append(",");
                }
                sql.append("`").append(key).append("`").append("=").append("\"").append(data.get(key)).append("\"");
            }
        }
        sql.append(" where id=").append(data.get("id"));
        System.out.println(sql);
        return sql.toString();
    }

6. 可视化列表

界面

在这里插入图片描述

描述

可视化列表页面,提供可视化的增加修改删除操作

7. 可视化详情

界面

在这里插入图片描述

描述

对数据集进行条件过滤、数据聚合、面板设置等。目前支持图表类型有:条形图、折线图、面积图、饼图和词云图

核心代码

数据聚合

@Override
    public ChartResp queryAggregationData(Integer id, Map filters, YAggregation y, XAggregation x) {
        Experiment experiment = expMapper.queryExpById(id);
        List<VisualFilter> filterList = new ArrayList<>();
        for (Object key : filters.keySet()) {
            VisualFilter f = JSON.parseObject(JSONObject.toJSONString(filters.get(key)), VisualFilter.class);
            filterList.add(f);
        }
        if (StringUtils.isNotBlank(x.getXField())) {
            String sql = SqlUtil.dataAggregationSql(experiment.getTableName(), filterList, y, x);
            List<Map<String, Object>> maps = subMapper.execAggregation(sql);
            List<String> xAxis = new ArrayList<>();
            List<String> yAxis = new ArrayList<>();
            maps.forEach(map -> {
                xAxis.add(String.valueOf(map.get(x.getXField())));
                yAxis.add(String.valueOf(map.get(Constant.SORT_METRIC)));
            });
            return new ChartResp(xAxis, yAxis);
        }
        return new ChartResp();
    }

8. 系统用户管理

界面

在这里插入图片描述

描述

系统管理员可以通过该界面对普通用户进行增加修改删除操作

9. 系统操作日志

界面

在这里插入图片描述

描述

系统操作日志,对用户操作时的描述,地址,参数等信息进行记录

核心代码
public class UserLogAspect {
    private static final Logger logger = LoggerFactory.getLogger(UserLogAspect.class);

    private LogService logService;
    private HttpSession httpSession;

    @Autowired
    public UserLogAspect(LogService logService, HttpSession httpSession) {
        this.logService = logService;
        this.httpSession = httpSession;
    }

    /**
     * 注解Pointcut切入点
     * 定义出一个或一组方法,当执行这些方法时可产生通知
     * 指向你的切面类方法
     * 由于这里使用了自定义注解所以指向你的自定义注解
     */
    @Pointcut("@annotation(com.example.exp.annotation.ExpLog)")
    public void logPointCut() {
    }

    @Around("logPointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        long beginTime = System.currentTimeMillis();
        // 执行方法
        Object result = point.proceed();
        // 执行时长(毫秒)
        long time = System.currentTimeMillis() - beginTime;
        // 异步保存日志
        saveLog(point, time, null);
        return result;
    }

    /**
     * 异常通知 记录操作报错日志
     */
    @AfterThrowing(pointcut = "logPointCut()", throwing = "e")
    public void doAfterThrowing(JoinPoint point, Throwable e) throws Exception {
        saveLog(point, 0, e.getMessage());
    }

    void saveLog(JoinPoint joinPoint, long time, String errorInfo) throws Exception {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        UserLog userLog = new UserLog();
        ExpLog expLog = method.getAnnotation(ExpLog.class);
        if (expLog != null) {
            // 注解上的描述
            userLog.setDes(expLog.description());
        }
        // 获取request
        HttpServletRequest request = HttpContextUtils.getRequest();
        // 设置根路径
        String path = request.getContextPath();
        String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path;
        userLog.setRoot(basePath);
        // 获取uri
        userLog.setUri(request.getRequestURI());
        // 请求方式
        userLog.setMethod(request.getMethod());
        // 用户名
        User user = (User) httpSession.getAttribute("user");
        String username = user.getUsername();
        // 请求的参数
        userLog.setParams(getServiceMethodParams(joinPoint));
        if (StringUtils.isBlank(username)) {
            userLog.setUsername("获取到的用户名为空");
        } else {
            userLog.setUsername(username);
        }
        userLog.setSpendTime((int) time);
        // 相应状态 1:成功, 0:失败
        userLog.setResult(1);
        // 日志类型
        userLog.setType("info");
        // 错误信息
        if (errorInfo != null) {
            userLog.setErrorInfo(errorInfo);
            userLog.setResult(0);
            userLog.setType("error");
        }
        // 保存系统日志
        logService.save(userLog);
    }

    /**
     * 获取json格式的参数用于存储到数据库中
     *
     * @param joinPoint
     * @return
     * @throws Exception
     */
    private String getServiceMethodParams(JoinPoint joinPoint)
            throws Exception {
        Object[] arguments = joinPoint.getArgs();
        ObjectMapper om = new ObjectMapper();
        // 上传文件时的特殊处理
        String methodName = joinPoint.getSignature().getName();
        if (methodName.equals("uploadExpDataFile")) {
            return om.writeValueAsString(arguments[1]);
        }
        return om.writeValueAsString(arguments);
    }

}

10. 实验数据管理

界面

在这里插入图片描述

描述

系统管理员可以对实验数据直接操作

四、总结

后续将添加地图可视化,丰富数据聚合类型

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/774249.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

idea集成jrebel完成热部署

idea集成jrebel完成热部署 今天想把idea设置成热部署&#xff0c;结果按照教程设置完还是不行&#xff08;一般教程请自行百度&#xff09;。实在受不了了就集成jrebel插件来完成热部署 一、下载插件 首先在idea的settings里的plugs搜索jrebel&#xff0c;将插件安装&#xff…

写字楼/办公楼能源管理系统的具体应用 安科瑞 许敏

0 引言 随着社会的进步&#xff0c;我国经济的快速发展&#xff0c;企业的办公环境和方式发生了巨大的变化&#xff0c;专业的写字楼在各大城市遍布林立。写字楼的出现使得各地企业办公集中化、高效化&#xff0c;然而写字楼物业管理的同步发展对于企业服务来说更是一个很大的…

HarmonyOS/OpenHarmony元服务开发-ArkTS卡片相关模块

图1 ArkTS卡片相关模块 FormExtensionAbility&#xff1a;卡片扩展模块&#xff0c;提供卡片创建、销毁、刷新等生命周期回调。 FormExtensionContext&#xff1a;FormExtensionAbility的上下供接口实现更新卡片、设置卡片更新时间、获取卡片信息、请求发布卡片等。 formIn…

高精尖领域数据暴增,分布式存储渐当大任

近年来&#xff0c;数据存储市场“最靓的仔”无疑就是分布式存储。 大模型火了之后&#xff0c;围绕Chat的应用也越来越多&#xff0c;通过AI生成图片、报表、音视频的应用比比皆是。众所周知&#xff0c;要想训练出一个有学习能力的、可理解的、响应迅速的大模型应用&#xf…

Hook技术 - 某站cookie解析

本文仅供学习交流&#xff0c;只提供关键思路不会给出完整代码&#xff0c;严禁用于非法用途&#xff0c;若有侵权请联系我删除&#xff01; 目标网站&#xff1a;aHR0cHM6Ly93d3cuYmlsaWJpbGkuY29tLw 目标接口&#xff1a;Cmh0dHBzOi8vYXBpLmJpbGliaWxpLmNvbS94L3dlYi1pbnRl…

小程序逆向动态调试及修复过程

微信小程序逆向 : https://codeooo.blog.csdn.net/article/details/120219900 一 &#xff1a;_typeof3 is not a function 小程序解包之后&#xff0c;在微信开发者工具当中点开调试器&#xff0c;选择Console选项卡&#xff0c;发现左侧的预览界面是空白的&#xff0c;虽然…

leetcode 40. 组合总和 II

2023.7.19 此题为 组合总和 的升级版。本题的特殊之处在于 给定的candidates数组只一个无序且包含重复元素的数组&#xff0c;并且最终的解集不能包含重复的组合。 所以本题的关键在于去重。那么&#xff0c;此类题的去重分为两种&#xff0c;一种是解集内部去重&#xff0c;灵…

设计模式-享元模式在Java中的使用示例-围棋软件

场景 享元模式 简介 当一个软件系统在运行时产生的对象数量太多&#xff0c;将导致运行代价过高&#xff0c;带来系统性能下降等问题。 例如在一个文本字符串中存在很多重复的字符&#xff0c;如果每一个字符都用一个单独的对象来表示&#xff0c;将会占用 较多的内存空间…

【网络】网络编程套接字

目录 一、预备知识 1、网络通信理解 2、源IP地址和目的IP地址 3、端口号 二、网络字节序 三、socket编程接口 1、socket常见API 2、sockaddr结构 3、sockaddr结构体 3.1、sockaddr结构体 3.2、sockaddr_in结构体 四、简单的UDP网络程序 1、创建套接字接口 2、绑定…

C# OpenCvSharp 直方图均衡化 图像去雾

直方图 直方图均衡化 自适应的直方图均衡化 全局直方图均衡化 局部直方图均衡化 对比度调整 代码 using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; us…

TRT3-trt-basic - 5.1 封装插件

之前TRTbasic4是新增插件&#xff0c;这次我们看看不新增插件&#xff0c;仅凭封装可不可以达到一样的功能 首先可以看到这次的g.op不再是MYSELU了&#xff0c;而是plugin&#xff0c;那为什么.cu还能识别出来呢&#xff1f; 是因为在这里做了一个通用的plugin DEFINE_BUILTIN…

常用API学习03(Java)

String char charAt(int index) 返回char指定索引处的值 char[] toCharArray() 将此字符串转换为新的字符数组 int compareTo(String anotherString) 按字典顺序比较两个字符串 boolean contains(CharSequence s) 当且仅当此字符串包含指定的char值序列才返…

osg osgQt模块 基于 QGLWidget 源码分析

osgQt基于QGLWidget实现了在Qt窗口内OSG渲染操作。Qt以其开源、跨平台、方便快捷、现代化的界面风格等优点&#xff0c;已经成为了目前桌面版CAD/CAE/CAM等软件开发的首选组件。因此&#xff0c;非常有必要在OSG的基础之上&#xff0c;研究Qt桌面系统内集成OSG渲染功能的相关技…

Linux查看机器内存空间

执行 fdisk命令查看磁盘空间 fdisk -l更多方法参考&#xff1a; Linux检查磁盘空间

一种工业机器人绝对精度的提升方法

摘要&#xff1a;一种新的校准方法&#xff0c;使用动作捕捉作为测量工具&#xff0c;利用ELM神经网络作为非几何误差源补偿&#xff0c;提升工业机器人的绝对精度。 工业机器人的绝对精度是评估其综合性能的重要指标之一。然而&#xff0c;由于机器人受到多种因素的影响&#…

概率论的学习和整理19:条件概率我知道,但什么是条件期望?---用来解决递归问题(草稿)

目录 1 目标问题&#xff1a; 什么是条件期望&#xff1f; 条件期望有什么用&#xff1f; 2 条件期望&#xff0c;全期望公式 3 条件期望&#xff0c;全期望公式 和 条件概率&#xff0c;全概率公式的区别和联系 3.1 公式如下 3.2 区别和联系 3.3 概率和随机过程 4 有什…

8年软件测试工程师感悟 —— 写给还在迷茫中的朋友

去年还在全网声讨互联网企业996呢&#xff0c;今年突然没声音了&#xff0c;也不用讨论在哪个路灯上吊死互联网资本家了&#xff0c;因为都被裁了。 继教育培训领域大幅度裁员之后&#xff0c;大厂裁员消息也开始陆续传出&#xff0c;百度AIG,MEG多条业务线进行精简&#xff0…

线程系列 3 - 关于 CompletableFuture

线程系列3-关于 CompletableFuture 1、从 Future 接口说起2、CompletableFuture 对 Future 的改进2.1、CompletionStage 接口类2.2、runAsync 和 supplyAsync 创建子任务2.3、 whenComplete 和 exceptionally 异步任务回调钩子2.4、调用 handle() 方法统一处理异常和结果2.5、异…

重温黑盒、白盒与灰盒测试方法

黑盒、白盒和灰盒测试方法是软件测试中常用的测试策略&#xff0c;用于评估系统的功能和质量。 对于黑盒、白盒与灰盒测试方法的理解&#xff0c;几年前我在某乎做过一个概念性的回答&#xff0c;当时提问者询问&#xff1a;如何跟非技术人员解释黑盒、白盒、灰盒测试的区别&a…

UG\NX二次开发 使用exception类,异常处理的方法

文章作者:里海 来源网站:https://blog.csdn.net/WangPaiFeiXingYuan 简介 异常处理是一种编程中常见的错误处理机制,它允许程序在遇到错误或异常情况时优雅地处理。在C++中,异常类是一种重要的异常处理工具。 当程序发生错误或异常时,我们可以使用excepti…