SpringBoot+PostgreSQL + 硅基流動(dòng)大模型從零搭建 Text-to-SQL 智能問(wèn)答系統(tǒng)
目錄前言一、項(xiàng)目整體介紹1.1 什么是 Text-to-SQL1.2 系統(tǒng)核心功能1.3 技術(shù)選型說(shuō)明二、本地環(huán)境準(zhǔn)備2.1 開(kāi)發(fā)環(huán)境硬性要求2.2 PostgreSQL 數(shù)據(jù)庫(kù)初始化2.3 硅基流動(dòng) API Key 申請(qǐng)步驟三、項(xiàng)目完整目錄結(jié)構(gòu)四、Maven 依賴(lài)與全局配置4.1 pom.xml 完整依賴(lài)4.2 application.yml 配置詳解五、數(shù)據(jù)庫(kù)表結(jié)構(gòu)與測(cè)試數(shù)據(jù)5.1 schema.sql 建表語(yǔ)句5.2 data.sql 初始化測(cè)試數(shù)據(jù)六、實(shí)體類(lèi)與數(shù)據(jù)訪問(wèn)層代碼6.1 Member.java 實(shí)體映射類(lèi)6.2 MemberRepository 數(shù)據(jù)訪問(wèn)接口七、核心硅基流動(dòng)大模型 API 對(duì)接服務(wù) LlmService開(kāi)發(fā)重點(diǎn)說(shuō)明八、核心業(yè)務(wù)整合服務(wù) Text2SqlService安全邏輯重點(diǎn)說(shuō)明九、Controller 頁(yè)面路由與 API 接口十、前端頁(yè)面代碼實(shí)現(xiàn)10.1 首頁(yè) index.html會(huì)員數(shù)據(jù)總覽10.2 問(wèn)答頁(yè)面 chat.html核心交互頁(yè)面十一、項(xiàng)目總結(jié)11.1 項(xiàng)目核心亮點(diǎn)11.2 開(kāi)發(fā)踩坑 FAQQ1啟動(dòng)項(xiàng)目時(shí)報(bào) PostgreSQL 連接失敗Q2大模型返回 ERROR:API 調(diào)用失敗Q3生成的 SQL 查詢(xún)不出數(shù)據(jù)前言最近公司內(nèi)部有個(gè)需求業(yè)務(wù)同事不會(huì)寫(xiě) SQL每次查會(huì)員數(shù)據(jù)都要找后端開(kāi)發(fā)幫忙來(lái)回溝通效率極低。想著能不能做一套自然語(yǔ)言轉(zhuǎn) SQL 的小工具業(yè)務(wù)人員輸入中文描述就能自動(dòng)查數(shù)據(jù)庫(kù)不用懂任何數(shù)據(jù)庫(kù)語(yǔ)法。調(diào)研了一圈方案本地部署大模型硬件成本太高、推理速度慢國(guó)內(nèi)可直接調(diào)用的公有大模型 API 里硅基流動(dòng)性?xún)r(jià)比不錯(cuò)對(duì)中文 SQL 生成適配很好搭配 SpringBootPostgreSQL 就能快速落地。本篇文章我會(huì)把完整搭建流程、踩坑細(xì)節(jié)、安全處理邏輯全部整理出來(lái)從環(huán)境準(zhǔn)備、數(shù)據(jù)庫(kù)建表、后端分層開(kāi)發(fā)、大模型 API 對(duì)接再到前端問(wèn)答頁(yè)面一套完整流程直接照著跑就能出效果新手也能跟著實(shí)現(xiàn)。一、項(xiàng)目整體介紹1.1 什么是 Text-to-SQL簡(jiǎn)單說(shuō)就是自然語(yǔ)言轉(zhuǎn)結(jié)構(gòu)化 SQL 語(yǔ)句非技術(shù)人員只用大白話描述查詢(xún)需求后端對(duì)接大模型自動(dòng)翻譯成標(biāo)準(zhǔn) SQL執(zhí)行后把表格數(shù)據(jù)返回前端展示。 舉個(gè)實(shí)際場(chǎng)景業(yè)務(wù)輸入 “查詢(xún)所有積分超過(guò) 1 萬(wàn)的金卡會(huì)員”系統(tǒng)自動(dòng)生成SELECT * FROM member WHERE level 金卡會(huì)員 AND points 10000并展示匹配數(shù)據(jù)完全不用人工寫(xiě)查詢(xún)語(yǔ)句。1.2 系統(tǒng)核心功能這套小系統(tǒng)主要做了 5 個(gè)實(shí)用能力滿(mǎn)足基礎(chǔ)業(yè)務(wù)查詢(xún)場(chǎng)景自然語(yǔ)言智能問(wèn)答中文提問(wèn)自動(dòng)生成 SQL 并返回?cái)?shù)據(jù)表結(jié)果會(huì)員數(shù)據(jù)總覽頁(yè)面打開(kāi)首頁(yè)直接查看全量會(huì)員數(shù)據(jù)簡(jiǎn)單統(tǒng)計(jì)總條數(shù)SQL 可視化展示大模型生成的原始 SQL 清理后完整展示支持復(fù)制復(fù)用SQL 安全攔截機(jī)制強(qiáng)制只允許 SELECT 查詢(xún)杜絕刪表、改數(shù)據(jù)等危險(xiǎn)操作防止注入風(fēng)險(xiǎn)自適應(yīng)前端頁(yè)面原生 JSThymeleaf 實(shí)現(xiàn)電腦、平板打開(kāi)都能正常使用1.3 技術(shù)選型說(shuō)明選型沒(méi)有追求花里胡哨的框架選用成熟穩(wěn)定、上手門(mén)檻低的技術(shù)方便后續(xù)二次改造分層技術(shù)棧版本選擇理由后端主框架SpringBoot3.2.0生態(tài)完善Web、JPA、JDBC 一鍵集成企業(yè)主流技術(shù)棧數(shù)據(jù)庫(kù)PostgreSQL14 及以上開(kāi)源免費(fèi)支持復(fù)雜查詢(xún)、中文注釋、自增序列企業(yè)數(shù)據(jù)分析常用ORM 層Spring Data JPA無(wú)指定版本簡(jiǎn)化單表 CRUD 開(kāi)發(fā)不用手寫(xiě)基礎(chǔ) SQL原生 SQL 執(zhí)行JdbcTemplate內(nèi)置大模型生成動(dòng)態(tài) SQL 后需要原生執(zhí)行工具返回表格數(shù)據(jù)頁(yè)面模板Thymeleaf內(nèi)置服務(wù)端渲染頁(yè)面不用前后端分離小型工具開(kāi)發(fā)更輕量化大模型服務(wù)硅基流動(dòng) API在線調(diào)用國(guó)內(nèi)合規(guī)大模型服務(wù)中文理解強(qiáng)SQL 生成準(zhǔn)確率高不用本地部署前端交互原生 JavaScript無(wú)框架無(wú)需引入 Vue/React減少打包、跨域等額外問(wèn)題快速實(shí)現(xiàn)問(wèn)答交互代碼簡(jiǎn)化工具Lombok最新穩(wěn)定版省去實(shí)體類(lèi) get/set/toString 冗余代碼二、本地環(huán)境準(zhǔn)備2.1 開(kāi)發(fā)環(huán)境硬性要求JDK 版本最低 JDK17推薦 JDK21SpringBoot3.x 強(qiáng)制要求高版本 JDK低版本會(huì)直接啟動(dòng)報(bào)錯(cuò)構(gòu)建工具M(jìn)aven3.8 或者 Gradle8本文全程使用 Maven數(shù)據(jù)庫(kù)PostgreSQL14 及以上本地安裝或者 Docker 啟動(dòng)均可開(kāi)發(fā)工具IDEA2023 以上版本最佳VS Code 搭配 Java 插件也能開(kāi)發(fā)2.2 PostgreSQL 數(shù)據(jù)庫(kù)初始化本地裝好 PostgreSQL 后打開(kāi)數(shù)據(jù)庫(kù)客戶(hù)端pgAdmin/psql 命令行執(zhí)行下面 SQL新建專(zhuān)屬數(shù)據(jù)庫(kù)和用戶(hù)避免和本地其他業(yè)務(wù)庫(kù)沖突-- 創(chuàng)建項(xiàng)目專(zhuān)用數(shù)據(jù)庫(kù) CREATE DATABASE llm_texttosql; -- 創(chuàng)建數(shù)據(jù)庫(kù)訪問(wèn)用戶(hù) CREATE USER postgres WITH PASSWORD postgres; -- 給用戶(hù)分配該庫(kù)全部操作權(quán)限 GRANT ALL PRIVILEGES ON DATABASE llm_texttosql TO postgres;踩坑提醒很多新手直接用 postgres 默認(rèn)庫(kù)存業(yè)務(wù)表后期多項(xiàng)目開(kāi)發(fā)容易表名沖突單獨(dú)建庫(kù)是好習(xí)慣。2.3 硅基流動(dòng) API Key 申請(qǐng)步驟想要調(diào)用大模型生成 SQL必須先拿到接口密鑰步驟很簡(jiǎn)單瀏覽器打開(kāi)硅基流動(dòng)官網(wǎng)完成手機(jī)號(hào)注冊(cè)登錄進(jìn)入控制臺(tái) - API 密鑰管理復(fù)制生成專(zhuān)屬 Key注意妥善保存只展示一次模型選擇新手推薦tencent/Hunyuan-MT-7B或Qwen/Qwen2.5-7B-Instruct對(duì)中文 SQL 適配度最高計(jì)費(fèi)說(shuō)明新用戶(hù)一般贈(zèng)送免費(fèi)調(diào)用額度測(cè)試完全夠用正式使用按需充值三、項(xiàng)目完整目錄結(jié)構(gòu)標(biāo)準(zhǔn) SpringBoot 分層架構(gòu)嚴(yán)格按照 Controller-Service-Repository 分層資源文件分類(lèi)存放后續(xù)擴(kuò)展多表、多接口不會(huì)混亂llm-text-to-sql/ ├── src/ │ ├── main/ │ │ ├── java/ │ │ │ └── com/example/text2sql/ │ │ │ ├── Text2SqlApplication.java # 項(xiàng)目啟動(dòng)類(lèi) │ │ │ ├── config/ │ │ │ │ └── WebConfig.java # Web擴(kuò)展配置本文基礎(chǔ)版暫未拓展預(yù)留擴(kuò)展 │ │ │ ├── controller/ │ │ │ │ └── Text2SqlController.java # 頁(yè)面路由前后端API接口 │ │ │ ├── entity/ │ │ │ │ └── Member.java # 會(huì)員數(shù)據(jù)庫(kù)實(shí)體映射類(lèi) │ │ │ ├── repository/ │ │ │ │ └── MemberRepository.java # JPA數(shù)據(jù)訪問(wèn)層 │ │ │ └── service/ │ │ │ ├── LlmService.java # 硅基流動(dòng)大模型API對(duì)接核心類(lèi) │ │ │ └── Text2SqlService.java # 業(yè)務(wù)核心整合服務(wù)LLMSQL執(zhí)行安全校驗(yàn) │ │ └── resources/ │ │ ├── application.yml # 全局配置文件數(shù)據(jù)庫(kù)、大模型參數(shù)全部寫(xiě)在這里 │ │ ├── schema.sql # 項(xiàng)目啟動(dòng)自動(dòng)執(zhí)行建表語(yǔ)句 │ │ ├── data.sql # 測(cè)試會(huì)員初始化數(shù)據(jù) │ │ └── templates/ │ │ ├── index.html # 首頁(yè)會(huì)員數(shù)據(jù)總覽頁(yè)面 │ │ └── chat.html # 智能問(wèn)答交互頁(yè)面 └── pom.xml # Maven依賴(lài)管理文件四、Maven 依賴(lài)與全局配置4.1 pom.xml 完整依賴(lài)所有用到的依賴(lài)全部貼出直接復(fù)制替換項(xiàng)目 pom.xml 即可無(wú)多余冗余包?xml version1.0 encodingUTF-8? project xmlnshttp://maven.apache.org/POM/4.0.0 xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd modelVersion4.0.0/modelVersion parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version3.2.0/version /parent groupIdcom.example/groupId artifactIdtext2sql/artifactId version1.0.0/version nametext2sql/name descriptionText-to-SQL會(huì)員智能問(wèn)答系統(tǒng)/description properties java.version17/java.version /properties dependencies !-- SpringBoot Web容器提供接口訪問(wèn)能力 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency !-- Thymeleaf頁(yè)面模板引擎渲染前端html -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-thymeleaf/artifactId /dependency !-- Spring Data JPA簡(jiǎn)化單表CRUD開(kāi)發(fā) -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-data-jpa/artifactId /dependency !-- JdbcTemplate執(zhí)行大模型生成的動(dòng)態(tài)SQL -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-jdbc/artifactId /dependency !-- PostgreSQL數(shù)據(jù)庫(kù)驅(qū)動(dòng)runtime運(yùn)行時(shí)生效 -- dependency groupIdorg.postgresql/groupId artifactIdpostgresql/artifactId scoperuntime/scope /dependency !-- Lombok簡(jiǎn)化實(shí)體類(lèi)代碼不用手動(dòng)寫(xiě)get/set -- dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependency /dependencies build plugins plugin groupIdorg.springframework.boot/groupId artifactIdspring-boot-maven-plugin/artifactId configuration excludes exclude groupIdorg.projectlombok/groupId artifactIdlombok/artifactId /exclude /excludes /configuration /plugin /plugins /build /project4.2 application.yml 配置詳解所有配置做了詳細(xì)注釋新手能看懂每一項(xiàng)作用注意替換自己的硅基流動(dòng) API Key# 服務(wù)端口配置訪問(wèn)地址localhost:8080 server: port: 8080 spring: application: name: text2sql-llm-demo # PostgreSQL數(shù)據(jù)庫(kù)連接配置和前面初始化庫(kù)對(duì)應(yīng) datasource: url: jdbc:postgresql://localhost:5432/llm_texttosql username: postgres password: postgres driver-class-name: org.postgresql.Driver # JPA持久化配置 jpa: hibernate: ddl-auto: none # 關(guān)閉自動(dòng)建表統(tǒng)一使用schema.sql腳本管理表結(jié)構(gòu)線上更安全 show-sql: true # 控制臺(tái)打印執(zhí)行的SQL調(diào)試排錯(cuò)很方便 properties: hibernate: format_sql: true # 格式化打印SQL不會(huì)擠成一行 dialect: org.hibernate.dialect.PostgreSQLDialect # 項(xiàng)目啟動(dòng)自動(dòng)執(zhí)行初始化SQL腳本 sql: init: mode: always # 每次重啟項(xiàng)目都執(zhí)行腳本測(cè)試環(huán)境使用生產(chǎn)建議改成embedded schema-locations: classpath:schema.sql >五、數(shù)據(jù)庫(kù)表結(jié)構(gòu)與測(cè)試數(shù)據(jù)5.1 schema.sql 建表語(yǔ)句設(shè)計(jì)一張會(huì)員業(yè)務(wù)表覆蓋姓名、等級(jí)、積分、入會(huì)時(shí)間等常用查詢(xún)維度增加字段注釋、索引提升查詢(xún)效率-- 創(chuàng)建會(huì)員信息業(yè)務(wù)表 CREATE TABLE IF NOT EXISTS member ( id SERIAL PRIMARY KEY, -- 自增主鍵會(huì)員唯一ID name VARCHAR(50) NOT NULL, -- 會(huì)員姓名非空 gender VARCHAR(10), -- 性別男/女 age INTEGER, -- 年齡 phone VARCHAR(20) UNIQUE, -- 手機(jī)號(hào)唯一約束防止重復(fù)錄入 email VARCHAR(100), -- 聯(lián)系郵箱 level VARCHAR(20) DEFAULT 普通會(huì)員, -- 會(huì)員等級(jí)普通/銀卡/金卡/鉆石 points INTEGER DEFAULT 0, -- 賬戶(hù)積分 join_date DATE, -- 入會(huì)日期 status VARCHAR(20) DEFAULT 活躍, -- 賬號(hào)狀態(tài)活躍/凍結(jié)/注銷(xiāo) created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- PostgreSQL專(zhuān)屬字段中文注釋數(shù)據(jù)庫(kù)客戶(hù)端可直接查看業(yè)務(wù)含義 COMMENT ON TABLE member IS 門(mén)店會(huì)員信息主表; COMMENT ON COLUMN member.id IS 會(huì)員主鍵ID; COMMENT ON COLUMN member.name IS 會(huì)員真實(shí)姓名; COMMENT ON COLUMN member.gender IS 會(huì)員性別; COMMENT ON COLUMN member.age IS 會(huì)員年齡; COMMENT ON COLUMN member.phone IS 綁定手機(jī)號(hào); COMMENT ON COLUMN member.email IS 預(yù)留郵箱; COMMENT ON COLUMN member.level IS 會(huì)員會(huì)員等級(jí); COMMENT ON COLUMN member.points IS 累計(jì)消費(fèi)積分; COMMENT ON COLUMN member.join_date IS 首次入會(huì)時(shí)間; COMMENT ON COLUMN member.status IS 賬號(hào)使用狀態(tài); COMMENT ON COLUMN member.created_at IS 數(shù)據(jù)創(chuàng)建時(shí)間; -- 建立常用查詢(xún)字段索引大數(shù)據(jù)量下提升查詢(xún)速度 CREATE INDEX IF NOT EXISTS idx_member_level ON member(level); CREATE INDEX IF NOT EXISTS idx_member_points ON member(points);5.2 data.sql 初始化測(cè)試數(shù)據(jù)批量插入多條測(cè)試會(huì)員數(shù)據(jù)加入ON CONFLICT沖突判斷避免項(xiàng)目重復(fù)啟動(dòng)報(bào)唯一鍵沖突-- 初始化會(huì)員數(shù)據(jù)使用ON CONFLICT避免重復(fù)插入 INSERT INTO member (name, gender, age, phone, email, level, points, join_date, status) VALUES (張三, 男, 28, 13800138001, zhangsanexample.com, 金卡會(huì)員, 15800, 2023-01-15, 活躍), (李四, 女, 35, 13800138002, lisiexample.com, 鉆石會(huì)員, 32000, 2022-03-20, 活躍), (王五, 男, 22, 13800138003, wangwuexample.com, 普通會(huì)員, 500, 2024-01-10, 活躍), (趙六, 女, 41, 13800138004, zhaoliuexample.com, 銀卡會(huì)員, 8200, 2023-06-01, 活躍), (錢(qián)七, 男, 30, 13800138005, qianqiexample.com, 金卡會(huì)員, 12500, 2023-08-15, 活躍), (孫八, 女, 27, 13800138006, sunbaexample.com, 普通會(huì)員, 1200, 2024-02-20, 活躍), (周九, 男, 45, 13800138007, zhoujiuexample.com, 鉆石會(huì)員, 45000, 2021-11-05, 活躍), (吳十, 女, 19, 13800138008, wushiexample.com, 普通會(huì)員, 200, 2024-05-01, 活躍), (鄭十一, 男, 33, 13800138009, zheng11example.com, 銀卡會(huì)員, 6800, 2023-04-10, 凍結(jié)), (馮十二, 女, 38, 13800138010, feng12example.com, 金卡會(huì)員, 18900, 2022-12-25, 活躍), (陳十三, 男, 25, 13800138011, chen13example.com, 普通會(huì)員, 800, 2024-03-18, 活躍), (褚十四, 女, 42, 13800138012, chu14example.com, 鉆石會(huì)員, 52000, 2021-06-30, 活躍), (衛(wèi)十五, 男, 29, 13800138013, wei15example.com, 金卡會(huì)員, 14200, 2023-09-12, 活躍), (蔣十六, 女, 36, 13800138014, jiang16example.com, 銀卡會(huì)員, 9500, 2023-05-08, 活躍), (沈十七, 男, 23, 13800138015, shen17example.com, 普通會(huì)員, 350, 2024-04-22, 注銷(xiāo)), (韓十八, 女, 48, 13800138016, han18example.com, 鉆石會(huì)員, 61000, 2020-08-14, 活躍), (楊十九, 男, 31, 13800138017, yang19example.com, 金卡會(huì)員, 16500, 2023-07-19, 活躍), (朱二十, 女, 26, 13800138018, zhu20example.com, 普通會(huì)員, 950, 2024-01-05, 活躍), (秦廿一, 男, 39, 13800138019, qin21example.com, 銀卡會(huì)員, 7800, 2023-02-28, 活躍), (尤廿二, 女, 34, 13800138020, you22example.com, 金卡會(huì)員, 13800, 2023-10-11, 活躍) ON CONFLICT (phone) DO NOTHING;六、實(shí)體類(lèi)與數(shù)據(jù)訪問(wèn)層代碼6.1 Member.java 實(shí)體映射類(lèi)使用 Lombok 簡(jiǎn)化代碼字段和數(shù)據(jù)庫(kù)一一對(duì)應(yīng)區(qū)分日期、時(shí)間類(lèi)型package com.example.text2sql.entity; import jakarta.persistence.*; import lombok.Data; import java.time.LocalDate; import java.time.LocalDateTime; /** * 會(huì)員信息實(shí)體類(lèi) * * 對(duì)應(yīng)數(shù)據(jù)庫(kù)表member * * 字段說(shuō)明 * - id: 會(huì)員ID主鍵自增 * - name: 會(huì)員姓名 * - gender: 性別男/女 * - age: 年齡 * - phone: 手機(jī)號(hào)唯一 * - email: 郵箱 * - level: 會(huì)員等級(jí)普通會(huì)員/銀卡會(huì)員/金卡會(huì)員/鉆石會(huì)員 * - points: 積分 * - joinDate: 入會(huì)日期 * - status: 狀態(tài)活躍/凍結(jié)/注銷(xiāo) * - createdAt: 創(chuàng)建時(shí)間 */ Entity Table(name member) Data // Lombok注解自動(dòng)生成getter/setter/toString等方法 public class Member { /** * 會(huì)員ID主鍵自增 */ Id GeneratedValue(strategy GenerationType.IDENTITY) private Long id; /** * 會(huì)員姓名非空最大長(zhǎng)度50 */ Column(name name, nullable false, length 50) private String name; /** * 性別可選值男/女最大長(zhǎng)度10 */ Column(name gender, length 10) private String gender; /** * 年齡 */ Column(name age) private Integer age; /** * 手機(jī)號(hào)唯一約束最大長(zhǎng)度20 */ Column(name phone, length 20) private String phone; /** * 郵箱最大長(zhǎng)度100 */ Column(name email, length 100) private String email; /** * 會(huì)員等級(jí) * 可選值普通會(huì)員、銀卡會(huì)員、金卡會(huì)員、鉆石會(huì)員 * 默認(rèn)值普通會(huì)員 */ Column(name level, length 20) private String level; /** * 積分默認(rèn)值0 */ Column(name points) private Integer points; /** * 入會(huì)日期 */ Column(name join_date) private LocalDate joinDate; /** * 會(huì)員狀態(tài) * 可選值活躍、凍結(jié)、注銷(xiāo) * 默認(rèn)值活躍 */ Column(name status, length 20) private String status; /** * 記錄創(chuàng)建時(shí)間默認(rèn)當(dāng)前時(shí)間 */ Column(name created_at) private LocalDateTime createdAt; }6.2 MemberRepository 數(shù)據(jù)訪問(wèn)接口繼承 JpaRepository 自帶基礎(chǔ) CRUD額外擴(kuò)展幾個(gè)常用條件查詢(xún)方法方便首頁(yè)數(shù)據(jù)統(tǒng)計(jì)package com.example.text2sql.repository; import com.example.text2sql.entity.Member; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; import java.util.List; /** * 會(huì)員數(shù)據(jù)訪問(wèn)接口 * * 功能說(shuō)明 * - 基于Spring Data JPA提供Member實(shí)體的CRUD操作 * - 繼承JpaRepository自動(dòng)獲得以下方法 * - findById(id): 根據(jù)ID查找會(huì)員 * - findAll(): 查找所有會(huì)員 * - save(member): 保存會(huì)員 * - deleteById(id): 根據(jù)ID刪除會(huì)員 * - count(): 統(tǒng)計(jì)會(huì)員數(shù)量 * * 自定義查詢(xún)方法Spring Data JPA自動(dòng)生成SQL * - findByLevel(level): 根據(jù)會(huì)員等級(jí)查詢(xún) * - findByStatus(status): 根據(jù)狀態(tài)查詢(xún) * - findByGender(gender): 根據(jù)性別查詢(xún) * - findByAgeGreaterThan(age): 查詢(xún)年齡大于指定值的會(huì)員 * - findByPointsGreaterThan(points): 查詢(xún)積分大于指定值的會(huì)員 */ Repository public interface MemberRepository extends JpaRepositoryMember, Long { /** * 根據(jù)會(huì)員等級(jí)查詢(xún)會(huì)員列表 * * param level 會(huì)員等級(jí)普通會(huì)員/銀卡會(huì)員/金卡會(huì)員/鉆石會(huì)員 * return 該等級(jí)的會(huì)員列表 */ ListMember findByLevel(String level); /** * 根據(jù)會(huì)員狀態(tài)查詢(xún)會(huì)員列表 * * param status 會(huì)員狀態(tài)活躍/凍結(jié)/注銷(xiāo) * return 該狀態(tài)的會(huì)員列表 */ ListMember findByStatus(String status); /** * 根據(jù)性別查詢(xún)會(huì)員列表 * * param gender 性別男/女 * return 該性別的會(huì)員列表 */ ListMember findByGender(String gender); /** * 查詢(xún)年齡大于指定值的會(huì)員 * * param age 年齡閾值 * return 年齡大于指定值的會(huì)員列表 */ ListMember findByAgeGreaterThan(Integer age); /** * 查詢(xún)積分大于指定值的會(huì)員 * * param points 積分閾值 * return 積分大于指定值的會(huì)員列表 */ ListMember findByPointsGreaterThan(Integer points); }七、核心硅基流動(dòng)大模型 API 對(duì)接服務(wù) LlmService這是整個(gè)項(xiàng)目最核心的類(lèi)負(fù)責(zé)組裝提示詞、發(fā)起 HTTP 請(qǐng)求調(diào)用大模型、解析返回的 SQL 語(yǔ)句每一步都加了異常捕獲接口調(diào)用失敗會(huì)返回明確錯(cuò)誤標(biāo)識(shí)方便前端提示用戶(hù)。package com.example.text2sql.service; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.time.Duration; import java.util.HashMap; import java.util.List; import java.util.Map; /** * 大模型對(duì)接服務(wù)封裝硅基流動(dòng)API所有交互邏輯 */ Service public class LlmService { // 從yml配置文件讀取大模型接口參數(shù) Value(${siliconflow.api.url}) private String apiUrl; Value(${siliconflow.api.key}) private String apiKey; Value(${siliconflow.api.model}) private String model; private final ObjectMapper objectMapper; // 構(gòu)造注入JSON序列化工具 public LlmService(ObjectMapper objectMapper) { this.objectMapper objectMapper; } /** * 接收用戶(hù)自然語(yǔ)言調(diào)用大模型生成PostgreSQL標(biāo)準(zhǔn)SQL * param question 用戶(hù)輸入中文查詢(xún)問(wèn)題 * return 生成SQL異常統(tǒng)一返回ERROR:開(kāi)頭錯(cuò)誤信息 */ public String textToSql(String question) { // 系統(tǒng)提示詞告訴大模型數(shù)據(jù)庫(kù)表結(jié)構(gòu)、輸出規(guī)范是SQL生成準(zhǔn)確率關(guān)鍵 String systemPrompt 你是專(zhuān)業(yè)PostgreSQL SQL生成助手嚴(yán)格根據(jù)下方member表結(jié)構(gòu)把用戶(hù)中文問(wèn)題轉(zhuǎn)換成可直接執(zhí)行的SQL語(yǔ)句。 數(shù)據(jù)庫(kù)表member完整結(jié)構(gòu) - id: 會(huì)員ID (SERIAL PRIMARY KEY) - name: 會(huì)員姓名 (VARCHAR(50)) - gender: 性別 (VARCHAR(10)) 可選值僅男、女 - age: 年齡 (INTEGER) - phone: 手機(jī)號(hào) (VARCHAR(20)) - email: 郵箱 (VARCHAR(100)) - level: 會(huì)員等級(jí) (VARCHAR(20)) 可選值普通會(huì)員、銀卡會(huì)員、金卡會(huì)員、鉆石會(huì)員 - points: 累計(jì)積分 (INTEGER) - join_date: 入會(huì)日期 (DATE) - status: 賬號(hào)狀態(tài) (VARCHAR(20)) 可選值活躍、凍結(jié)、注銷(xiāo) - created_at: 創(chuàng)建時(shí)間 (TIMESTAMP) 強(qiáng)制輸出要求 1. 只輸出純SQL語(yǔ)句不要任何解釋、說(shuō)明文字 2. 嚴(yán)格遵循PostgreSQL語(yǔ)法中文字符串用單引號(hào)包裹 3. 用戶(hù)問(wèn)題無(wú)法生成有效查詢(xún)時(shí)直接返回固定文本ERROR:無(wú)法理解的問(wèn)題 4. 禁止生成DELETE、UPDATE、DROP、ALTER等修改、刪除類(lèi)SQL只輸出SELECT相關(guān)語(yǔ)句 ; try { // 組裝大模型請(qǐng)求體 MapString, Object requestBody new HashMap(); requestBody.put(model, model); // 系統(tǒng)角色消息表結(jié)構(gòu)規(guī)則約束 MapString, String systemMsg Map.of(role, system, content, systemPrompt); // 用戶(hù)提問(wèn)消息 MapString, String userMsg Map.of(role, user, content, question); requestBody.put(messages, List.of(systemMsg, userMsg)); requestBody.put(max_tokens, 512); // temperature越低輸出結(jié)果越固定、不會(huì)隨意發(fā)揮SQL場(chǎng)景建議0.1 requestBody.put(temperature, 0.1); // JSON序列化請(qǐng)求參數(shù) String jsonReq objectMapper.writeValueAsString(requestBody); // 創(chuàng)建HTTP客戶(hù)端發(fā)起接口調(diào)用 HttpClient httpClient HttpClient.newBuilder() .connectTimeout(Duration.ofSeconds(30)) .build(); HttpRequest request HttpRequest.newBuilder() .uri(URI.create(apiUrl)) .header(Authorization, Bearer apiKey) .header(Content-Type, application/json) .timeout(Duration.ofSeconds(60)) .POST(HttpRequest.BodyPublishers.ofString(jsonReq)) .build(); // 接收接口返回結(jié)果 HttpResponseString response httpClient.send(request, HttpResponse.BodyHandlers.ofString()); // 接口正常返回200狀態(tài)碼解析生成的SQL if (response.statusCode() 200) { JsonNode root objectMapper.readTree(response.body()); JsonNode choices root.get(choices); if (choices ! null choices.isArray() choices.size() 0) { return choices.get(0).get(message).get(content).asText().trim(); } return ERROR:大模型返回?cái)?shù)據(jù)格式異常; } else { // 接口調(diào)用失敗返回狀態(tài)碼方便排查 return ERROR:API調(diào)用失敗HTTP狀態(tài)碼: response.statusCode(); } } catch (Exception e) { // 捕獲所有網(wǎng)絡(luò)、序列化異常統(tǒng)一封裝錯(cuò)誤信息 return ERROR: e.getMessage(); } } }開(kāi)發(fā)重點(diǎn)說(shuō)明提示詞設(shè)計(jì)完整把表字段、枚舉值、語(yǔ)法規(guī)則全部傳給大模型是避免生成錯(cuò)誤 SQL 的核心如果后期 SQL 經(jīng)常出錯(cuò)優(yōu)先優(yōu)化提示詞而非調(diào)整代碼。temperature 參數(shù)文本創(chuàng)作場(chǎng)景會(huì)調(diào)高到 0.7-1但是 SQL 生成需要精準(zhǔn)設(shè)置 0.1 限制模型隨機(jī)發(fā)揮。統(tǒng)一錯(cuò)誤前綴所有異常都用ERROR:開(kāi)頭上層業(yè)務(wù)服務(wù)直接判斷前綴就能區(qū)分正常 SQL 和錯(cuò)誤信息邏輯更簡(jiǎn)潔。八、核心業(yè)務(wù)整合服務(wù) Text2SqlService串聯(lián)大模型調(diào)用、SQL 清洗、安全校驗(yàn)、數(shù)據(jù)庫(kù)執(zhí)行整套流程增加安全攔截邏輯防止用戶(hù)誘導(dǎo)大模型生成刪改數(shù)據(jù) SQL是保障系統(tǒng)安全的關(guān)鍵層。package com.example.text2sql.service; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Service; import java.util.*; /** * Text-to-SQL整合業(yè)務(wù)服務(wù)串聯(lián)大模型、SQL安全校驗(yàn)、數(shù)據(jù)庫(kù)執(zhí)行 */ Service public class Text2SqlService { private final LlmService llmService; private final JdbcTemplate jdbcTemplate; private final ObjectMapper objectMapper; // 構(gòu)造注入依賴(lài) public Text2SqlService(LlmService llmService, JdbcTemplate jdbcTemplate, ObjectMapper objectMapper) { this.llmService llmService; this.jdbcTemplate jdbcTemplate; this.objectMapper objectMapper; } /** * 完整問(wèn)答處理主流程 * 1.調(diào)用大模型生成SQL → 2.清理SQL多余標(biāo)記 → 3.安全校驗(yàn)只允許SELECT → 4.執(zhí)行查詢(xún)返回?cái)?shù)據(jù) * param question 用戶(hù)輸入中文問(wèn)題 * return 包含原始問(wèn)題、生成SQL、查詢(xún)結(jié)果、成功/失敗標(biāo)識(shí)的Map */ public MapString, Object query(String question) { MapString, Object resultMap new HashMap(); resultMap.put(question, question); // 第一步調(diào)用大模型獲取SQL String rawSql llmService.textToSql(question); resultMap.put(generatedSql, rawSql); // 判斷大模型是否返回錯(cuò)誤 if (rawSql.startsWith(ERROR)) { resultMap.put(success, false); resultMap.put(error, rawSql); return resultMap; } // 第二步清洗SQL去除AI附帶的markdown代碼塊標(biāo)記 String cleanSql cleanSqlText(rawSql); resultMap.put(cleanedSql, cleanSql); try { // 第三步安全校驗(yàn)攔截非查詢(xún)類(lèi)SQL if (!checkSqlSafe(cleanSql)) { resultMap.put(success, false); resultMap.put(error, 安全攔截系統(tǒng)僅支持SELECT查詢(xún)語(yǔ)句禁止修改/刪除數(shù)據(jù)操作); return resultMap; } // 第四步執(zhí)行動(dòng)態(tài)SQL返回表格數(shù)據(jù) ListMapString, Object dataList jdbcTemplate.queryForList(cleanSql); resultMap.put(success, true); resultMap.put(data, dataList); resultMap.put(count, dataList.size()); } catch (Exception e) { // SQL語(yǔ)法錯(cuò)誤、表不存在等數(shù)據(jù)庫(kù)異常捕獲 resultMap.put(success, false); resultMap.put(error, SQL執(zhí)行失敗 e.getMessage()); } return resultMap; } /** * 清理大模型返回SQL附帶的多余符號(hào)比如sql、、SQL:前綴 */ private String cleanSqlText(String sql) { if (sql null || sql.isEmpty()) return ; sql sql.replaceAll(sql, ).replaceAll(, ).trim(); if (sql.toLowerCase().startsWith(sql:)) { sql sql.substring(4).trim(); } return sql; } /** * SQL安全校驗(yàn)僅允許SELECT開(kāi)頭支持WITH子句CTE查詢(xún) * 攔截UPDATE/DELETE/DROP/ALTER等危險(xiǎn)操作規(guī)避數(shù)據(jù)安全風(fēng)險(xiǎn) */ private boolean checkSqlSafe(String sql) { String upperSql sql.toUpperCase().trim(); return upperSql.startsWith(SELECT) || upperSql.startsWith(WITH); } /** * 查詢(xún)?nèi)繒?huì)員數(shù)據(jù)首頁(yè)總覽頁(yè)面使用 */ public ListMapString, Object getAllMemberData() { return jdbcTemplate.queryForList(SELECT * FROM member ORDER BY id ASC); } }安全邏輯重點(diǎn)說(shuō)明很多新手做 Text-to-SQL 項(xiàng)目會(huì)忽略安全問(wèn)題直接執(zhí)行大模型返回的 SQL一旦有人刻意誘導(dǎo)大模型生成DROP TABLE member整張業(yè)務(wù)表會(huì)直接刪除。本文做了兩層防護(hù)提示詞約束大模型禁止生成修改類(lèi) SQL代碼層二次校驗(yàn) SQL 開(kāi)頭關(guān)鍵字雙重?cái)r截避免數(shù)據(jù)事故。九、Controller 頁(yè)面路由與 API 接口區(qū)分頁(yè)面跳轉(zhuǎn)接口和 JSON 數(shù)據(jù)接口使用 Controller 返回頁(yè)面ResponseBody 返回 JSON接口注釋清晰方便后續(xù)對(duì)接前端或者第三方系統(tǒng)。package com.example.text2sql.controller; import com.example.text2sql.service.Text2SqlService; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.*; import java.util.List; import java.util.Map; /** * 系統(tǒng)控制器頁(yè)面跳轉(zhuǎn)、前端問(wèn)答API統(tǒng)一處理 */ Controller public class Text2SqlController { private final Text2SqlService text2SqlService; public Text2SqlController(Text2SqlService text2SqlService) { this.text2SqlService text2SqlService; } /** * 首頁(yè)會(huì)員數(shù)據(jù)總覽頁(yè)面 */ GetMapping(/) public String indexPage(Model model) { ListMapString, Object allMember text2SqlService.getAllMemberData(); model.addAttribute(members, allMember); model.addAttribute(totalCount, allMember.size()); return index; } /** * 智能問(wèn)答聊天頁(yè)面 */ GetMapping(/chat) public String chatPage() { return chat; } /** * 問(wèn)答核心API接口前端AJAX異步調(diào)用 * 請(qǐng)求體{question:查詢(xún)鉆石會(huì)員} */ PostMapping(/api/query) ResponseBody public MapString, Object queryData(RequestBody MapString, String request) { String question request.get(question); if (question null || question.trim().length() 0) { return Map.of(success, false, error, 輸入內(nèi)容不能為空請(qǐng)描述你的查詢(xún)需求); } return text2SqlService.query(question); } /** * 獲取全量會(huì)員數(shù)據(jù)接口可供第三方調(diào)用 */ GetMapping(/api/members) ResponseBody public ListMapString, Object getMemberList() { return text2SqlService.getAllMemberData(); } }十、前端頁(yè)面代碼實(shí)現(xiàn)10.1 首頁(yè) index.html會(huì)員數(shù)據(jù)總覽使用 Thymeleaf 循環(huán)渲染會(huì)員表格簡(jiǎn)單統(tǒng)計(jì)會(huì)員總數(shù)頁(yè)面樣式簡(jiǎn)潔適配辦公場(chǎng)景。body div classheader h1 全部會(huì)員數(shù)據(jù)總覽/h1 div classnav a href/chat進(jìn)入AI智能問(wèn)答/a /div /div div classcount-card h3當(dāng)前會(huì)員總條數(shù)span th:text${totalCount}0/span/h3 /div table thead tr thID/th th姓名/th th性別/th th年齡/th th手機(jī)號(hào)/th th會(huì)員等級(jí)/th th累計(jì)積分/th th賬號(hào)狀態(tài)/th th入會(huì)日期/th /tr /thead tbody tr th:eachitem : ${members} td th:text${item.id}/td td th:text${item.name}/td td th:text${item.gender}/td td th:text${item.age}/td td th:text${item.phone}/td td th:text${item.level}/td td th:text${item.points}/td td th:text${item.status}/td td th:text${item.join_date}/td /tr /tbody /table /body展示效果如下10.2 問(wèn)答頁(yè)面 chat.html核心交互頁(yè)面原生 JS 實(shí)現(xiàn)表單提交、異步請(qǐng)求、結(jié)果渲染自帶示例快捷提問(wèn)按鈕增加 HTML 轉(zhuǎn)義函數(shù)防止 XSS 攻擊展示生成 SQL 和查詢(xún)表格。script // 填充示例問(wèn)題到輸入框 function fillExample(text) { document.getElementById(questionInput).value text; } // 表單提交監(jiān)聽(tīng) const form document.getElementById(queryForm); form.addEventListener(submit, async function(e) { e.preventDefault(); const inputVal document.getElementById(questionInput).value.trim(); if (!inputVal) { alert(請(qǐng)輸入查詢(xún)問(wèn)題); return; } try { // 調(diào)用后端查詢(xún)接口 const res await fetch(/api/query, { method: POST, headers: { Content-Type: application/json }, body: JSON.stringify({question: inputVal}) }); const result await res.json(); renderResult(inputVal, result); } catch (err) { alert(網(wǎng)絡(luò)請(qǐng)求失敗 err.message); } }); // 渲染查詢(xún)結(jié)果到頁(yè)面 function renderResult(question, res) { let htmlStr div classresult-item div classquestion? 你的問(wèn)題${htmlEscape(question)}/div; if (res.success) { htmlStr div生成SQL語(yǔ)句/div div classsql-block${htmlEscape(res.cleanedSql)}/div div匹配到${res.count}條數(shù)據(jù)/div table thead tr ${Object.keys(res.data[0] || {}).map(k th${htmlEscape(k)}/th).join()} /tr /thead tbody ${res.data.map(row tr ${Object.values(row).map(v td${htmlEscape(v || )}/td).join()} /tr ).join()} /tbody /table ; } else { htmlStr div classerror-text? 查詢(xún)失敗${htmlEscape(res.error)}/div; } htmlStr /div; // 最新結(jié)果插入最上方 document.getElementById(resultContainer).insertAdjacentHTML(afterbegin, htmlStr); } // HTML轉(zhuǎn)義防止XSS注入 function htmlEscape(text) { const div document.createElement(div); div.textContent text; return div.innerHTML; } /script十一、項(xiàng)目總結(jié)11.1 項(xiàng)目核心亮點(diǎn)完整可落地從數(shù)據(jù)庫(kù)建表、后端分層、大模型對(duì)接、前端頁(yè)面全套代碼復(fù)制即可運(yùn)行無(wú)缺失模塊雙層 SQL 安全防護(hù)提示詞約束 代碼關(guān)鍵字?jǐn)r截杜絕刪改表等高危操作企業(yè)內(nèi)部使用更放心輕量化無(wú)復(fù)雜依賴(lài)不引入 Vue、Redis、消息隊(duì)列等重型組件小型工具快速開(kāi)發(fā)部署用戶(hù)友好前端自帶示例快捷提問(wèn)自動(dòng)展示生成 SQL業(yè)務(wù)人員可以復(fù)制 SQL 復(fù)用完善異常捕獲大模型接口報(bào)錯(cuò)、SQL 語(yǔ)法錯(cuò)誤、空輸入全部做友好提示便于排查問(wèn)題。11.2 開(kāi)發(fā)踩坑 FAQQ1啟動(dòng)項(xiàng)目時(shí)報(bào) PostgreSQL 連接失敗A檢查 yml 數(shù)據(jù)庫(kù) url、賬號(hào)密碼確認(rèn)本地 PostgreSQL 服務(wù)正常啟動(dòng)5432 端口沒(méi)有被占用同時(shí)確認(rèn) llm_texttosql 數(shù)據(jù)庫(kù)已提前創(chuàng)建。Q2大模型返回 ERROR:API 調(diào)用失敗A核對(duì)硅基流動(dòng) API Key 是否復(fù)制正確Key 前后不要帶空格檢查本地網(wǎng)絡(luò)是否能訪問(wèn)硅基流動(dòng)外網(wǎng)接口新用戶(hù)查看是否還有免費(fèi)調(diào)用額度。Q3生成的 SQL 查詢(xún)不出數(shù)據(jù)A優(yōu)先檢查提示詞里的字段枚舉值是否和數(shù)據(jù)庫(kù)一致比如 “金卡會(huì)員” 不能寫(xiě)成 “金卡”其次優(yōu)化提示詞增加 1-2 條查詢(xún)示例給大模型參考提升匹配準(zhǔn)確率。這套 Text-to-SQL 系統(tǒng)完全適配中小企業(yè)內(nèi)部數(shù)據(jù)查詢(xún)場(chǎng)景不用業(yè)務(wù)人員學(xué)習(xí) SQL 語(yǔ)法開(kāi)發(fā)維護(hù)成本很低。文章里所有代碼都是實(shí)際運(yùn)行調(diào)試后的完整版本大家可以直接復(fù)制搭建有任何搭建報(bào)錯(cuò)、功能拓展的問(wèn)題。行文倉(cāng)促定有不足之處歡迎各位朋友在評(píng)論區(qū)批評(píng)指正不勝感激。

相關(guān)新聞

LLMRouter:智能模型調(diào)度器,16+策略實(shí)現(xiàn)多模型路由與成本優(yōu)化

LLMRouter:智能模型調(diào)度器,16+策略實(shí)現(xiàn)多模型路由與成本優(yōu)化

1. 項(xiàng)目概述:為什么我們需要一個(gè)“智能模型調(diào)度器”?最近在折騰大模型應(yīng)用落地的朋友,估計(jì)都遇到過(guò)這個(gè)頭疼的問(wèn)題:手頭有好幾個(gè)模型API,比如GPT-4、Claude 3、DeepSeek,還有一堆開(kāi)源的Llama、Qwen。每個(gè)模…

2026/8/2 3:34:40 閱讀更多
UniApp微信小程序白屏問(wèn)題全鏈路排查指南:從原理到實(shí)戰(zhàn)解決

UniApp微信小程序白屏問(wèn)題全鏈路排查指南:從原理到實(shí)戰(zhàn)解決

1. 問(wèn)題引入:當(dāng)你的UniApp小程序變成一片空白 做UniApp微信小程序開(kāi)發(fā),最讓人頭皮發(fā)麻的瞬間之一,莫過(guò)于本地調(diào)試一切正常,真機(jī)預(yù)覽或上傳體驗(yàn)版后,頁(yè)面加載出來(lái)卻是一片刺眼的白屏。這感覺(jué)就像你精心準(zhǔn)備了滿(mǎn)漢全席&…

2026/8/2 3:34:40 閱讀更多
SO加固脫殼實(shí)戰(zhàn):Frida內(nèi)存Dump與ELF結(jié)構(gòu)修復(fù)詳解

SO加固脫殼實(shí)戰(zhàn):Frida內(nèi)存Dump與ELF結(jié)構(gòu)修復(fù)詳解

1. 項(xiàng)目概述:一次完整的SO加固脫殼實(shí)戰(zhàn)在移動(dòng)安全逆向分析領(lǐng)域,遇到加固保護(hù)的SO(共享對(duì)象庫(kù))文件是家常便飯。這些SO文件被廠商通過(guò)各種技術(shù)手段(如代碼混淆、加密、虛擬化)保護(hù)起來(lái),直接拖進(jìn)I…

2026/8/2 4:44:56 閱讀更多
GeoServer跨域CORS插件安裝與配置全攻略:從原理到安全實(shí)踐

GeoServer跨域CORS插件安裝與配置全攻略:從原理到安全實(shí)踐

1. 項(xiàng)目緣起:為什么GeoServer的跨域設(shè)置是個(gè)“老大難”問(wèn)題? 如果你和我一樣,長(zhǎng)期在WebGIS領(lǐng)域摸爬滾打,那么對(duì)“跨域”這兩個(gè)字一定又愛(ài)又恨。愛(ài)的是,它代表了現(xiàn)代Web應(yīng)用靈活、開(kāi)放的特性;恨的是&#x…

2026/8/2 4:44:56 閱讀更多
不是所有人都能看到所有數(shù)據(jù):理解企業(yè)權(quán)限模型

不是所有人都能看到所有數(shù)據(jù):理解企業(yè)權(quán)限模型

從客戶(hù)管理案例出發(fā),拆開(kāi)角色、數(shù)據(jù)范圍、字段權(quán)限和操作權(quán)限 上一篇,我們把客戶(hù)表和跟進(jìn)記錄做成了銷(xiāo)售儀表盤(pán)。儀表盤(pán)讓管理者能看到客戶(hù)總數(shù)、階段分布、來(lái)源分布和待跟進(jìn)明細(xì)。系統(tǒng)變得更有用了,但也馬上帶來(lái)一個(gè)更現(xiàn)實(shí)的問(wèn)題&#xff1a…

2026/8/2 4:44:56 閱讀更多
STM32+ESP8266物聯(lián)網(wǎng)時(shí)鐘:NTP校時(shí)、語(yǔ)音播報(bào)與模塊化開(kāi)發(fā)實(shí)踐

STM32+ESP8266物聯(lián)網(wǎng)時(shí)鐘:NTP校時(shí)、語(yǔ)音播報(bào)與模塊化開(kāi)發(fā)實(shí)踐

1. 項(xiàng)目概述與核心價(jià)值最近在整理工作室的舊項(xiàng)目,翻出了一個(gè)幾年前做的智能時(shí)鐘,功能挺全:能通過(guò)Wi-Fi自動(dòng)從網(wǎng)絡(luò)對(duì)時(shí),還能在整點(diǎn)用語(yǔ)音播報(bào)時(shí)間,時(shí)間顯示亮度可以自己調(diào)節(jié)。當(dāng)時(shí)用的是STM32做主控,ESP8266…

2026/8/2 4:44:56 閱讀更多
Suricata規(guī)則與Lua腳本實(shí)戰(zhàn):從基礎(chǔ)檢測(cè)到容器化部署

Suricata規(guī)則與Lua腳本實(shí)戰(zhàn):從基礎(chǔ)檢測(cè)到容器化部署

1. 從一條“勸退”規(guī)則說(shuō)起:為什么Suricata規(guī)則讓人頭疼?如果你剛開(kāi)始接觸Suricata,打開(kāi)一個(gè)規(guī)則文件,看到滿(mǎn)屏的msg、flow、content、pcre,是不是感覺(jué)像在看天書(shū)?這幾乎是每個(gè)網(wǎng)絡(luò)安全分析新手都會(huì)遇到的“…

2026/8/2 4:44:42 閱讀更多
Linux密碼安全深度解析:從/etc/shadow到SHA512破解與防御實(shí)戰(zhàn)

Linux密碼安全深度解析:從/etc/shadow到SHA512破解與防御實(shí)戰(zhàn)

1. 項(xiàng)目概述:從一份文件到安全意識(shí)的覺(jué)醒在Linux系統(tǒng)管理的日常工作中,/etc/shadow文件就像一座守護(hù)用戶(hù)密碼的堡壘,它安靜地躺在/etc目錄下,卻承載著整個(gè)系統(tǒng)訪問(wèn)控制的核心秘密。這個(gè)項(xiàng)目標(biāo)題——“l(fā)inux 密碼文件 /etc/shadow&…

2026/8/2 4:34:42 閱讀更多
MoneyPrinterPlus實(shí)戰(zhàn)指南:AI視頻批量生成與自動(dòng)化發(fā)布完整解決方案

MoneyPrinterPlus實(shí)戰(zhàn)指南:AI視頻批量生成與自動(dòng)化發(fā)布完整解決方案

MoneyPrinterPlus實(shí)戰(zhàn)指南:AI視頻批量生成與自動(dòng)化發(fā)布完整解決方案 【免費(fèi)下載鏈接】MoneyPrinterPlus AI一鍵批量生成各類(lèi)短視頻,自動(dòng)批量混剪短視頻,自動(dòng)把視頻發(fā)布到抖音,快手,小紅書(shū),視頻號(hào)上,賺錢(qián)從來(lái)沒(méi)有這么容易過(guò)! 支持本地語(yǔ)音模型chatTTS,fasterwhisper,…

2026/8/2 0:04:00 閱讀更多
3分鐘搞定!QQ空間歷史說(shuō)說(shuō)完整備份終極指南

3分鐘搞定!QQ空間歷史說(shuō)說(shuō)完整備份終極指南

3分鐘搞定!QQ空間歷史說(shuō)說(shuō)完整備份終極指南 【免費(fèi)下載鏈接】GetQzonehistory 獲取QQ空間發(fā)布的歷史說(shuō)說(shuō) 項(xiàng)目地址: https://gitcode.com/GitHub_Trending/ge/GetQzonehistory 你是否曾想過(guò),那些年發(fā)過(guò)的QQ空間說(shuō)說(shuō),那些記錄青春的文字…

2026/8/2 0:04:01 閱讀更多
MoneyPrinterPlus實(shí)戰(zhàn)指南:AI視頻批量生成與自動(dòng)化發(fā)布完整解決方案

MoneyPrinterPlus實(shí)戰(zhàn)指南:AI視頻批量生成與自動(dòng)化發(fā)布完整解決方案

MoneyPrinterPlus實(shí)戰(zhàn)指南:AI視頻批量生成與自動(dòng)化發(fā)布完整解決方案 【免費(fèi)下載鏈接】MoneyPrinterPlus AI一鍵批量生成各類(lèi)短視頻,自動(dòng)批量混剪短視頻,自動(dòng)把視頻發(fā)布到抖音,快手,小紅書(shū),視頻號(hào)上,賺錢(qián)從來(lái)沒(méi)有這么容易過(guò)! 支持本地語(yǔ)音模型chatTTS,fasterwhisper,…

2026/8/2 0:04:00 閱讀更多
3分鐘搞定!QQ空間歷史說(shuō)說(shuō)完整備份終極指南

3分鐘搞定!QQ空間歷史說(shuō)說(shuō)完整備份終極指南

3分鐘搞定!QQ空間歷史說(shuō)說(shuō)完整備份終極指南 【免費(fèi)下載鏈接】GetQzonehistory 獲取QQ空間發(fā)布的歷史說(shuō)說(shuō) 項(xiàng)目地址: https://gitcode.com/GitHub_Trending/ge/GetQzonehistory 你是否曾想過(guò),那些年發(fā)過(guò)的QQ空間說(shuō)說(shuō),那些記錄青春的文字…

2026/8/2 0:04:01 閱讀更多
AMAT 0100-02186 I/O 分配 PCB

AMAT 0100-02186 I/O 分配 PCB

AMAT 0100-02186 I/O分配PCB板是應(yīng)用材料(Applied Materials)公司生產(chǎn)的一款用于半導(dǎo)體設(shè)備的I/O信號(hào)分配電路板。該型號(hào)(0100-02186)的核心特點(diǎn)如下:專(zhuān)用于Endura等半導(dǎo)體工藝腔室。集成信號(hào)路由與分配功能。連接控制…

2026/8/2 2:51:21 閱讀更多
Nissei Corp FFMN-32L-10-T0 40AX 三相異步電動(dòng)機(jī)

Nissei Corp FFMN-32L-10-T0 40AX 三相異步電動(dòng)機(jī)

Nissei Corp FFMN-32L-10-T0 40AX 三相異步電動(dòng)機(jī)是日本日清(Nissei)品牌的一款工業(yè)用三相異步電機(jī),適用于自動(dòng)化設(shè)備及通用機(jī)械驅(qū)動(dòng)。該型號(hào)(FFMN-32L-10-T0 40AX)的核心特點(diǎn)如下:三相交流異步電動(dòng)機(jī)。額定…

2026/8/2 2:52:49 閱讀更多