Groovy代码示例
/**
* @author 管理员
* @codeName OnlineDocWpsPersonal
* @description 新建
* @createTime 2024-08-05
*/
class OnlineDocCustom implements OnlineDocPersonalPlugin {
private static String CONTENT_TYPE = "application/json;charset=utf-8"
//厂商域名
private static String HOST = "https://{host}"
//用户授权url
private static String AUTH_CODE_URL = "/{auth_user_path}"
//获取token url
private static String QUERY_ACCESS_TOKEN_URL = "/{query_token_path}?"
//刷新token url
private static String REFRESH_ACCESS_TOKEN_URL = "/{refresh_token_path}?"
//获取文件列表url
private static String QUERY_FILE_LIST_URL = "/{query_files_path}?"
//授权回调地址后缀
private static String CALLBACK_URL_SUFFIX = "/custom/authRedirect"
//保存token信息到本地缓存,wps默认24小时
private void saveAccessToken2Cache(String tenantId, String userId, String pluginApiName, Map<String, Object> runtimeData, int expiresSecond) {
Map tokenMap = [:]
tokenMap.putAll(runtimeData)
tokenMap.put("token_expires_millis", DateTime.now().toTimestamp() + (expiresSecond - 30) * 1000)
String key = "OnlineDocCustomAccess_token_" + userId + "_" + pluginApiName
Cache cache = Fx.cache.getDefaultCache()
String value = Fx.json.toJson(tokenMap)
cache.put(key, value, expiresSecond)
log.info("saveAccessToken2Cache key:" + key)
}
//获取token信息从本地缓存中
private String getAccessTokenFromCache(String tenantId, String userId, String pluginApiName) {
String key = "OnlineDocCustomAccess_token_" + userId + "_" + pluginApiName
Cache cache = Fx.cache.getDefaultCache()
String value = cache.get(key) as String
if (value == null) {
log.info("getAccessTokenFromCache key:" + key + " value:null")
return null
}
Map tokenMap = Fx.json.parse(value)
String token = tokenMap["access_token"] as String
Long expired = tokenMap["token_expires_millis"] as Long
if (expired <= DateTime.now().toTimestamp()) {
log.info("getAccessTokenFromCache key:" + key + " value:expired")
return null
}
log.info("getAccessTokenFromCache key:" + key)
return token
}
//获取runtimeData
private Map getOrRefreshAccessToken(String pluginApiName, Map devInfo, Map authRuntimeData, FunctionContext context) {
String accessTokenInCache = getAccessTokenFromCache(context.getTenantId(), context.getUserId(), pluginApiName)
if (accessTokenInCache != null) {
Map<String, Object> refreshMap = [:]
refreshMap.put("errorCode", 0)
refreshMap.put("accessToken", accessTokenInCache)
return refreshMap
}
//刷新accessToken
Map<String, Object> refreshMap = refreshAccessToken(pluginApiName, devInfo, authRuntimeData, context)
return refreshMap
}
//构建header
private Map buildHeader(String appId, String appKey, String uri, Map body) {
//
Map header = [:]
header["Content-Type"] = CONTENT_TYPE
//...
return header
}
//企业认证 暂时未用到
public EnterpriseAuth.Result enterpriseAuth(EnterpriseAuth.Arg arg, FunctionContext context) {
log.info("enterpriseAuth arg:" + arg)
String appId = arg.getDevInfo().get("appId") as String
String appKey = arg.getDevInfo().get("appKey") as String
String pluginApiName = arg.getPluginApiName()
//获取company token
Map tokenMap = queryCompanyToken(pluginApiName, appId, appKey)
String companyToken = tokenMap["companyToken"] as String
int errorCode = tokenMap["errorCode"] as Integer
String errorMessage = tokenMap["errorMessage"] as String
if (errorCode != 0) {
Fx.message.throwException(errorMessage)
}
EnterpriseAuth.Result result = new EnterpriseAuth.Result()
//result.setErrorCode(0)
//result.setErrorMessage(errorMessage)
log.info("enterpriseAuth result:" + result)
return result
}
//刷新access token
private Map<String, Object> refreshAccessToken(String pluginApiName, Map<String, String> devInfo, Map<String, Object> authRuntimeData, FunctionContext context) {
log.info("refreshAccessToken arg:" + pluginApiName)
Map authMap = authRuntimeData
if (authMap.isEmpty()) {
//从未授权,模拟金山错误码,下发授权url
Map<String, Object> resultMap = [:]
resultMap.put("errorCode", 100050)
resultMap.put("errorMessage", "never to auth")
log.info("refreshAccessToken failed resultMap:" + resultMap)
return resultMap
}
String appId = devInfo.get("appId")
String appKey = devInfo.get("appKey")
String refreshToken = authMap.get("refresh_token")
String queryRefreshTokenUrl = HOST + REFRESH_ACCESS_TOKEN_URL + "appid=" + appId + "&appkey=" + appKey + "&refresh_token=" + refreshToken
Map headers = ["Content-Type": CONTENT_TYPE]
Map requestBody = [:]
log.info("refreshAccessToken request begin:" + queryRefreshTokenUrl)
def (Boolean error, HttpResult httpResult, String errorMessage) = Fx.http.post(queryRefreshTokenUrl, headers, requestBody, 10000, false, 0)
log.info("refreshAccessToken request finish:" + httpResult)
//检查请求错误
if (error || httpResult == null) {
log.info("refreshAccessToken request null error :" + errorMessage)
Map<String, Object> resultMap = [:]
resultMap.put("errorCode", -1)
resultMap.put("errorMessage", "request error:" + errorMessage)
return resultMap
}
Map contentMap = null
if (httpResult.statusCode == 200) {
contentMap = httpResult.content as Map
} else {
contentMap = Fx.json.parse(httpResult.content as String)
}
Integer bizErrorCode = contentMap.get("result") as Integer
String bizErrorMessage = contentMap.get("msg") as String
//检查请求业务错误
if (bizErrorCode != 0) {
Map<String, Object> resultMap = [:]
resultMap.put("errorCode", bizErrorCode)
resultMap.put("errorMessage", bizErrorMessage)
log.info("refreshAccessToken request biz failed error:" + contentMap)
return resultMap
}
//补充过期时间戳
Map tokenMap = contentMap["token"] as Map
String accessToken = tokenMap["access_token"] as String
Integer expiresSecond = tokenMap["expires_in"] as Integer
//保存到本地缓存
saveAccessToken2Cache(context.getTenantId(), context.getUserId(), pluginApiName, tokenMap, expiresSecond)
//返回结果
Map<String, Object> resultMap = [:]
resultMap.put("errorCode", 0)
resultMap.put("accessToken", accessToken)
log.info("refreshAccessToken finish success:" + resultMap.size())
return resultMap
}
//请求access token
private Map<String, Object> queryAccessToken(String pluginApiName,
String appId,
String appKey,
String code,
FunctionContext context) {
String queryTokenUrl = HOST + QUERY_ACCESS_TOKEN_URL + "appid=" + appId + "&appkey=" + appKey + "&code=" + code
Map headers = ["Content-Type": CONTENT_TYPE]
log.info("queryAccessToken request begin:" + queryTokenUrl)
def (Boolean error, HttpResult httpResult, String errorMessage) = Fx.http.get(queryTokenUrl, headers, 10000, false, 0)
log.info("queryAccessToken request finish:" + httpResult)
//检查请求错误
if (error || httpResult == null) {
log.info("queryAccessToken request null error :" + errorMessage)
Map resultMap = [:]
resultMap["errorCode"] = -1
resultMap["errorMessage"] = "request error:" + errorMessage
return resultMap
}
Map contentMap = null
if (httpResult.statusCode == 200) {
contentMap = httpResult.content as Map
} else {
contentMap = Fx.json.parse(httpResult.content as String)
}
Integer bizErrorCode = contentMap.get("result") as Integer
String bizErrorMessage = contentMap.get("msg") as String
//检查请求业务错误
if (bizErrorCode != 0) {
log.info("queryAccessToken request biz failed error:" + contentMap)
Map resultMap = [:]
resultMap["errorCode"] = bizErrorCode
resultMap["errorMessage"] = bizErrorMessage
return resultMap
}
//补充过期时间戳
Map tokenMap = contentMap["token"] as Map
Integer expiresSecond = tokenMap["expires_in"] as Integer
//保存到本地缓存
saveAccessToken2Cache(context.getTenantId(), context.getUserId(), pluginApiName, tokenMap, expiresSecond)
//返回结果
Map resultMap = [:]
resultMap["errorCode"] = 0
resultMap["runtimeData"] = tokenMap
resultMap["expiredTime"] = DateTime.now().toTimestamp() + 90 * 24 * 3600 * 1000L
return resultMap
}
//获取个人授权url
public GetPersonalAuthUrl.Result getPersonalAuthUrl(GetPersonalAuthUrl.Arg arg, FunctionContext context) {
//标准日志,请勿删除
log.info("getPersonalAuthUrl arg:" + arg)
/*此处开始加业务*/
String appId = arg.getDevInfo().get("appId")
String callbackUrl = arg.getDevInfo().get("callbackUrl") + CALLBACK_URL_SUFFIX
//拼装纷享业务参数
Map<String, String> authMap = [:]
authMap.put("pluginApiName", arg.getPluginApiName())
authMap.put("enterpriseId", context.getTenantId())
authMap.put("employeeId", context.getUserId())
//拼接如state中
String state = Fx.crypto.base64.encode(Fx.json.toJson(authMap) as byte[])
String authUrl = HOST + AUTH_CODE_URL + "&appid=" + appId + "&redirect_uri=" + callbackUrl + "&state=" + state
GetPersonalAuthUrl.Result result = new GetPersonalAuthUrl.Result()
result.setErrorCode(0)
result.setAuthUrl(authUrl)
//标准日志,请勿删除
log.info("getPersonalAuthUrl result:" + result)
return result
}
//处理callback回来的数据
public ActionCallback.Result actionCallback(ActionCallback.Arg arg, FunctionContext context) {
//标准日志,请勿删除
log.info("actionCallback arg:" + arg)
/*此处开始加业务*/
String pluginApiName = arg.getPluginApiName()
String code = arg.getCode()
String appId = arg.getDevInfo().get("appId")
String appKey = arg.getDevInfo().get("appKey")
//获取用户授权token
Map accessTokenResultMap = queryAccessToken(pluginApiName, appId, appKey, code, context)
Integer errorCode = accessTokenResultMap["errorCode"] as Integer
String errorMessage = accessTokenResultMap["errorMessage"] as String
if (errorCode != 0) {
ActionCallback.Result result = new ActionCallback.Result()
result.setErrorCode(errorCode)
result.setErrorMessage(errorMessage)
return result
}
ActionCallback.Result result = new ActionCallback.Result()
result.setErrorCode(errorCode)
result.setRuntimeData(accessTokenResultMap["runtimeData"] as Map)
result.setExpiredTime(accessTokenResultMap["expiredTime"] as Long)
//标准日志,请勿删除
log.info("actionCallback result:" + result)
return result
}
//请求文件列表
public QueryFileList.Result queryFileList(QueryFileList.Arg arg, FunctionContext context) {
//标准日志,请勿删除
log.info("queryFileList arg:" + arg)
String pluginApiName = arg.getPluginApiName()
String appId = arg.getDevInfo().get("appId") as String
String appKey = arg.getDevInfo().get("appKey") as String
Map authRuntimeData = arg.getAuthRuntimeData()
Map pluginRuntimeData = arg.getPluginRuntimeData()
Integer errorCode = 0
String errorMessage = null
//获取or更新accessToken
Map accessTokenMap = getOrRefreshAccessToken(pluginApiName, arg.getDevInfo(), authRuntimeData, context)
errorCode = accessTokenMap["errorCode"] as Integer
errorMessage = accessTokenMap["errorMessage"] as String
String accessToken = accessTokenMap["accessToken"] as String
if (errorCode != 0) {
//返回失败结果
return buildQueryFileListFailedResult(arg.getPluginApiName(), errorCode, errorMessage, arg.getDevInfo(), context)
}
//查询文件
String parentId = null
Integer offset = extraInfo.getOrDefault("next_offset", 0) as Integer
Integer pageNumber = extraInfo.getOrDefault("pageNumber", 0) as Integer
if (arg.isNeedNextPage()) {
pageNumber += 1
}
//进入选择的文件夹
if (arg.getFolder() != null) {
parentId = arg.getFolder().getFileId()
}
//请求文件列表清单
Map fileListMap = queryFiles(arg.getPluginApiName(), appId, appKey, companyToken, accessToken, parentId, offset, pageNumber)
errorCode = fileListMap.get("errorCode") as Integer
errorMessage = fileListMap.get("errorMessage") as String
List<Map> fileList = fileListMap.get("fileList") as List
extraInfo["next_offset"] = fileListMap["next_offset"] as Integer
extraInfo["pageNumber"] = pageNumber
if (errorCode != 0) {
//返回失败结果
return buildQueryFileListFailedResult(arg.getPluginApiName(), errorCode, errorMessage, arg.getDevInfo(), context)
}
//返回结果
QueryFileList.Result result = new QueryFileList.Result()
result.setErrorCode(errorCode)
result.setErrorMessage(errorMessage)
result.setFileList(fileList)
result.setHasMore(fileListMap["hasMore"] as boolean)
result.setExtraInfo(extraInfo)
//标准日志,请勿删除
log.info("queryFileList result:" + result)
return result
}
private QueryFileList.Result buildQueryFileListFailedResult(String pluginApiName,
int errorCode,
String errorMessage,
Map devInfo,
FunctionContext context) {
//以下错误需要用户重新授权
if (errorCode == 1234567890) {
//检查是否需要授权
GetPersonalAuthUrl.Arg authUrlArg = new GetPersonalAuthUrl.Arg()
authUrlArg.setPluginApiName(pluginApiName)
authUrlArg.setDevInfo(devInfo)
GetPersonalAuthUrl.Result authUrlResult = getPersonalAuthUrl(authUrlArg, context)
//
QueryFileList.Result result = new QueryFileList.Result()
result.setErrorCode(errorCode)
result.setErrorMessage(errorMessage)
result.setAuthUrl(authUrlResult.getAuthUrl())
return result
}
Fx.message.throwException(errorMessage)
}
//查询文件列表 pageNumber从0开始
private Map<String, Object> queryFiles(String pluginApiName, String appId, String appKey,
String companyToken, String accessToken,
String parentId, int offset, int pageNumber) {
int pageSize = 20
int tmpOffset = pageNumber > 0 ? pageNumber * pageSize + 1 : 0
String bizUrl = QUERY_FILE_LIST_URL
bizUrl += "access_token=" + accessToken + "&company_token=" + companyToken + "&offset=" + tmpOffset + "&count=" + pageSize
if (parentId != null) {
bizUrl += "&parent_id=" + parentId
}
String url = HOST + bizUrl
Map headers = buildHeader(appId, appKey, bizUrl, null)
log.info("queryFiles request begin:" + url)
def (Boolean error, HttpResult httpResult, String errorMessage) = Fx.http.get(url, headers, 10000, false, 0)
log.info("queryFiles request finish:" + httpResult)
//检查请求错误
if (error || httpResult == null) {
log.info("queryFiles request null error :" + errorMessage)
Map<String, Object> resultMap = [:]
resultMap.put("errorCode", -1)
resultMap.put("errorMessage", "request error:" + errorMessage)
return resultMap
}
Map contentMap = null
if (httpResult.statusCode == 200) {
contentMap = httpResult.content as Map
} else {
contentMap = Fx.json.parse(httpResult.content as String)
}
Integer bizErrorCode = contentMap.get("result") as Integer
String bizErrorMessage = contentMap.get("msg") as String
//检查请求业务错误
if (bizErrorCode != 0) {
log.info("queryFiles request biz failed error:" + contentMap)
Map<String, Object> resultMap = [:]
resultMap.put("errorCode", bizErrorCode)
resultMap.put("errorMessage", bizErrorMessage)
return resultMap
}
List<Map> fileList = contentMap["files"] as List
int nextOffset = contentMap["next_offset"] as int
Map<String, Object> resultMap = [:]
resultMap.put("errorCode", 0)
resultMap.put("fileList", convert2FileInfo(pluginApiName, fileList))
resultMap.put("hasMore", nextOffset > 0)
resultMap.put("next_offset", nextOffset)
return resultMap
}
//转换成规范结构
private List<Map> convert2FileInfo(String pluginApiName, List<Map> fileList) {
List<Map> fileInfoList = []
fileList.each() { item ->
Map fileInfo = [:]
fileInfo.put("fileId", item.get("xx_file_id"))
fileInfo.put("fileName", item.get("xx_file_name"))
fileInfo.put("fileExt", findFileExtension(item.get("xx_file_name") as String))
fileInfo.put("fileType", convertFileType(item.get("xx_file_type") as String))
fileInfo.put("fileSize", item.get("xx_file_size"))
fileInfo.put("createTime", 1000 * (item.get("xx_ctime") as long))
fileInfo.put("updateTime", 1000 * (item.get("xx_mtime") as long))
fileInfo.put("pluginApiName", pluginApiName)
fileInfoList.add(fileInfo)
}
return fileInfoList
}
private String findFileExtension(String fileName) {
def parts = fileName.split("\\.")
if (parts.size() > 1) {
return parts[parts.size() - 1]
}
return null
}
//检查文件类型,只支持文件夹、文件、未知
private String convertFileType(String fileType) {
switch (fileType) {
case "file":
case "sharefile":
return "file"
case "folder":
case "linkfolder":
return "folder"
default:
return "unknown"
}
}
static void main(String[] args) {
}
}