若依代碼生成器原理velocity模板引擎(自用)

1.源碼分析

代碼生成器:導入表結構(預覽、編輯、刪除、同步)、生成前后端代碼

代碼生成器表結構說明:

若依提供了兩張核心表來存儲導入的業務表信息:

gen_table存儲業務表的基本信息 ,它對應于配置代碼基本信息和生成信息的頁面

gen_table_column存儲業務表的字段信息 它對應于配置代碼字段信息的頁面。

這兩張表是一對多的關系,一張業務表可以有多個字段的信息,所以在字段信息表中有個外鍵table_id指向

代碼生成器目錄結構:

查詢數據庫列表

當管理員在界面上點擊導入按鈕時,會彈出一個對話框,此時,前端需要向后端發送請求,查詢數據庫并返回到前端,展示當前項目庫中所有待導入的業務表。

前端:

此功能涉及前端相關的代碼位于views/tool/index.vue這個視圖組件中,負責實現導入業務表的用戶界面和交互邏輯。

/** 打開導入表彈窗 */
function openImportTable() {proxy.$refs["importRef"].show();
}

后端:

后端處理邏輯則在代碼生成模塊的GenController中,負責接收前端的請求,處理業務邏輯,并返回查詢結果。

/*** 查詢數據庫列表*/
@PreAuthorize("@ss.hasPermi('tool:gen:list')")
@GetMapping("/db/list")
public TableDataInfo dataList(GenTable genTable)
{startPage();List<GenTable> list = genTableService.selectDbTableList(genTable);return getDataTable(list);
}

具體的執行流程如下圖:

導入表結構:

前端:此功能涉及前端相關的代碼位于views/tool/importTable.vue這個視圖組件中,負責實現導入業務表的用戶界面和交互邏輯。

/** 導入按鈕操作 */
function handleImportTable() {const tableNames = tables.value.join(",");if (tableNames == "") {proxy.$modal.msgError("請選擇要導入的表");return;}importTable({ tables: tableNames }).then(res => {proxy.$modal.msgSuccess(res.msg);if (res.code === 200) {visible.value = false;emit("ok");}});
}

后端:后端處理邏輯則在代碼生成模塊的GenController中,負責接收前端的請求,處理業務邏輯,保存業務表的基本信息和字段信息

/*** 導入表結構(保存)*/
@PreAuthorize("@ss.hasPermi('tool:gen:import')")
@Log(title = "代碼生成", businessType = BusinessType.IMPORT)
@PostMapping("/importTable")
public AjaxResult importTableSave(String tables)
{// 將表名字符串轉換為數組 tb_task_collect,tb_vendout_runningString[] tableNames = Convert.toStrArray(tables);// 查詢表信息List<GenTable> tableList = genTableService.selectDbTableListByNames(tableNames);// 導入表結構(保存)genTableService.importGenTable(tableList, SecurityUtils.getUsername());return success();
}

具體的執行的流程如下圖:

生成代碼

首先管理員,選中需要下載的業務表,并點擊生成按鈕來觸發代碼生成并下載的過程。

前端隨后向后端發送請求,這個請求會告知服務器需要生成代碼的業務表。

前端:

負責實現這一功能的前端代碼位于views/tool/index.vue這個視圖組件中,負責實現生成業務表的用戶界面和交互邏輯。

/** 生成代碼操作 */
function handleGenTable(row) {const tbNames = row.tableName || tableNames.value;if (tbNames == "") {proxy.$modal.msgError("請選擇要生成的數據");return;}if (row.genType === "1") {genCode(row.tableName).then(response => {proxy.$modal.msgSuccess("成功生成到自定義路徑:" + row.genPath);});} else {proxy.$download.zip("/tool/gen/batchGenCode?tables=" + tbNames, "ruoyi.zip");}
}

后端:后端的邏輯處理則在代碼生成模塊的GenController中,這里是處理前端請求、執行代碼生成邏輯,將生成的代碼字節流通過HTTP響應返回給客戶端。

/*** 批量生成代碼*/
@PreAuthorize("@ss.hasPermi('tool:gen:code')")
@Log(title = "代碼生成", businessType = BusinessType.GENCODE)
@GetMapping("/batchGenCode")
public void batchGenCode(HttpServletResponse response, String tables) throws IOException
{// 將表名字符串轉換為數組 tb_task_collect,tb_vendout_runningString[] tableNames = Convert.toStrArray(tables);// 根據表名下載生成的代碼字節數組byte[] data = genTableService.downloadCode(tableNames);// 將生成的代碼字節流通過HTTP響應返回給客戶端genCode(response, data);
}

具體的執行的流程如下圖:

問題分析

我們已經對代碼生成器的工作原理有了一定的了解,接下來我們解決一些項目中使用的問題,比如:

每次生成代碼都需要修改作者,去除實體類前綴過于繁瑣,現在我們可以修改generator.yml配置文件來調整為自己項目的

# 代碼生成
gen:# 作者author: itheima# 默認生成包路徑 system 需改成自己的模塊名稱 如 system monitor toolpackageName: com.dkd.manage# 自動去除表前綴,默認是falseautoRemovePre: true# 表前綴(生成類名不會包含表前綴,多個用逗號分隔)tablePrefix: sys_,tb_

velocity模板引擎

  • Velocity官網:https://velocity.apache.org
  • Velocitv是一個|ava模板引擎,它使用特定語法在模板中嵌入lava對象數據,實現界面與代碼的分離

常見的應用場景:

  • Web內容生成 : 生成動態Web頁面。

  • 代碼生成 : 生成Java源代碼、SQL腳本、XML配置文件等。

  • 網頁靜態化 : 生成靜態網頁。

入門

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>velocity快速入門</title>
</head>
<body><h3>心懷夢想,堅持不懈,成功即在前方。加油少年!!</h3></body>
</html>

準備模板

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>velocity快速入門</title>
</head>
<body><h3>心懷夢想,堅持不懈,成功即在前方。${message}</h3></body>
</html>

上述代碼中的 加油少年 修改為了 ${message} 這是一個動態變量(占位符),方便動態填充數據

數據填充

編寫java代碼實現數據填充,并生成文件

package com.dkd.test;import com.dkd.generator.util.VelocityInitializer;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;import java.io.FileWriter;
import java.util.List;public class VelocityDemoTest {public static void main(String[] args) throws Exception {//1. 初始化模板引擎VelocityInitializer.initVelocity();//2. 準備數據模型VelocityContext velocityContext = new VelocityContext();velocityContext.put("message", "加油朋友!!");//3. 讀取模板Template template = Velocity.getTemplate("vm/index.html.vm", "UTF-8");//4. 渲染模板FileWriter fileWriter = new FileWriter("D:\\workspace\\index.html");template.merge(velocityContext, fileWriter);fileWriter.close();}
}

基礎語法-變量

Velocity中的變量有兩類

  • 在模板中定義變量: #set開頭,比如 #set($name = "velocity")

  • 獲取變量的的值: $name 或者 ${name}

下面是案例,基于剛才的入門案例模板改進

##雙#號 是vm的注釋

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>velocity快速入門</title>
</head>
<body><h3>心懷夢想,堅持不懈,成功即在前方。${message}</h3>
##定義變量
#set ($name = "velocity")##輸出變量
第一種情況:${name} <br>
第二種情況:$name## 第三種情況:orderService#set($column = "order")
字符串拼接:${column}Service <br>
</body>
</html>

對象的定義獲取

在ruoyi-generator模塊下新增一個區域的實體類

package com.dkd.test;import com.dkd.common.annotation.Excel;
import com.dkd.common.core.domain.BaseEntity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;/*** 區域管理對象 tb_region** @author itheima* @date 2024-06-05*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Region extends BaseEntity {private static final long serialVersionUID = 1L;/*** 主鍵ID*/private Long id;/*** 區域名稱*/@Excel(name = "區域名稱")private String regionName;}

準備模型數據

package com.dkd.test;import com.dkd.generator.util.VelocityInitializer;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;import java.io.FileWriter;
import java.util.List;public class VelocityDemoTest {public static void main(String[] args) throws Exception {//1. 初始化模板引擎VelocityInitializer.initVelocity();//2. 準備數據模型VelocityContext velocityContext = new VelocityContext();velocityContext.put("message", "加油朋友!!");// 創建區域對象Region region = new Region(1L, "北京北五環");velocityContext.put("region", region);//3. 讀取模板Template template = Velocity.getTemplate("vm/index.html.vm", "UTF-8");//4. 渲染模板FileWriter fileWriter = new FileWriter("D:\\workspace\\index.html");template.merge(velocityContext, fileWriter);fileWriter.close();}
}

動態模板展示數據

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>velocity快速入門</title>
</head>
<body><h3>心懷夢想,堅持不懈,成功即在前方。${message}</h3>## 定義變量
#set($name = "velocity")## 輸出變量
第一種情況:${name} <br>
第二種情況:$name <br>## 第三種情況:orderService
#set($column = "order")
字符串拼接:${column}Service <br>
<hr>## 獲取區域對象中的數據
區域ID:$region.id <br>
區域名稱:${region.regionName} <br>
<hr>
</body>
</html>

基礎語法-循環

循環的語法:#foreach(...) ... #end

##定義一個集合
#set($list = ["春", "夏", "秋", "冬"])    ## 遍歷循環
#foreach($item in $list)序號[$foreach.count] $item <br> ## count從1開始 index從0開始
#end

準備模型數據

// 創建區域對象
Region region1 = new Region(1L, "北京北五環");
Region region2 = new Region(2L, "北京北四環");
velocityContext.put("region", region1);
List<Region> regionList = List.of(region1, region2);
velocityContext.put("regionList", regionList);

動態模板展示數據

## 遍歷區域
#foreach($item in $regionList)序號[$foreach.count],區域ID:$item.id,區域名稱:$item.regionName <br>
#end
</br>

基礎語法-if判斷

判斷的語法:#if(condition) ... #elseif(condition) ... #else ... #end

##定義變量
#set($score=80)## if判斷
#if($score>=80)優秀
#elseif($score>=60)及格
#else不及格
#end
## 對象obj不為空才會執行里面的邏輯
#if($obj) ..... #end## 對象obj為空才會執行里面的邏輯
#if(!$obj) ..... #end

模板閱讀

我們不需要使用velocity去開發新的模板,若依已經提供好了,在它基礎上進行調整即可

下面這個是關于實體類的模板

package ${packageName}.domain;## 根據列類型獲取導入包
#foreach ($import in $importList)
import ${import};
#end
## 導入Apache Commons Lang庫,用于對象的toString方法
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
## 導入項目自定義的Excel注解,用于生成Excel文檔
import com.dkd.common.annotation.Excel;
#if($table.crud || $table.sub)
## 如果表具有CRUD操作或子表,繼承BaseEntity
import com.dkd.common.core.domain.BaseEntity;
#elseif($table.tree)
## 如果表是樹形結構,繼承TreeEntity
import com.dkd.common.core.domain.TreeEntity;
#end/*** ${functionName}對象 ${tableName}** @author ${author}* @date ${datetime}*/
#if($table.crud || $table.sub)#set($Entity="BaseEntity")
#elseif($table.tree)#set($Entity="TreeEntity")
#end
public class ${ClassName} extends ${Entity}{   ## 定義類的序列化版本ID
private static final long serialVersionUID = 1L;
## 根據表的列定義實體類的屬性
#foreach ($column in $columns)## 如果不是父類的屬性,則生成屬性#if(!$table.isSuperColumn($column.javaField))/** $column.columnComment */## 如果字段需要在列表中展示,使用Excel注解標記#if($column.list)#set($parentheseIndex=$column.columnComment.indexOf("("))#if($parentheseIndex != -1)#set($comment=$column.columnComment.substring(0, $parentheseIndex))#else#set($comment=$column.columnComment)#end#if($parentheseIndex != -1)@Excel(name = "${comment}", readConverterExp = "$column.readConverterExp()")#elseif($column.javaType == 'Date')@JsonFormat(pattern = "yyyy-MM-dd")@Excel(name = "${comment}", width = 30, dateFormat = "yyyy-MM-dd")#else@Excel(name = "${comment}")#end#endprivate $column.javaType $column.javaField;#end
#end
## 如果表有子表,定義子表信息的集合
#if($table.sub)
/** $table.subTable.functionName信息 */
private List<${subClassName}> ${subclassName}List;#end
## 為每個屬性字段生成getter和setter方法
#foreach ($column in $columns)#if(!$table.isSuperColumn($column.javaField))#if($column.javaField.length() > 2 && $column.javaField.substring(1,2).matches("[A-Z]"))#set($AttrName=$column.javaField)#else#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})#endpublic void set${AttrName}($column.javaType $column.javaField){this.$column.javaField = $column.javaField;}public $column.javaType get${AttrName}(){return $column.javaField;}#end
#end
## 如果表有子表,生成子表信息的getter和setter方法
#if($table.sub)
public List<${subClassName}> get${subClassName}List(){return ${subclassName}List;}public void set${subClassName}List(List<${subClassName}> ${subclassName}List){this.${subclassName}List = ${subclassName}List;}#end
## 重寫toString方法,使用Apache Commons Lang的ToStringBuilder
@Override
public String toString() {return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)#foreach ($column in $columns)#if($column.javaField.length() > 2 && $column.javaField.substring(1,2).matches("[A-Z]"))#set($AttrName=$column.javaField)#else#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})#end.append("${column.javaField}", get${AttrName}())#end#if($table.sub).append("${subclassName}List", get${subClassName}List())#end.toString();}}

Lombok集成

目前,我們已經基本熟悉了velocity的作用和一些語法,那接下來,我們就通過這些知識來去改造若依框架的代碼生成部分

common模塊的pom.xml中添加lombok坐標

<!--  lombok工具-->
<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId>
</dependency>

修改模板

enerator模塊的domain.java.vm模板中添加lombok注解

package ${packageName}.domain;## 根據列類型獲取導入包
#foreach ($import in $importList)
import ${import};
#end
## 導入項目自定義的Excel注解,用于生成Excel文檔
import com.dkd.common.annotation.Excel;
#if($table.crud || $table.sub)
## 如果表具有CRUD操作或子表,繼承BaseEntity
import com.dkd.common.core.domain.BaseEntity;
#elseif($table.tree)
## 如果表是樹形結構,繼承TreeEntity
import com.dkd.common.core.domain.TreeEntity;
#end
## 注意lombok導包
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;/*** ${functionName}對象 ${tableName}** @author ${author}* @date ${datetime}*/
#if($table.crud || $table.sub)
#set($Entity="BaseEntity")
#elseif($table.tree)
#set($Entity="TreeEntity")
#end
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class ${ClassName} extends ${Entity}{   ## 定義類的序列化版本ID
private static final long serialVersionUID=1L;
## 根據表的列定義實體類的屬性
#foreach ($column in $columns)## 如果不是父類的屬性,則生成屬性#if(!$table.isSuperColumn($column.javaField))/** $column.columnComment */## 如果字段需要在列表中展示,使用Excel注解標記#if($column.list)#set($parentheseIndex=$column.columnComment.indexOf("("))#if($parentheseIndex != -1)#set($comment=$column.columnComment.substring(0, $parentheseIndex))#else#set($comment=$column.columnComment)#end#if($parentheseIndex != -1)@Excel(name = "${comment}", readConverterExp = "$column.readConverterExp()")#elseif($column.javaType == 'Date')@JsonFormat(pattern = "yyyy-MM-dd")@Excel(name = "${comment}", width = 30, dateFormat = "yyyy-MM-dd")#else@Excel(name = "${comment}")#end#endprivate $column.javaType $column.javaField;#end
#end
## 如果表有子表,定義子表信息的集合
#if($table.sub)
/** $table.subTable.functionName信息 */
private List<${subClassName}> ${subclassName}List;#end
}
  • 正常添加了關于lombok的注解

  • 刪除了set 、 get 、toString 等方法

可以把生成后的代碼,拷貝到項目中,如果訂單管理能夠正常訪問和操作,就算修改成功了,后期再次生成的代碼,全部都支持lombok

Swagger集成

generator模塊的 controller.java.vm模板中添加Swagger注解

package ${packageName}.controller;import java.util.List;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.dkd.common.annotation.Log;
import com.dkd.common.core.controller.BaseController;
import com.dkd.common.core.domain.AjaxResult;
import com.dkd.common.enums.BusinessType;
import ${packageName}.domain.${ClassName};
import ${packageName}.service.I${ClassName}Service;
import com.dkd.common.utils.poi.ExcelUtil;
#if($table.crud || $table.sub)
import com.dkd.common.core.page.TableDataInfo;
#elseif($table.tree)
#end
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;/*** ${functionName}Controller** @author ${author}* @date ${datetime}*/
@Api(tags = "${functionName}Controller")
@RestController
@RequestMapping("/${moduleName}/${businessName}")
public class ${ClassName}Controller extends BaseController
{@Autowiredprivate I${ClassName}Service ${className}Service;/*** 查詢${functionName}列表*/@ApiOperation("查詢${functionName}列表")@PreAuthorize("@ss.hasPermi('${permissionPrefix}:list')")@GetMapping("/list")
#if($table.crud || $table.sub)public TableDataInfo list(${ClassName} ${className}){startPage();List<${ClassName}> list = ${className}Service.select${ClassName}List(${className});return getDataTable(list);}
#elseif($table.tree)public AjaxResult list(${ClassName} ${className}){List<${ClassName}> list = ${className}Service.select${ClassName}List(${className});return success(list);}
#end/*** 導出${functionName}列表*/@ApiOperation("導出${functionName}列表")@PreAuthorize("@ss.hasPermi('${permissionPrefix}:export')")@Log(title = "${functionName}", businessType = BusinessType.EXPORT)@PostMapping("/export")public void export(HttpServletResponse response, ${ClassName} ${className}){List<${ClassName}> list = ${className}Service.select${ClassName}List(${className});ExcelUtil<${ClassName}> util = new ExcelUtil<${ClassName}>(${ClassName}.class);util.exportExcel(response, list, "${functionName}數據");}/*** 獲取${functionName}詳細信息*/@ApiOperation("獲取${functionName}詳細信息")@PreAuthorize("@ss.hasPermi('${permissionPrefix}:query')")@GetMapping(value = "/{${pkColumn.javaField}}")public AjaxResult getInfo(@PathVariable("${pkColumn.javaField}") ${pkColumn.javaType} ${pkColumn.javaField}){return success(${className}Service.select${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaField}));}/*** 新增${functionName}*/@ApiOperation("新增${functionName}")@PreAuthorize("@ss.hasPermi('${permissionPrefix}:add')")@Log(title = "${functionName}", businessType = BusinessType.INSERT)@PostMappingpublic AjaxResult add(@RequestBody ${ClassName} ${className}){return toAjax(${className}Service.insert${ClassName}(${className}));}/*** 修改${functionName}*/@ApiOperation("修改${functionName}")@PreAuthorize("@ss.hasPermi('${permissionPrefix}:edit')")@Log(title = "${functionName}", businessType = BusinessType.UPDATE)@PutMappingpublic AjaxResult edit(@RequestBody ${ClassName} ${className}){return toAjax(${className}Service.update${ClassName}(${className}));}/*** 刪除${functionName}*/@ApiOperation("刪除${functionName}")@PreAuthorize("@ss.hasPermi('${permissionPrefix}:remove')")@Log(title = "${functionName}", businessType = BusinessType.DELETE)@DeleteMapping("/{${pkColumn.javaField}s}")public AjaxResult remove(@PathVariable ${pkColumn.javaType}[] ${pkColumn.javaField}s){return toAjax(${className}Service.delete${ClassName}By${pkColumn.capJavaField}s(${pkColumn.javaField}s));}
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/76905.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/76905.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/76905.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

如何制定有效的風險應對計劃

制定有效的風險應對計劃的核心在于&#xff1a; 識別潛在風險、評估風險的影響與概率、選擇合適的應對策略、建立動態監控和反饋機制。 其中&#xff0c;識別潛在風險是最為關鍵的第一步。只有準確識別出可能的風險&#xff0c;才能在后續的評估、應對、監控等環節中做到有的放…

A2A協議實現詳解及示例

A2A協議概述 A2A (Agent2Agent) 是Google推出的一個開放協議&#xff0c;旨在使AI智能體能夠安全地相互通信和協作。該協議打破了孤立智能體系統之間的壁壘&#xff0c;實現了復雜的跨應用自動化。[1] A2A協議的核心目標是讓不同的AI代理能夠相互通信、安全地交換信息以及在各…

【中級軟件設計師】前趨圖 (附軟考真題)

【中級軟件設計師】前趨圖 (附軟考真題) 目錄 【中級軟件設計師】前趨圖 (附軟考真題)一、歷年真題三、真題的答案與解析答案解析 復習技巧&#xff1a; 若已掌握【前趨圖】相關知識&#xff0c;可直接刷以下真題&#xff1b; 若對知識一知半解&#xff0c;建議略讀題目&#x…

調節磁盤和CPU的矛盾——InnoDB的Buffer Pool

緩存的重要性 無論是用于存儲用戶數據的索引【聚簇索引、二級索引】還是各種系統數據&#xff0c;都是以頁的形式存放在表空間中【對一個/幾個實際文件的抽象&#xff0c;存儲在磁盤上】如果需要訪問某頁的數據&#xff0c;就會把完整的頁數據加載到內存中【即使只訪問頁中的一…

springboot和springcloud的區別

1. ?目的與功能? ?1)Spring Boot?: 主要用于快速構建獨立的、生產級的 Spring 應用程序。它通過自動配置和嵌入式服務器等特性,簡化了微服務的開發、啟動和部署,使開發者能夠專注于業務邏輯而非繁瑣的配置。?Spring Boot是一個快速開發的框架,旨在簡化Java應用程序的開…

耘想WinNAS:以聊天交互重構NAS生態,開啟AI時代的存儲革命

一、傳統NAS的交互困境與范式瓶頸 在傳統NAS&#xff08;網絡附加存儲&#xff09;領域&#xff0c;用戶需通過復雜的圖形界面或命令行工具完成文件管理、權限配置、數據檢索等操作&#xff0c;學習成本高且效率低下。例如&#xff0c;用戶若需搜索特定文件&#xff0c;需手動…

在斷網的時候,websocket 一直在CLOSING 狀態

現象 websocket 先連接成功&#xff0c;然后斷網。 由于維護了一套心跳機制&#xff0c;前端發起心跳&#xff0c;如果一段時間內沒有收到服務端返回的心跳。則表示連接斷開。 用心跳的方式處理斷網的兜底情況。 然而&#xff0c;此時網絡是斷開的&#xff0c;在代碼中直接調…

基于AWS的大模型調用場景:10大成本優化實戰方案

大模型訓練與推理是AI領域的計算密集型場景&#xff0c;如何在AWS上實現高性能與低成本的雙重目標&#xff1f;本文從實例選型、彈性伸縮、存儲優化等角度&#xff0c;分享10個經過驗證的AWS成本優化策略&#xff0c;幫助企業節省30%以上成本。 一、大模型場景的成本痛點分析 計…

【網絡原理】TCP/IP協議五層模型

目錄 一. 協議的分層 二. OSI七層網絡協議 三. TCP/IP五層網絡協議 四. 網絡設備所在分層 五. 封裝 六. 分用 七. 傳輸中的封裝和分用 八. 數據單位術語 一. 協議的分層 常見的分層為兩種OSI七層模型和TCP/IP五層模型 為什么要協議分層&#xff1f; 在網絡通信中&…

科技快訊 | 阿里云百煉MCP服務上線;英偉達官宣:CUDA 工具鏈將全面原生支持 Python

李飛飛團隊最新AI報告&#xff1a;中美模型性能差距近乎持平 4月8日&#xff0c;斯坦福大學以人為本人工智能研究所發布《2025年人工智能指數報告》。報告顯示&#xff0c;2023年AI性能顯著提升&#xff0c;AI應用加速&#xff0c;投資增長&#xff0c;中美AI模型差距縮小。報告…

貓咪如廁檢測與分類識別系統系列【三】融合yolov11目標檢測

? 前情提要 家里養了三只貓咪&#xff0c;其中一只布偶貓經常出入廁所。但因為平時忙于學業&#xff0c;沒法時刻關注牠的行為。我知道貓咪的如廁頻率和時長與健康狀況密切相關&#xff0c;頻繁如廁可能是泌尿問題&#xff0c;停留過久也可能是便秘或不適。為了更科學地了解牠…

2025年燃氣證書:傳承與發展的行業紐帶

回溯歷史長河&#xff0c;能源的利用與人類文明的發展息息相關。從遠古時期的鉆木取火&#xff0c;到如今廣泛應用的燃氣能源&#xff0c;每一次能源的變革都推動著社會的巨大進步。而在現代燃氣行業蓬勃發展的背后&#xff0c;燃氣從業人員資格證書正扮演著傳承與發展的重要紐…

在Ubuntu下進行單片機開發是否需要關閉Secure Boot

1. Secure Boot的作用 功能&#xff1a;Secure Boot是UEFI的安全功能&#xff0c;旨在阻止未經驗證的驅動或操作系統啟動&#xff0c;防止惡意軟件篡改引導過程。 影響范圍&#xff1a;它主要限制的是操作系統啟動階段加載的內核級驅動&#xff08;如顯卡驅動、虛擬化模塊&…

國達陶瓷重磅推出陶瓷羅馬柱外墻整裝尖端新產品“冠巖臻石”

近日&#xff0c;記者在佛山國達建材有限公司&#xff08;以下簡稱國達陶瓷&#xff09;董事長楊建平處了解到&#xff0c;該公司重磅推出的“冠巖臻石”新產品&#xff0c;是屬于陶瓷羅馬柱外墻整裝產品中的尖端產品。新產品自面市之后&#xff0c;深受高端用戶的青睞與認可。…

【分享】Ftrans文件擺渡系統:既保障傳輸安全,又提供強集成支持

【分享】Ftrans文件擺渡系統&#xff1a;既保障傳輸安全&#xff0c;又提供強集成支持&#xff01; 在數字化浪潮中&#xff0c;企業對數據安全愈發重視&#xff0c;網絡隔離成為保護核心數據的關鍵防線&#xff0c;比如隔離成研發網-辦公網、生產網-測試網、內網-外網等。網絡…

實驗一 字符串匹配實驗

一、實驗目的 1&#xff0e;熟悉匯編語言編程環境和DEBUG調試程序的使用。 2&#xff0e;掌握鍵盤輸入字符串的方法和分支程序的設計。 二、實驗內容 編程實現&#xff1a;從鍵盤分別輸入兩個字符串&#xff0c;然后進行比較&#xff0c;若兩個字符串的長度…

添加登錄和注冊功能

先寫前端再寫后端 前提&#xff1a;ideavue3mybatisspringBoot3前后端分離實現對一張表的增刪改查&#xff08;完整代碼版&#xff09;-CSDN博客 項目地址 1.添加一個Login.vue視圖 <template><div class"login_container"><div class"login…

【Windows】系統安全移除移動存儲設備指南:告別「設備被占用」彈窗

Windows系統安全移除移動存儲設備指南&#xff1a;告別「設備被占用」彈窗 解決移動硬盤和U盤正在被占用無法彈出 一、問題背景 使用Windows系統時&#xff0c;經常遇到移動硬盤/U盤彈出失敗提示「設備正在使用中」&#xff0c;即使已關閉所有可見程序。本文將系統梳理已驗證…

Springboot下載文件, 文件名中文是亂碼, 空格變加號

默認把文件名放上去, 中文會亂碼, 文件名種有空格, 就會被截斷 public void download(HttpServletResponse response){// 文件名先進行url編碼, 避免亂碼問題// 把用%20進行替換fileName URLEncoder.encode(fileName, "UTF-8").replace("", "%20&qu…

MySQL 超詳細安裝教程與常見問題解決方案

一、MySQL 安裝教程 1. Windows 系統安裝&#xff08;以 MySQL 8.0 為例&#xff09; 步驟 1&#xff1a;下載 MySQL Installer 訪問 MySQL 官網下載頁面。 選擇 Windows (x86, 64-bit), MSI Installer&#xff08;推薦使用完整版 mysql-installer-web-community-8.0.xx.xx.…