智能BI项目第二期

该项目是全程跟着鱼皮,还未进行功能扩展。为了方便日后复习,打算将一些重要的步骤记录下来。

该笔记着重记录后端代码,前端了解大致方向即可

项目概述

BI:即商业智能:Business Intelligence

过去

       需要手动导入数据、选择要分析的字段和图表,并由专业的数据分析师完成分析,最后得出结论。

现在

       用户只需输入想要分析的目标,并上传原始数据,系统将利用 AI 自动生成可视化图表。这样,会比较方便

端口占用

Windows Hyper-V 虚拟化平台占用了端口

什么是Hyper-V : 

为了使硬件虚拟化,即创建一个没有物理形式的硬件环境,你需要在物理计算机和虚拟机之间有一个中介。这个接口被称为管理程序。物理主机系统可以映射到共享主机硬件(父分区)的多个虚拟客户系统(子分区)。微软已经创建了自己的管理程序 Hyper-V,它包含在 Windows 10、11 或 Windows 8 的专业版本中。该软件也被安装在 Windows Server 中。

第一种方法:

查看被占用的端口,然后选择一个没被占用的端口启动项目。cmd命令打开输入

netsh interface ipv4 show excludedportrange protocol=tcp

 第二种方法:

直接禁用Hyper-V(不推荐)

前端结构介绍

大概了解即可,不用刻意背,用着用着就懂了

检查提交的代码,是否规范(代码校验的小工)

umi 配置,包含由,构路建等配置

整个项目所用的框架,Ant Design Pro 的核心配置

Ant Design Pro 默认设置,可以更改页面样式

一个示例的数据(没用,删掉)

代理,方便本地开发(一般用不到,不用删)

定义网页路由(就是输入哪个网址,跳转到哪个页面)

本地模拟数据(删掉)

icon目录中存放了页面的图标(删掉)

框架自动生成的隐藏文件(不用管)

业务通用组件

国际化资源(删掉)

业务页面入口和常用模板

后台接口服务

可以把自带的删掉,这里先删除 swagger目录,ant-design-pro 先不删,现在项目里很多内容用到它,等后面改完再删。

控制页面访问权限

整个 Ant Design Pro 框架的主要文件

全局样式

全局 JS

开发 app 或 h5 网页指定多种不同的配置。(删掉)例如:生成项目名称、项目图标尺寸等...一般打包的时候用到

控制前端页面发送请求的配置。例如:你向哪个地址发送请求,请求发送错误,怎么处理等.....

h5 网页离线时优化页面的体验

测试工具(删掉)

全是未引用(删掉)

保证前端项目代码规范。在 webstorm 中可以按[Ctrl+Alt+L]格式化代码,可以让代码看起来更美观。

单元测试框架(删掉)

控制语法

整个项目的默认文档

模板优化:

替换logo

public目录下的一些图片是ant design pro自带的图片,我们只要把对应文件后缀和名称的图片覆盖后,就可以更改成我们自己的图片

 替换 Logo 网站替换 favicon 网站

favicon:网站标签图表、logo:登入页面的图标

美化配置

修改标题

ctrl + shift + f 全局搜索 Ant Design

ctrl + shift + r 全局替换

像网页上的一些东西,都可以用这种类似的方法修改,包括底下的导航栏都行 

开发登入页

在:src/pages/User/Login/index.tsx操作,其他没用的都可以删掉

介绍大致框架

 前端不会,大概知道每个地方干啥的就好了,具体实现以后再说

智能分析业务流程

用户输入

a. 分析目标

b. 上传原始数据(excel)

c. 更精细化地控制图表:比如图表类型、图表名称等

后端校验

a. 校验用户的输入否合法(比如长度)

b. 成本控制(次数统计和校验、鉴权等)

  1. 把处理后的excel数据输入给 AI 模型(调用 Al 接口,星球内部同学可以免费使用 Al 接口),让 AI 模型给我们提供图表信息、结论代码
  2. 图表信息(是一段 json 配置,是一段代码)、结论文本在前端进行展示

代码编写

FileController里有一个uploadFile,用来上传文件,我们可以将他改造成我们需要的上传excel,并对其进行解析。将整个方法复制到ChartController里,并重命名为GenChartByAi

缺失字段如何补充?

红色框框内是我们要补充的字段,填写好后复制

直接粘贴进去

然后在实体类补上缺失的参数,如果我们的model/dto/chart包下的各种请求参数需要的话,也要补上

注意:mapper层里的sql语句也要记得补上

该字段相关的业务逻辑要补上(与名字相关的是模糊查询)

对输入进行简单的判断(不要相信前端的限制,后端也要编写对应代码)

/**
 * 智能分析
 *
 * @param multipartFile
 * @param genChartByAiRequest
 * @param request
 * @return
 */
@PostMapping("/gen")
public BaseResponse<String> genChartByAi(@RequestPart("file") MultipartFile multipartFile,
                                         GenChartByAiRequest genChartByAiRequest, HttpServletRequest request) {
    String name = genChartByAiRequest.getName();
    String goal = genChartByAiRequest.getGoal();
    String chartType = genChartByAiRequest.getChartType();

    // 校验
    // 如果分析目标为空,就抛出请求参数错误异常,并给出提示
    ThrowUtils.throwIf(StringUtils.isBlank(goal), ErrorCode.PARAMS_ERROR, "目标为空");
    // 如果名称不为空,并且名称长度大于100,就抛出异常,并给出提示
    ThrowUtils.throwIf(StringUtils.isNotBlank(name) && name.length() > 100, ErrorCode.PARAMS_ERROR, "名称过长");

    // 读取到用户上传的 excel 文件,进行一个处理
    User loginUser = userService.getLoginUser(request);
    // 文件目录:根据业务、用户来划分
    String uuid = RandomStringUtils.randomAlphanumeric(8);
    String filename = uuid + "-" + multipartFile.getOriginalFilename();
    File file = null;
    try {

        // 返回可访问地址
        return ResultUtils.success("");
    } catch (Exception e) {
        //            log.error("file upload error, filepath = " + filepath, e);
        throw new BusinessException(ErrorCode.SYSTEM_ERROR, "上传失败");
    } finally {
        if (file != null) {
            // 删除临时文件
            boolean delete = file.delete();
            if (!delete) {
                //                    log.error("file delete error, filepath = {}", filepath);
            }
        }
    }
}

如何写好一个好的promt

Al提词技巧1:持续输入,持续优化

第一次问: 我给你三行数据,请帮我分析网站的增长趋势,数据如下:

第一行:日期: 1号, 用户数: 10人

第二行:日期: 2号,用户数: 20人

第三行:日期: 3号,用户数: 30人

AI提词技巧2:数据压缩(内容压缩,比如把很长的内容提取关键词,也可以让Al来做)

第二次问:我给你三行数据,请帮我分析网站的增长趋势,数据如下:

表头:日期,用户数

1号,10

2号,20

3号,30

第三次问: 我给你三行数据,请帮我用最少的字数压缩这段数据

第一行:日期: 1号, 用户数: 10人

第二行:日期: 2号,用户数: 20人

第三行:日期: 3号,用户数: 30人

Al回答: 日期1-3号,用户数分别为10/20/30人。


用 chatGPT 也好,还是用鱼聪明,无论使用哪种 AI 模型,输入都是数据而非文件。那怎么输入数据呢?并且输入的字数也会有限制。

所以我们要尽可能地压缩数据,读取excel文件,转化成csv格式

excel转csv格式

基础模板

package com.ptu.api.utils;

import cn.hutool.core.collection.CollUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.support.ExcelTypeEnum;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.util.ResourceUtils;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * EasyExcel 测试
 *
 * @author <a href="https://github.com/liyupi">程序员鱼皮</a>
 * @from <a href="https://yupi.icu">编程导航知识星球</a>
 */
@SpringBootTest
public class EasyExcelTest {

    @Test
    /**
     * 该Java代码从类路径加载名为“网站数据.xlsx”的文件,并使用EasyExcel库读取其中的数据。主要功能包括:
     * 指定Excel类型为XLSX。
     * 默认读取第一个工作表(sheet)。
     * 设置表头行号为0,表示第一行是表头。
     * 同步读取Excel数据到一个包含多行数据的List中,每行数据以Map形式存储,键为列索引,值为单元格内容。
     */
    public void doImport() throws FileNotFoundException {
        /**
         * 该Java代码段使用EasyExcel库从上传的多部分文件流中读取Excel数据(格式为XLSX),并执行以下操作:
         * 读取第一个工作表。
         * 设置表头行号为0(即第一行为表头)。
         * 同步执行读取操作,并将结果存储在list变量中。
         */
        List<Map<Integer, String>> list = null;

            list = EasyExcel.read(ResourceUtils.getFile("classpath:网站数据.xlsx"))
                    .excelType(ExcelTypeEnum.XLSX)
                    .sheet()
                    .headRowNumber(0)
                    .doReadSync();


        //最终的数据
        StringBuilder sb = new StringBuilder();
        Map<Integer, String> headerMap = list.get(0);
        List<String> headList = headerMap.values().stream().filter(ObjectUtils::isNotEmpty).collect(Collectors.toList());
        /**
         * 使用 StringUtils.join(headList, ",") 将 headList(第一行的标题)用逗号连接成一个字符串。
         * 连接结果追加到 sb 对象中。
         * 在追加的内容后添加换行符。
         */
        sb.append(StringUtils.join(headList,",")).append("\n");
        for(int i = 1;i < list.size();i++){
            Map<Integer, String> dataMap = list.get(i);
            List<String> dataList = dataMap.values().stream().filter(ObjectUtils::isNotEmpty).collect(Collectors.toList());
            sb.append(StringUtils.join(dataList)).append("\n");
        }
        System.out.println(sb.toString());
    }

}

基础模块优化

为了满足业务需求,可以对遍历到的数据进行过滤,这里对表格里删除的数据,但是读取过程依然会读出来(这是没有必要的)

package com.yupi.springbootinit.utils;

import cn.hutool.core.collection.CollUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.support.ExcelTypeEnum;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.ResourceUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.FileNotFoundException;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * Excel 相关工具类
 */
public class ExcelUtils {
    public static String excelToCsv(MultipartFile multipartFile) {
        File file = null;
        try {
            file = ResourceUtils.getFile("classpath:网站数据.xlsx");
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        // 读取数据
        List<Map<Integer, String>> list = EasyExcel.read(file)
                .excelType(ExcelTypeEnum.XLSX)
                .sheet()
                .headRowNumber(0)
                .doReadSync();
        // 如果数据为空
        if (CollUtil.isEmpty(list)) {
            return "";
        }
        // 转换为 csv
        // 读取表头(第一行)
        LinkedHashMap<Integer, String> headerMap = (LinkedHashMap) list.get(0);
        List<String> headerList = headerMap.values().stream().filter(ObjectUtils::isNotEmpty).collect(Collectors.toList());
        System.out.println(StringUtils.join(headerList,","));
        // 读取数据(读取完表头之后,从第一行开始读取)
        for (int i = 1; i < list.size(); i++) {
            LinkedHashMap<Integer, String> dataMap = (LinkedHashMap) list.get(i);
            List<String> dataList = dataMap.values().stream().filter(ObjectUtils::isNotEmpty).collect(Collectors.toList());
            System.out.println(StringUtils.join(dataList,","));
        }
        System.out.println(list);
        return "";
    }

    public static void main(String[] args) {
            excelToCsv(null);
    }
}

创建工具类

package com.ptu.api.utils;

import cn.hutool.core.collection.CollUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.support.ExcelTypeEnum;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.ResourceUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Slf4j
public class ExcelUtils {
    public static String excelToCsv(MultipartFile multipartFile){

        /**
         * 该Java代码段使用EasyExcel库从上传的多部分文件流中读取Excel数据(格式为XLSX),并执行以下操作:
         * 读取第一个工作表。
         * 设置表头行号为0(即第一行为表头)。
         * 同步执行读取操作,并将结果存储在list变量中。
         */
        List<Map<Integer, String>> list = null;
        try {
            list = EasyExcel.read(multipartFile.getInputStream())
                    .excelType(ExcelTypeEnum.XLSX)
                    .sheet()
                    .headRowNumber(0)
                    .doReadSync();
        } catch (IOException e) {
            log.error("表格处理错误",e);
        }

        if (CollUtil.isEmpty(list)){
            return "";
        }
        //最终的数据
        StringBuilder sb = new StringBuilder();
       Map<Integer, String> headerMap = list.get(0);
        List<String> headList = headerMap.values().stream().filter(ObjectUtils::isNotEmpty).collect(Collectors.toList());
        sb.append(StringUtils.join(headList,",")).append("\n");
        for(int i = 1;i < list.size();i++){
            Map<Integer, String> dataMap =  list.get(i);
            List<String> dataList = dataMap.values().stream().filter(ObjectUtils::isNotEmpty).collect(Collectors.toList());
            sb.append(StringUtils.join(dataList)).append("\n");
        }
        return sb.toString();
    }

    public static void main(String[] args) {
        excelToCsv(null);
    }
}

调用AI

输入:
系统预设(提前告诉他职责、功能回复格式要求) + 分析目标 + 压缩后的数据

最简单的系统预设:
你是一个数据分析师,接下来我会给你我的分析目标和原始数据,请告诉我分析结论。

AI提词技巧3:
在系统(模型)层面做预设效果一般来说,蚍直接拼接在用户消息里效果更好一些。

AI提词技巧4:
除了系统预设外,额外关联一问一答两条消息,相当于给Al一个提示。

/**
     * 智能分析
     *
     * @param multipartFile
     * @param genChartByAiRequest
     * @param request
     * @return
     */
    @PostMapping("/gen")
    public BaseResponse<String> genChartByAi(@RequestPart("file") MultipartFile multipartFile,
                                             GenChartByAiRequest genChartByAiRequest, HttpServletRequest request) {
        String name = genChartByAiRequest.getName();
        String goal = genChartByAiRequest.getGoal();
        String chartType = genChartByAiRequest.getChartType();

        // 校验
        // 如果分析目标为空,就抛出请求参数错误异常,并给出提示
        ThrowUtils.throwIf(StringUtils.isBlank(goal), ErrorCode.PARAMS_ERROR, "目标为空");
        // 如果名称不为空,并且名称长度大于100,就抛出异常,并给出提示
        ThrowUtils.throwIf(StringUtils.isNotBlank(name) && name.length() > 100, ErrorCode.PARAMS_ERROR, "名称过长");

        // 用户输入
        StringBuilder userInput = new StringBuilder();
        userInput.append("你是一个数据分析师,接下来我会给你我的分析目标和原始数据,请告诉我分析结论。").append("\n");
        userInput.append("分析目标:").append(goal).append("\n");

        // 压缩后的数据(把multipartFile传进来,其他的东西先注释)
        String result = ExcelUtils.excelToCsv(multipartFile);
        userInput.append("数据:").append(result).append("\n");
        return ResultUtils.success(userInput.toString());
//        // 读取到用户上传的 excel 文件,进行一个处理
//        User loginUser = userService.getLoginUser(request);
//        // 文件目录:根据业务、用户来划分
//        String uuid = RandomStringUtils.randomAlphanumeric(8);
//        String filename = uuid + "-" + multipartFile.getOriginalFilename();
//        File file = null;
//        try {
//
//            // 返回可访问地址
//            return ResultUtils.success("");
//        } catch (Exception e) {
            log.error("file upload error, filepath = " + filepath, e);
//            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "上传失败");
//        } finally {
//            if (file != null) {
//                // 删除临时文件
//                boolean delete = file.delete();
//                if (!delete) {
                    log.error("file delete error, filepath = {}", filepath);
//                }
//            }
//        }
    }

系统预设最好是放在模型里,效果更好

这一期就到这里了,具体如何调用AI下期再来,拜拜溜~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值