pentaho(keetle)使用手冊

来源:https://www.cnblogs.com/ycit/archive/2023/09/08/17687558.html
-Advertisement-
Play Games

# pentaho使用 先展示一下用途和效果 ![image](https://jsd.cdn.zzko.cn/gh/YuanjunXu/Images@main/src/image.1gzusdgfiiao.webp) ## 1. 環境準備 ### 1.1 pentaho是什麼? > `pentah ...


pentaho使用

先展示一下用途和效果

image

1. 環境準備

1.1 pentaho是什麼?

pentaho可讀作“彭塔湖”,原名keetlekeetlepentaho公司收購後改名而來。

pentaho是一款開源ETL工具,純java編寫的C/S模式的工具,可綠色免安裝,開箱即用。支持Windows、macOS、Linux平臺。

pentaho有2個核心設計,即轉換作業

轉換是一個包含輸入、邏輯處理、輸出的完整過程,即ETL。

作業是一個提供定時執行轉換的機制,即定時服務調度。

pentaho官網下載鏈接:Pentaho Community Edition Download | Hitachi Vantara

image

pentaho由主要四部分組成

  • Spoon.bat/Spoon.sh :勺子,是一個圖形化界面,可圖形化操作轉換和作業
  • Pan.bat/Pan.sh : 煎鍋,可用命令行方式調用轉換
  • Kitchen.bat/Kitchen.sh : 廚房,可用命令行方式調用作業
  • Carte.bat/Carte.sh : 菜單,是一個輕量級web容器,可建立專用、遠程的ETL Server

1.2 pentaho安裝

Windows

由於是純java編寫,依賴jdk環境。所以需要先配置jdk環境,這裡省略。

從官網下載pentaho安裝包後,直接解壓。

image

MacOS

tar -zxvf 安裝包路徑 -C 目標路徑

Linux

tar -zxvf 安裝包路徑 -C 目標路徑

目錄結構

重點目錄以及執行文件說明

  • lib目錄 : 這是依賴庫目錄,例如各個資料庫的jdbc驅動,都放在此目錄下
  • logs目錄 :這是轉換和作業運行的預設日誌輸出目錄
  • simple-jndi目錄 :這是各個資料庫的JNDI連接信息的全局配置
  • Spoon.bat/Spoon.sh :勺子,是一個圖形化界面,可圖形化操作轉換和作業
  • Pan.bat/Pan.sh : 煎鍋,可用命令行方式調用轉換
  • Kitchen.bat/Kitchen.sh : 廚房,可用命令行方式調用作業
  • Carte.bat/Carte.sh : 菜單,是一個輕量級web容器,可建立專用、遠程的ETL Server

在window上運行就用.bat格式腳本,MacOS 或者 Linux 平臺上使用.sh格式腳本


2. 開始使用

pentaho內置了豐富的數據處理組件,本章節主要對pentaho界面上各個功能組件作用進行說明。

2.1 啟動圖形化界面

Windows

運行 Spoon.bat 

MacOS

運行 Spoon.sh

Linux

運行 Spoon.sh

運行後會短暫沒有任何反應,等待會議,就會出現界面

image

主對象樹中有轉換作業

  • 轉換:所有的數據處理工作都在轉換中完成
  • 作業:這是一個任務

開始數據處理工作前,必需新建一個轉換,因為只有新建了之後,才能使用數據處理組件,此時的核心對象樹是空的。

2.2 轉換

在 “核心對象樹 –> 轉換 –> 右鍵 –> 新建” 或 在 “文件 –> 新建 –> 轉換” ,新建一個轉換,核心對象樹就會出現各類組件。依靠這些組件組合使用,完成數據處理工作。

image

2.2.1 主對象樹

一個轉換就是一個數據處理工作流程。這裡主要是轉換的配置,例如數據源連接,運行配置等。

image

2.2.2 核心對象樹

包含各類數據處理組件。

2.3 數據處理組件

這裡對一些常用的組件進行說明

輸入

輸入組件,即各類數據源,例如資料庫,json,xml等

輸出

輸出組件,將處理後的數據進行輸出保存

轉換

這是數據轉換的核心,在這裡完成數據處理

應用

包含一些數據處理外的操作,例如發送郵件,寫日誌等

流程

用於控制數據處理流程,例如開始,結束,終止等

腳本

當內置轉換組件完成不了數據處理的邏輯時,即可使用腳本組件,用自定義代碼的方式來完成處理邏輯

查詢

用於一些查詢請求,例如http請求,資料庫查詢某個表是否存在等

連接

可用於多表,單表處理完後,進行記錄合併


2.4 作業組件

在“文件–>新建–>作業”創建一個作業。

主對象樹包含作業運行配置,DB連接配置等

image

核心對象樹包含作業的各類組件

image


通用

作業流程組件,有開始、轉換、成功、空處理等

郵件

發送郵件

文件管理

文件操作,創建、刪除等

條件

條件處理,例如判斷某個文件是否存在

腳本

使用shell,js、sql等腳本處理複雜作業邏輯

應用

作業處理,例如終止作業、寫日誌等

文件傳輸

定時作業來上傳、下載文件


2.5 使用

上面介紹了各個組件用途,現在來完成一個完整的數據處理工作流程。

啟動應用


新建轉換

在 “核心對象樹 –> 轉換 –> 右鍵 –> 新建” 或 在 “文件 –> 新建 –> 轉換” ,新建一個轉換


配置DB連接

主對象樹中選擇DB連接,右鍵新建

image

註意:連接資料庫之前需要下載對應的jdbc驅動,例如連接pgsql則需要下載 postgresql-version.jar,r然後將驅動包放到安裝目錄下的\lib目錄


這裡以kingbase V8為例,因為這個踩了坑。經歷如下:

內置的數據源里有KingbaseES,本以為可以直接用,結果發現連不上,報驅動錯誤。可能是因為內置的驅動版本跟資料庫版本不一致,因為Kingbase V8的驅動不向前相容。更新驅動後,依然不行。

然後發現,內置還有Generic database選項,這個是用來自定義連接內置數據源之外的資料庫的。使用jdbc方式連接,需要一個連接串,驅動包名(前提是下載了對應的驅動包),用戶名,密碼。然而,這種方式依然不行……

後來一想,乾脆用pgsql的方式來連接kingbase,沒想到連接成功!


image


選擇輸入

因為數據源是資料庫,所以這裡從輸入組件中選擇表輸入,將其拖入到右側面板中

image


配置輸入

雙擊“表輸入”組件 或 右鍵選擇 “編輯步驟”

image

點擊獲取SQL查詢語句,會彈出界面選擇數據表

image

選擇一個數據表後,提示

image

選擇“是”

image

這裡會自動填充獲取數據的sql,也可以在這裡加上各種where條件,獲取需要的數據

點擊“確定”

image


配置輸出

如果是表結構一致,則可使用

因為目標數據源也是資料庫,所以這裡選擇表輸出。從輸出組件選擇表輸出,拖入轉換視圖中

image

然後進行步驟連接

方式一:按住shift鍵,滑鼠左鍵點選“輸入步驟”,會出現箭頭,然後連接到“輸出步驟”

方式二:滑鼠左鍵框選輸入和輸出,然後右鍵,選擇”新建節點連接“,選擇”起始步驟“,”目標步驟“

image

點擊“確定”

連接後如下:

image

雙擊“表輸出”或右鍵選擇“編輯步驟”

image

選擇目標資料庫中的數據表,然後點擊”確定“

選擇表輸出,無法配置欄位映射,所以前提是表結構一致才可使用。如果是異構表,需要欄位映射的,則需要使用 插入/更新 組件

如果輸入表和輸出表結構不一致,即異構表,則需要使用插入/更新組件。從輸出中選擇插入/更新拖入轉換視圖中,然後進行步驟連接,進入輸出配置

image

註意:一定要正確連接步驟,否則這步無法獲取輸入欄位,輸出欄位

欄位映射配置好後如下

image

點擊“確定”

image

image

然後點擊轉換視圖中的image按鈕,這個是運行

image

這個運行是運行一次,完成後就結束了。如果要定時運行,則需要作業

點擊“啟動” 會彈出界面 保存 當前轉換

image

輸入保存的文件名稱,然後點擊“Save”即可

image

每個步驟都顯示綠色的箭頭,說明沒有錯誤,正確的執行完了轉換。也可以在日誌輸出查看.

日誌:完成處理 (I=1, O=1, R=1, W=1, U=0, E=0)中的 I=1 表示 輸入 1 行,O=1表示 輸出 1 行,R=1 表示 讀取 1 行,W=1 表示 寫入 1 行

然後看一下數據輸出結果

源表

image

目標表

image

定時作業

如果需要定時執行同步過程,那麼就需要引入作業。在“文件–>新建–>作業” 創建一個作業。

在“通用”中選擇Start拖入作業視圖中

image

然後選擇轉換拖入視圖,併進行步驟連接。

image

雙擊“轉換”或右鍵選擇“編輯作業入口”

image

點擊“確定”

然後選成功組件拖入視圖,並連接步驟

image

雙擊視圖中的Start組件或右鍵”編輯作業入口“,進行作業調度配置

image

點擊運行視圖中的image按鈕。一個定時作業即完成

定時作業調度期間,程式不能退出!程式退出,作業即停止

至此一個完整的數據處理作業完成了。

3. 案例

3.1 簡單同步

本部分對簡單同步進行說明。簡單同步是指不涉及複雜計算、轉換等同步工作。

3.1.1 單表

即一對一同步,A表數據同步到B表,A與B的欄位數量、類型、名稱可能都不一樣,因此需要一些欄位類型轉換,這都很容易。

處理過程詳見2.5章節


3.1.2 多表

即2個及以上的表往一個表同步,同樣也需要欄位映射、類型轉換等操作。

外鍵關聯

這種通過某個欄位(外鍵)關聯的表,處理思路是在獲取數據時,通過sql聯表查詢,獲取到全部需要的數據。然後用單表同步方式進行處理。

多表合併

如果是異構表的話,獲取到每個數據源後,使用Multiway merge join多路合併組件處理

image

image

合併後的記錄可作為一個單表,然後進行單表同步的處理

合併是笛卡爾積,即A表n條記錄,B表n條記錄,結果就是n x n條記錄,欄位是A、B表全部欄位,這種方式不建議採用,會消耗更多記憶體資源。建議拆分成單表同步

如果是同構表的話,可拆分為多個單表同步處理。


3.2 複雜同步

本部分對涉及到數據計算、轉換的同步工作進行說明。有些複雜操作,無法直接使用組件進行處理,需要用到Script組件。這裡主要對如何使用腳本組件完成數據處理進行說明。

這裡先展示一個實際案例

image

這個過程是多表同步到一個表、涉及到欄位類型轉換、補充欄位和值、數據計算、增補數據。由於計算和增補數據使用內置組件無法完成,因此這裡使用了java腳本組件,自定義代碼進行數據處理。

這裡對欄位類型轉換增加列給某列設置值java腳本進行說明。

欄位類型轉換

例如 數字類型 轉為 字元串,字元串 轉為 日期時間……

轉換中選擇欄位選擇組件

image

雙擊“欄位選擇”或右鍵選擇“編輯步驟”

image

選擇“元數據”,在“欄位名稱”列選擇欄位,然後在“類型”列選擇目標類型

image


增加列並設置隨機數

輸入中找到生成隨機數組件,拖入視圖並連接步驟

image

雙擊生成隨機數或右鍵選擇“編輯步驟”

image

在“名稱”列輸入需要增加的欄位名,類型選擇生成隨機數規則

image

點擊“確定”後,運行轉換,在preview data處可預覽數據,可以看到增加了一列 uid 也有值

image


將列的值設置為常量

例如將上面隨機數組件生成的值設置為常量1。在轉換中選擇將欄位設置為常量組件,並連接步驟

image

雙擊“將欄位設置為常量”或右鍵選擇“編輯步驟”

image

在“欄位”列選擇需要設置的欄位,這裡選擇上一步驟生成的“uid”欄位,在“值替換”列輸入值。

點擊“確定”,運行轉換,然後預覽數據,可以看到uid的值被替換為1

image


java腳本

腳本有Java腳本、JavaScript腳本,SQL腳本等。這裡使用Java腳本,腳本的目的是處理內置組件處理不了的邏輯。例如有10個地層,但是數據源中只記錄了前9個地層,最後一個需要根據計算得到。

拖入Java腳本組件到轉換視圖中並連接步驟

image

雙擊“Java腳本”或右鍵選擇“編輯步驟”

image

然後展開Code Snippits\Common use

image

選擇Main拖入右側編輯區,Main是整個腳本處理入口

image

其預設腳本結構如下

public boolean processRow(StepMetaInterface smi, StepDataInterface sdi) throws KettleException {
  if (first) {
    first = false;
	// 代碼邏輯區域
      
    /* TODO: Your code here. (Using info fields)

    FieldHelper infoField = get(Fields.Info, "info_field_name");

    RowSet infoStream = findInfoRowSet("info_stream_tag");

    Object[] infoRow = null;

    int infoRowCount = 0;

    // Read all rows from info step before calling getRow() method, which returns first row from any
    // input rowset. As rowMeta for info and input steps varies getRow() can lead to errors.
    while((infoRow = getRowFrom(infoStream)) != null){

      // do something with info data
      infoRowCount++;
    }
    */
  }

  Object[] r = getRow();

  if (r == null) {
    setOutputDone();
    return false;
  }

  // It is always safest to call createOutputRow() to ensure that your output row's Object[] is large
  // enough to handle any new fields you are creating in this step.
  r = createOutputRow(r, data.outputRowMeta.size());

  /* TODO: Your code here. (See Sample)

  // Get the value from an input field
  String foobar = get(Fields.In, "a_fieldname").getString(r);

  foobar += "bar";
    
  // Set a value in a new output field
  get(Fields.Out, "output_fieldname").setValue(r, foobar);

  */
  // Send the row on to the next step.
  putRow(data.outputRowMeta, r);

  return true;
}

TODO區域就是代碼編輯區域,其它是預設腳本函數

點擊”確定“,然後再次打開Java腳本,就能看到輸入輸出欄位信息了

image

完整實現地層計算並補充最後一層的Java腳本代碼邏輯如下

// 這裡是需要用的 java API 所導入的包
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.lang.*;
import java.math.BigDecimal;
import java.util.*;

// 核心處理過程入口
public boolean processRow(StepMetaInterface smi, StepDataInterface sdi) throws KettleException {

 if (first) {
    first = false;
	    logBasic("----------------------欄位-------------------------");
        String projectCount = "project_count";
        String knumber = "knumber";
        String depth = "depth";
        String dep = "dep";
        String layerorder = "layerorder";
        String id = "id";
 
		logBasic("----------------------獲取輸入流-------------------------");
        // 輸入數據流 input 是消息步驟中設置的標簽名
        RowSet infoStream = findInfoRowSet("input"); 


 		Object[] infoRow = null;
    	int infoRowCount = 0;

		logBasic("----------------------遍曆數據流,將其載入到map-------------------------");
        // 遍曆數據流,將其載入到map,便於操作
        // 根據 項目索引+鑽孔索引 分組
        Map<String, ArrayList<Object[]>> groups = new HashMap<String, ArrayList<Object[]>>();
        while((infoRow = getRowFrom(infoStream)) != null){
            // 獲取欄位值
        	String prjCode = get(TransformClassBase.Fields.In, projectCount).getString(infoRow);
            String drillCode = get(TransformClassBase.Fields.In, knumber).getString(infoRow);
            String groupKey = prjCode + drillCode;

            if (!groups.containsKey(groupKey)) {
                logBasic("----------------------創建分組-------------------------");
                groups.put(groupKey,new ArrayList<Object[]>());
            }
			logBasic("----------------------添加數據到分組-------------------------");
            ArrayList<Object[]> objects = (ArrayList<Object[]>)groups.get(groupKey);
            objects.add(infoRow);

            logBasic("----------------------添加數據到輸出流-------------------------");
			// 將當前行拷貝一份
            Object[] row=infoRow;
            // 創建一個輸出行
             row = createOutputRow(infoRow, data.outputRowMeta.size());
            //putRow(infoStream.getRowMeta(), row);
            // 將輸出行添加到輸出數據集
            putRow(data.outputRowMeta, row);

      		infoRowCount++;
    	}
           
        logBasic("----------------------分組完成,處理最後一條數據-------------------------");
        // 將最後一條數據拷貝一份,場地分層索引+1,層底深度dep 賦值為 鑽孔深度 depth,然後將此行數數據添加
		Object[] keys = groups.keySet().toArray();
		for (int i = 0; i < keys.length; i++)  {
			 String s = keys[i].toString();
			 logBasic("----------------------當前分組-----------------------:"+ s);	
        	  ArrayList<Object[]> list = (ArrayList<Object[]>) groups.get(s);
		
			
              Object[] last = (Object[])list.get(list.size() - 1);         
              Object[] newLast=last;
            // 設置 layerorder 的值
           	  String layerorderVal = get(TransformClassBase.Fields.In, layerorder).getString(last);
			  BigDecimal v = new BigDecimal(layerorderVal);
              v = v.add(new BigDecimal(1));
              get(TransformClassBase.Fields.Out, layerorder).setValue(newLast, v);

            // 設置 dep 的值
              String layerDepVal = get(TransformClassBase.Fields.In, depth).getString(last);
         	  BigDecimal v2 = new BigDecimal(layerDepVal);
              get(TransformClassBase.Fields.Out, dep).setValue(newLast, v2);
            
			 // 設置id
              String idVal = UUID.randomUUID().toString();
              get(TransformClassBase.Fields.Out, id).setValue(newLast, idVal);
              
			  
			  logBasic("----------------------添加數據到輸出流-------------------------");
			  newLast=createOutputRow(newLast, data.outputRowMeta.size());
              // 將新的一行數據添加到輸出數據集
              putRow(data.outputRowMeta, newLast);

        }        
        



    /* TODO: Your code here. (Using info fields)

    FieldHelper infoField = get(Fields.Info, "info_field_name");

    RowSet infoStream = findInfoRowSet("info_stream_tag");

    Object[] infoRow = null;

    int infoRowCount = 0;

    // Read all rows from info step before calling getRow() method, which returns first row from any
    // input rowset. As rowMeta for info and input steps varies getRow() can lead to errors.
    while((infoRow = getRowFrom(infoStream)) != null){

      // do something with info data
      infoRowCount++;
    }
    */
  }


  Object[] r = getRow();
	 logBasic("----------------------getRow-----------------------:"+ r);	


  if (r == null) {
    setOutputDone();
    return false;
  }

  // It is always safest to call createOutputRow() to ensure that your output row's Object[] is large
  // enough to handle any new fields you are creating in this step.
  r = createOutputRow(r, data.outputRowMeta.size());
 logBasic("----------------------createOutputRow-----------------------:"+ r);	

  /* TODO: Your code here. (See Sample)

  // Get the value from an input field
  String foobar = get(Fields.In, "a_fieldname").getString(r);

  foobar += "bar";
    
  // Set a value in a new output field
  get(Fields.Out, "output_fieldname").setValue(r, foobar);

  */
  // Send the row on to the next step.
  putRow(data.outputRowMeta, r);

  return true;
}

至此 Java腳本處理完成。

痛(坑)點總結:

1.腳本編輯區是個文本編輯框,不能像IDEA一樣幫助寫代碼,只能通過日誌進行輸出驗證邏輯

2.建議通用的不涉及pentaho的java代碼操作,可以在IDEA中完成,然後拷貝到腳本編輯區。例如需要導入的包就是在IDEA中通過智能導入,然後拷貝的

驗證一下數據,圖中標記的行,就是根據前2行數據計算而來,然後進行補充的。在數據源中只記錄了前2行數據。

image

本文來自博客園,作者:宣君{https://www.nhit.icu/},轉載請註明原文鏈接:https://www.cnblogs.com/ycit/p/17687558.html


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 本教程適用於idea所有版本,並支持目前最新的2023.2.1版本。直接激活到2099年,支持windows、mac、linux。本文先講windows,mac和linux的跟win的激活方式大差不差。如果已經有了idea,想激活到2099的直接看步驟5 1.先去idea官網下載,官網下載地址:ht ...
  • 支持.Net Core(2.0及以上)與.Net Framework(4.5及以上) 可以部署在Docker, Windows, Linux, Mac。 分散式唯一Id,顧名思義,是指在全世界任何一臺電腦上都不會重覆的唯一Id。 在單機/單伺服器/單資料庫的小型應用中,不需要用到這類東西。但在高並 ...
  • # 前言 ### 在上一篇文章[【基於ASP.NET ZERO,開發SaaS版供應鏈管理系統】](https://www.cnblogs.com/freedyang/p/17679280.html)中有提到對Webhook功能的擴展改造,本文詳細介紹一下具體過程。 ### Webhook功能操作說明 ...
  • 一說tp大多數人想到的是PHP使用tp,但今天不說PHP 說說c#使用tp 由於tp比較久遠 網上的資料又是少之又少 接下來說說tp的一些基本用法 1.首先就是數據綁定了 <%tp:foreach collection="{$model.Items}" var="m"%> <td>{$m.name} ...
  • [toc] # Linux運維工程師面試題(9) > 祝各位小伙伴們早日找到自己心儀的工作。 > 持續學習才不會被淘汰。 > 地球不爆炸,我們不放假。 > 機會總是留給有有準備的人的。 > 加油,打工人! ## 1 pod 的生命周期 第一階段: - Pending:正在創建 Pod 但是 Pod ...
  • 下麵的系列文章記錄瞭如何使用一塊linux開發扳和一塊OLED屏幕實現視頻的播放: 1) [項目介紹](https://www.cnblogs.com/kfggww/p/17672932.html) 2) [為OLED屏幕開發I2C驅動](https://www.cnblogs.com/kfggww ...
  • 為了讓程式能快點,特意瞭解了CPU的各種原理,比如多核、超線程、NUMA、睿頻、功耗、GPU、大小核再到分支預測、cache_line失效、加鎖代價、IPC等各種指標(都有對應的代碼和測試數據)都會在這系列文章中得到答案。當然一定會有程式員最關心的分支預測案例、Disruptor無鎖案例、cache ...
  • [TOC](【後端面經-資料庫】Redis數據結構和底層數據類型) 聲明:Redis的相關知識是面試的一大熱門知識點,同時也是一個龐大的體系,所涉及的知識點非常多,如果用一篇文章羅列,往往會陷入知識海洋中無法感知其全貌,因此,這段時間我會試著拆分Redis的相關章節,輔以思維導圖的形式介紹Redis ...
一周排行
    -Advertisement-
    Play Games
  • 前言 微服務架構已經成為搭建高效、可擴展系統的關鍵技術之一,然而,現有許多微服務框架往往過於複雜,使得我們普通開發者難以快速上手並體驗到微服務帶了的便利。為瞭解決這一問題,於是作者精心打造了一款最接地氣的 .NET 微服務框架,幫助我們輕鬆構建和管理微服務應用。 本框架不僅支持 Consul 服務註 ...
  • 先看一下效果吧: 如果不會寫動畫或者懶得寫動畫,就直接交給Blend來做吧; 其實Blend操作起來很簡單,有點類似於在操作PS,我們只需要設置關鍵幀,滑鼠點來點去就可以了,Blend會自動幫我們生成我們想要的動畫效果. 第一步:要創建一個空的WPF項目 第二步:右鍵我們的項目,在最下方有一個,在B ...
  • Prism:框架介紹與安裝 什麼是Prism? Prism是一個用於在 WPF、Xamarin Form、Uno 平臺和 WinUI 中構建鬆散耦合、可維護和可測試的 XAML 應用程式框架 Github https://github.com/PrismLibrary/Prism NuGet htt ...
  • 在WPF中,屏幕上的所有內容,都是通過畫筆(Brush)畫上去的。如按鈕的背景色,邊框,文本框的前景和形狀填充。藉助畫筆,可以繪製頁面上的所有UI對象。不同畫筆具有不同類型的輸出( 如:某些畫筆使用純色繪製區域,其他畫筆使用漸變、圖案、圖像或繪圖)。 ...
  • 前言 嗨,大家好!推薦一個基於 .NET 8 的高併發微服務電商系統,涵蓋了商品、訂單、會員、服務、財務等50多種實用功能。 項目不僅使用了 .NET 8 的最新特性,還集成了AutoFac、DotLiquid、HangFire、Nlog、Jwt、LayUIAdmin、SqlSugar、MySQL、 ...
  • 本文主要介紹攝像頭(相機)如何採集數據,用於類似攝像頭本地顯示軟體,以及流媒體數據傳輸場景如傳屏、視訊會議等。 攝像頭採集有多種方案,如AForge.NET、WPFMediaKit、OpenCvSharp、EmguCv、DirectShow.NET、MediaCaptre(UWP),網上一些文章以及 ...
  • 前言 Seal-Report 是一款.NET 開源報表工具,擁有 1.4K Star。它提供了一個完整的框架,使用 C# 編寫,最新的版本採用的是 .NET 8.0 。 它能夠高效地從各種資料庫或 NoSQL 數據源生成日常報表,並支持執行複雜的報表任務。 其簡單易用的安裝過程和直觀的設計界面,我們 ...
  • 背景需求: 系統需要對接到XXX官方的API,但因此官方對接以及管理都十分嚴格。而本人部門的系統中包含諸多子系統,系統間為了穩定,程式間多數固定Token+特殊驗證進行調用,且後期還要提供給其他兄弟部門系統共同調用。 原則上:每套系統都必須單獨接入到官方,但官方的接入複雜,還要官方指定機構認證的證書 ...
  • 本文介紹下電腦設備關機的情況下如何通過網路喚醒設備,之前電源S狀態 電腦Power電源狀態- 唐宋元明清2188 - 博客園 (cnblogs.com) 有介紹過遠程喚醒設備,後面這倆天瞭解多了點所以單獨加個隨筆 設備關機的情況下,使用網路喚醒的前提條件: 1. 被喚醒設備需要支持這WakeOnL ...
  • 前言 大家好,推薦一個.NET 8.0 為核心,結合前端 Vue 框架,實現了前後端完全分離的設計理念。它不僅提供了強大的基礎功能支持,如許可權管理、代碼生成器等,還通過採用主流技術和最佳實踐,顯著降低了開發難度,加快了項目交付速度。 如果你需要一個高效的開發解決方案,本框架能幫助大家輕鬆應對挑戰,實 ...