diff --git a/rec-admin/rec-admin-biz/src/main/java/cn/icanci/rec/admin/biz/spi/AbstractLoadSPI.java b/rec-admin/rec-admin-biz/src/main/java/cn/icanci/rec/admin/biz/spi/AbstractLoadSPI.java index d74936cd7b28e51bf41c8acc527251172975731c..8c89d03901d52861e5ebd910fa958b67da48cbe7 100644 --- a/rec-admin/rec-admin-biz/src/main/java/cn/icanci/rec/admin/biz/spi/AbstractLoadSPI.java +++ b/rec-admin/rec-admin-biz/src/main/java/cn/icanci/rec/admin/biz/spi/AbstractLoadSPI.java @@ -9,10 +9,5 @@ import cn.icanci.rec.engine.sdk.extensions.SpringBean; */ @SpringBean({ WebApiService.class }) public abstract class AbstractLoadSPI { - /** - * TODO 待测试 staticWebApiService - */ - protected static WebApiService staticWebApiService; - - protected WebApiService webApiService; + protected WebApiService webApiService; } diff --git a/rec-admin/rec-admin-dal/src/main/java/cn/icanci/rec/admin/dal/mongodb/daointerface/BaseDataDAO.java b/rec-admin/rec-admin-dal/src/main/java/cn/icanci/rec/admin/dal/mongodb/daointerface/BaseDataDAO.java index e8064a588e89bead348528f3fc4c11fdd9bb40bd..5654bd8d29b8a8a0aee4eee86518bf63e6dac849 100644 --- a/rec-admin/rec-admin-dal/src/main/java/cn/icanci/rec/admin/dal/mongodb/daointerface/BaseDataDAO.java +++ b/rec-admin/rec-admin-dal/src/main/java/cn/icanci/rec/admin/dal/mongodb/daointerface/BaseDataDAO.java @@ -44,7 +44,6 @@ public interface BaseDataDAO extends BaseDAO { */ String scriptContent = "scriptContent"; /** - * TODO 是否有必要 * 脚本执行返回类型(只能是基本数据类型) */ @Deprecated diff --git a/rec-admin/rec-admin-dal/src/main/java/cn/icanci/rec/admin/dal/mongodb/dateobject/BaseDataDO.java b/rec-admin/rec-admin-dal/src/main/java/cn/icanci/rec/admin/dal/mongodb/dateobject/BaseDataDO.java index 370ee2a768e9da33c480e1331392daf72e7d0ab2..14716579a85f21acd0e251ce71451aeec09f185b 100644 --- a/rec-admin/rec-admin-dal/src/main/java/cn/icanci/rec/admin/dal/mongodb/dateobject/BaseDataDO.java +++ b/rec-admin/rec-admin-dal/src/main/java/cn/icanci/rec/admin/dal/mongodb/dateobject/BaseDataDO.java @@ -40,12 +40,10 @@ public class BaseDataDO extends BaseDO { */ private String scriptContent; /** - * TODO 是否有必要 * 脚本执行返回类型(只能是基本数据类型) * * @see ResultTypeEnum#name() */ - @Deprecated private String resultType; public String getDomainCode() { diff --git a/rec-admin/rec-admin-dal/src/main/java/cn/icanci/rec/admin/dal/mongodb/mongo/MongoBaseDataDAO.java b/rec-admin/rec-admin-dal/src/main/java/cn/icanci/rec/admin/dal/mongodb/mongo/MongoBaseDataDAO.java index ba41358081c748e0c638cfb0a003d95bba602e7e..61132ca2839ef683a7c6bd9da30fda07655ad84e 100644 --- a/rec-admin/rec-admin-dal/src/main/java/cn/icanci/rec/admin/dal/mongodb/mongo/MongoBaseDataDAO.java +++ b/rec-admin/rec-admin-dal/src/main/java/cn/icanci/rec/admin/dal/mongodb/mongo/MongoBaseDataDAO.java @@ -43,7 +43,6 @@ public class MongoBaseDataDAO extends AbstractBaseDAO implements Bas public PageList pageQuery(BaseDataDO baseDataDO, int pageNum, int pageSize) { Criteria criteria = Criteria.where(BaseDataColumn.env).is(DEFAULT_ENV); - // TODO 查询参数与页面确认 if (StringUtils.isNotBlank(baseDataDO.getDomainCode())) { // 不分区大小写查询,其中操作符"i":表示不分区大小写 criteria.and(BaseDataColumn.domainCode).regex("^.*" + baseDataDO.getDomainCode() + ".*$", "i"); diff --git a/rec-admin/rec-admin-views/src/main/resources/vueboot/config/dev.env.js b/rec-admin/rec-admin-views/src/main/resources/vueboot/config/dev.env.js index 94c199ea07365731b6a5dbb2280e3ecec3e5706e..f8bd8b8130b938e0e9890270b92283ffaf3bb070 100644 --- a/rec-admin/rec-admin-views/src/main/resources/vueboot/config/dev.env.js +++ b/rec-admin/rec-admin-views/src/main/resources/vueboot/config/dev.env.js @@ -4,6 +4,5 @@ const prodEnv = require('./prod.env') module.exports = merge(prodEnv, { NODE_ENV: '"development"', - // TODO 更新为后端接口 ,统一网关的接口 BASE_API: '"http://localhost:9999"', }) diff --git a/rec-admin/rec-admin-web/src/main/java/cn/icanci/rec/admin/web/model/BaseData.java b/rec-admin/rec-admin-web/src/main/java/cn/icanci/rec/admin/web/model/BaseData.java index 0f10c32edbe19f223e155009f2b62ee3c42b6cee..b8c21ee55da32473c31812111c7ad6dcede4ba53 100644 --- a/rec-admin/rec-admin-web/src/main/java/cn/icanci/rec/admin/web/model/BaseData.java +++ b/rec-admin/rec-admin-web/src/main/java/cn/icanci/rec/admin/web/model/BaseData.java @@ -41,12 +41,10 @@ public class BaseData extends Base { */ private String scriptContent; /** - * TODO 是否有必要 * 脚本执行返回类型(只能是基本数据类型) * * @see ResultTypeEnum#name() */ - @Deprecated private String resultType; public String getDomainCode() { diff --git a/rec-common/src/main/java/cn/icanci/rec/common/aggregation/model/BaseDataDTO.java b/rec-common/src/main/java/cn/icanci/rec/common/aggregation/model/BaseDataDTO.java index fe0173fd690b3726df7bab875faf298c752c8fd2..0a53cbde24dde80d7857abf14cfd8f418237aa91 100644 --- a/rec-common/src/main/java/cn/icanci/rec/common/aggregation/model/BaseDataDTO.java +++ b/rec-common/src/main/java/cn/icanci/rec/common/aggregation/model/BaseDataDTO.java @@ -43,12 +43,10 @@ public class BaseDataDTO extends BaseDTO { */ private String scriptContent; /** - * TODO 是否有必要 * 脚本执行返回类型(只能是基本数据类型) * * @see ResultTypeEnum#name() */ - @Deprecated private String resultType; /** diff --git a/rec-common/src/main/java/cn/icanci/rec/common/utils/FastBase64.java b/rec-common/src/main/java/cn/icanci/rec/common/utils/FastBase64.java new file mode 100644 index 0000000000000000000000000000000000000000..132b8ad51456c52421accdde21f9475bcdd19043 --- /dev/null +++ b/rec-common/src/main/java/cn/icanci/rec/common/utils/FastBase64.java @@ -0,0 +1,522 @@ +package cn.icanci.rec.common.utils; + +import java.util.Arrays; + +/** + * @author icanci + * @since 1.0 Created in 2022/11/20 15:36 + */ +public class FastBase64 { + private static final char[] CA = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".toCharArray(); + private static final int[] IA = new int[256]; + + static { + Arrays.fill(IA, -1); + for (int i = 0, iS = CA.length; i < iS; i++) + IA[CA[i]] = i; + IA['='] = 0; + } + + // **************************************************************************************** + // * char[] version + // **************************************************************************************** + + /** Encodes a raw byte array into a BASE64 char[] representation i accordance with RFC 2045. + * @param sArr The bytes to convert. If null or length 0 an empty array will be returned. + * @param lineSep Optional "\r\n" after 76 characters, unless end of file.
+ * No line separator will be in breach of RFC 2045 which specifies max 76 per line but will be a + * little faster. + * @return A BASE64 encoded array. Never null. + */ + public final static char[] encodeToChar(byte[] sArr, boolean lineSep) { + // Check special case + int sLen = sArr != null ? sArr.length : 0; + if (sLen == 0) + return new char[0]; + + int eLen = (sLen / 3) * 3; // Length of even 24-bits. + int cCnt = ((sLen - 1) / 3 + 1) << 2; // Returned character count + int dLen = cCnt + (lineSep ? (cCnt - 1) / 76 << 1 : 0); // Length of returned array + char[] dArr = new char[dLen]; + + // Encode even 24-bits + for (int s = 0, d = 0, cc = 0; s < eLen;) { + // Copy next three bytes into lower 24 bits of int, paying attension to sign. + int i = (sArr[s++] & 0xff) << 16 | (sArr[s++] & 0xff) << 8 | (sArr[s++] & 0xff); + + // Encode the int into four chars + dArr[d++] = CA[(i >>> 18) & 0x3f]; + dArr[d++] = CA[(i >>> 12) & 0x3f]; + dArr[d++] = CA[(i >>> 6) & 0x3f]; + dArr[d++] = CA[i & 0x3f]; + + // Add optional line separator + if (lineSep && ++cc == 19 && d < dLen - 2) { + dArr[d++] = '\r'; + dArr[d++] = '\n'; + cc = 0; + } + } + + // Pad and encode last bits if source isn't even 24 bits. + int left = sLen - eLen; // 0 - 2. + if (left > 0) { + // Prepare the int + int i = ((sArr[eLen] & 0xff) << 10) | (left == 2 ? ((sArr[sLen - 1] & 0xff) << 2) : 0); + + // Set last four chars + dArr[dLen - 4] = CA[i >> 12]; + dArr[dLen - 3] = CA[(i >>> 6) & 0x3f]; + dArr[dLen - 2] = left == 2 ? CA[i & 0x3f] : '='; + dArr[dLen - 1] = '='; + } + return dArr; + } + + /** Decodes a BASE64 encoded char array. All illegal characters will be ignored and can handle both arrays with + * and without line separators. + * @param sArr The source array. null or length 0 will return an empty array. + * @return The decoded array of bytes. May be of length 0. Will be null if the legal characters + * (including '=') isn't divideable by 4. (I.e. definitely corrupted). + */ + public final static byte[] decode(char[] sArr) { + // Check special case + int sLen = sArr != null ? sArr.length : 0; + if (sLen == 0) + return new byte[0]; + + // Count illegal characters (including '\r', '\n') to know what size the returned array will be, + // so we don't have to reallocate & copy it later. + int sepCnt = 0; // Number of separator characters. (Actually illegal characters, but that's a bonus...) + for (int i = 0; i < sLen; i++) // If input is "pure" (I.e. no line separators or illegal chars) base64 this loop can be commented out. + if (IA[sArr[i]] < 0) + sepCnt++; + + // Check so that legal chars (including '=') are evenly divideable by 4 as specified in RFC 2045. + if ((sLen - sepCnt) % 4 != 0) + return null; + + int pad = 0; + for (int i = sLen; i > 1 && IA[sArr[--i]] <= 0;) + if (sArr[i] == '=') + pad++; + + int len = ((sLen - sepCnt) * 6 >> 3) - pad; + + byte[] dArr = new byte[len]; // Preallocate byte[] of exact length + + for (int s = 0, d = 0; d < len;) { + // Assemble three bytes into an int from four "valid" characters. + int i = 0; + for (int j = 0; j < 4; j++) { // j only increased if a valid char was found. + int c = IA[sArr[s++]]; + if (c >= 0) + i |= c << (18 - j * 6); + else + j--; + } + // Add the bytes + dArr[d++] = (byte) (i >> 16); + if (d < len) { + dArr[d++] = (byte) (i >> 8); + if (d < len) + dArr[d++] = (byte) i; + } + } + return dArr; + } + + /** Decodes a BASE64 encoded char array that is known to be resonably well formatted. The method is about twice as + * fast as {@link #decode(char[])}. The preconditions are:
+ * + The array must have a line length of 76 chars OR no line separators at all (one line).
+ * + Line separator must be "\r\n", as specified in RFC 2045 + * + The array must not contain illegal characters within the encoded string
+ * + The array CAN have illegal characters at the beginning and end, those will be dealt with appropriately.
+ * @param sArr The source array. Length 0 will return an empty array. null will throw an exception. + * @return The decoded array of bytes. May be of length 0. + */ + public final static byte[] decodeFast(char[] sArr) { + // Check special case + int sLen = sArr.length; + if (sLen == 0) + return new byte[0]; + + int sIx = 0, eIx = sLen - 1; // Start and end index after trimming. + + // Trim illegal chars from start + while (sIx < eIx && IA[sArr[sIx]] < 0) + sIx++; + + // Trim illegal chars from end + while (eIx > 0 && IA[sArr[eIx]] < 0) + eIx--; + + // get the padding count (=) (0, 1 or 2) + int pad = sArr[eIx] == '=' ? (sArr[eIx - 1] == '=' ? 2 : 1) : 0; // Count '=' at end. + int cCnt = eIx - sIx + 1; // Content count including possible separators + int sepCnt = sLen > 76 ? (sArr[76] == '\r' ? cCnt / 78 : 0) << 1 : 0; + + int len = ((cCnt - sepCnt) * 6 >> 3) - pad; // The number of decoded bytes + byte[] dArr = new byte[len]; // Preallocate byte[] of exact length + + // Decode all but the last 0 - 2 bytes. + int d = 0; + for (int cc = 0, eLen = (len / 3) * 3; d < eLen;) { + // Assemble three bytes into an int from four "valid" characters. + int i = IA[sArr[sIx++]] << 18 | IA[sArr[sIx++]] << 12 | IA[sArr[sIx++]] << 6 | IA[sArr[sIx++]]; + + // Add the bytes + dArr[d++] = (byte) (i >> 16); + dArr[d++] = (byte) (i >> 8); + dArr[d++] = (byte) i; + + // If line separator, jump over it. + if (sepCnt > 0 && ++cc == 19) { + sIx += 2; + cc = 0; + } + } + + if (d < len) { + // Decode last 1-3 bytes (incl '=') into 1-3 bytes + int i = 0; + for (int j = 0; sIx <= eIx - pad; j++) + i |= IA[sArr[sIx++]] << (18 - j * 6); + + for (int r = 16; d < len; r -= 8) + dArr[d++] = (byte) (i >> r); + } + + return dArr; + } + + // **************************************************************************************** + // * byte[] version + // **************************************************************************************** + + /** Encodes a raw byte array into a BASE64 byte[] representation i accordance with RFC 2045. + * @param sArr The bytes to convert. If null or length 0 an empty array will be returned. + * @param lineSep Optional "\r\n" after 76 characters, unless end of file.
+ * No line separator will be in breach of RFC 2045 which specifies max 76 per line but will be a + * little faster. + * @return A BASE64 encoded array. Never null. + */ + public final static byte[] encodeToByte(byte[] sArr, boolean lineSep) { + return encodeToByte(sArr, 0, sArr != null ? sArr.length : 0, lineSep); + } + + /** Encodes a raw byte array into a BASE64 byte[] representation i accordance with RFC 2045. + * @param sArr The bytes to convert. If null an empty array will be returned. + * @param sOff The starting position in the bytes to convert. + * @param sLen The number of bytes to convert. If 0 an empty array will be returned. + * @param lineSep Optional "\r\n" after 76 characters, unless end of file.
+ * No line separator will be in breach of RFC 2045 which specifies max 76 per line but will be a + * little faster. + * @return A BASE64 encoded array. Never null. + */ + public final static byte[] encodeToByte(byte[] sArr, int sOff, int sLen, boolean lineSep) { + // Check special case + if (sArr == null || sLen == 0) + return new byte[0]; + + int eLen = (sLen / 3) * 3; // Length of even 24-bits. + int cCnt = ((sLen - 1) / 3 + 1) << 2; // Returned character count + int dLen = cCnt + (lineSep ? (cCnt - 1) / 76 << 1 : 0); // Length of returned array + byte[] dArr = new byte[dLen]; + + // Encode even 24-bits + for (int s = sOff, d = 0, cc = 0; s < sOff + eLen;) { + // Copy next three bytes into lower 24 bits of int, paying attension to sign. + int i = (sArr[s++] & 0xff) << 16 | (sArr[s++] & 0xff) << 8 | (sArr[s++] & 0xff); + + // Encode the int into four chars + dArr[d++] = (byte) CA[(i >>> 18) & 0x3f]; + dArr[d++] = (byte) CA[(i >>> 12) & 0x3f]; + dArr[d++] = (byte) CA[(i >>> 6) & 0x3f]; + dArr[d++] = (byte) CA[i & 0x3f]; + + // Add optional line separator + if (lineSep && ++cc == 19 && d < dLen - 2) { + dArr[d++] = '\r'; + dArr[d++] = '\n'; + cc = 0; + } + } + + // Pad and encode last bits if source isn't an even 24 bits. + int left = sLen - eLen; // 0 - 2. + if (left > 0) { + // Prepare the int + int i = ((sArr[sOff + eLen] & 0xff) << 10) | (left == 2 ? ((sArr[sOff + sLen - 1] & 0xff) << 2) : 0); + + // Set last four chars + dArr[dLen - 4] = (byte) CA[i >> 12]; + dArr[dLen - 3] = (byte) CA[(i >>> 6) & 0x3f]; + dArr[dLen - 2] = left == 2 ? (byte) CA[i & 0x3f] : (byte) '='; + dArr[dLen - 1] = '='; + } + return dArr; + } + + /** Decodes a BASE64 encoded byte array. All illegal characters will be ignored and can handle both arrays with + * and without line separators. + * @param sArr The source array. Length 0 will return an empty array. null will throw an exception. + * @return The decoded array of bytes. May be of length 0. Will be null if the legal characters + * (including '=') isn't divideable by 4. (I.e. definitely corrupted). + */ + public final static byte[] decode(byte[] sArr) { + return decode(sArr, 0, sArr.length); + } + + /** Decodes a BASE64 encoded byte array. All illegal characters will be ignored and can handle both arrays with + * and without line separators. + * @param sArr The source array. null will throw an exception. + * @param sOff The starting position in the source array. + * @param sLen The number of bytes to decode from the source array. Length 0 will return an empty array. + * @return The decoded array of bytes. May be of length 0. Will be null if the legal characters + * (including '=') isn't divideable by 4. (I.e. definitely corrupted). + */ + public final static byte[] decode(byte[] sArr, int sOff, int sLen) { + // Count illegal characters (including '\r', '\n') to know what size the returned array will be, + // so we don't have to reallocate & copy it later. + int sepCnt = 0; // Number of separator characters. (Actually illegal characters, but that's a bonus...) + for (int i = 0; i < sLen; i++) // If input is "pure" (I.e. no line separators or illegal chars) base64 this loop can be commented out. + if (IA[sArr[sOff + i] & 0xff] < 0) + sepCnt++; + + // Check so that legal chars (including '=') are evenly divideable by 4 as specified in RFC 2045. + if ((sLen - sepCnt) % 4 != 0) + return null; + + int pad = 0; + for (int i = sLen; i > 1 && IA[sArr[sOff + --i] & 0xff] <= 0;) + if (sArr[sOff + i] == '=') + pad++; + + int len = ((sLen - sepCnt) * 6 >> 3) - pad; + + byte[] dArr = new byte[len]; // Preallocate byte[] of exact length + + for (int s = 0, d = 0; d < len;) { + // Assemble three bytes into an int from four "valid" characters. + int i = 0; + for (int j = 0; j < 4; j++) { // j only increased if a valid char was found. + int c = IA[sArr[sOff + s++] & 0xff]; + if (c >= 0) + i |= c << (18 - j * 6); + else + j--; + } + + // Add the bytes + dArr[d++] = (byte) (i >> 16); + if (d < len) { + dArr[d++] = (byte) (i >> 8); + if (d < len) + dArr[d++] = (byte) i; + } + } + + return dArr; + } + + /** Decodes a BASE64 encoded byte array that is known to be resonably well formatted. The method is about twice as + * fast as {@link #decode(byte[])}. The preconditions are:
+ * + The array must have a line length of 76 chars OR no line separators at all (one line).
+ * + Line separator must be "\r\n", as specified in RFC 2045 + * + The array must not contain illegal characters within the encoded string
+ * + The array CAN have illegal characters at the beginning and end, those will be dealt with appropriately.
+ * @param sArr The source array. Length 0 will return an empty array. null will throw an exception. + * @return The decoded array of bytes. May be of length 0. + */ + public final static byte[] decodeFast(byte[] sArr) { + // Check special case + int sLen = sArr.length; + if (sLen == 0) + return new byte[0]; + + int sIx = 0, eIx = sLen - 1; // Start and end index after trimming. + + // Trim illegal chars from start + while (sIx < eIx && IA[sArr[sIx] & 0xff] < 0) + sIx++; + + // Trim illegal chars from end + while (eIx > 0 && IA[sArr[eIx] & 0xff] < 0) + eIx--; + + // get the padding count (=) (0, 1 or 2) + int pad = sArr[eIx] == '=' ? (sArr[eIx - 1] == '=' ? 2 : 1) : 0; // Count '=' at end. + int cCnt = eIx - sIx + 1; // Content count including possible separators + int sepCnt = sLen > 76 ? (sArr[76] == '\r' ? cCnt / 78 : 0) << 1 : 0; + + int len = ((cCnt - sepCnt) * 6 >> 3) - pad; // The number of decoded bytes + byte[] dArr = new byte[len]; // Preallocate byte[] of exact length + + // Decode all but the last 0 - 2 bytes. + int d = 0; + for (int cc = 0, eLen = (len / 3) * 3; d < eLen;) { + // Assemble three bytes into an int from four "valid" characters. + int i = IA[sArr[sIx++]] << 18 | IA[sArr[sIx++]] << 12 | IA[sArr[sIx++]] << 6 | IA[sArr[sIx++]]; + + // Add the bytes + dArr[d++] = (byte) (i >> 16); + dArr[d++] = (byte) (i >> 8); + dArr[d++] = (byte) i; + + // If line separator, jump over it. + if (sepCnt > 0 && ++cc == 19) { + sIx += 2; + cc = 0; + } + } + + if (d < len) { + // Decode last 1-3 bytes (incl '=') into 1-3 bytes + int i = 0; + for (int j = 0; sIx <= eIx - pad; j++) + i |= IA[sArr[sIx++]] << (18 - j * 6); + + for (int r = 16; d < len; r -= 8) + dArr[d++] = (byte) (i >> r); + } + + return dArr; + } + + // **************************************************************************************** + // * String version + // **************************************************************************************** + + /** Encodes a raw byte array into a BASE64 String representation i accordance with RFC 2045. + * @param sArr The bytes to convert. If null or length 0 an empty array will be returned. + * @param lineSep Optional "\r\n" after 76 characters, unless end of file.
+ * No line separator will be in breach of RFC 2045 which specifies max 76 per line but will be a + * little faster. + * @return A BASE64 encoded array. Never null. + */ + public final static String encodeToString(byte[] sArr, boolean lineSep) { + // Reuse char[] since we can't create a String incrementally anyway and StringBuffer/Builder would be slower. + return new String(encodeToChar(sArr, lineSep)); + } + + /** Decodes a BASE64 encoded String. All illegal characters will be ignored and can handle both strings with + * and without line separators.
+ * Note! It can be up to about 2x the speed to call decode(str.toCharArray()) instead. That + * will create a temporary array though. This version will use str.charAt(i) to iterate the string. + * @param str The source string. null or length 0 will return an empty array. + * @return The decoded array of bytes. May be of length 0. Will be null if the legal characters + * (including '=') isn't divideable by 4. (I.e. definitely corrupted). + */ + public final static byte[] decode(String str) { + // Check special case + int sLen = str != null ? str.length() : 0; + if (sLen == 0) + return new byte[0]; + + // Count illegal characters (including '\r', '\n') to know what size the returned array will be, + // so we don't have to reallocate & copy it later. + int sepCnt = 0; // Number of separator characters. (Actually illegal characters, but that's a bonus...) + for (int i = 0; i < sLen; i++) // If input is "pure" (I.e. no line separators or illegal chars) base64 this loop can be commented out. + if (IA[str.charAt(i)] < 0) + sepCnt++; + + // Check so that legal chars (including '=') are evenly divideable by 4 as specified in RFC 2045. + if ((sLen - sepCnt) % 4 != 0) + return null; + + // Count '=' at end + int pad = 0; + for (int i = sLen; i > 1 && IA[str.charAt(--i)] <= 0;) + if (str.charAt(i) == '=') + pad++; + + int len = ((sLen - sepCnt) * 6 >> 3) - pad; + + byte[] dArr = new byte[len]; // Preallocate byte[] of exact length + + for (int s = 0, d = 0; d < len;) { + // Assemble three bytes into an int from four "valid" characters. + int i = 0; + for (int j = 0; j < 4; j++) { // j only increased if a valid char was found. + int c = IA[str.charAt(s++)]; + if (c >= 0) + i |= c << (18 - j * 6); + else + j--; + } + // Add the bytes + dArr[d++] = (byte) (i >> 16); + if (d < len) { + dArr[d++] = (byte) (i >> 8); + if (d < len) + dArr[d++] = (byte) i; + } + } + return dArr; + } + + /** Decodes a BASE64 encoded string that is known to be resonably well formatted. The method is about twice as + * fast as {@link #decode(String)}. The preconditions are:
+ * + The array must have a line length of 76 chars OR no line separators at all (one line).
+ * + Line separator must be "\r\n", as specified in RFC 2045 + * + The array must not contain illegal characters within the encoded string
+ * + The array CAN have illegal characters at the beginning and end, those will be dealt with appropriately.
+ * @param s The source string. Length 0 will return an empty array. null will throw an exception. + * @return The decoded array of bytes. May be of length 0. + */ + public final static byte[] decodeFast(String s) { + // Check special case + int sLen = s.length(); + if (sLen == 0) + return new byte[0]; + + int sIx = 0, eIx = sLen - 1; // Start and end index after trimming. + + // Trim illegal chars from start + while (sIx < eIx && IA[s.charAt(sIx) & 0xff] < 0) + sIx++; + + // Trim illegal chars from end + while (eIx > 0 && IA[s.charAt(eIx) & 0xff] < 0) + eIx--; + + // get the padding count (=) (0, 1 or 2) + int pad = s.charAt(eIx) == '=' ? (s.charAt(eIx - 1) == '=' ? 2 : 1) : 0; // Count '=' at end. + int cCnt = eIx - sIx + 1; // Content count including possible separators + int sepCnt = sLen > 76 ? (s.charAt(76) == '\r' ? cCnt / 78 : 0) << 1 : 0; + + int len = ((cCnt - sepCnt) * 6 >> 3) - pad; // The number of decoded bytes + byte[] dArr = new byte[len]; // Preallocate byte[] of exact length + + // Decode all but the last 0 - 2 bytes. + int d = 0; + for (int cc = 0, eLen = (len / 3) * 3; d < eLen;) { + // Assemble three bytes into an int from four "valid" characters. + int i = IA[s.charAt(sIx++)] << 18 | IA[s.charAt(sIx++)] << 12 | IA[s.charAt(sIx++)] << 6 | IA[s.charAt(sIx++)]; + + // Add the bytes + dArr[d++] = (byte) (i >> 16); + dArr[d++] = (byte) (i >> 8); + dArr[d++] = (byte) i; + + // If line separator, jump over it. + if (sepCnt > 0 && ++cc == 19) { + sIx += 2; + cc = 0; + } + } + + if (d < len) { + // Decode last 1-3 bytes (incl '=') into 1-3 bytes + int i = 0; + for (int j = 0; sIx <= eIx - pad; j++) + i |= IA[s.charAt(sIx++)] << (18 - j * 6); + + for (int r = 16; d < len; r -= 8) + dArr[d++] = (byte) (i >> r); + } + + return dArr; + } +} diff --git a/rec-common/src/main/java/cn/icanci/rec/common/utils/GZipUtils.java b/rec-common/src/main/java/cn/icanci/rec/common/utils/GZipUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..ba402a35a23f6ae4cb71a32fcae8ce11c08b5e89 --- /dev/null +++ b/rec-common/src/main/java/cn/icanci/rec/common/utils/GZipUtils.java @@ -0,0 +1,200 @@ +package cn.icanci.rec.common.utils; + +import java.io.*; +import java.util.zip.GZIPInputStream; +import java.util.zip.GZIPOutputStream; + +import org.apache.commons.io.IOUtils; + +/** + * @author icanci + * @since 1.0 Created in 2022/11/20 15:37 + */ +public abstract class GZipUtils { + public static final int BUFF = 1024; + public static final String EXT = ".gz"; + + /********************************************************************************** + * 压缩 + **********************************************************************************/ + + /** + * 压缩二进制数据 + * @param data 待压缩数据 + * @return byte[] 压缩数据 + * @throws Exception + */ + public static byte[] compress(byte[] data) throws Exception { + ByteArrayInputStream bais = new ByteArrayInputStream(data); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + + compress(bais, baos); + + data = baos.toByteArray(); + + baos.flush(); + IOUtils.closeQuietly(baos); + IOUtils.closeQuietly(bais); + + return data; + } + + /** + * 文件压缩 + * @param path 源文件路径 + * @throws Exception + */ + public static void compress(String path) throws Exception { + compress(path, true); + } + + /** + * 文件压缩 + * @param path 源文件路径 + * @param delete 是否删除源文件,压缩成功后 + * @throws Exception + */ + public static void compress(String path, boolean delete) throws Exception { + File file = new File(path); + compress(file, delete); + } + + /** + * 压缩文件 + * @param file 源文件 + * @throws Exception + */ + public static void compress(File file) throws Exception { + compress(file, true); + } + + /** + * 压缩文件 + * @param file 源文件 + * @param delete 是否删除源文件,压缩成功后 + * @throws Exception + */ + public static void compress(File file, boolean delete) throws Exception { + FileInputStream fis = new FileInputStream(file); + FileOutputStream fos = new FileOutputStream(file.getPath() + EXT); + + compress(fis, fos); + + fos.flush(); + IOUtils.closeQuietly(fos); + IOUtils.closeQuietly(fis); + + if (delete) { + file.delete(); + } + } + + /** + * 二进制数据压缩 + * @param in 输入流 + * @param out 输出流 + * @throws Exception + */ + public static void compress(InputStream in, OutputStream out) throws Exception { + GZIPOutputStream gos = new GZIPOutputStream(out); + + byte[] data = new byte[BUFF]; + for (int count = in.read(data, 0, BUFF); count != -1; count = in.read(data, 0, BUFF)) { + gos.write(data, 0, count); + } + + gos.finish(); + gos.flush(); + IOUtils.closeQuietly(gos); + } + + /********************************************************************************** + * 解压缩 + **********************************************************************************/ + + /** + * 解压缩二进制数据 + * @param data 压缩的二进制数据 + * @return byte[] 解压缩的二进制数据 + * @throws Exception + */ + public static byte[] decompress(byte[] data) throws Exception { + ByteArrayInputStream bais = new ByteArrayInputStream(data); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + + decompress(bais, baos); + + data = baos.toByteArray(); + + baos.flush(); + IOUtils.closeQuietly(baos); + IOUtils.closeQuietly(bais); + return data; + } + + /** + * 解压缩文件 + * @param file 源文件 + * @throws Exception + */ + public static void decompress(File file) throws Exception { + decompress(file, true); + } + + /** + * 解压缩文件 + * @param path 源文件路径 + * @throws Exception + */ + public static void decompress(String path) throws Exception { + decompress(path, true); + } + + /** + * 解压缩文件 + * @param path 源文件路径 + * @param delete 是否删除源文件,解压缩完成 + * @throws Exception + */ + public static void decompress(String path, boolean delete) throws Exception { + decompress(new File(path), delete); + } + + /** + * 解压缩文件 + * @param file 源文件 + * @param delete 解压缩完成后,是否删除源文件 + * @throws Exception + */ + public static void decompress(File file, boolean delete) throws Exception { + FileInputStream in = new FileInputStream(file); + FileOutputStream out = new FileOutputStream(file.getPath().replace(EXT, "")); + + decompress(in, out); + + out.flush(); + IOUtils.closeQuietly(out); + IOUtils.closeQuietly(in); + + if (delete) { + file.delete(); + } + } + + /** + * 解压缩文件 + * @param in 输入流 + * @param out 输出流 + * @throws Exception + */ + public static void decompress(InputStream in, OutputStream out) throws Exception { + GZIPInputStream gis = new GZIPInputStream(in); + + byte[] data = new byte[BUFF]; + for (int count = gis.read(data, 0, BUFF); count != -1; count = gis.read(data, 0, BUFF)) { + out.write(data, 0, count); + } + + IOUtils.closeQuietly(gis); + } +} diff --git a/rec-core/src/main/java/cn/icanci/rec/core/facade/RuleExecutorFacade.java b/rec-core/src/main/java/cn/icanci/rec/core/facade/RuleExecutorFacade.java index 8f9403d89a8ff8391314223937a82988292c3569..64421775aa079ba3aaed60479330e43cb57394a2 100644 --- a/rec-core/src/main/java/cn/icanci/rec/core/facade/RuleExecutorFacade.java +++ b/rec-core/src/main/java/cn/icanci/rec/core/facade/RuleExecutorFacade.java @@ -1,10 +1,23 @@ package cn.icanci.rec.core.facade; +import cn.icanci.rec.core.model.ExecutorRequest; +import cn.icanci.rec.core.model.ExecutorResponse; +import cn.icanci.rec.core.utils.ParamUtils; +import cn.icanci.rec.engine.sdk.actuator.RuleEngineRequest; +import cn.icanci.rec.engine.sdk.actuator.RuleEngineResponse; +import cn.icanci.rec.engine.sdk.exception.ValidatorException; +import cn.icanci.rec.engine.sdk.rule.EngineExecutor; + +import javax.annotation.Resource; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** - * TODO + * HTTP 执行接口 * * @author icanci * @since 1.0 Created in 2022/11/12 22:45 @@ -12,4 +25,57 @@ import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/rec/core") public class RuleExecutorFacade { + @Resource + private EngineExecutor engineExecutor; + + @PostMapping("doExecutor") + public ExecutorResponse doExecutor(@RequestBody ExecutorRequest request) { + try { + // 1.数据基本校验 + validator(request); + // 2.数据转换 + RuleEngineRequest req = mapper(request); + RuleEngineResponse execute = engineExecutor.execute(req); + // 3.执行结果转换 + boolean success = execute.isSuccess(); + if (success) { + return ExecutorResponse.success(execute.getResult()); + } else { + return ExecutorResponse.fail(execute.getErrorMessage()); + } + } catch (Exception e) { + return ExecutorResponse.fail(e.getMessage()); + } + } + + /** + * 数据验证 + * + * @param request request + */ + private void validator(ExecutorRequest request) { + if (request == null) { + throw new ValidatorException("request is Null!"); + } + if (StringUtils.isBlank(request.getDomainCode())) { + throw new ValidatorException("domainCode is empty!"); + } + if (StringUtils.isBlank(request.getSceneCode())) { + throw new ValidatorException("sceneCode is empty!"); + } + } + + /** + * 数据转换 + * + * @param request request + * @return 返回转换的参数 + */ + private RuleEngineRequest mapper(ExecutorRequest request) { + RuleEngineRequest ret = new RuleEngineRequest(); + ret.setDomainCode(request.getDomainCode()); + ret.setSceneCode(request.getSceneCode()); + ret.setParameters(ParamUtils.json2Obj(request.getZipParam())); + return ret; + } } diff --git a/rec-core/src/main/java/cn/icanci/rec/core/model/ExecutorRequest.java b/rec-core/src/main/java/cn/icanci/rec/core/model/ExecutorRequest.java new file mode 100644 index 0000000000000000000000000000000000000000..37849c1b47be236122390e9373283e73ecf2f297 --- /dev/null +++ b/rec-core/src/main/java/cn/icanci/rec/core/model/ExecutorRequest.java @@ -0,0 +1,42 @@ +package cn.icanci.rec.core.model; + +import java.io.Serializable; + +/** + * @author icanci + * @since 1.0 Created in 2022/11/20 15:40 + */ +public class ExecutorRequest implements Serializable { + private static final long serialVersionUID = 2384799016451677948L; + + /** 域Code */ + private String domainCode; + /** 域场景 */ + private String sceneCode; + /** 使用 ParamUtils 压缩的数据 */ + private String zipParam; + + public String getDomainCode() { + return domainCode; + } + + public void setDomainCode(String domainCode) { + this.domainCode = domainCode; + } + + public String getSceneCode() { + return sceneCode; + } + + public void setSceneCode(String sceneCode) { + this.sceneCode = sceneCode; + } + + public String getZipParam() { + return zipParam; + } + + public void setZipParam(String zipParam) { + this.zipParam = zipParam; + } +} diff --git a/rec-core/src/main/java/cn/icanci/rec/core/model/ExecutorResponse.java b/rec-core/src/main/java/cn/icanci/rec/core/model/ExecutorResponse.java new file mode 100644 index 0000000000000000000000000000000000000000..116d433793a831984631ca00bbd8985c95841625 --- /dev/null +++ b/rec-core/src/main/java/cn/icanci/rec/core/model/ExecutorResponse.java @@ -0,0 +1,54 @@ +package cn.icanci.rec.core.model; + +import java.io.Serializable; + +/** + * @author icanci + * @since 1.0 Created in 2022/11/20 15:42 + */ +public class ExecutorResponse implements Serializable { + private static final long serialVersionUID = 4349964322782309144L; + private boolean success; + + private String errorMessage; + /** 执行结果 */ + private Object result; + + public static ExecutorResponse fail(String errorMessage) { + ExecutorResponse response = new ExecutorResponse(); + response.setSuccess(false); + response.setErrorMessage(errorMessage); + return response; + } + + public static ExecutorResponse success(Object result) { + ExecutorResponse response = new ExecutorResponse(); + response.setSuccess(true); + response.setResult(result); + return response; + } + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public String getErrorMessage() { + return errorMessage; + } + + public void setErrorMessage(String errorMessage) { + this.errorMessage = errorMessage; + } + + public Object getResult() { + return result; + } + + public void setResult(Object result) { + this.result = result; + } +} diff --git a/rec-core/src/main/java/cn/icanci/rec/core/utils/ParamUtils.java b/rec-core/src/main/java/cn/icanci/rec/core/utils/ParamUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..e3c4c3de238f3c29c98a72cf44fb7621d4375939 --- /dev/null +++ b/rec-core/src/main/java/cn/icanci/rec/core/utils/ParamUtils.java @@ -0,0 +1,90 @@ +package cn.icanci.rec.core.utils; + +import cn.icanci.rec.common.utils.FastBase64; +import cn.icanci.rec.common.utils.GZipUtils; + +import java.nio.charset.StandardCharsets; +import java.util.Map; + +import org.apache.commons.collections4.MapUtils; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.util.Base64Utils; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.parser.Feature; +import com.alibaba.fastjson.serializer.SerializerFeature; +import com.google.common.collect.Maps; + +/** + * 参数工具类 + * 解决问题: + * 1.传送的参数inputJson过大的数据压缩gzip + * 2.参数inputJson的fastJson序列化、反序列化方式统一 + * + * @author zwy48359 + * @version Id: DateUtils.java, v 0.1 2021-08-02 10:59 zwy48359 Exp $$ + */ +public class ParamUtils { + /** 日志 */ + private static final Logger LOG = LoggerFactory.getLogger(ParamUtils.class); + + /** 序列化属性 */ + private static final SerializerFeature[] DISABLE_CIRCULAR_REFER_FEATURES = { SerializerFeature.WriteMapNullValue, SerializerFeature.WriteNullListAsEmpty, + SerializerFeature.WriteNullNumberAsZero, SerializerFeature.WriteNullBooleanAsFalse, + SerializerFeature.DisableCircularReferenceDetect, SerializerFeature.WriteNullStringAsEmpty, + SerializerFeature.SortField, SerializerFeature.SkipTransientField, + SerializerFeature.WriteClassName, SerializerFeature.WriteDateUseDateFormat }; + + /** 反序列化属性 */ + private static final Feature[] DESERIALIZER_FEATURES = { Feature.SupportAutoType, Feature.AllowUnQuotedFieldNames, Feature.AllowSingleQuotes, + Feature.InternFieldNames, Feature.AllowArbitraryCommas, Feature.IgnoreNotMatch }; + + /** + * 业务接入方调用: + * 传送的动态参数调用此方法,生成实际传输的inputJson + * + * @param parameters 动态参数map + * @return 处理后的json + */ + public static String obj2Json(Map parameters) { + if (MapUtils.isEmpty(parameters)) { + return StringUtils.EMPTY; + } + + // 1.将map执行fastJson: 去除引用Reference,忽略get方法 + String jsonStr = JSON.toJSONString(parameters, DISABLE_CIRCULAR_REFER_FEATURES); + + // 2. 将json执行gzip压缩+base64编码 + try { + byte[] gzip = GZipUtils.compress(jsonStr.getBytes(StandardCharsets.UTF_8)); + return FastBase64.encodeToString(gzip, false); + } catch (Exception e) { + LOG.error("[ParamUtils][obj2Json] err:{}", e.getMessage()); + } + return StringUtils.EMPTY; + } + + /** + * json转动态参数map + * + * @param json json + * @return 动态参数map + */ + public static Map json2Obj(String json) { + if (StringUtils.isBlank(json)) { + return Maps.newHashMap(); + } + // 1. 将json执行base64解码+gzip解压缩 + try { + String decodeRequest = new String(GZipUtils.decompress(Base64Utils.decode(json.getBytes()))); + // 2. 将fastJson转成map执行: 去除引用Reference,忽略get方法 + return JSON.parseObject(decodeRequest, Map.class, DESERIALIZER_FEATURES); + } catch (Exception e) { + LOG.error("[ParamUtils][json2Obj] err:{}", e.getMessage()); + } + return Maps.newHashMap(); + } + +} diff --git a/rec-engine/rec-engine-sdk/src/main/java/cn/icanci/rec/engine/sdk/rule/EngineExecutor.java b/rec-engine/rec-engine-sdk/src/main/java/cn/icanci/rec/engine/sdk/rule/EngineExecutor.java index 042234dc6303392870b57d994ca0cb0b0107e65f..e03a5d11dce9c0941ed8cbeb12ba95c7dfb95a7c 100644 --- a/rec-engine/rec-engine-sdk/src/main/java/cn/icanci/rec/engine/sdk/rule/EngineExecutor.java +++ b/rec-engine/rec-engine-sdk/src/main/java/cn/icanci/rec/engine/sdk/rule/EngineExecutor.java @@ -83,9 +83,10 @@ public final class EngineExecutor { case LIST: executorResult = ruleListExecutor(strategy.getDomainCode(), strategy.getRuleListInfo(), ruleMode, bindings); break; - case TREE: - executorResult = ruleTreeExecutor(strategy.getDomainCode(), strategy.getRuleTreeInfo(), ruleMode, bindings); - break; + // TODO 一期版本不处理Tree + // case TREE: + // executorResult = ruleTreeExecutor(strategy.getDomainCode(), strategy.getRuleTreeInfo(), ruleMode, bindings); + // break; default: // no op } diff --git a/rec-engine/rec-engine-sdk/src/main/java/cn/icanci/rec/engine/sdk/rule/runtime/BaseDataCompiled.java b/rec-engine/rec-engine-sdk/src/main/java/cn/icanci/rec/engine/sdk/rule/runtime/BaseDataCompiled.java index 0ec4e8af6e2984e37e51e4a3413eaed24d1634c8..dac3f80aabd2e1393202e10bddb816059b9303a4 100644 --- a/rec-engine/rec-engine-sdk/src/main/java/cn/icanci/rec/engine/sdk/rule/runtime/BaseDataCompiled.java +++ b/rec-engine/rec-engine-sdk/src/main/java/cn/icanci/rec/engine/sdk/rule/runtime/BaseDataCompiled.java @@ -47,7 +47,6 @@ public class BaseDataCompiled extends BaseCompiled { * * @see ResultTypeEnum#name() */ - @Deprecated private String resultType; /** * 脚本执行引擎 diff --git a/rec-engine/rec-engine-sdk/src/main/java/cn/icanci/rec/engine/sdk/rule/runtime/RuleAggregationCompiled.java b/rec-engine/rec-engine-sdk/src/main/java/cn/icanci/rec/engine/sdk/rule/runtime/RuleAggregationCompiled.java index 890472cf43e2ab43efb7d2dcf05dc6ea6d1430ba..cd22a878e3423dc3f109dfe93c5d187770fe14ca 100644 --- a/rec-engine/rec-engine-sdk/src/main/java/cn/icanci/rec/engine/sdk/rule/runtime/RuleAggregationCompiled.java +++ b/rec-engine/rec-engine-sdk/src/main/java/cn/icanci/rec/engine/sdk/rule/runtime/RuleAggregationCompiled.java @@ -22,7 +22,6 @@ public class RuleAggregationCompiled { private SceneCompiled scene; /** 策略 */ private StrategyCompiled strategy; - // TODO 规则树的执行构建 public DomainCompiled getDomain() { return domain;