本文為您介紹自定義插件的使用方法。
能力概述
Quick BI提供插件化的能力,將場景預處理、后處理等實現對外暴露,由外部系統(tǒng)按需進行定制和擴展。比如文件上傳前、報表發(fā)布前、數據查詢前等不同的熱點方法。Quick BI的自定義插件具有以下特性:
提供開放、靈活的自定義插件能力,方便外部開發(fā)人員快速構建應用,實現在自身的業(yè)務系統(tǒng)中,對Quick BI的資源、權限、登錄等進行相應的管理。
提供統(tǒng)一的插件配置頁面,實現插件代碼的快速上傳、激活、更新等操作。
需要注意的是:
插件啟用后,被插件攔截的功能可能存在影響,建議在開發(fā)環(huán)境中進行調試驗證通過后再發(fā)布到正式環(huán)境;若無開發(fā)環(huán)境,請?zhí)崆爸獣允褂貌寮娘L險:Quick BI作為一款工具產品,僅提供通道能力,用戶在使用產品所作的任何插件上傳操作,屬于用戶自主行為,由此產生的任何風險和問題由用戶自行承擔。
前提條件
只有組織管理員才能進入自定義插件頁面,并上傳、修改、刪除驅動文件,若其他角色想要擁有相應權限,可自定義角色并賦予相應的權限。
插件使用說明
注冊自定義插件
您可以按照圖示步驟進入自定義插件管理界面。
單擊注冊插件,勾選插件使用承諾函。
單擊確定后進入注冊插件界面并進行以下配置。
配置項
說明
插件名稱
自定義填寫插件名稱,不得超過50個字符以及特殊字符。
插件類型
支持文件上傳管理、報表訪問管理和登錄管理的插件。
其中,文件上傳管理支持數據源文件上傳和批量導入用戶文件上傳。
插件上傳
支持jar包和zip包類型的插件文件上傳。
插件包大小不得超過50M。
插件資源存儲位置可選為OSS、本地服務器、Minio、數據庫等,根據您的Quick BI服務當前的配置而定。
插件執(zhí)行類名
插件自定義類名,用于識別您的插件包中需要在Quick BI中執(zhí)行的核心邏輯位置。例如:org.example.ExampleFileUploadHandler。
上下文依賴信息
設置插件依賴的上下文內容,用于在Quick BI中透傳必要信息到插件代碼中。
例如:{"verifyCode":"f9677df9"}。
插件執(zhí)行順序
默認為1,若不修改該信息,默認按照1執(zhí)行。
A插件設置為1,B插件設置為1,此時按照插件最新修改時間排序,后修改先執(zhí)行。(修改時間逆向排序)。
A插件設置為1,B插件設置為2,按照插件設置的排序執(zhí)行,先執(zhí)行A插件,后執(zhí)行B插件。
單擊確定,成功注冊插件。
說明插件在注冊后,默認是失效的狀態(tài),需要進行激活操作后才可以使用。
管理自定義插件
您可以對自定義插件進行編輯(①)、激活/失效插件(②)和刪除(③)操作。
單擊操作列的圖標,更新插件。
說明需要先失效插件后才可以進行插件的更新操作。
插件更新支持編輯注冊時的各項內容,插件在更新后默認為失效的狀態(tài),需要再次手動激活后才可以使用。
單擊操作列的眼睛圖標,激活或失效插件。
說明當插件的運行狀態(tài)為失效狀態(tài)時,單擊圖標,激活插件;
當插件的運行狀態(tài)為激活狀態(tài)時,單擊圖標,失效插件。
單擊操作列的圖標,在彈出的二次確認框中單擊確定后刪除插件。
插件開發(fā)指南
文件上傳管理
在進行文件上傳管理的自定義插件代碼開發(fā)時,需要根據以下示例代碼進行二次開發(fā):
該示例代碼是一個文件插件開發(fā)的簡單例子,它在數據源中的上傳文件中使用,插件會在使用之后在您每一個上傳的文件名之后添加_plg后綴。
它的主要實現代碼如下:
# 實現FileUploadCustomHandler接口
public class ExampleFileUploadHandler implements FileUploadCustomHandler {
# 日志邏輯,固定代碼
private static final Logger _log = LoggerFactory.getLogger("thirdPlugin");
@Override
public FileUploadParameter process(FileUploadParameter fileUploadParameter, CustomContext context) throws Throwable {
MultipartFile file = fileUploadParameter.getFile();
long size = file.getSize();
_log.info("文件大小為"+size);
fileUploadParameter.setIdentifyName(fileUploadParameter.getIdentifyName()+"_plg");
return fileUploadParameter;
}
}
在實現文件插件的開發(fā)中,您需要通過實現插件門面接口來構造插件功能,首先,您需要實現FileUploadCustomHandler接口,對其中的process方法重寫,process方法中兩個參數,分別為
FileUploadParameter:
它為插件擴展點上游傳遞下來的上傳文件內容以及元信息,類其中包含了這些參數,您可以按需更改后,將類繼續(xù)傳遞給下游。
/** 服務端臨時文件 */
private MultipartFile file;
/** 文件類型 */
private String fileType;
/** 自定義標識名稱 */
private String identifyName;
/** 分隔符 */
//private Integer delimeter = 1; // 暫時無效
/** 數據編碼 */
//private String charset; // 暫時無效
/** 是否包含表頭, 默認包含 */
private Boolean withHead = true;
/** 是否需要截取空格, 默認需要 */
private Boolean trim = true;
/**
* 上傳文件指定的列類型
*/
private String tableColumnModelList;
/**
* 用戶自定義物理表名
*/
private String physicalTableName;
CustomContext:
它包含了設置的上下文參數,也就是您在開放平臺-自定義插件中設置的上下文依賴信息(json格式設置),您可以使用context.getValue("xxx")取到這些信息進行更加靈活的配置。
將參數進行處理后,您需要return改造之后的FileUploadParameter,以便擴展點下游進行處理。
這就是一個完整的文件插件開發(fā)過程。
批量導入用戶文件管理
它的作用范圍主要在此處,提供對批量導入的Excel上傳后修改/校驗的邏輯。
這是一個文件解碼的例子,一些文件被zip壓縮并重命名了xls形式,需要解碼其中內容后錄入。
主要實現代碼如下:
# 實現UserFileUploadCustomHandler接口
public class ExampleContextHandler implements UserFileUploadCustomHandler {
private static final Logger _log = LoggerFactory.getLogger("thirdPlugin");
@Override
public MultipartFile process(MultipartFile fileInfo, CustomContext context) throws Throwable {
_log.info("ExampleContextHandler process");
return unzipMultipartFile(fileInfo);
}
public MultipartFile unzipMultipartFile(MultipartFile multipartFile) throws IOException {
List<MultipartFile> unzippedFiles = new ArrayList<>();
ZipInputStream zipInputStream = new ZipInputStream(multipartFile.getInputStream());
ZipEntry zipEntry = zipInputStream.getNextEntry();
byte[] buffer = new byte[1024];
while (zipEntry != null) {
String fileName = zipEntry.getName();
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
int length;
while ((length = zipInputStream.read(buffer)) > 0) {
byteArrayOutputStream.write(buffer, 0, length);
}
// Use the MockMultipartFile to create a new MultipartFile from the unzipped bytes
MockMultipartFile unzippedFile = new MockMultipartFile(
"file",
fileName,
multipartFile.getContentType(),
byteArrayOutputStream.toByteArray());
unzippedFiles.add(unzippedFile);
byteArrayOutputStream.close();
zipEntry = zipInputStream.getNextEntry();
}
zipInputStream.closeEntry();
zipInputStream.close();
return unzippedFiles.get(0);
}
}
它其中僅包含了兩個參數:
fileInfo:
此項為上傳的MultipartFile文件,可以根據需要對它處理或校驗后傳遞給下游。
CustomContext:
他包含了設置的上下文參數,也就是您在開放平臺-自定義插件中設置的上下文依賴信息(json格式設置),您可以使用context.getValue("xxx")取到這些信息進行更加靈活的配置。
將參數進行處理后,您需要return true或者false,為true的話可以正常訪問,為false的話會提示用戶“您無當前報表的訪問權限,請申請權限”。
這就是一個完整的報表訪問插件開發(fā)過程。
報表訪問管理
示例代碼:
這是一個報表訪問插件開發(fā)的簡單例子,它的功能為當報表的名稱前綴為密級、且用戶昵稱的前綴為非密時,攔截用戶的訪問,提示您無訪問當前報表的權限。
報表訪問管理的主要實現代碼如下:
# 實現ReportAuthCustomHandler接口
public class ExampleReportViewPluginHandler implements ReportAuthCustomHandler {
private static final Logger _log = (Logger) LoggerFactory.getLogger("thirdPlugin");
@Override
public ReportAuthParameter process(ReportAuthParameter reportAuthParameter, CustomContext context) throws Throwable {
String reportName = reportAuthParameter.getReportTreeParameter().getName();
String nick = reportAuthParameter.getUserInfoParameter().getNickName();
if (reportName.startsWith("密級") && nick.startsWith("非密")) {
reportAuthParameter.setAllowed(false);
}
return reportAuthParameter;
}
}
在實現報表訪問插件的開發(fā)中,您需要通過實現插件門面接口來構造插件功能,首先,您需要實現ReportAuthCustomHandler接口,對其中的process方法重寫,process方法中兩個參數,分別為
ReportAuthParameter:
它其中包含了插件擴展點上游傳遞下來的報表信息以及用戶身份信息,您可以根據這些信息判斷是否將當前報表的查看權限開放給當前用戶:
ReportTreeParameter是其中的報表信息類,有如下屬性:
public class ReportTreeParameter {
/**
* 報表id
*/
private String treeId
/**
* 名稱 db_column: name
*/
private String name;
/**
* owner工號 db_column: owner_num
*/
private String ownerNum;
/**
* owner名字 db_column: owner_name
*/
private String ownerName;
/**
* 創(chuàng)建日期 db_column: gmt_create
*/
private Date gmtCreate;
/**
* 修改日期 db_column: gmt_modified
*/
private Date gmtModified;
/**
* 工作空間id db_column: workspace_id
*/
private String workspaceId;
/**
* 授權級別
*/
private Integer authLevel ;
/**
* 子類型: DashboardSubTypeEnum
*/
private Integer subType;
/**
* 描述
*/
private String description;
/**
* 是否分享給所有工作空間成員
*/
private Integer shareToWorkspace;
}
UserInfoParameter是其中的用戶信息類,有如下屬性:
public class UserInfoParameter {
/**
* email
*/
private String userEmail;
/**
* phone
*/
private String userPhone;
/**
* 昵稱
*/
private String nickName;
/**
* 用戶名 登錄名
*/
private String userName;
/**
* 用于前端顯示
*/
private String displayName;
/**
* baseId
*/
private String baseId;
/**
* 平臺通信標識
*/
private String userId;
/**
* 主子賬號類型
*/
private Integer accountType;
/**
* 若加入了組織 須 設置組織id
*/
private String organizationId;
/**
* 賬號是否被刪除。
*/
private Boolean isDeleted;
}
CustomContext:
他包含了設置的上下文參數,也就是您在開放平臺-自定義插件中設置的上下文依賴信息(json格式設置),您可以使用context.getValue("xxx")取到這些信息進行更加靈活的配置。
將參數進行處理后,您需要return true或者false,為true的話可以正常訪問,為false的話會提示用戶“您無當前報表的訪問權限,請申請權限”。
這就是一個完整的報表訪問插件開發(fā)過程。
登錄插件管理
實現步驟說明
這是一個登錄插件開發(fā)的簡單例子,以企業(yè)擁有自定義的登錄流程為例。
企業(yè)使用登錄插件時需要先重定向至身份認證服務器地址,后續(xù)通過企業(yè)身份認證服務器回調Quick BI側的插件登錄回調接口([Quick BI域名]/login/plugin/verify)傳遞認證成功的用戶信息,最后在登出時需要重定向至指定地址。針對這種場景,登錄管理插件的核心代碼實現如下:
public class ExampleLoginCustomHandler extends AbstractLoginCustomHandler {
public static final String AUTH_HOST = "http://example.auth.login.com";
public static final String REDIRECT_HOST = "http://example.reidrect.com";
private static final Logger _log = LoggerFactory.getLogger("thirdPlugin");
@Override
public String getAuthServerUrl(HttpServletRequest request) {
//您域名下的身份認證服務器地址
return AUTH_HOST;
}
@Override
public PluginUserAccount login(HttpServletRequest request, CustomContext customContext) throws PluginLoginRedirectException, PluginCustomException{
//獲取您身份認證服務器回調傳遞的用戶身份信息
String accountName = Optional.ofNullable(request.getParameter("accountName")).orElse("插件用戶名");
String accountId = Optional.ofNullable(request.getParameter("accountId")).orElse("123456");
String nick = Optional.ofNullable(request.getParameter("nick")).orElse("插件用戶昵稱");
_log.info("[custom login] query user info with: account name = {}", accountName);
PluginUserAccount userAccount = new PluginUserAccount();
//設置用戶信息
userAccount.setAccoutId(accountId);
userAccount.setAccountName(accountName);
userAccount.setNick(accountName);
return userAccount;
}
@Override
public void logout(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, CustomContext customContext) throws PluginLoginRedirectException {
//用戶登出操作
_log.info("執(zhí)行用戶態(tài)登出操作");
throw new PluginLoginRedirectException(null, REDIRECT_HOST);
}
}
當用戶使用插件登錄時,Quick BI會自動讀取插件內部登錄方法傳來的用戶身份信息并作為賬戶登錄Quick BI。在具體實現插件時,需要關注以下細節(jié):
首先,需要繼承AbstractLoginCustomHandler抽象類,對其中的getAuthServerUrl、login、logout方法重寫,可以通過類的常量PLUGIN_VERIFY_URI獲取插件登錄的回調URI。
1.getAuthServerUrl方法
入參有一個參數HttpServletRequest:它提供了一系列的方法,以便在Servlet中獲取請求的信息和處理請求的參數。
用戶需要在getAuthServerUrl方法中指定想要跳轉的身份認證服務地址,該地址會在Quick BI登錄頁選擇插件登錄時進行跳轉。
2.login方法
入參中有兩個參數,分別為:
HttpServletRequest:用來處理HTTP請求,它提供了一系列的方法,以便在Servlet中獲取請求的信息和處理請求的參數。
CustomContext :這個參數類封裝了用戶在開放平臺-自定義插件設置時定義的上下文參數,這些參數以JSON格式存在。用戶可以使用 context.getValue("xxx") 方法獲取這些設置,從而實現更為靈活和定制化的配置。
返回參數是PluginUserAccount,它是返回的用戶信息類,包含如下屬性:
public class PluginUserAccount {
/**
* 用戶唯一id
*/
private String accoutId;
/**
* 用戶名
*/
private String accountName;
/**
* 用戶昵稱
*/
private String nick;
/**
* 用戶其他信息
*/
private Map<String, String> extInfo;
}
3.logout方法
入參中有三個參數,分別為:
HttpServletRequest:用來處理HTTP請求,它提供了一系列的方法,以便在Servlet中獲取請求的信息和處理請求的參數。
HttpServletResponse:描述HTTP響應消息的對象,主要用于在服務器端處理 HTTP 響應。
CustomContext:這個參數類封裝了用戶在開放平臺-自定義插件設置時定義的上下文參數,這些參數以JSON格式存在。用戶可以使用 context.getValue("xxx") 方法獲取這些設置,從而實現更為靈活和定制化的配置。
異常和重定向
可以通過實現BasicError接口定義一些自定義登錄錯誤枚舉配合插件重定向異常PluginLoginRedirectException進行拋出,拋出的異常會在Quick BI登錄層進行捕獲跳轉。
實現BasicError接口的代碼例子:
public enum PluginLoginError implements BasicError {
/**
* 登錄校驗異常
*/
LOGIN_ACCOUNT_PASSWORD_ERROR("PLUGIN_002001", "賬號或密碼錯誤"),
LOGIN_ACCOUNT_NOT_EXIST("PLUGIN_002002", "賬號不存在"),
LOGIN_CIPHER_ERROR("PLUGIN_002003", "驗證碼錯誤"),;
private final String code;
private final String message;
PluginLoginError(String code, String message) {
this.code = code;
this.message = message;
}
@Override
public String getCode() {
return code;
}
@Override
public String getMessage() {
return message;
}
@Override
public String getMessage(Locale locale) {
return null;
}
@Override
public Class<? extends BasicException> getExceptionClass() {
return null;
}
@Override
public Class<? extends BasicThrowException> getThrowExceptionClass() {
return null;
}
}
通過拋出PluginLoginRedirectException可以讓Quick BI重定向到指定的地址,具體代碼如下:
public class PluginLoginRedirectException extends BasicException {
private String redirectUrl;
public PluginLoginRedirectException(BasicError error) {
super((BasicError)(null == error ? PluginAuthxError.REDIRECT_URL_DEBUG : error));
}
public PluginLoginRedirectException(BasicError error, String redirectUrl) {
super((BasicError)(null == error ? PluginAuthxError.REDIRECT_URL_DEBUG : error));
this.redirectUrl = redirectUrl;
}
public String getRedirectUrl() {
return this.redirectUrl;
}
}