一个html实现数据库自定义查询

news2025/7/27 5:22:06

使用场景

应用上线后甲方频繁的找开发查询数据库数据,且没有固定的查询规律,产品经理也没有规划报表需求。

实现方案

后端开放自定义sql查询,屏蔽所有数据库的高危操作,将常用查询的sql放在一个html中的js中直接查询,功能满足以下需求:

  • 可动态设置接口地址,方便查看测试和正式数据库
  • 可预设常用查询sql
  • 支持cte查询,支持sql注释
  • 动态表头,分页显示总条数
  • 保留查询历史记录
  • 预览长文本支持单机常看详情
  • 最重要的,尽量简单,单页面实现,无其他依赖

效果图

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

实现代码

后端java接口

	@Autowired
    private JdbcTemplate jdbcTemplate;

    @PostMapping("/query/sql/page")
    @Operation(summary = "通用SQL分页查询", description = "仅允许执行SELECT语句并支持分页")
    public ResponseResult<PageResponse> executePagedQuery(@RequestBody SqlQueryRequest request) {
        String sql = request.getSql();
        Integer pageSize = request.getPageSize();
        Integer pageIndex = request.getPageIndex();

        try {
            // 校验参数
            if (sql == null || sql.trim().isEmpty() || !isSafeSelectSql(sql)) {
                return ResponseResult.fail(400, "无效的SQL语句或非SELECT查询");
            }
            if (pageSize == null || pageSize <= 0) {
                pageSize = 10;
            }
            if (pageIndex == null || pageIndex < 1) {
                pageIndex = 1;
            }

            // 构建 count SQL
            // 修改 executePagedQuery 方法中调用 buildCountSql 的部分:
            String safeSql = removeSingleLineComments(sql);
            String countSql = buildCountSql(safeSql);

            // 执行 count 查询
            Long total = jdbcTemplate.queryForObject(countSql, Long.class);

            // 添加分页条件
            String pagedSql = safeSql + " LIMIT " + pageSize + " OFFSET " + ((pageIndex - 1) * pageSize);

            // 执行分页查询
            List<Map<String, Object>> result = jdbcTemplate.queryForList(pagedSql);

            // 返回分页结果
            return ResponseResult.success(PageResponse.of(result, total, pageIndex, pageSize));
        } catch (Exception e) {
            log.error("执行分页SQL失败: {}", e.getMessage(), e);
            return ResponseResult.fail(500, "SQL执行异常:" + e.getMessage());
        }
    }


    // 构建 COUNT SQL
    private String buildCountSql(String originalSql) {
        return "SELECT COUNT(*) FROM (" + originalSql + ") AS tmp";
    }
    /**
     * 移除 SQL 中的单行注释(以 "--" 开头的部分)
     */
    private String removeSingleLineComments(String sql) {
        if (sql == null) {
            return null;
        }
        // 使用正则表达式匹配 "--" 注释并移除整行
        return sql.replaceAll("--[^\n]*", "");
    }
    /**
     * 判断是否为安全的 SELECT 查询(支持 CTE)
     */
    private boolean isSafeSelectSql(String sql) {
        if (sql == null || sql.trim().isEmpty()) {
            return false;
        }

        String trimmedSql = sql.trim().toLowerCase();

        // 使用正则判断是否是以 SELECT 或 WITH 开头,并且不包含危险关键词
        return trimmedSql.matches("(?s:^(select|with).*$)")
               && !containsForbiddenKeywords(trimmedSql);
    }

    /**
     * 检查 SQL 是否包含非法关键字
     */
    private boolean containsForbiddenKeywords(String sql) {
        List<String> forbiddenKeywords = Arrays.asList(
            "\\b(delete|update|insert|drop|truncate|merge|exec|execute|create|alter|grant|revoke|call|load|replace|upsert|copy)\\b",
            "into",
            "for update"
        );

        for (String pattern : forbiddenKeywords) {
            if (Pattern.compile(pattern, Pattern.CASE_INSENSITIVE).matcher(sql).find()) {
                return true;
            }
        }
        return false;
    }

为了解决跨域问题,还需要添加如下代码

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/test/**")     // 允许跨域的路径
                .allowedOrigins("*")       // 允许所有来源
                .allowedMethods("GET", "POST", "PUT", "OPTIONS")
                .allowedHeaders("*")       // 允许所有头部
                .exposedHeaders("*")
                .allowCredentials(false)    // 关闭凭据支持
                .maxAge(3600);             // 预检请求的有效期

    }
}

单页面

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8"/>
    <title>通用SQL查询页面</title>
    <!-- 引入 Element Plus 样式 -->
    <link rel="stylesheet" href="https://unpkg.com/element-plus/dist/index.css"/>
    <style>
        body {
            margin: 20px;
        }

        .form-container {
            margin-bottom: 20px;
        }

        .table-wrapper {
            max-height: 500px;
            overflow-y: auto;
        }

        .split-container {
            display: flex;
            gap: 20px;
        }

        .left-panel {
            flex: 1;
        }

        .right-panel {
            margin-top: 1rem;
            width: 100px;
            display: flex;
            flex-direction: column;
            gap: 15px;
            align-items: center;
        }
    </style>
</head>
<body>
<div id="app">
    <!-- SQL 查询表单 -->
    <el-card class="form-container">
        <el-form label-position="top" :model="form" @submit.prevent="fetchData">
            <div class="split-container">
                <!-- 左边 SQL 输入区 -->
                <div class="left-panel">
                    <el-form-item label="SQL语句" style="margin-bottom: 0;">
                        <template #label>
                            SQL语句
                            <span style="color: red; font-size: 12px; margin-left: 10px;">
                              (不可输入任何修改和删除相关的语句)
                            </span>
                            <span style="color: #00d28c; font-size: 12px; margin-left: 10px;">
                              (“--” 可以忽略单行语句,移除可添加筛选条件)
                            </span>
                        </template>
                        <el-input v-model="form.sql" type="textarea" rows="8" placeholder="请输入SQL语句"></el-input>
                    </el-form-item>
                </div>

                <!-- 右边参数+按钮 -->
                <div class="right-panel">
                    <el-button type="primary" @click="showApiRootDialog = true" style="width: 100%;">接口地址
                    </el-button>

                    <!-- 内置查询按钮 -->
                    <el-popover placement="left" trigger="click" width="250px">
                        <template #reference>
                            <el-button type="primary" style="width: 100%;">内置查询</el-button>
                        </template>
                        <div>
                            <h4 style="margin-top: 0;">预设查询列表</h4>
                            <ul style="list-style: none; padding-left: 0;">
                                <li v-for="(preset, index) in presetQueries" :key="index">
                                    <el-link @click="usePresetQuery(preset)" type="primary">{{ preset.name }}</el-link>
                                </li>
                            </ul>
                        </div>
                    </el-popover>

                    <!-- 历史按钮 -->
                    <el-button plain @click="showHistoryDialog = true" style="width: 100%;">历史查询</el-button>

                    <!-- 查询按钮 -->
                    <el-button type="success" native-type="submit" style="width: 100%;">查询</el-button>

                </div>
            </div>

        </el-form>
    </el-card>

    <!-- 表格展示 -->
    <el-table :data="tableData" border class="table-wrapper" v-loading="loading">
        <el-table-column v-for="(col, index) in columns" :key="index" :label="col">
            <template #default="scope">
          <span
                  :title="scope.row[col]"
                  @click="showFullContentDialog(scope.row[col])"
                  style="display: inline-block; max-width: 300px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; cursor: pointer;"
          >
            {{ scope.row[col] }}
          </span>
            </template>
        </el-table-column>
    </el-table>

    <!-- 查看完整内容弹窗 -->
    <el-dialog v-model="showContentDialog" title="完整内容" width="60%">
        <pre style="white-space: pre-wrap; word-wrap: break-word;">{{ fullContent }}</pre>
    </el-dialog>
    <!-- 分页组件 -->
  <div style="display: flex; justify-content: flex-end; align-items: center; margin-top: 20px;">
    <span style="margin-right: 15px; font-size: 14px;">共 {{ pageInfo.total }} 条</span>
    <el-pagination
            layout="prev, pager, next"
            :total="pageInfo.total"
            v-model:current-page="form.pageIndex"
            :page-size="form.pageSize"
            @current-change="handlePageChange"
            background
    />
  </div>
    <!-- 接口设置弹窗 -->
    <el-dialog v-model="showApiRootDialog" title="api设置">
        <el-form label-position="top">
            <!-- 接口地址 -->
            <el-form-item label="后端接口地址">
                <el-input v-model="apiUrl" placeholder="请输入后端接口地址"></el-input>
            </el-form-item>

            <!-- 每页条数 + 历史缓存条数 并排布局 -->
            <div style="display: flex; gap: 10px;">
                <el-form-item label="每页条数" style="flex: 1;">
                    <el-input-number v-model="form.pageSize" :min="1" :max="100" style="width: 100%;"></el-input-number>
                </el-form-item>
                <el-form-item label="历史缓存条数" style="flex: 1;">
                    <el-input-number v-model="historyCacheSize" :min="1" :max="50" style="width: 100%;"></el-input-number>
                </el-form-item>
            </div>
        </el-form>
        <template #footer>
            <el-button @click="showApiRootDialog = false">取消</el-button>
            <el-button type="primary" @click="saveRootPath">确定</el-button>
        </template>
    </el-dialog>

    <!-- 历史记录弹窗 -->
    <el-dialog v-model="showHistoryDialog" title="历史查询记录">
        <el-scrollbar style="height: 300px;">
            <ul style="list-style: none; padding-left: 0; margin: 0;">
                <li v-for="(item, index) in historyList" :key="index" style="padding: 8px 0 4px;">
                    <el-link @click="useHistory(item)" type="primary">{{ item }}</el-link>
                    <div v-if="index < historyList.length - 1"
                         style="border-bottom: 1px solid #eee; margin: 4px 0;"></div>
                </li>
            </ul>
        </el-scrollbar>
        <template #footer>
            <el-button @click="clearHistory">清空历史</el-button>
            <el-button @click="showHistoryDialog = false">关闭</el-button>
        </template>
    </el-dialog>
</div>

<!-- 引入 Vue 3 -->
<script src="https://unpkg.com/vue@3.4.15/dist/vue.global.prod.js"></script>
<!-- 引入 axios -->
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<!-- 引入 Element Plus 组件库 -->
<script src="https://unpkg.com/element-plus/dist/index.full.min.js"></script>

<script>
    const {createApp} = Vue;

    createApp({
        data() {

            // 预设查询
            const presetQueries = [
                {name: "设施查询", sql: "  SELECT a.id AS \"设施Id\",a.name AS \"设施名称\",bt.name AS \"设施类型\",cc.CITY_NAME AS \"城市名称\",a.PROPERTIES as \"详细属性\",a.DISTRICT AS \"区县\"\n" +
                        "    FROM building a\n" +
                        "     LEFT JOIN BUILDING_TYPE bt ON a.TYPE=bt.ID\n" +
                        "     LEFT JOIN CITY_CONFIG cc ON a.city = cc.city_code\n" +
                        "     -- where bt.name = '供水厂'"},
                {name: "城市信息", sql: "  SELECT city_code AS \"城市编码\",city_name AS \"城市名称\",DISTRICTS AS \"区县\",CITY_CONFIG.SMS_ENABLE AS \"短信开关\"\n" +
                        "   FROM city_config"},
                {name: "指定时间区间预警数量统计", sql: "SELECT \"LEVEL\" AS \"预警等级\" ,count(1) AS \"触发次数\"\n" +
                        "FROM FORECAST_RAIN_LOG frl  \n" +
                        "WHERE frl.CREATE_TIME BETWEEN '2025-06-01 22:00:00.000' AND '2025-06-02 01:00:00.000' \n" +
                        "-- AND frl.\"LEVEL\" >0\n" +
                        "-- AND frl.RAIN_ID = 184\n" +
                        "GROUP BY frl.LEVEL"},
                {name: "短信发送记录", sql: "SELECT sl.CITY_CODE AS \"城市编码\",sl.CITY_NAME AS \"城市\",TO_CHAR(sl.SEND_TIME, 'YYYY-MM-DD HH24:MI:SS') AS \"发送时间\",sl.USERNAME AS \"接收人\",sl.PHONE AS \"手机号\",sl.CONTENT AS \"短信内容\",sl.WARNING_LEVEL AS \"预警等级\"\n" +
                        "FROM SMS_LOG sl\n" +
                        "WHERE 1=1 \n" +
                        "-- AND sl.CITY_NAME  = '百色市' \n" +
                        "-- AND sl.SEND_TIME BETWEEN '2025-05-01 22:00:00.000' AND '2025-06-02 01:00:00.000'\n" +
                        "-- AND sl.USERNAME = '黄科谕'\n" +
                        "ORDER BY sl.SEND_TIME DESC"},
                {name: "设施降雨预警历史记录", sql: "SELECT \n" +
                        "    b.id AS \"设施id\",b.name AS \"设施名称\",\n" +
                        "    a.val AS \"降雨量\", a.LEVEL AS \"预警等级\",\n" +
                        "    TO_CHAR(a.CREATE_TIME, 'yyyy-MM-dd HH24:mi:ss') as \"预警时间\",\n" +
                        "    cc.CITY_NAME AS \"城市\", cc.city_code AS \"城市编码\"\n" +
                        "FROM FORECAST_RAIN_LOG a   \n" +
                        "LEFT JOIN building b ON a.RAIN_ID = b.id\n" +
                        "LEFT JOIN CITY_CONFIG cc ON cc.city_code = b.city\n" +
                        "WHERE a.create_time > '2025-06-01 09:30:00' \n" +
                        "  AND a.create_time < '2025-06-03 14:30:30' \n" +
                        "  AND a.level > 0\n" +
                        "  --筛选i设施\n" +
                        "  -- AND b.id = 826\n" +
                        "  --筛选预警等级\n" +
                        "  -- AND a.level = 2\n" +
                        "ORDER BY a.CREATE_TIME ASC"},
                {name: "指定时间区间发生预警的设施", sql: "SELECT b.id AS \"设施id\",cc.CITY_NAME AS \"城市\",b.name AS \"设施名称\",b.PROPERTIES AS \"详细属性\"\n" +
                        "FROM building b \n" +
                        "LEFT JOIN CITY_CONFIG cc ON b.city = cc.CITY_CODE\n" +
                        "WHERE b.id in (\n" +
                        "\tSELECT DISTINCT frl.RAIN_ID  FROM FORECAST_RAIN_LOG frl \n" +
                        "WHERE frl.\"LEVEL\" >0  AND frl.CREATE_TIME BETWEEN '2025-06-01 22:00:00.000' AND '2025-06-02 01:00:00.000' \n" +
                        ")\n" +
                        "ORDER BY CITY_NAME"},
                {name: "当前触发短信预警的设施", sql: "WITH three_hour_records AS (\n" +
                        "                SELECT\n" +
                        "                    frl.*,\n" +
                        "                    ROW_NUMBER() OVER (PARTITION BY frl.RAIN_ID ORDER BY frl.CREATE_TIME DESC) as rn\n" +
                        "                FROM FORECAST_RAIN_LOG frl\n" +
                        "                WHERE frl.DURATION = 3\n" +
                        "                  -- 筛选最近3小时的数据\n" +
                        "                  AND frl.CREATE_TIME >= DATEADD(HOUR, -3, SYSDATE)\n" +
                        "            )\n" +
                        "         -- 查询降雨预警>0且三小时内没发短信的设施\n" +
                        "        ,constant_alert AS (\n" +
                        "            SELECT\n" +
                        "            t.RAIN_ID\n" +
                        "            FROM three_hour_records t\n" +
                        "            -- 最新预警等级大于0\n" +
                        "            WHERE t.rn =1 and t.LEVEL > 0\n" +
                        "            -- 且三小时内没有短信记录\n" +
                        "            AND NOT EXISTS (\n" +
                        "            SELECT 1 FROM SMS_LOG s\n" +
                        "            WHERE s.SEND_TIME >= DATEADD(HOUR, -3, SYSDATE)\n" +
                        "            AND s.building_ids LIKE '%'||t.RAIN_ID||'%'\n" +
                        "            )\n" +
                        "        )\n" +
                        "        -- 检测最新预警等级大于3小时内已发短信的预警等级的设施\n" +
                        "        ,risk_increased AS (\n" +
                        "          SELECT\n" +
                        "            l1.RAIN_ID\n" +
                        "            FROM three_hour_records l1\n" +
                        "            LEFT JOIN (\n" +
                        "                SELECT \n" +
                        "                    building_ids,\n" +
                        "                    WARNING_LEVEL,\n" +
                        "                    ROW_NUMBER() OVER (PARTITION BY building_ids ORDER BY SEND_TIME DESC) as sms_rn\n" +
                        "                FROM SMS_LOG\n" +
                        "                WHERE SEND_TIME >= DATEADD(HOUR, -3, SYSDATE)\n" +
                        "            ) s ON s.building_ids LIKE '%'||l1.RAIN_ID||'%' AND s.sms_rn = 1\n" +
                        "            WHERE l1.rn = 1\n" +
                        "            AND l1.LEVEL > s.WARNING_LEVEL\n" +
                        "        )\n" +
                        "        -- 合并两类需要发送短信的设施\n" +
                        "        SELECT\n" +
                        "            b.id, b.city AS cityCode, c.CITY_NAME AS cityName,\n" +
                        "            b.name, b.PROPERTIES,\n" +
                        "            lr.CREATE_TIME AS updatedAt,\n" +
                        "            lr.val as val,\n" +
                        "            lr.level as level\n" +
                        "        FROM building b\n" +
                        "        INNER JOIN (\n" +
                        "        \t-- 设施ID去重合并\n" +
                        "            SELECT RAIN_ID FROM risk_increased\n" +
                        "            UNION\n" +
                        "            SELECT RAIN_ID FROM constant_alert\n" +
                        "        ) combined ON b.id = combined.RAIN_ID\n" +
                        "        INNER JOIN three_hour_records lr ON b.id = lr.RAIN_ID AND lr.rn = 1\n" +
                        "        LEFT JOIN city_config c ON b.city = c.CITY_CODE\n" +
                        "        -- 只给内涝设施发送短信且城市开关打开\n" +
                        "        where b.type in (3,36) and c.sms_enable = 1"},
            ];
            // 获取本地缓存的 SQL,默认为空字符串
            const lastSql = localStorage.getItem('lastSql') || '';

            // 获取默认 SQL:优先用本地缓存,否则用第一个预设 SQL
            const defaultSql = lastSql || (presetQueries.length >  0 ? presetQueries[0].sql : '');

            return {
                apiUrl: localStorage.getItem('apiUrl') || 'http://192.168.10.160:9876/test/query/sql/page',
                form: {
                    sql: defaultSql,
                    pageSize: parseInt(localStorage.getItem('pageSize')) || 10,
                    pageIndex: 1
                },
                tableData: [],
                columns: [],
                pageInfo: {
                    total: 0,
                    pages: 0
                },
                loading: false,
                presetQueries: presetQueries,

                // 控制弹窗
                showApiRootDialog: false,
                showHistoryDialog: false,

                // 历史查询
                historyList: JSON.parse(localStorage.getItem('historySqls') || '[]'),
                historyCacheSize: parseInt(localStorage.getItem('historyCacheSize')) || 10,

                //单元格详情
                fullContent: '',
                showContentDialog: false
            };
        },
        methods: {
            async fetchData() {
                this.loading = true;

                if (!this.apiUrl || !this.form.sql.trim()) {
                    this.$message.warning("请填写完整的后端地址和SQL语句");
                    this.loading = false;
                    return;
                }

                try {
                    const res = await axios.post(this.apiUrl, {
                        sql: this.form.sql,
                        pageSize: this.form.pageSize,
                        pageIndex: this.form.pageIndex
                    });

                    if (res.data.code === 0 && res.data.data) {
                        this.tableData = res.data.data.records || [];
                        this.pageInfo.total = res.data.data.total || 0;
                        this.pageInfo.pages = res.data.data.pages || 0;
                        this.columns = this.tableData.length > 0 ? Object.keys(this.tableData[0]) : [];

                        // 只有在 SQL 不同的情况下才添加历史
                        const lastSql = localStorage.getItem('lastSql');
                        if (this.form.sql.trim() !== (lastSql || '')) {
                            this.addToHistory(this.form.sql);
                            localStorage.setItem('lastSql', this.form.sql);
                        }
                    } else {
                        this.$message.error(res.data.msg || '查询失败');
                    }
                } catch (err) {
                    this.$message.error('接口调用失败,请检查网络或SQL语句');
                    console.error(err);
                } finally {
                    this.loading = false;
                }
            },
            handlePageChange(page) {
                this.form.pageIndex = page;
                this.fetchData();
            },
            saveRootPath() {
                localStorage.setItem('apiUrl', this.apiUrl);
                localStorage.setItem('pageSize', this.form.pageSize);
                localStorage.setItem('historyCacheSize', this.historyCacheSize);
                this.showApiRootDialog = false;
            },
            useHistory(sql) {
                this.form.sql = sql;
                this.showHistoryDialog = false;
                this.fetchData();
            },
            showFullContentDialog(content) {
                this.fullContent = content;
                this.showContentDialog = true;
            },
            addToHistory(sql) {
                let index = this.historyList.indexOf(sql);
                if (index !== -1) {
                    this.historyList.splice(index, 1); // 移除旧的
                }
                this.historyList.unshift(sql);

                // 使用 historyCacheSize 控制最大缓存条数
                this.historyList = this.historyList.slice(0, this.historyCacheSize);

                localStorage.setItem('historySqls', JSON.stringify(this.historyList));
            },
            clearHistory() {
                this.historyList = [];
                localStorage.removeItem('historySqls');
            },
            usePresetQuery(preset) {
                this.form.sql = preset.sql;
                this.fetchData(); // 可选:点击即自动查询
            },
        }
    }).use(ElementPlus).mount('#app');
</script>
</body>
</html>

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

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

相关文章

鸿蒙电脑会在国内逐渐取代windows电脑吗?

点击上方关注 “终端研发部” 设为“星标”&#xff0c;和你一起掌握更多数据库知识 10年内应该不会 用Windows、MacOS操作系统的后果是你的个人信息可能会被美国FBI看到&#xff0c;但绝大多数人的信息FBI没兴趣去看 你用某家公司的电脑系统,那就得做好被某些人监视的下场,相信…

持续领跑中国异地组网路由器市场,贝锐蒲公英再次登顶销量榜首

作为国产远程连接SaaS服务的创领者&#xff0c;贝锐持续引领行业发展&#xff0c;旗下贝锐蒲公英异地组网路由器&#xff0c;凭借出色的技术实力和市场表现&#xff0c;斩获2024年线上电商平台市场销量份额中国第一的佳绩&#xff0c;充分彰显了其在网络解决方案与异地组网领域…

Spring AI 系列3: Promt提示词

一、Promt提示词 Promt提示是引导 AI 模型生成特定输出的输入&#xff0c; 提示的设计和措辞会显著影响模型的响应。 在 Spring AI 中与 AI 模型交互的最低层级&#xff0c;处理提示有点类似于在 Spring MVC 中管理”视图”。 这涉及创建带有动态内容占位符的大段文本。 这些占…

Redis:安装与常用命令

&#x1f308; 个人主页&#xff1a;Zfox_ &#x1f525; 系列专栏&#xff1a;Redis &#x1f525; 安装 Redis 使⽤apt安装 apt install redis -y⽀持远程连接 修改 /etc/redis/redis.conf 修改 bind 127.0.0.1 为 bind 0.0.0.0 修改 protected-mode yes 为 protected-mo…

Mac 芯片系列 安装cocoapod 教程

安装声明&#xff1a; 本人是在搭梯子的环境下安装成功&#xff0c;前提是必须安装好安装homebrew环境。 1.检测rudy的源 2.查看源(目的:检测rudy的源) gem sources - l 3.移除源(目的:移除rudy自带的源) gem sources --remove https://rubygems.org/ 4.更换源(目的:替换成国…

智启未来:AI重构制造业供应链的五大革命性突破

一、需求预测&#xff1a;让供应链“未卜先知” 1.1 从经验判断到数据预言 传统供应链依赖人工分析历史数据&#xff0c;但面对市场波动、设备突发故障等不确定性&#xff0c;往往反应滞后。AI通过整合工业物联网&#xff08;IIoT&#xff09;传感器数据、生产排程、供应商交…

Linux进程间通信----简易进程池实现

进程池的模拟实现 1.进程池的原理&#xff1a; 是什么 进程池是一种多进程编程模式&#xff0c;核心思想是先创建好一定数量的子进程用作当作资源&#xff0c;这些进程可以帮助完成任务并且重复利用&#xff0c;避免频繁的进程的创建和销毁的开销。 下面我们举例子来帮助理…

解锁Java多级缓存:性能飞升的秘密武器

一、引言 文末有彩蛋 在当今高并发、低延迟的应用场景中&#xff0c;传统的单级缓存策略往往难以满足性能需求。随着系统规模扩大&#xff0c;数据访问的瓶颈逐渐显现&#xff0c;如何高效管理缓存成为开发者面临的重大挑战。多级缓存架构应运而生&#xff0c;通过分层缓存设…

(纳芯微)NCA9548- DTSXR 具有复位功能的八通道 I²C 开关、所有I/O端子均可承受5.5V输入电压

深圳市润泽芯电子有限公司 推荐NOVOSENSE(纳芯微)品牌 NCA9548- DTSXR TSSOP-24封装 NCA9548- DTSXR 具有复位功能的八通道 IC 开关、所有I/O端子均可承受5.5V输入电压 产品描述 NCA9548是通过I2C总线控制的八路双向转换开关。 SCL / SDA上行数据分散到八对下行数据或通道。…

013旅游网站设计技术详解:打造一站式旅游服务平台

旅游网站设计技术详解&#xff1a;打造一站式旅游服务平台 在互联网与旅游业深度融合的时代&#xff0c;旅游网站成为人们规划行程、预订服务的重要工具。一个功能完备的旅游网站&#xff0c;通过用户管理、订单管理等核心模块&#xff0c;实现用户与管理员的高效交互。本文将…

2024 CKA模拟系统制作 | Step-By-Step | 12、题目搭建-创建多容器Pod

目录 免费获取题库配套 CKA_v1.31_模拟系统 一、题目 二、考点分析 1. 多容器 Pod 的理解 2. YAML 配置规范 3. 镜像版本控制 三、考点详细讲解 1. 多容器 Pod 的工作原理 2. 容器端口冲突处理 3. 资源隔离机制 四、实验环境搭建步骤 总结 免费获取题库配套 CKA_v…

优化 Spring Boot API 性能:利用 GZIP 压缩处理大型有效载荷

引言 在构建需要处理和传输大量数据的API服务时&#xff0c;响应时间是一个关键的性能指标。一个常见的场景是&#xff0c;即使后端逻辑和数据库查询已得到充分优化&#xff0c;当API端点返回大型数据集&#xff08;例如&#xff0c;数千条记录的列表&#xff09;时&#xff0…

【C盘瘦身】给DevEco Studio中HarmonyOSEmulator(鸿蒙模拟器)换个地方,一键移动给C盘瘦身

文章目录 一、HarmonyOSEmulator的安装路径二、修改路径 一、HarmonyOSEmulator的安装路径 之前安装了华为的DevEco Studio&#xff0c;当时没注意&#xff0c;后来C盘告急&#xff0c;想着估计是鸿蒙的模拟器占用空间比较大&#xff0c;一检查还真是躺在C盘。路径如下&#x…

ORACLE 缺失 OracleDBConsoleorcl服务导致https://xxx:port/em 不能访问

这个原因是&#xff0c;操作过一下 ORCL的服务配置变更导致的。 再PATH中添加个环境变量&#xff0c;路径如下 管理员权限运行cmd 等待创建完成 大概3分钟 查看服务 点击第一个访问&#xff0c;下图登录后的截图

VScode自动添加指定内容

在 VS Code 中&#xff0c;可以通过配置 用户代码片段&#xff08;User Snippets&#xff09; 或使用 文件模板扩展 来实现新建指定文件类型时自动添加指定内容。以下是具体方法&#xff1a; 方法 1&#xff1a;使用 VS Code 内置的「用户代码片段」 适用场景&#xff1a;适用…

Ubuntu 22.04 安装 Nacos 记录

Ubuntu 22.04 安装 Nacos 记录 本文记录了在 Ubuntu 22.04 系统上安装 Nacos 的完整过程&#xff0c;适用于本地测试或生产部署的基础搭建。 一、官方资源 官网下载地址&#xff1a;https://nacos.io/download/nacos-server/官网文档&#xff1a;https://nacos.io/docs/lates…

相机--RGBD相机

教程 分类原理和标定 原理 视频总结 双目相机和RGBD相机原理 作用 RGBD相机RGB相机深度&#xff1b; RGB-D相机同时获取两种核心数据&#xff1a;RGB彩色图像和深度图像&#xff08;Depth Image&#xff09;。 1. RGB彩色图像 数据格式&#xff1a; 标准三通道矩阵&#…

记一次idea中lombok无法使用的解决方案

在注解处理器下&#xff0c;一般 Default 为“启用注解处理”和“从项目类路径获取处理器”&#xff0c;但是我的项目中的为选择“处理器路径”&#xff0c;导致了无法识别lombok&#xff0c;因此&#xff0c;需要改为使用“从项目类路径获取处理器”这个选项。如下图所示&…

贪心算法应用:硬币找零问题详解

贪心算法与硬币找零问题详解 贪心算法&#xff08;Greedy Algorithm&#xff09;在解决优化问题时表现出简洁高效的特点&#xff0c;尤其适用于特定结构的组合优化问题。本文将用2万字篇幅&#xff0c;深入探讨贪心算法在硬币找零问题中的应用&#xff0c;覆盖算法原理、正确性…

深入理解 x86 汇编中的重复前缀:REP、REPZ/REPE、REPNZ/REPNE(进阶详解版)

一、重复前缀&#xff1a;串操作的 “循环加速器” 如果你写过汇编代码&#xff0c;一定遇到过需要重复处理大量数据的场景&#xff1a; 复制 1000 字节的内存块比较两个长达 200 字符的字符串在缓冲区中搜索特定的特征值 手动用loop指令编写循环&#xff1f;代码冗长不说&a…