diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..7d05e99 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,10 @@ +# 默认忽略的文件 +/shelf/ +/workspace.xml +# 基于编辑器的 HTTP 客户端请求 +/httpRequests/ +# 依赖于环境的 Maven 主目录路径 +/mavenHomeManager.xml +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..03d9549 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/lingniu-platform.iml b/.idea/lingniu-platform.iml new file mode 100644 index 0000000..d6ebd48 --- /dev/null +++ b/.idea/lingniu-platform.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..639900d --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..b5cfd88 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/idp/backend/idp-starter/src/main/resources/auth.sql b/idp/backend/idp-starter/src/main/resources/auth.sql deleted file mode 100644 index b482a46..0000000 --- a/idp/backend/idp-starter/src/main/resources/auth.sql +++ /dev/null @@ -1,54 +0,0 @@ -CREATE TABLE oauth2_authorization ( - id varchar(100) NOT NULL COMMENT '授权记录的唯一标识符', - registered_client_id varchar(100) NOT NULL COMMENT '注册客户端的ID,关联到客户端注册表', - principal_name varchar(200) NOT NULL COMMENT '授权主体的名称(通常是用户ID或用户名)', - authorization_grant_type varchar(100) NOT NULL COMMENT '授权类型(如:authorization_code, client_credentials, refresh_token等)', - authorized_scopes varchar(1000) DEFAULT NULL COMMENT '已授权的范围(用空格分隔的scope列表)', - attributes blob DEFAULT NULL COMMENT '附加属性,以二进制格式存储', - state varchar(500) DEFAULT NULL COMMENT 'OAuth2状态参数,用于防止CSRF攻击', - authorization_code_value blob DEFAULT NULL COMMENT '授权码的值(加密存储)', - authorization_code_issued_at timestamp NULL DEFAULT NULL COMMENT '授权码的颁发时间', - authorization_code_expires_at timestamp NULL DEFAULT NULL COMMENT '授权码的过期时间', - authorization_code_metadata blob DEFAULT NULL COMMENT '授权码的元数据', - access_token_value blob DEFAULT NULL COMMENT '访问令牌的值(加密存储)', - access_token_issued_at timestamp NULL DEFAULT NULL COMMENT '访问令牌的颁发时间', - access_token_expires_at timestamp NULL DEFAULT NULL COMMENT '访问令牌的过期时间', - access_token_metadata blob DEFAULT NULL COMMENT '访问令牌的元数据', - access_token_type varchar(100) DEFAULT NULL COMMENT '访问令牌类型(如:Bearer)', - access_token_scopes varchar(1000) DEFAULT NULL COMMENT '访问令牌的有效范围', - oidc_id_token_value blob DEFAULT NULL COMMENT 'OIDC ID令牌的值(加密存储)', - oidc_id_token_issued_at timestamp NULL DEFAULT NULL COMMENT 'OIDC ID令牌的颁发时间', - oidc_id_token_expires_at timestamp NULL DEFAULT NULL COMMENT 'OIDC ID令牌的过期时间', - oidc_id_token_metadata blob DEFAULT NULL COMMENT 'OIDC ID令牌的元数据', - refresh_token_value blob DEFAULT NULL COMMENT '刷新令牌的值(加密存储)', - refresh_token_issued_at timestamp NULL DEFAULT NULL COMMENT '刷新令牌的颁发时间', - refresh_token_expires_at timestamp NULL DEFAULT NULL COMMENT '刷新令牌的过期时间', - refresh_token_metadata blob DEFAULT NULL COMMENT '刷新令牌的元数据', - user_code_value blob DEFAULT NULL COMMENT '设备流用户码的值(加密存储)', - user_code_issued_at timestamp NULL DEFAULT NULL COMMENT '设备流用户码的颁发时间', - user_code_expires_at timestamp NULL DEFAULT NULL COMMENT '设备流用户码的过期时间', - user_code_metadata blob DEFAULT NULL COMMENT '设备流用户码的元数据', - device_code_value blob DEFAULT NULL COMMENT '设备流设备码的值(加密存储)', - device_code_issued_at timestamp NULL DEFAULT NULL COMMENT '设备流设备码的颁发时间', - device_code_expires_at timestamp NULL DEFAULT NULL COMMENT '设备流设备码的过期时间', - device_code_metadata blob DEFAULT NULL COMMENT '设备流设备码的元数据', - PRIMARY KEY (id) -) COMMENT='OAuth2 授权表,存储所有OAuth2和OpenID Connect的授权信息'; - -CREATE INDEX idx_registered_client_id ON oauth2_authorization(registered_client_id); -CREATE INDEX idx_principal_name ON oauth2_authorization(principal_name); -CREATE INDEX idx_state ON oauth2_authorization(state); -CREATE INDEX idx_access_token_expires_at ON oauth2_authorization(access_token_expires_at); -CREATE INDEX idx_refresh_token_expires_at ON oauth2_authorization(refresh_token_expires_at); -CREATE INDEX idx_authorization_code_expires_at ON oauth2_authorization(authorization_code_expires_at); - - -CREATE TABLE oauth2_authorization_consent ( - registered_client_id varchar(100) NOT NULL COMMENT '注册客户端的ID,关联到客户端注册表', - principal_name varchar(200) NOT NULL COMMENT '授权主体的名称(通常是用户ID或用户名)', - authorities varchar(1000) NOT NULL COMMENT '已授予的权限列表(逗号分隔的权限字符串)', - PRIMARY KEY (registered_client_id, principal_name) -) COMMENT='OAuth2授权同意表,存储用户对客户端的授权同意记录'; - -CREATE INDEX idx_oauth2_authorization_consent_principal_name ON oauth2_authorization_consent(principal_name); -CREATE INDEX idx_oauth2_authorization_consent_client_id ON oauth2_authorization_consent(registered_client_id); \ No newline at end of file diff --git a/idp/frontend/.env.development b/idp/frontend/.env.development index 130999b..46a1dbe 100644 --- a/idp/frontend/.env.development +++ b/idp/frontend/.env.development @@ -4,7 +4,8 @@ VITE_APP_TITLE = IDP统一登录系统 # 开发环境配置 VITE_APP_ENV = 'development' +VITE_APP_BASE_URL=/idp-ui/ # 开发环境 -VITE_APP_BASE_API = '/dev-api' +VITE_APP_BASE_API = '/idp-api2' VITE_APP_DEFAULT_PAGE='' diff --git a/idp/frontend/.env.production b/idp/frontend/.env.production index 417f85c..295fd1b 100644 --- a/idp/frontend/.env.production +++ b/idp/frontend/.env.production @@ -3,9 +3,9 @@ VITE_APP_TITLE = IDP统一登录系统 # 生产环境配置 VITE_APP_ENV = 'production' - +VITE_APP_BASE_URL=/idp-ui/ # 生产环境 -VITE_APP_BASE_API = '/prod-api' +VITE_APP_BASE_API = '/idp-api2' # 是否在打包时开启压缩,支持 gzip 和 brotli VITE_BUILD_COMPRESS = gzip diff --git a/idp/frontend/src/api/login.ts b/idp/frontend/src/api/login.ts index d5b96a6..e6eb48f 100644 --- a/idp/frontend/src/api/login.ts +++ b/idp/frontend/src/api/login.ts @@ -91,7 +91,7 @@ export function logout(): Promise> { } // 获取验证码 -export function getCodeImg(): Promise> { +export function getCodeImg(): Promise { return request({ url: '/captcha/image', headers: { diff --git a/idp/frontend/src/main.ts b/idp/frontend/src/main.ts index 7f6241b..0889a6f 100644 --- a/idp/frontend/src/main.ts +++ b/idp/frontend/src/main.ts @@ -6,13 +6,11 @@ import App from './App.vue' import router from './router' import 'virtual:svg-icons-register' import SvgIcon from '@/components/SvgIcon' -import elementIcons from '@/components/SvgIcon/svgicon' const app = createApp(App) app.use(createPinia()) app.use(router) app.use(ElementPlus) -app.use(elementIcons) app.component('svg-icon', SvgIcon) app.mount('#app') \ No newline at end of file diff --git a/idp/frontend/src/utils/jsencrypt.ts b/idp/frontend/src/utils/jsencrypt.ts index d774c1d..e452a9d 100644 --- a/idp/frontend/src/utils/jsencrypt.ts +++ b/idp/frontend/src/utils/jsencrypt.ts @@ -13,15 +13,15 @@ const privateKey: string = 'MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAqhHy 'UP8iWi1Qw0Y=' // 加密 -export function encrypt(txt: string): string | false { +export function encrypt(txt: string): string { const encryptor = new JSEncrypt() encryptor.setPublicKey(publicKey) // 设置公钥 - return encryptor.encrypt(txt) // 对数据进行加密 + return encryptor.encrypt(txt)|| "" // 对数据进行加密 } // 解密 -export function decrypt(txt: string): string | false { +export function decrypt(txt: string): string { const encryptor = new JSEncrypt() encryptor.setPrivateKey(privateKey) // 设置私钥 - return encryptor.decrypt(txt) // 对数据进行解密 + return encryptor.decrypt(txt) || ""// 对数据进行解密 } \ No newline at end of file diff --git a/idp/frontend/src/views/LoginPage.vue b/idp/frontend/src/views/LoginPage.vue index ce967a8..d2e9371 100644 --- a/idp/frontend/src/views/LoginPage.vue +++ b/idp/frontend/src/views/LoginPage.vue @@ -65,7 +65,7 @@ "; + String escape = EscapeUtil.escape(html); + // String html = "ipt>alert(\"XSS\")ipt>"; + // String html = "<123"; + // String html = "123>"; + System.out.println("clean: " + EscapeUtil.clean(html)); + System.out.println("escape: " + escape); + System.out.println("unescape: " + EscapeUtil.unescape(escape)); + } +} diff --git a/portal/backend/portal-common/src/main/java/org/lingniu/portal/common/utils/html/HTMLFilter.java b/portal/backend/portal-common/src/main/java/org/lingniu/portal/common/utils/html/HTMLFilter.java new file mode 100644 index 0000000..6b3b72e --- /dev/null +++ b/portal/backend/portal-common/src/main/java/org/lingniu/portal/common/utils/html/HTMLFilter.java @@ -0,0 +1,570 @@ +package org.lingniu.portal.common.utils.html; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * HTML过滤器,用于去除XSS漏洞隐患。 + * + * @author lingniu + */ +public final class HTMLFilter +{ + /** + * regex flag union representing /si modifiers in php + **/ + private static final int REGEX_FLAGS_SI = Pattern.CASE_INSENSITIVE | Pattern.DOTALL; + private static final Pattern P_COMMENTS = Pattern.compile("", Pattern.DOTALL); + private static final Pattern P_COMMENT = Pattern.compile("^!--(.*)--$", REGEX_FLAGS_SI); + private static final Pattern P_TAGS = Pattern.compile("<(.*?)>", Pattern.DOTALL); + private static final Pattern P_END_TAG = Pattern.compile("^/([a-z0-9]+)", REGEX_FLAGS_SI); + private static final Pattern P_START_TAG = Pattern.compile("^([a-z0-9]+)(.*?)(/?)$", REGEX_FLAGS_SI); + private static final Pattern P_QUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)=([\"'])(.*?)\\2", REGEX_FLAGS_SI); + private static final Pattern P_UNQUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)(=)([^\"\\s']+)", REGEX_FLAGS_SI); + private static final Pattern P_PROTOCOL = Pattern.compile("^([^:]+):", REGEX_FLAGS_SI); + private static final Pattern P_ENTITY = Pattern.compile("&#(\\d+);?"); + private static final Pattern P_ENTITY_UNICODE = Pattern.compile("&#x([0-9a-f]+);?"); + private static final Pattern P_ENCODE = Pattern.compile("%([0-9a-f]{2});?"); + private static final Pattern P_VALID_ENTITIES = Pattern.compile("&([^&;]*)(?=(;|&|$))"); + private static final Pattern P_VALID_QUOTES = Pattern.compile("(>|^)([^<]+?)(<|$)", Pattern.DOTALL); + private static final Pattern P_END_ARROW = Pattern.compile("^>"); + private static final Pattern P_BODY_TO_END = Pattern.compile("<([^>]*?)(?=<|$)"); + private static final Pattern P_XML_CONTENT = Pattern.compile("(^|>)([^<]*?)(?=>)"); + private static final Pattern P_STRAY_LEFT_ARROW = Pattern.compile("<([^>]*?)(?=<|$)"); + private static final Pattern P_STRAY_RIGHT_ARROW = Pattern.compile("(^|>)([^<]*?)(?=>)"); + private static final Pattern P_AMP = Pattern.compile("&"); + private static final Pattern P_QUOTE = Pattern.compile("\""); + private static final Pattern P_LEFT_ARROW = Pattern.compile("<"); + private static final Pattern P_RIGHT_ARROW = Pattern.compile(">"); + private static final Pattern P_BOTH_ARROWS = Pattern.compile("<>"); + + // @xxx could grow large... maybe use sesat's ReferenceMap + private static final ConcurrentMap P_REMOVE_PAIR_BLANKS = new ConcurrentHashMap<>(); + private static final ConcurrentMap P_REMOVE_SELF_BLANKS = new ConcurrentHashMap<>(); + + /** + * set of allowed html elements, along with allowed attributes for each element + **/ + private final Map> vAllowed; + /** + * counts of open tags for each (allowable) html element + **/ + private final Map vTagCounts = new HashMap<>(); + + /** + * html elements which must always be self-closing (e.g. "") + **/ + private final String[] vSelfClosingTags; + /** + * html elements which must always have separate opening and closing tags (e.g. "") + **/ + private final String[] vNeedClosingTags; + /** + * set of disallowed html elements + **/ + private final String[] vDisallowed; + /** + * attributes which should be checked for valid protocols + **/ + private final String[] vProtocolAtts; + /** + * allowed protocols + **/ + private final String[] vAllowedProtocols; + /** + * tags which should be removed if they contain no content (e.g. "" or "") + **/ + private final String[] vRemoveBlanks; + /** + * entities allowed within html markup + **/ + private final String[] vAllowedEntities; + /** + * flag determining whether comments are allowed in input String. + */ + private final boolean stripComment; + private final boolean encodeQuotes; + /** + * flag determining whether to try to make tags when presented with "unbalanced" angle brackets (e.g. "" + * becomes " text "). If set to false, unbalanced angle brackets will be html escaped. + */ + private final boolean alwaysMakeTags; + + /** + * Default constructor. + */ + public HTMLFilter() + { + vAllowed = new HashMap<>(); + + final ArrayList a_atts = new ArrayList<>(); + a_atts.add("href"); + a_atts.add("target"); + vAllowed.put("a", a_atts); + + final ArrayList img_atts = new ArrayList<>(); + img_atts.add("src"); + img_atts.add("width"); + img_atts.add("height"); + img_atts.add("alt"); + vAllowed.put("img", img_atts); + + final ArrayList no_atts = new ArrayList<>(); + vAllowed.put("b", no_atts); + vAllowed.put("strong", no_atts); + vAllowed.put("i", no_atts); + vAllowed.put("em", no_atts); + + vSelfClosingTags = new String[] { "img" }; + vNeedClosingTags = new String[] { "a", "b", "strong", "i", "em" }; + vDisallowed = new String[] {}; + vAllowedProtocols = new String[] { "http", "mailto", "https" }; // no ftp. + vProtocolAtts = new String[] { "src", "href" }; + vRemoveBlanks = new String[] { "a", "b", "strong", "i", "em" }; + vAllowedEntities = new String[] { "amp", "gt", "lt", "quot" }; + stripComment = true; + encodeQuotes = true; + alwaysMakeTags = false; + } + + /** + * Map-parameter configurable constructor. + * + * @param conf map containing configuration. keys match field names. + */ + @SuppressWarnings("unchecked") + public HTMLFilter(final Map conf) + { + + assert conf.containsKey("vAllowed") : "configuration requires vAllowed"; + assert conf.containsKey("vSelfClosingTags") : "configuration requires vSelfClosingTags"; + assert conf.containsKey("vNeedClosingTags") : "configuration requires vNeedClosingTags"; + assert conf.containsKey("vDisallowed") : "configuration requires vDisallowed"; + assert conf.containsKey("vAllowedProtocols") : "configuration requires vAllowedProtocols"; + assert conf.containsKey("vProtocolAtts") : "configuration requires vProtocolAtts"; + assert conf.containsKey("vRemoveBlanks") : "configuration requires vRemoveBlanks"; + assert conf.containsKey("vAllowedEntities") : "configuration requires vAllowedEntities"; + + vAllowed = Collections.unmodifiableMap((HashMap>) conf.get("vAllowed")); + vSelfClosingTags = (String[]) conf.get("vSelfClosingTags"); + vNeedClosingTags = (String[]) conf.get("vNeedClosingTags"); + vDisallowed = (String[]) conf.get("vDisallowed"); + vAllowedProtocols = (String[]) conf.get("vAllowedProtocols"); + vProtocolAtts = (String[]) conf.get("vProtocolAtts"); + vRemoveBlanks = (String[]) conf.get("vRemoveBlanks"); + vAllowedEntities = (String[]) conf.get("vAllowedEntities"); + stripComment = conf.containsKey("stripComment") ? (Boolean) conf.get("stripComment") : true; + encodeQuotes = conf.containsKey("encodeQuotes") ? (Boolean) conf.get("encodeQuotes") : true; + alwaysMakeTags = conf.containsKey("alwaysMakeTags") ? (Boolean) conf.get("alwaysMakeTags") : true; + } + + private void reset() + { + vTagCounts.clear(); + } + + // --------------------------------------------------------------- + // my versions of some PHP library functions + public static String chr(final int decimal) + { + return String.valueOf((char) decimal); + } + + public static String htmlSpecialChars(final String s) + { + String result = s; + result = regexReplace(P_AMP, "&", result); + result = regexReplace(P_QUOTE, """, result); + result = regexReplace(P_LEFT_ARROW, "<", result); + result = regexReplace(P_RIGHT_ARROW, ">", result); + return result; + } + + // --------------------------------------------------------------- + + /** + * given a user submitted input String, filter out any invalid or restricted html. + * + * @param input text (i.e. submitted by a user) than may contain html + * @return "clean" version of input, with only valid, whitelisted html elements allowed + */ + public String filter(final String input) + { + reset(); + String s = input; + + s = escapeComments(s); + + s = balanceHTML(s); + + s = checkTags(s); + + s = processRemoveBlanks(s); + + // s = validateEntities(s); + + return s; + } + + public boolean isAlwaysMakeTags() + { + return alwaysMakeTags; + } + + public boolean isStripComments() + { + return stripComment; + } + + private String escapeComments(final String s) + { + final Matcher m = P_COMMENTS.matcher(s); + final StringBuffer buf = new StringBuffer(); + if (m.find()) + { + final String match = m.group(1); // (.*?) + m.appendReplacement(buf, Matcher.quoteReplacement("")); + } + m.appendTail(buf); + + return buf.toString(); + } + + private String balanceHTML(String s) + { + if (alwaysMakeTags) + { + // + // try and form html + // + s = regexReplace(P_END_ARROW, "", s); + // 不追加结束标签 + s = regexReplace(P_BODY_TO_END, "<$1>", s); + s = regexReplace(P_XML_CONTENT, "$1<$2", s); + + } + else + { + // + // escape stray brackets + // + s = regexReplace(P_STRAY_LEFT_ARROW, "<$1", s); + s = regexReplace(P_STRAY_RIGHT_ARROW, "$1$2><", s); + + // + // the last regexp causes '<>' entities to appear + // (we need to do a lookahead assertion so that the last bracket can + // be used in the next pass of the regexp) + // + s = regexReplace(P_BOTH_ARROWS, "", s); + } + + return s; + } + + private String checkTags(String s) + { + Matcher m = P_TAGS.matcher(s); + + final StringBuffer buf = new StringBuffer(); + while (m.find()) + { + String replaceStr = m.group(1); + replaceStr = processTag(replaceStr); + m.appendReplacement(buf, Matcher.quoteReplacement(replaceStr)); + } + m.appendTail(buf); + + // these get tallied in processTag + // (remember to reset before subsequent calls to filter method) + final StringBuilder sBuilder = new StringBuilder(buf.toString()); + for (String key : vTagCounts.keySet()) + { + for (int ii = 0; ii < vTagCounts.get(key); ii++) + { + sBuilder.append(""); + } + } + s = sBuilder.toString(); + + return s; + } + + private String processRemoveBlanks(final String s) + { + String result = s; + for (String tag : vRemoveBlanks) + { + if (!P_REMOVE_PAIR_BLANKS.containsKey(tag)) + { + P_REMOVE_PAIR_BLANKS.putIfAbsent(tag, Pattern.compile("<" + tag + "(\\s[^>]*)?>")); + } + result = regexReplace(P_REMOVE_PAIR_BLANKS.get(tag), "", result); + if (!P_REMOVE_SELF_BLANKS.containsKey(tag)) + { + P_REMOVE_SELF_BLANKS.putIfAbsent(tag, Pattern.compile("<" + tag + "(\\s[^>]*)?/>")); + } + result = regexReplace(P_REMOVE_SELF_BLANKS.get(tag), "", result); + } + + return result; + } + + private static String regexReplace(final Pattern regex_pattern, final String replacement, final String s) + { + Matcher m = regex_pattern.matcher(s); + return m.replaceAll(replacement); + } + + private String processTag(final String s) + { + // ending tags + Matcher m = P_END_TAG.matcher(s); + if (m.find()) + { + final String name = m.group(1).toLowerCase(); + if (allowed(name)) + { + if (!inArray(name, vSelfClosingTags)) + { + if (vTagCounts.containsKey(name)) + { + vTagCounts.put(name, vTagCounts.get(name) - 1); + return ""; + } + } + } + } + + // starting tags + m = P_START_TAG.matcher(s); + if (m.find()) + { + final String name = m.group(1).toLowerCase(); + final String body = m.group(2); + String ending = m.group(3); + + // debug( "in a starting tag, name='" + name + "'; body='" + body + "'; ending='" + ending + "'" ); + if (allowed(name)) + { + final StringBuilder params = new StringBuilder(); + + final Matcher m2 = P_QUOTED_ATTRIBUTES.matcher(body); + final Matcher m3 = P_UNQUOTED_ATTRIBUTES.matcher(body); + final List paramNames = new ArrayList<>(); + final List paramValues = new ArrayList<>(); + while (m2.find()) + { + paramNames.add(m2.group(1)); // ([a-z0-9]+) + paramValues.add(m2.group(3)); // (.*?) + } + while (m3.find()) + { + paramNames.add(m3.group(1)); // ([a-z0-9]+) + paramValues.add(m3.group(3)); // ([^\"\\s']+) + } + + String paramName, paramValue; + for (int ii = 0; ii < paramNames.size(); ii++) + { + paramName = paramNames.get(ii).toLowerCase(); + paramValue = paramValues.get(ii); + + // debug( "paramName='" + paramName + "'" ); + // debug( "paramValue='" + paramValue + "'" ); + // debug( "allowed? " + vAllowed.get( name ).contains( paramName ) ); + + if (allowedAttribute(name, paramName)) + { + if (inArray(paramName, vProtocolAtts)) + { + paramValue = processParamProtocol(paramValue); + } + params.append(' ').append(paramName).append("=\\\"").append(paramValue).append("\\\""); + } + } + + if (inArray(name, vSelfClosingTags)) + { + ending = " /"; + } + + if (inArray(name, vNeedClosingTags)) + { + ending = ""; + } + + if (ending == null || ending.length() < 1) + { + if (vTagCounts.containsKey(name)) + { + vTagCounts.put(name, vTagCounts.get(name) + 1); + } + else + { + vTagCounts.put(name, 1); + } + } + else + { + ending = " /"; + } + return "<" + name + params + ending + ">"; + } + else + { + return ""; + } + } + + // comments + m = P_COMMENT.matcher(s); + if (!stripComment && m.find()) + { + return "<" + m.group() + ">"; + } + + return ""; + } + + private String processParamProtocol(String s) + { + s = decodeEntities(s); + final Matcher m = P_PROTOCOL.matcher(s); + if (m.find()) + { + final String protocol = m.group(1); + if (!inArray(protocol, vAllowedProtocols)) + { + // bad protocol, turn into local anchor link instead + s = "#" + s.substring(protocol.length() + 1); + if (s.startsWith("#//")) + { + s = "#" + s.substring(3); + } + } + } + + return s; + } + + private String decodeEntities(String s) + { + StringBuffer buf = new StringBuffer(); + + Matcher m = P_ENTITY.matcher(s); + while (m.find()) + { + final String match = m.group(1); + final int decimal = Integer.decode(match).intValue(); + m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal))); + } + m.appendTail(buf); + s = buf.toString(); + + buf = new StringBuffer(); + m = P_ENTITY_UNICODE.matcher(s); + while (m.find()) + { + final String match = m.group(1); + final int decimal = Integer.valueOf(match, 16).intValue(); + m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal))); + } + m.appendTail(buf); + s = buf.toString(); + + buf = new StringBuffer(); + m = P_ENCODE.matcher(s); + while (m.find()) + { + final String match = m.group(1); + final int decimal = Integer.valueOf(match, 16).intValue(); + m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal))); + } + m.appendTail(buf); + s = buf.toString(); + + s = validateEntities(s); + return s; + } + + private String validateEntities(final String s) + { + StringBuffer buf = new StringBuffer(); + + // validate entities throughout the string + Matcher m = P_VALID_ENTITIES.matcher(s); + while (m.find()) + { + final String one = m.group(1); // ([^&;]*) + final String two = m.group(2); // (?=(;|&|$)) + m.appendReplacement(buf, Matcher.quoteReplacement(checkEntity(one, two))); + } + m.appendTail(buf); + + return encodeQuotes(buf.toString()); + } + + private String encodeQuotes(final String s) + { + if (encodeQuotes) + { + StringBuffer buf = new StringBuffer(); + Matcher m = P_VALID_QUOTES.matcher(s); + while (m.find()) + { + final String one = m.group(1); // (>|^) + final String two = m.group(2); // ([^<]+?) + final String three = m.group(3); // (<|$) + // 不替换双引号为",防止json格式无效 regexReplace(P_QUOTE, """, two) + m.appendReplacement(buf, Matcher.quoteReplacement(one + two + three)); + } + m.appendTail(buf); + return buf.toString(); + } + else + { + return s; + } + } + + private String checkEntity(final String preamble, final String term) + { + + return ";".equals(term) && isValidEntity(preamble) ? '&' + preamble : "&" + preamble; + } + + private boolean isValidEntity(final String entity) + { + return inArray(entity, vAllowedEntities); + } + + private static boolean inArray(final String s, final String[] array) + { + for (String item : array) + { + if (item != null && item.equals(s)) + { + return true; + } + } + return false; + } + + private boolean allowed(final String name) + { + return (vAllowed.isEmpty() || vAllowed.containsKey(name)) && !inArray(name, vDisallowed); + } + + private boolean allowedAttribute(final String name, final String paramName) + { + return allowed(name) && (vAllowed.isEmpty() || vAllowed.get(name).contains(paramName)); + } +} \ No newline at end of file diff --git a/portal/backend/portal-common/src/main/java/org/lingniu/portal/common/utils/http/HttpHelper.java b/portal/backend/portal-common/src/main/java/org/lingniu/portal/common/utils/http/HttpHelper.java new file mode 100644 index 0000000..88b9a55 --- /dev/null +++ b/portal/backend/portal-common/src/main/java/org/lingniu/portal/common/utils/http/HttpHelper.java @@ -0,0 +1,55 @@ +package org.lingniu.portal.common.utils.http; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import jakarta.servlet.ServletRequest; +import org.apache.commons.lang3.exception.ExceptionUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 通用http工具封装 + * + * @author lingniu + */ +public class HttpHelper +{ + private static final Logger LOGGER = LoggerFactory.getLogger(HttpHelper.class); + + public static String getBodyString(ServletRequest request) + { + StringBuilder sb = new StringBuilder(); + BufferedReader reader = null; + try (InputStream inputStream = request.getInputStream()) + { + reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)); + String line = ""; + while ((line = reader.readLine()) != null) + { + sb.append(line); + } + } + catch (IOException e) + { + LOGGER.warn("getBodyString出现问题!"); + } + finally + { + if (reader != null) + { + try + { + reader.close(); + } + catch (IOException e) + { + LOGGER.error(ExceptionUtils.getMessage(e)); + } + } + } + return sb.toString(); + } +} diff --git a/portal/backend/portal-common/src/main/java/org/lingniu/portal/common/utils/http/HttpUtils.java b/portal/backend/portal-common/src/main/java/org/lingniu/portal/common/utils/http/HttpUtils.java new file mode 100644 index 0000000..70b2f4c --- /dev/null +++ b/portal/backend/portal-common/src/main/java/org/lingniu/portal/common/utils/http/HttpUtils.java @@ -0,0 +1,293 @@ +package org.lingniu.portal.common.utils.http; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.PrintWriter; +import java.net.ConnectException; +import java.net.SocketTimeoutException; +import java.net.URL; +import java.net.URLConnection; +import java.nio.charset.StandardCharsets; +import java.security.cert.X509Certificate; +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSession; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.lingniu.portal.common.constant.Constants; +import org.lingniu.portal.common.utils.StringUtils; +import org.springframework.http.MediaType; + +/** + * 通用http发送方法 + * + * @author lingniu + */ +public class HttpUtils +{ + private static final Logger log = LoggerFactory.getLogger(HttpUtils.class); + + /** + * 向指定 URL 发送GET方法的请求 + * + * @param url 发送请求的 URL + * @return 所代表远程资源的响应结果 + */ + public static String sendGet(String url) + { + return sendGet(url, StringUtils.EMPTY); + } + + /** + * 向指定 URL 发送GET方法的请求 + * + * @param url 发送请求的 URL + * @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。 + * @return 所代表远程资源的响应结果 + */ + public static String sendGet(String url, String param) + { + return sendGet(url, param, Constants.UTF8); + } + + /** + * 向指定 URL 发送GET方法的请求 + * + * @param url 发送请求的 URL + * @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。 + * @param contentType 编码类型 + * @return 所代表远程资源的响应结果 + */ + public static String sendGet(String url, String param, String contentType) + { + StringBuilder result = new StringBuilder(); + BufferedReader in = null; + try + { + String urlNameString = StringUtils.isNotBlank(param) ? url + "?" + param : url; + log.info("sendGet - {}", urlNameString); + URL realUrl = new URL(urlNameString); + URLConnection connection = realUrl.openConnection(); + connection.setRequestProperty("accept", "*/*"); + connection.setRequestProperty("connection", "Keep-Alive"); + connection.setRequestProperty("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"); + connection.connect(); + in = new BufferedReader(new InputStreamReader(connection.getInputStream(), contentType)); + String line; + while ((line = in.readLine()) != null) + { + result.append(line); + } + log.info("recv - {}", result); + } + catch (ConnectException e) + { + log.error("调用HttpUtils.sendGet ConnectException, url=" + url + ",param=" + param, e); + } + catch (SocketTimeoutException e) + { + log.error("调用HttpUtils.sendGet SocketTimeoutException, url=" + url + ",param=" + param, e); + } + catch (IOException e) + { + log.error("调用HttpUtils.sendGet IOException, url=" + url + ",param=" + param, e); + } + catch (Exception e) + { + log.error("调用HttpsUtil.sendGet Exception, url=" + url + ",param=" + param, e); + } + finally + { + try + { + if (in != null) + { + in.close(); + } + } + catch (Exception ex) + { + log.error("调用in.close Exception, url=" + url + ",param=" + param, ex); + } + } + return result.toString(); + } + + /** + * 向指定 URL 发送POST方法的请求 + * + * @param url 发送请求的 URL + * @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。 + * @return 所代表远程资源的响应结果 + */ + public static String sendPost(String url, String param) + { + return sendPost(url, param, MediaType.APPLICATION_FORM_URLENCODED_VALUE); + } + + /** + * 向指定 URL 发送POST方法的请求 + * + * @param url 发送请求的 URL + * @param param 请求参数 + * @param contentType 内容类型 + * @return 所代表远程资源的响应结果 + */ + public static String sendPost(String url, String param, String contentType) + { + PrintWriter out = null; + BufferedReader in = null; + StringBuilder result = new StringBuilder(); + try + { + log.info("sendPost - {}", url); + URL realUrl = new URL(url); + URLConnection conn = realUrl.openConnection(); + conn.setRequestProperty("accept", "*/*"); + conn.setRequestProperty("connection", "Keep-Alive"); + conn.setRequestProperty("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"); + conn.setRequestProperty("Accept-Charset", "utf-8"); + conn.setRequestProperty("Content-Type", contentType); + conn.setDoOutput(true); + conn.setDoInput(true); + out = new PrintWriter(conn.getOutputStream()); + out.print(param); + out.flush(); + in = new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8)); + String line; + while ((line = in.readLine()) != null) + { + result.append(line); + } + log.info("recv - {}", result); + } + catch (ConnectException e) + { + log.error("调用HttpUtils.sendPost ConnectException, url=" + url + ",param=" + param, e); + } + catch (SocketTimeoutException e) + { + log.error("调用HttpUtils.sendPost SocketTimeoutException, url=" + url + ",param=" + param, e); + } + catch (IOException e) + { + log.error("调用HttpUtils.sendPost IOException, url=" + url + ",param=" + param, e); + } + catch (Exception e) + { + log.error("调用HttpsUtil.sendPost Exception, url=" + url + ",param=" + param, e); + } + finally + { + try + { + if (out != null) + { + out.close(); + } + if (in != null) + { + in.close(); + } + } + catch (IOException ex) + { + log.error("调用in.close Exception, url=" + url + ",param=" + param, ex); + } + } + return result.toString(); + } + + public static String sendSSLPost(String url, String param) + { + return sendSSLPost(url, param, MediaType.APPLICATION_FORM_URLENCODED_VALUE); + } + + public static String sendSSLPost(String url, String param, String contentType) + { + StringBuilder result = new StringBuilder(); + String urlNameString = url + "?" + param; + try + { + log.info("sendSSLPost - {}", urlNameString); + SSLContext sc = SSLContext.getInstance("SSL"); + sc.init(null, new TrustManager[] { new TrustAnyTrustManager() }, new java.security.SecureRandom()); + URL console = new URL(urlNameString); + HttpsURLConnection conn = (HttpsURLConnection) console.openConnection(); + conn.setRequestProperty("accept", "*/*"); + conn.setRequestProperty("connection", "Keep-Alive"); + conn.setRequestProperty("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"); + conn.setRequestProperty("Accept-Charset", "utf-8"); + conn.setRequestProperty("Content-Type", contentType); + conn.setDoOutput(true); + conn.setDoInput(true); + + conn.setSSLSocketFactory(sc.getSocketFactory()); + conn.setHostnameVerifier(new TrustAnyHostnameVerifier()); + conn.connect(); + InputStream is = conn.getInputStream(); + BufferedReader br = new BufferedReader(new InputStreamReader(is)); + String ret = ""; + while ((ret = br.readLine()) != null) + { + if (ret != null && !"".equals(ret.trim())) + { + result.append(new String(ret.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8)); + } + } + log.info("recv - {}", result); + conn.disconnect(); + br.close(); + } + catch (ConnectException e) + { + log.error("调用HttpUtils.sendSSLPost ConnectException, url=" + url + ",param=" + param, e); + } + catch (SocketTimeoutException e) + { + log.error("调用HttpUtils.sendSSLPost SocketTimeoutException, url=" + url + ",param=" + param, e); + } + catch (IOException e) + { + log.error("调用HttpUtils.sendSSLPost IOException, url=" + url + ",param=" + param, e); + } + catch (Exception e) + { + log.error("调用HttpsUtil.sendSSLPost Exception, url=" + url + ",param=" + param, e); + } + return result.toString(); + } + + private static class TrustAnyTrustManager implements X509TrustManager + { + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType) + { + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType) + { + } + + @Override + public X509Certificate[] getAcceptedIssuers() + { + return new X509Certificate[] {}; + } + } + + private static class TrustAnyHostnameVerifier implements HostnameVerifier + { + @Override + public boolean verify(String hostname, SSLSession session) + { + return true; + } + } +} \ No newline at end of file diff --git a/portal/backend/portal-common/src/main/java/org/lingniu/portal/common/utils/http/UserAgentUtils.java b/portal/backend/portal-common/src/main/java/org/lingniu/portal/common/utils/http/UserAgentUtils.java new file mode 100644 index 0000000..4757d19 --- /dev/null +++ b/portal/backend/portal-common/src/main/java/org/lingniu/portal/common/utils/http/UserAgentUtils.java @@ -0,0 +1,254 @@ +package org.lingniu.portal.common.utils.http; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import org.lingniu.portal.common.utils.StringUtils; +import nl.basjes.parse.useragent.UserAgent; +import nl.basjes.parse.useragent.UserAgentAnalyzer; + +/** + * UserAgent解析工具类 + * + * @author lingniu + */ +public class UserAgentUtils +{ + public static final String UNKNOWN = ""; + + // 浏览器正则表达式模式 + private static final Pattern CHROME_PATTERN = Pattern.compile("Chrome/(\\d+)(?:\\.\\d+)*"); + private static final Pattern FIREFOX_PATTERN = Pattern.compile("Firefox/(\\d+)(?:\\.\\d+)*"); + private static final Pattern EDGE_PATTERN = Pattern.compile("Edg(?:e)?/(\\d+)(?:\\.\\d+)*"); + private static final Pattern SAFARI_PATTERN = Pattern.compile("Version/(\\d+)(?:\\.\\d+)*"); + private static final Pattern OPERA_PATTERN = Pattern.compile("Opera/(\\d+)(?:\\.\\d+)*"); + private static final Pattern IE_PATTERN = Pattern.compile("(?:MSIE |Trident/.*rv:)(\\d+)(?:\\.\\d+)*"); + private static final Pattern SAMSUNG_PATTERN = Pattern.compile("SamsungBrowser/(\\d+)(?:\\.\\d+)*"); + private static final Pattern UC_PATTERN = Pattern.compile("UCBrowser/(\\d+)(?:\\.\\d+)*"); + private static final Pattern QQ_PATTERN = Pattern.compile("QQBrowser/(\\d+)(?:\\.\\d+)*"); + private static final Pattern WECHAT_PATTERN = Pattern.compile("MicroMessenger/(\\d+)(?:\\.\\d+)*"); + private static final Pattern BAIDU_PATTERN = Pattern.compile("baidubrowser/(\\d+)(?:\\.\\d+)*"); + + // 操作系统正则表达式模式 + private static final Pattern WINDOWS_PATTERN = Pattern.compile("Windows NT (\\d+\\.\\d+)"); + private static final Pattern MACOS_PATTERN = Pattern.compile("Mac OS X (\\d+[_\\d]*)"); + private static final Pattern ANDROID_PATTERN = Pattern.compile("Android (\\d+)(?:\\.\\d+)*"); + private static final Pattern IOS_PATTERN = Pattern.compile("OS[\\s_](\\d+)(?:_\\d+)*"); + private static final Pattern LINUX_PATTERN = Pattern.compile("Linux"); + private static final Pattern CHROMEOS_PATTERN = Pattern.compile("CrOS"); + + private static final UserAgentAnalyzer userAgentAnalyzer = UserAgentAnalyzer + .newBuilder().hideMatcherLoadStats() + .withCache(5000) + .showMinimalVersion() + .withField(UserAgent.AGENT_NAME_VERSION) + .withField(UserAgent.OPERATING_SYSTEM_NAME_VERSION) + .build(); + + /** + * 获取客户端浏览器 + */ + public static String getBrowser(String userAgent) + { + UserAgent.ImmutableUserAgent iua = userAgentAnalyzer.parse(userAgent); + String agentNameVersion = iua.get(UserAgent.AGENT_NAME_VERSION).getValue(); + if (StringUtils.isBlank(agentNameVersion) || agentNameVersion.contains("??")) + { + return formatBrowser(userAgent); + } + return agentNameVersion; + } + + /** + * 获取客户端操作系统 + */ + public static String getOperatingSystem(String userAgent) + { + UserAgent.ImmutableUserAgent iua = userAgentAnalyzer.parse(userAgent); + String operatingSystemNameVersion = iua.get(UserAgent.OPERATING_SYSTEM_NAME_VERSION).getValue(); + if (StringUtils.isBlank(operatingSystemNameVersion) || operatingSystemNameVersion.contains("??")) + { + return formatOperatingSystem(userAgent); + } + return operatingSystemNameVersion; + } + + /** + * 全面浏览器检测 + */ + private static String formatBrowser(String browser) + { + // Chrome系列浏览器 + Matcher chromeMatcher = CHROME_PATTERN.matcher(browser); + if (chromeMatcher.find() && (browser.contains("Chrome") || browser.contains("CriOS"))) + { + return "Chrome" + chromeMatcher.group(1); + } + // Firefox + Matcher firefoxMatcher = FIREFOX_PATTERN.matcher(browser); + if (firefoxMatcher.find()) + { + return "Firefox" + firefoxMatcher.group(1); + } + // Edge浏览器 + Matcher edgeMatcher = EDGE_PATTERN.matcher(browser); + if (edgeMatcher.find()) + { + return "Edge" + edgeMatcher.group(1); + } + // Safari浏览器(需排除Chrome) + Matcher safariMatcher = SAFARI_PATTERN.matcher(browser); + if (safariMatcher.find() && !browser.contains("Chrome")) + { + return "Safari" + safariMatcher.group(1); + } + // 微信内置浏览器 + Matcher wechatMatcher = WECHAT_PATTERN.matcher(browser); + if (wechatMatcher.find()) + { + return "WeChat" + wechatMatcher.group(1); + } + // UC浏览器 + Matcher ucMatcher = UC_PATTERN.matcher(browser); + if (ucMatcher.find()) + { + return "UC Browser" + ucMatcher.group(1); + } + // QQ浏览器 + Matcher qqMatcher = QQ_PATTERN.matcher(browser); + if (qqMatcher.find()) + { + return "QQ Browser" + qqMatcher.group(1); + } + // 百度浏览器 + Matcher baiduMatcher = BAIDU_PATTERN.matcher(browser); + if (baiduMatcher.find()) + { + return "Baidu Browser" + baiduMatcher.group(1); + } + // Samsung浏览器 + Matcher samsungMatcher = SAMSUNG_PATTERN.matcher(browser); + if (samsungMatcher.find()) + { + return "Samsung Browser" + samsungMatcher.group(1); + } + // Opera浏览器 + Matcher operaMatcher = OPERA_PATTERN.matcher(browser); + if (operaMatcher.find()) + { + return "Opera" + operaMatcher.group(1); + } + // IE浏览器 + Matcher ieMatcher = IE_PATTERN.matcher(browser); + if (ieMatcher.find()) + { + return "Internet Explorer" + ieMatcher.group(1); + } + return UNKNOWN; + } + + /** + * 检测操作系统 + */ + private static String formatOperatingSystem(String operatingSystem) + { + // Windows系统 + Matcher windowsMatcher = WINDOWS_PATTERN.matcher(operatingSystem); + if (windowsMatcher.find()) + { + return "Windows" + getWindowsVersionDisplay(windowsMatcher.group(1)); + } + // macOS系统 + Matcher macMatcher = MACOS_PATTERN.matcher(operatingSystem); + if (macMatcher.find()) + { + String version = macMatcher.group(1).replace("_", "."); + return "macOS" + extractMajorVersion(version); + } + // Android系统 + Matcher androidMatcher = ANDROID_PATTERN.matcher(operatingSystem); + if (androidMatcher.find()) + { + return "Android" + extractMajorVersion(androidMatcher.group(1)); + } + // iOS系统 + Matcher iosMatcher = IOS_PATTERN.matcher(operatingSystem); + if (iosMatcher.find() && (operatingSystem.contains("iPhone") || operatingSystem.contains("iPad"))) + { + return "iOS" + extractMajorVersion(iosMatcher.group(1)); + } + // Linux系统 + if (LINUX_PATTERN.matcher(operatingSystem).find() && !operatingSystem.contains("Android")) + { + return "Linux"; + } + // Chrome OS + if (CHROMEOS_PATTERN.matcher(operatingSystem).find()) + { + return "Chrome OS"; + } + return UNKNOWN; + } + + /** + * 提取优化的主版本号 + */ + private static String extractMajorVersion(String fullVersion) + { + if (StringUtils.isEmpty(fullVersion)) + { + return StringUtils.EMPTY; + } + try + { + // 清理版本号中的非数字字符 + String cleanVersion = fullVersion.replaceAll("[^0-9.]", ""); + String[] parts = cleanVersion.split("\\."); + if (parts.length > 0) + { + String firstPart = parts[0]; + if (firstPart.matches("\\d+")) + { + int version = Integer.parseInt(firstPart); + + // 处理三位数版本号(如142 -> 14) + if (version >= 100) + { + return String.valueOf(version / 10); + } + return firstPart; + } + } + } + catch (NumberFormatException e) + { + // 版本号解析失败,返回原始值 + } + return fullVersion; + } + + /** + * Windows版本号显示优化 + */ + private static String getWindowsVersionDisplay(String version) + { + switch (version) + { + case "10.0": + return "10"; + case "6.3": + return "8.1"; + case "6.2": + return "8"; + case "6.1": + return "7"; + case "6.0": + return "Vista"; + case "5.1": + return "XP"; + case "5.0": + return "2000"; + default: + return extractMajorVersion(version); + } + } +} diff --git a/portal/backend/portal-common/src/main/java/org/lingniu/portal/common/utils/ip/AddressUtils.java b/portal/backend/portal-common/src/main/java/org/lingniu/portal/common/utils/ip/AddressUtils.java new file mode 100644 index 0000000..e97c845 --- /dev/null +++ b/portal/backend/portal-common/src/main/java/org/lingniu/portal/common/utils/ip/AddressUtils.java @@ -0,0 +1,56 @@ +package org.lingniu.portal.common.utils.ip; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import org.lingniu.portal.common.config.ProjectConfig; +import org.lingniu.portal.common.constant.Constants; +import org.lingniu.portal.common.utils.StringUtils; +import org.lingniu.portal.common.utils.http.HttpUtils; + +/** + * 获取地址类 + * + * @author lingniu + */ +public class AddressUtils +{ + private static final Logger log = LoggerFactory.getLogger(AddressUtils.class); + + // IP地址查询 + public static final String IP_URL = "http://whois.pconline.com.cn/ipJson.jsp"; + + // 未知地址 + public static final String UNKNOWN = "XX XX"; + + public static String getRealAddressByIP(String ip) + { + // 内网不查询 + if (IpUtils.internalIp(ip)) + { + return "内网IP"; + } + if (ProjectConfig.isAddressEnabled()) + { + try + { + String rspStr = HttpUtils.sendGet(IP_URL, "ip=" + ip + "&json=true", Constants.GBK); + if (StringUtils.isEmpty(rspStr)) + { + log.error("获取地理位置异常 {}", ip); + return UNKNOWN; + } + JSONObject obj = JSON.parseObject(rspStr); + String region = obj.getString("pro"); + String city = obj.getString("city"); + return String.format("%s %s", region, city); + } + catch (Exception e) + { + log.error("获取地理位置异常 {}", ip); + } + } + return UNKNOWN; + } +} diff --git a/portal/backend/portal-common/src/main/java/org/lingniu/portal/common/utils/ip/IpUtils.java b/portal/backend/portal-common/src/main/java/org/lingniu/portal/common/utils/ip/IpUtils.java new file mode 100644 index 0000000..d1f4b8c --- /dev/null +++ b/portal/backend/portal-common/src/main/java/org/lingniu/portal/common/utils/ip/IpUtils.java @@ -0,0 +1,382 @@ +package org.lingniu.portal.common.utils.ip; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import jakarta.servlet.http.HttpServletRequest; +import org.lingniu.portal.common.utils.ServletUtils; +import org.lingniu.portal.common.utils.StringUtils; + +/** + * 获取IP方法 + * + * @author lingniu + */ +public class IpUtils +{ + public final static String REGX_0_255 = "(25[0-5]|2[0-4]\\d|1\\d{2}|[1-9]\\d|\\d)"; + // 匹配 ip + public final static String REGX_IP = "((" + REGX_0_255 + "\\.){3}" + REGX_0_255 + ")"; + public final static String REGX_IP_WILDCARD = "(((\\*\\.){3}\\*)|(" + REGX_0_255 + "(\\.\\*){3})|(" + REGX_0_255 + "\\." + REGX_0_255 + ")(\\.\\*){2}" + "|((" + REGX_0_255 + "\\.){3}\\*))"; + // 匹配网段 + public final static String REGX_IP_SEG = "(" + REGX_IP + "\\-" + REGX_IP + ")"; + + /** + * 获取客户端IP + * + * @return IP地址 + */ + public static String getIpAddr() + { + return getIpAddr(ServletUtils.getRequest()); + } + + /** + * 获取客户端IP + * + * @param request 请求对象 + * @return IP地址 + */ + public static String getIpAddr(HttpServletRequest request) + { + if (request == null) + { + return "unknown"; + } + String ip = request.getHeader("x-forwarded-for"); + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) + { + ip = request.getHeader("Proxy-Client-IP"); + } + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) + { + ip = request.getHeader("X-Forwarded-For"); + } + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) + { + ip = request.getHeader("WL-Proxy-Client-IP"); + } + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) + { + ip = request.getHeader("X-Real-IP"); + } + + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) + { + ip = request.getRemoteAddr(); + } + + return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : getMultistageReverseProxyIp(ip); + } + + /** + * 检查是否为内部IP地址 + * + * @param ip IP地址 + * @return 结果 + */ + public static boolean internalIp(String ip) + { + byte[] addr = textToNumericFormatV4(ip); + return internalIp(addr) || "127.0.0.1".equals(ip); + } + + /** + * 检查是否为内部IP地址 + * + * @param addr byte地址 + * @return 结果 + */ + private static boolean internalIp(byte[] addr) + { + if (StringUtils.isNull(addr) || addr.length < 2) + { + return true; + } + final byte b0 = addr[0]; + final byte b1 = addr[1]; + // 10.x.x.x/8 + final byte SECTION_1 = 0x0A; + // 172.16.x.x/12 + final byte SECTION_2 = (byte) 0xAC; + final byte SECTION_3 = (byte) 0x10; + final byte SECTION_4 = (byte) 0x1F; + // 192.168.x.x/16 + final byte SECTION_5 = (byte) 0xC0; + final byte SECTION_6 = (byte) 0xA8; + switch (b0) + { + case SECTION_1: + return true; + case SECTION_2: + if (b1 >= SECTION_3 && b1 <= SECTION_4) + { + return true; + } + case SECTION_5: + switch (b1) + { + case SECTION_6: + return true; + } + default: + return false; + } + } + + /** + * 将IPv4地址转换成字节 + * + * @param text IPv4地址 + * @return byte 字节 + */ + public static byte[] textToNumericFormatV4(String text) + { + if (text.length() == 0) + { + return null; + } + + byte[] bytes = new byte[4]; + String[] elements = text.split("\\.", -1); + try + { + long l; + int i; + switch (elements.length) + { + case 1: + l = Long.parseLong(elements[0]); + if ((l < 0L) || (l > 4294967295L)) + { + return null; + } + bytes[0] = (byte) (int) (l >> 24 & 0xFF); + bytes[1] = (byte) (int) ((l & 0xFFFFFF) >> 16 & 0xFF); + bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF); + bytes[3] = (byte) (int) (l & 0xFF); + break; + case 2: + l = Integer.parseInt(elements[0]); + if ((l < 0L) || (l > 255L)) + { + return null; + } + bytes[0] = (byte) (int) (l & 0xFF); + l = Integer.parseInt(elements[1]); + if ((l < 0L) || (l > 16777215L)) + { + return null; + } + bytes[1] = (byte) (int) (l >> 16 & 0xFF); + bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF); + bytes[3] = (byte) (int) (l & 0xFF); + break; + case 3: + for (i = 0; i < 2; ++i) + { + l = Integer.parseInt(elements[i]); + if ((l < 0L) || (l > 255L)) + { + return null; + } + bytes[i] = (byte) (int) (l & 0xFF); + } + l = Integer.parseInt(elements[2]); + if ((l < 0L) || (l > 65535L)) + { + return null; + } + bytes[2] = (byte) (int) (l >> 8 & 0xFF); + bytes[3] = (byte) (int) (l & 0xFF); + break; + case 4: + for (i = 0; i < 4; ++i) + { + l = Integer.parseInt(elements[i]); + if ((l < 0L) || (l > 255L)) + { + return null; + } + bytes[i] = (byte) (int) (l & 0xFF); + } + break; + default: + return null; + } + } + catch (NumberFormatException e) + { + return null; + } + return bytes; + } + + /** + * 获取IP地址 + * + * @return 本地IP地址 + */ + public static String getHostIp() + { + try + { + return InetAddress.getLocalHost().getHostAddress(); + } + catch (UnknownHostException e) + { + } + return "127.0.0.1"; + } + + /** + * 获取主机名 + * + * @return 本地主机名 + */ + public static String getHostName() + { + try + { + return InetAddress.getLocalHost().getHostName(); + } + catch (UnknownHostException e) + { + } + return "未知"; + } + + /** + * 从多级反向代理中获得第一个非unknown IP地址 + * + * @param ip 获得的IP地址 + * @return 第一个非unknown IP地址 + */ + public static String getMultistageReverseProxyIp(String ip) + { + // 多级反向代理检测 + if (ip != null && ip.indexOf(",") > 0) + { + final String[] ips = ip.trim().split(","); + for (String subIp : ips) + { + if (false == isUnknown(subIp)) + { + ip = subIp; + break; + } + } + } + return StringUtils.substring(ip, 0, 255); + } + + /** + * 检测给定字符串是否为未知,多用于检测HTTP请求相关 + * + * @param checkString 被检测的字符串 + * @return 是否未知 + */ + public static boolean isUnknown(String checkString) + { + return StringUtils.isBlank(checkString) || "unknown".equalsIgnoreCase(checkString); + } + + /** + * 是否为IP + */ + public static boolean isIP(String ip) + { + return StringUtils.isNotBlank(ip) && ip.matches(REGX_IP); + } + + /** + * 是否为IP,或 *为间隔的通配符地址 + */ + public static boolean isIpWildCard(String ip) + { + return StringUtils.isNotBlank(ip) && ip.matches(REGX_IP_WILDCARD); + } + + /** + * 检测参数是否在ip通配符里 + */ + public static boolean ipIsInWildCardNoCheck(String ipWildCard, String ip) + { + String[] s1 = ipWildCard.split("\\."); + String[] s2 = ip.split("\\."); + boolean isMatchedSeg = true; + for (int i = 0; i < s1.length && !s1[i].equals("*"); i++) + { + if (!s1[i].equals(s2[i])) + { + isMatchedSeg = false; + break; + } + } + return isMatchedSeg; + } + + /** + * 是否为特定格式如:“10.10.10.1-10.10.10.99”的ip段字符串 + */ + public static boolean isIPSegment(String ipSeg) + { + return StringUtils.isNotBlank(ipSeg) && ipSeg.matches(REGX_IP_SEG); + } + + /** + * 判断ip是否在指定网段中 + */ + public static boolean ipIsInNetNoCheck(String iparea, String ip) + { + int idx = iparea.indexOf('-'); + String[] sips = iparea.substring(0, idx).split("\\."); + String[] sipe = iparea.substring(idx + 1).split("\\."); + String[] sipt = ip.split("\\."); + long ips = 0L, ipe = 0L, ipt = 0L; + for (int i = 0; i < 4; ++i) + { + ips = ips << 8 | Integer.parseInt(sips[i]); + ipe = ipe << 8 | Integer.parseInt(sipe[i]); + ipt = ipt << 8 | Integer.parseInt(sipt[i]); + } + if (ips > ipe) + { + long t = ips; + ips = ipe; + ipe = t; + } + return ips <= ipt && ipt <= ipe; + } + + /** + * 校验ip是否符合过滤串规则 + * + * @param filter 过滤IP列表,支持后缀'*'通配,支持网段如:`10.10.10.1-10.10.10.99` + * @param ip 校验IP地址 + * @return boolean 结果 + */ + public static boolean isMatchedIp(String filter, String ip) + { + if (StringUtils.isEmpty(filter) || StringUtils.isEmpty(ip)) + { + return false; + } + String[] ips = filter.split(";"); + for (String iStr : ips) + { + if (isIP(iStr) && iStr.equals(ip)) + { + return true; + } + else if (isIpWildCard(iStr) && ipIsInWildCardNoCheck(iStr, ip)) + { + return true; + } + else if (isIPSegment(iStr) && ipIsInNetNoCheck(iStr, ip)) + { + return true; + } + } + return false; + } +} \ No newline at end of file diff --git a/portal/backend/portal-common/src/main/java/org/lingniu/portal/common/utils/poi/ExcelHandlerAdapter.java b/portal/backend/portal-common/src/main/java/org/lingniu/portal/common/utils/poi/ExcelHandlerAdapter.java new file mode 100644 index 0000000..6f8fec3 --- /dev/null +++ b/portal/backend/portal-common/src/main/java/org/lingniu/portal/common/utils/poi/ExcelHandlerAdapter.java @@ -0,0 +1,24 @@ +package org.lingniu.portal.common.utils.poi; + +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.Workbook; + +/** + * Excel数据格式处理适配器 + * + * @author lingniu + */ +public interface ExcelHandlerAdapter +{ + /** + * 格式化 + * + * @param value 单元格数据值 + * @param args excel注解args参数组 + * @param cell 单元格对象 + * @param wb 工作簿对象 + * + * @return 处理后的值 + */ + Object format(Object value, String[] args, Cell cell, Workbook wb); +} diff --git a/portal/backend/portal-common/src/main/java/org/lingniu/portal/common/utils/poi/ExcelUtil.java b/portal/backend/portal-common/src/main/java/org/lingniu/portal/common/utils/poi/ExcelUtil.java new file mode 100644 index 0000000..50a4234 --- /dev/null +++ b/portal/backend/portal-common/src/main/java/org/lingniu/portal/common/utils/poi/ExcelUtil.java @@ -0,0 +1,1944 @@ +package org.lingniu.portal.common.utils.poi; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; +import java.math.BigDecimal; +import java.text.DecimalFormat; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Comparator; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; +import jakarta.servlet.http.HttpServletResponse; +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.RegExUtils; +import org.apache.commons.lang3.reflect.FieldUtils; +import org.apache.poi.hssf.usermodel.HSSFClientAnchor; +import org.apache.poi.hssf.usermodel.HSSFPicture; +import org.apache.poi.hssf.usermodel.HSSFPictureData; +import org.apache.poi.hssf.usermodel.HSSFShape; +import org.apache.poi.hssf.usermodel.HSSFSheet; +import org.apache.poi.hssf.usermodel.HSSFWorkbook; +import org.apache.poi.ooxml.POIXMLDocumentPart; +import org.apache.poi.ss.usermodel.BorderStyle; +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.CellStyle; +import org.apache.poi.ss.usermodel.CellType; +import org.apache.poi.ss.usermodel.ClientAnchor; +import org.apache.poi.ss.usermodel.DataFormat; +import org.apache.poi.ss.usermodel.DataValidation; +import org.apache.poi.ss.usermodel.DataValidationConstraint; +import org.apache.poi.ss.usermodel.DataValidationHelper; +import org.apache.poi.ss.usermodel.DateUtil; +import org.apache.poi.ss.usermodel.Drawing; +import org.apache.poi.ss.usermodel.FillPatternType; +import org.apache.poi.ss.usermodel.Font; +import org.apache.poi.ss.usermodel.HorizontalAlignment; +import org.apache.poi.ss.usermodel.IndexedColors; +import org.apache.poi.ss.usermodel.Name; +import org.apache.poi.ss.usermodel.PictureData; +import org.apache.poi.ss.usermodel.Row; +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.ss.usermodel.VerticalAlignment; +import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.ss.usermodel.WorkbookFactory; +import org.apache.poi.ss.util.CellRangeAddress; +import org.apache.poi.ss.util.CellRangeAddressList; +import org.apache.poi.util.IOUtils; +import org.apache.poi.xssf.streaming.SXSSFWorkbook; +import org.apache.poi.xssf.usermodel.XSSFClientAnchor; +import org.apache.poi.xssf.usermodel.XSSFDataValidation; +import org.apache.poi.xssf.usermodel.XSSFDrawing; +import org.apache.poi.xssf.usermodel.XSSFPicture; +import org.apache.poi.xssf.usermodel.XSSFShape; +import org.apache.poi.xssf.usermodel.XSSFSheet; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; +import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTMarker; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.lingniu.portal.common.annotation.Excel; +import org.lingniu.portal.common.annotation.Excel.ColumnType; +import org.lingniu.portal.common.annotation.Excel.Type; +import org.lingniu.portal.common.annotation.Excels; +import org.lingniu.portal.common.config.ProjectConfig; +import org.lingniu.portal.common.core.domain.AjaxResult; +import org.lingniu.portal.common.core.text.Convert; +import org.lingniu.portal.common.exception.UtilException; +import org.lingniu.portal.common.utils.DateUtils; +import org.lingniu.portal.common.utils.DictUtils; +import org.lingniu.portal.common.utils.StringUtils; +import org.lingniu.portal.common.utils.file.FileTypeUtils; +import org.lingniu.portal.common.utils.file.FileUtils; +import org.lingniu.portal.common.utils.file.ImageUtils; +import org.lingniu.portal.common.utils.reflect.ReflectUtils; + +/** + * Excel相关处理 + * + * @author lingniu + */ +public class ExcelUtil +{ + private static final Logger log = LoggerFactory.getLogger(ExcelUtil.class); + + public static final String SEPARATOR = ","; + + public static final String FORMULA_REGEX_STR = "=|-|\\+|@"; + + public static final String[] FORMULA_STR = { "=", "-", "+", "@" }; + + /** + * 用于dictType属性数据存储,避免重复查缓存 + */ + public Map sysDictMap = new HashMap(); + + /** + * Excel sheet最大行数,默认65536 + */ + public static final int sheetSize = 65536; + + /** + * 工作表名称 + */ + private String sheetName; + + /** + * 导出类型(EXPORT:导出数据;IMPORT:导入模板) + */ + private Type type; + + /** + * 工作薄对象 + */ + private Workbook wb; + + /** + * 工作表对象 + */ + private Sheet sheet; + + /** + * 样式列表 + */ + private Map styles; + + /** + * 导入导出数据列表 + */ + private List list; + + /** + * 注解列表 + */ + private List fields; + + /** + * 当前行号 + */ + private int rownum; + + /** + * 标题 + */ + private String title; + + /** + * 最大高度 + */ + private short maxHeight; + + /** + * 合并后最后行数 + */ + private int subMergedLastRowNum = 0; + + /** + * 合并后开始行数 + */ + private int subMergedFirstRowNum = 1; + + /** + * 对象的子列表方法 + */ + private Map subMethods; + + /** + * 对象的子列表属性 + */ + private Map> subFieldsMap; + + /** + * 统计列表 + */ + private Map statistics = new HashMap(); + + /** + * 实体对象 + */ + public Class clazz; + + /** + * 需要显示列属性 + */ + public String[] includeFields; + + /** + * 需要排除列属性 + */ + public String[] excludeFields; + + public ExcelUtil(Class clazz) + { + this.clazz = clazz; + } + + /** + * 仅在Excel中显示列属性 + * + * @param fields 列属性名 示例[单个"name"/多个"id","name"] + */ + public void showColumn(String... fields) + { + this.includeFields = fields; + } + + /** + * 隐藏Excel中列属性 + * + * @param fields 列属性名 示例[单个"name"/多个"id","name"] + */ + public void hideColumn(String... fields) + { + this.excludeFields = fields; + } + + public void init(List list, String sheetName, String title, Type type) + { + if (list == null) + { + list = new ArrayList(); + } + this.list = list; + this.sheetName = sheetName; + this.type = type; + this.title = title; + createExcelField(); + createWorkbook(); + createTitle(); + createSubHead(); + } + + /** + * 创建excel第一行标题 + */ + public void createTitle() + { + if (StringUtils.isNotEmpty(title)) + { + int titleLastCol = this.fields.size() - 1; + if (isSubList()) + { + for (List currentSubFields : subFieldsMap.values()) + { + titleLastCol = titleLastCol + currentSubFields.size() - 1; + } + } + Row titleRow = sheet.createRow(rownum == 0 ? rownum++ : 0); + titleRow.setHeightInPoints(30); + Cell titleCell = titleRow.createCell(0); + titleCell.setCellStyle(styles.get("title")); + titleCell.setCellValue(title); + sheet.addMergedRegion(new CellRangeAddress(titleRow.getRowNum(), titleRow.getRowNum(), 0, titleLastCol)); + } + } + + /** + * 创建对象的子列表名称 + */ + public void createSubHead() + { + if (isSubList()) + { + Row subRow = sheet.createRow(rownum); + int column = 0; + for (Object[] objects : fields) + { + Field field = (Field) objects[0]; + Excel attr = (Excel) objects[1]; + CellStyle cellStyle = styles.get(StringUtils.format("header_{}_{}", attr.headerColor(), attr.headerBackgroundColor())); + if (Collection.class.isAssignableFrom(field.getType())) + { + Cell cell = subRow.createCell(column); + cell.setCellValue(attr.name()); + cell.setCellStyle(cellStyle); + int subFieldSize = subFieldsMap != null ? subFieldsMap.get(field.getName()).size() : 0; + if (subFieldSize > 1) + { + CellRangeAddress cellAddress = new CellRangeAddress(rownum, rownum, column, column + subFieldSize - 1); + sheet.addMergedRegion(cellAddress); + } + column += subFieldSize; + } + else + { + Cell cell = subRow.createCell(column++); + cell.setCellValue(attr.name()); + cell.setCellStyle(cellStyle); + } + } + rownum++; + } + } + + /** + * 对excel表单默认第一个索引名转换成list + * + * @param is 输入流 + * @return 转换后集合 + */ + public List importExcel(InputStream is) + { + return importExcel(is, 0); + } + + /** + * 对excel表单默认第一个索引名转换成list + * + * @param is 输入流 + * @param titleNum 标题占用行数 + * @return 转换后集合 + */ + public List importExcel(InputStream is, int titleNum) + { + List list = null; + try + { + list = importExcel(StringUtils.EMPTY, is, titleNum); + } + catch (Exception e) + { + log.error("导入Excel异常{}", e.getMessage()); + throw new UtilException(e.getMessage()); + } + finally + { + IOUtils.closeQuietly(is); + } + return list; + } + + /** + * 对excel表单指定表格索引名转换成list + * + * @param sheetName 表格索引名 + * @param titleNum 标题占用行数 + * @param is 输入流 + * @return 转换后集合 + */ + public List importExcel(String sheetName, InputStream is, int titleNum) throws Exception + { + this.type = Type.IMPORT; + this.wb = WorkbookFactory.create(is); + List list = new ArrayList(); + // 如果指定sheet名,则取指定sheet中的内容 否则默认指向第1个sheet + Sheet sheet = StringUtils.isNotEmpty(sheetName) ? wb.getSheet(sheetName) : wb.getSheetAt(0); + if (sheet == null) + { + throw new IOException("文件sheet不存在"); + } + boolean isXSSFWorkbook = !(wb instanceof HSSFWorkbook); + Map> pictures = null; + if (isXSSFWorkbook) + { + pictures = getSheetPictures07((XSSFSheet) sheet, (XSSFWorkbook) wb); + } + else + { + pictures = getSheetPictures03((HSSFSheet) sheet, (HSSFWorkbook) wb); + } + // 获取最后一个非空行的行下标,比如总行数为n,则返回的为n-1 + int rows = sheet.getLastRowNum(); + if (rows > 0) + { + // 定义一个map用于存放excel列的序号和field. + Map cellMap = new HashMap(); + // 获取表头 + Row heard = sheet.getRow(titleNum); + if (heard == null) + { + throw new UtilException("文件标题行为空,请检查Excel文件格式"); + } + for (int i = 0; i < heard.getLastCellNum(); i++) + { + Cell cell = heard.getCell(i); + if (StringUtils.isNotNull(cell)) + { + String value = this.getCellValue(heard, i).toString(); + cellMap.put(value, i); + } + } + // 有数据时才处理 得到类的所有field. + List fields = this.getFields(); + Map fieldsMap = new HashMap(); + for (Object[] objects : fields) + { + Excel attr = (Excel) objects[1]; + Integer column = cellMap.get(attr.name()); + if (column != null) + { + fieldsMap.put(column, objects); + } + } + for (int i = titleNum + 1; i <= rows; i++) + { + // 从第2行开始取数据,默认第一行是表头. + Row row = sheet.getRow(i); + // 判断当前行是否是空行 + if (isRowEmpty(row)) + { + continue; + } + T entity = null; + for (Map.Entry entry : fieldsMap.entrySet()) + { + Object val = this.getCellValue(row, entry.getKey()); + + // 如果不存在实例则新建. + entity = (entity == null ? clazz.getDeclaredConstructor().newInstance() : entity); + // 从map中得到对应列的field. + Field field = (Field) entry.getValue()[0]; + Excel attr = (Excel) entry.getValue()[1]; + // 取得类型,并根据对象类型设置值. + Class fieldType = field.getType(); + if (String.class == fieldType) + { + String s = Convert.toStr(val); + if (s.matches("^\\d+\\.0$")) + { + val = StringUtils.substringBefore(s, ".0"); + } + else + { + String dateFormat = field.getAnnotation(Excel.class).dateFormat(); + if (StringUtils.isNotEmpty(dateFormat)) + { + val = parseDateToStr(dateFormat, val); + } + else + { + val = Convert.toStr(val); + } + } + } + else if ((Integer.TYPE == fieldType || Integer.class == fieldType) && StringUtils.isNumeric(Convert.toStr(val))) + { + val = Convert.toInt(val); + } + else if ((Long.TYPE == fieldType || Long.class == fieldType) && StringUtils.isNumeric(Convert.toStr(val))) + { + val = Convert.toLong(val); + } + else if (Double.TYPE == fieldType || Double.class == fieldType) + { + val = Convert.toDouble(val); + } + else if (Float.TYPE == fieldType || Float.class == fieldType) + { + val = Convert.toFloat(val); + } + else if (BigDecimal.class == fieldType) + { + val = Convert.toBigDecimal(val); + } + else if (Date.class == fieldType) + { + if (val instanceof String) + { + val = DateUtils.parseDate(val); + } + else if (val instanceof Double) + { + val = DateUtil.getJavaDate((Double) val); + } + } + else if (Boolean.TYPE == fieldType || Boolean.class == fieldType) + { + val = Convert.toBool(val, false); + } + if (StringUtils.isNotNull(fieldType)) + { + String propertyName = field.getName(); + if (StringUtils.isNotEmpty(attr.targetAttr())) + { + propertyName = field.getName() + "." + attr.targetAttr(); + } + if (StringUtils.isNotEmpty(attr.readConverterExp())) + { + val = reverseByExp(Convert.toStr(val), attr.readConverterExp(), attr.separator()); + } + else if (StringUtils.isNotEmpty(attr.dictType())) + { + if (!sysDictMap.containsKey(attr.dictType() + val)) + { + String dictValue = reverseDictByExp(Convert.toStr(val), attr.dictType(), attr.separator()); + sysDictMap.put(attr.dictType() + val, dictValue); + } + val = sysDictMap.get(attr.dictType() + val); + } + else if (!attr.handler().equals(ExcelHandlerAdapter.class)) + { + val = dataFormatHandlerAdapter(val, attr, null); + } + else if (ColumnType.IMAGE == attr.cellType() && StringUtils.isNotEmpty(pictures)) + { + StringBuilder propertyString = new StringBuilder(); + List images = pictures.get(row.getRowNum() + "_" + entry.getKey()); + for (PictureData picture : images) + { + byte[] data = picture.getData(); + String fileName = FileUtils.writeImportBytes(data); + propertyString.append(fileName).append(SEPARATOR); + } + val = StringUtils.stripEnd(propertyString.toString(), SEPARATOR); + } + ReflectUtils.invokeSetter(entity, propertyName, val); + } + } + list.add(entity); + } + } + return list; + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @param list 导出数据集合 + * @param sheetName 工作表的名称 + * @return 结果 + */ + public AjaxResult exportExcel(List list, String sheetName) + { + return exportExcel(list, sheetName, StringUtils.EMPTY); + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @param list 导出数据集合 + * @param sheetName 工作表的名称 + * @param title 标题 + * @return 结果 + */ + public AjaxResult exportExcel(List list, String sheetName, String title) + { + this.init(list, sheetName, title, Type.EXPORT); + return exportExcel(); + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @param response 返回数据 + * @param list 导出数据集合 + * @param sheetName 工作表的名称 + * @return 结果 + */ + public void exportExcel(HttpServletResponse response, List list, String sheetName) + { + exportExcel(response, list, sheetName, StringUtils.EMPTY); + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @param response 返回数据 + * @param list 导出数据集合 + * @param sheetName 工作表的名称 + * @param title 标题 + * @return 结果 + */ + public void exportExcel(HttpServletResponse response, List list, String sheetName, String title) + { + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + response.setCharacterEncoding("utf-8"); + this.init(list, sheetName, title, Type.EXPORT); + exportExcel(response); + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @param sheetName 工作表的名称 + * @return 结果 + */ + public AjaxResult importTemplateExcel(String sheetName) + { + return importTemplateExcel(sheetName, StringUtils.EMPTY); + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @param sheetName 工作表的名称 + * @param title 标题 + * @return 结果 + */ + public AjaxResult importTemplateExcel(String sheetName, String title) + { + this.init(null, sheetName, title, Type.IMPORT); + return exportExcel(); + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @param sheetName 工作表的名称 + * @return 结果 + */ + public void importTemplateExcel(HttpServletResponse response, String sheetName) + { + importTemplateExcel(response, sheetName, StringUtils.EMPTY); + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @param sheetName 工作表的名称 + * @param title 标题 + * @return 结果 + */ + public void importTemplateExcel(HttpServletResponse response, String sheetName, String title) + { + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + response.setCharacterEncoding("utf-8"); + this.init(null, sheetName, title, Type.IMPORT); + exportExcel(response); + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @return 结果 + */ + public void exportExcel(HttpServletResponse response) + { + try + { + writeSheet(); + wb.write(response.getOutputStream()); + } + catch (Exception e) + { + log.error("导出Excel异常{}", e.getMessage()); + } + finally + { + IOUtils.closeQuietly(wb); + } + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @return 结果 + */ + public AjaxResult exportExcel() + { + OutputStream out = null; + try + { + writeSheet(); + String filename = encodingFilename(sheetName); + out = new FileOutputStream(getAbsoluteFile(filename)); + wb.write(out); + return AjaxResult.success(filename); + } + catch (Exception e) + { + log.error("导出Excel异常{}", e.getMessage()); + throw new UtilException("导出Excel失败,请联系网站管理员!"); + } + finally + { + IOUtils.closeQuietly(wb); + IOUtils.closeQuietly(out); + } + } + + /** + * 创建写入数据到Sheet + */ + public void writeSheet() + { + // 取出一共有多少个sheet. + int sheetNo = Math.max(1, (int) Math.ceil(list.size() * 1.0 / sheetSize)); + for (int index = 0; index < sheetNo; index++) + { + createSheet(sheetNo, index); + + // 产生一行 + Row row = sheet.createRow(rownum); + int column = 0; + // 写入各个字段的列头名称 + for (Object[] os : fields) + { + Field field = (Field) os[0]; + Excel excel = (Excel) os[1]; + if (Collection.class.isAssignableFrom(field.getType())) + { + List currentSubFields = subFieldsMap.get(field.getName()); + for (Field subField : currentSubFields) + { + Excel subExcel = subField.getAnnotation(Excel.class); + this.createHeadCell(subExcel, row, column++); + } + } + else + { + this.createHeadCell(excel, row, column++); + } + } + if (Type.EXPORT.equals(type)) + { + fillExcelData(index); + addStatisticsRow(); + } + } + } + + /** + * 填充excel数据 + * + * @param index 序号 + */ + @SuppressWarnings("unchecked") + public void fillExcelData(int index) + { + int startNo = index * sheetSize; + int endNo = Math.min(startNo + sheetSize, list.size()); + int currentRowNum = rownum + 1; // 从标题行后开始 + + for (int i = startNo; i < endNo; i++) + { + Row row = sheet.createRow(currentRowNum); + T vo = (T) list.get(i); + int column = 0; + int maxSubListSize = getCurrentMaxSubListSize(vo); + for (Object[] os : fields) + { + Field field = (Field) os[0]; + Excel excel = (Excel) os[1]; + if (Collection.class.isAssignableFrom(field.getType())) + { + try + { + Collection subList = (Collection) getTargetValue(vo, field, excel); + List currentSubFields = subFieldsMap.get(field.getName()); + if (subList != null && !subList.isEmpty()) + { + int subIndex = 0; + for (Object subVo : subList) + { + Row subRow = sheet.getRow(currentRowNum + subIndex); + if (subRow == null) + { + subRow = sheet.createRow(currentRowNum + subIndex); + } + + int subColumn = column; + for (Field subField : currentSubFields) + { + Excel subExcel = subField.getAnnotation(Excel.class); + addCell(subExcel, subRow, (T) subVo, subField, subColumn++); + } + subIndex++; + } + } + column += currentSubFields.size(); + } + catch (Exception e) + { + log.error("填充集合数据失败", e); + } + } + else + { + // 创建单元格并设置值 + addCell(excel, row, vo, field, column); + if (maxSubListSize > 1 && excel.needMerge()) + { + sheet.addMergedRegion(new CellRangeAddress(currentRowNum, currentRowNum + maxSubListSize - 1, column, column)); + } + column++; + } + } + currentRowNum += maxSubListSize; + } + } + + /** + * 获取子列表最大数 + */ + private int getCurrentMaxSubListSize(T vo) + { + int maxSubListSize = 1; + for (Object[] os : fields) + { + Field field = (Field) os[0]; + if (Collection.class.isAssignableFrom(field.getType())) + { + try + { + Collection subList = (Collection) getTargetValue(vo, field, (Excel) os[1]); + if (subList != null && !subList.isEmpty()) + { + maxSubListSize = Math.max(maxSubListSize, subList.size()); + } + } + catch (Exception e) + { + log.error("获取集合大小失败", e); + } + } + } + return maxSubListSize; + } + + /** + * 创建表格样式 + * + * @param wb 工作薄对象 + * @return 样式列表 + */ + private Map createStyles(Workbook wb) + { + // 写入各条记录,每条记录对应excel表中的一行 + Map styles = new HashMap(); + CellStyle style = wb.createCellStyle(); + style.setAlignment(HorizontalAlignment.CENTER); + style.setVerticalAlignment(VerticalAlignment.CENTER); + Font titleFont = wb.createFont(); + titleFont.setFontName("Arial"); + titleFont.setFontHeightInPoints((short) 16); + titleFont.setBold(true); + style.setFont(titleFont); + DataFormat dataFormat = wb.createDataFormat(); + style.setDataFormat(dataFormat.getFormat("@")); + styles.put("title", style); + + style = wb.createCellStyle(); + style.setAlignment(HorizontalAlignment.CENTER); + style.setVerticalAlignment(VerticalAlignment.CENTER); + style.setBorderRight(BorderStyle.THIN); + style.setRightBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); + style.setBorderLeft(BorderStyle.THIN); + style.setLeftBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); + style.setBorderTop(BorderStyle.THIN); + style.setTopBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); + style.setBorderBottom(BorderStyle.THIN); + style.setBottomBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); + Font dataFont = wb.createFont(); + dataFont.setFontName("Arial"); + dataFont.setFontHeightInPoints((short) 10); + style.setFont(dataFont); + styles.put("data", style); + + style = wb.createCellStyle(); + style.setAlignment(HorizontalAlignment.CENTER); + style.setVerticalAlignment(VerticalAlignment.CENTER); + style.setDataFormat(dataFormat.getFormat("######0.00")); + Font totalFont = wb.createFont(); + totalFont.setFontName("Arial"); + totalFont.setFontHeightInPoints((short) 10); + style.setFont(totalFont); + styles.put("total", style); + + styles.putAll(annotationHeaderStyles(wb, styles)); + + styles.putAll(annotationDataStyles(wb)); + + return styles; + } + + /** + * 根据Excel注解创建表格头样式 + * + * @param wb 工作薄对象 + * @return 自定义样式列表 + */ + private Map annotationHeaderStyles(Workbook wb, Map styles) + { + Map headerStyles = new HashMap(); + for (Object[] os : fields) + { + Excel excel = (Excel) os[1]; + String key = StringUtils.format("header_{}_{}", excel.headerColor(), excel.headerBackgroundColor()); + if (!headerStyles.containsKey(key)) + { + CellStyle style = wb.createCellStyle(); + style.cloneStyleFrom(styles.get("data")); + style.setAlignment(HorizontalAlignment.CENTER); + style.setVerticalAlignment(VerticalAlignment.CENTER); + style.setFillForegroundColor(excel.headerBackgroundColor().index); + style.setFillPattern(FillPatternType.SOLID_FOREGROUND); + Font headerFont = wb.createFont(); + headerFont.setFontName("Arial"); + headerFont.setFontHeightInPoints((short) 10); + headerFont.setBold(true); + headerFont.setColor(excel.headerColor().index); + style.setFont(headerFont); + // 设置表格头单元格文本形式 + DataFormat dataFormat = wb.createDataFormat(); + style.setDataFormat(dataFormat.getFormat("@")); + headerStyles.put(key, style); + } + } + return headerStyles; + } + + /** + * 根据Excel注解创建表格列样式 + * + * @param wb 工作薄对象 + * @return 自定义样式列表 + */ + private Map annotationDataStyles(Workbook wb) + { + Map styles = new HashMap(); + for (Object[] os : fields) + { + Field field = (Field) os[0]; + Excel excel = (Excel) os[1]; + if (Collection.class.isAssignableFrom(field.getType())) + { + ParameterizedType pt = (ParameterizedType) field.getGenericType(); + Class subClass = (Class) pt.getActualTypeArguments()[0]; + List subFields = FieldUtils.getFieldsListWithAnnotation(subClass, Excel.class); + for (Field subField : subFields) + { + Excel subExcel = subField.getAnnotation(Excel.class); + annotationDataStyles(styles, subField, subExcel); + } + } + else + { + annotationDataStyles(styles, field, excel); + } + } + return styles; + } + + /** + * 根据Excel注解创建表格列样式 + * + * @param styles 自定义样式列表 + * @param field 属性列信息 + * @param excel 注解信息 + */ + public void annotationDataStyles(Map styles, Field field, Excel excel) + { + String key = StringUtils.format("data_{}_{}_{}_{}_{}", excel.align(), excel.color(), excel.backgroundColor(), excel.cellType(), excel.wrapText()); + if (!styles.containsKey(key)) + { + CellStyle style = wb.createCellStyle(); + style.setAlignment(excel.align()); + style.setVerticalAlignment(VerticalAlignment.CENTER); + style.setBorderRight(BorderStyle.THIN); + style.setRightBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); + style.setBorderLeft(BorderStyle.THIN); + style.setLeftBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); + style.setBorderTop(BorderStyle.THIN); + style.setTopBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); + style.setBorderBottom(BorderStyle.THIN); + style.setBottomBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); + style.setFillPattern(FillPatternType.SOLID_FOREGROUND); + style.setFillForegroundColor(excel.backgroundColor().getIndex()); + style.setWrapText(excel.wrapText()); + Font dataFont = wb.createFont(); + dataFont.setFontName("Arial"); + dataFont.setFontHeightInPoints((short) 10); + dataFont.setColor(excel.color().index); + style.setFont(dataFont); + if (ColumnType.TEXT == excel.cellType()) + { + DataFormat dataFormat = wb.createDataFormat(); + style.setDataFormat(dataFormat.getFormat("@")); + } + styles.put(key, style); + } + } + + /** + * 创建单元格 + */ + public Cell createHeadCell(Excel attr, Row row, int column) + { + // 创建列 + Cell cell = row.createCell(column); + // 写入列信息 + cell.setCellValue(attr.name()); + setDataValidation(attr, row, column); + cell.setCellStyle(styles.get(StringUtils.format("header_{}_{}", attr.headerColor(), attr.headerBackgroundColor()))); + if (isSubList()) + { + // 填充默认样式,防止合并单元格样式失效 + sheet.setDefaultColumnStyle(column, styles.get(StringUtils.format("data_{}_{}_{}_{}_{}", attr.align(), attr.color(), attr.backgroundColor(), attr.cellType(), attr.wrapText()))); + if (attr.needMerge()) + { + sheet.addMergedRegion(new CellRangeAddress(rownum - 1, rownum, column, column)); + } + } + return cell; + } + + /** + * 设置单元格信息 + * + * @param value 单元格值 + * @param attr 注解相关 + * @param cell 单元格信息 + */ + public void setCellVo(Object value, Excel attr, Cell cell) + { + if (ColumnType.STRING == attr.cellType() || ColumnType.TEXT == attr.cellType()) + { + String cellValue = Convert.toStr(value); + // 对于任何以表达式触发字符 =-+@开头的单元格,直接使用tab字符作为前缀,防止CSV注入。 + if (StringUtils.startsWithAny(cellValue, FORMULA_STR)) + { + cellValue = RegExUtils.replaceFirst(cellValue, FORMULA_REGEX_STR, "\t$0"); + } + if (value instanceof Collection && StringUtils.equals("[]", cellValue)) + { + cellValue = StringUtils.EMPTY; + } + cell.setCellValue(StringUtils.isNull(cellValue) ? attr.defaultValue() : cellValue + attr.suffix()); + } + else if (ColumnType.NUMERIC == attr.cellType()) + { + if (StringUtils.isNotNull(value)) + { + cell.setCellValue(StringUtils.contains(Convert.toStr(value), ".") ? Convert.toDouble(value) : Convert.toInt(value)); + } + } + else if (ColumnType.IMAGE == attr.cellType()) + { + ClientAnchor anchor = new XSSFClientAnchor(0, 0, 0, 0, (short) cell.getColumnIndex(), cell.getRow().getRowNum(), (short) (cell.getColumnIndex() + 1), cell.getRow().getRowNum() + 1); + String propertyValue = Convert.toStr(value); + if (StringUtils.isNotEmpty(propertyValue)) + { + List imagePaths = StringUtils.str2List(propertyValue, SEPARATOR); + for (String imagePath : imagePaths) + { + byte[] data = ImageUtils.getImage(imagePath); + getDrawingPatriarch(cell.getSheet()).createPicture(anchor, cell.getSheet().getWorkbook().addPicture(data, getImageType(data))); + } + } + } + } + + /** + * 获取画布 + */ + public static Drawing getDrawingPatriarch(Sheet sheet) + { + if (sheet.getDrawingPatriarch() == null) + { + sheet.createDrawingPatriarch(); + } + return sheet.getDrawingPatriarch(); + } + + /** + * 获取图片类型,设置图片插入类型 + */ + public int getImageType(byte[] value) + { + String type = FileTypeUtils.getFileExtendName(value); + if ("JPG".equalsIgnoreCase(type)) + { + return Workbook.PICTURE_TYPE_JPEG; + } + else if ("PNG".equalsIgnoreCase(type)) + { + return Workbook.PICTURE_TYPE_PNG; + } + return Workbook.PICTURE_TYPE_JPEG; + } + + /** + * 创建表格样式 + */ + public void setDataValidation(Excel attr, Row row, int column) + { + if (attr.name().indexOf("注:") >= 0) + { + sheet.setColumnWidth(column, 6000); + } + else + { + // 设置列宽 + sheet.setColumnWidth(column, (int) ((attr.width() + 0.72) * 256)); + } + if (StringUtils.isNotEmpty(attr.prompt()) || attr.combo().length > 0 || attr.comboReadDict()) + { + String[] comboArray = attr.combo(); + if (attr.comboReadDict()) + { + if (!sysDictMap.containsKey("combo_" + attr.dictType())) + { + String labels = DictUtils.getDictLabels(attr.dictType()); + sysDictMap.put("combo_" + attr.dictType(), labels); + } + String val = sysDictMap.get("combo_" + attr.dictType()); + comboArray = StringUtils.split(val, DictUtils.SEPARATOR); + } + if (comboArray.length > 15 || StringUtils.join(comboArray).length() > 255) + { + // 如果下拉数大于15或字符串长度大于255,则使用一个新sheet存储,避免生成的模板下拉值获取不到 + setXSSFValidationWithHidden(sheet, comboArray, attr.prompt(), 1, 100, column, column); + } + else + { + // 提示信息或只能选择不能输入的列内容. + setPromptOrValidation(sheet, comboArray, attr.prompt(), 1, 100, column, column); + } + } + } + + /** + * 添加单元格 + */ + @SuppressWarnings("deprecation") + public Cell addCell(Excel attr, Row row, T vo, Field field, int column) + { + Cell cell = null; + try + { + // 设置行高 + row.setHeight(maxHeight); + // 根据Excel中设置情况决定是否导出,有些情况需要保持为空,希望用户填写这一列. + if (attr.isExport()) + { + // 创建cell + cell = row.createCell(column); + if (isSubListValue(vo) && getListCellValue(vo) > 1 && attr.needMerge()) + { + if (subMergedLastRowNum >= subMergedFirstRowNum) + { + sheet.addMergedRegion(new CellRangeAddress(subMergedFirstRowNum, subMergedLastRowNum, column, column)); + } + } + cell.setCellStyle(styles.get(StringUtils.format("data_{}_{}_{}_{}_{}", attr.align(), attr.color(), attr.backgroundColor(), attr.cellType(), attr.wrapText()))); + + // 用于读取对象中的属性 + Object value = getTargetValue(vo, field, attr); + String dateFormat = attr.dateFormat(); + String readConverterExp = attr.readConverterExp(); + String separator = attr.separator(); + String dictType = attr.dictType(); + if (StringUtils.isNotEmpty(dateFormat) && StringUtils.isNotNull(value)) + { + cell.setCellStyle(createCellStyle(cell.getCellStyle(), dateFormat)); + cell.setCellValue(parseDateToStr(dateFormat, value)); + } + else if (StringUtils.isNotEmpty(readConverterExp) && StringUtils.isNotNull(value)) + { + cell.setCellValue(convertByExp(Convert.toStr(value), readConverterExp, separator)); + } + else if (StringUtils.isNotEmpty(dictType) && StringUtils.isNotNull(value)) + { + if (!sysDictMap.containsKey(dictType + value)) + { + String lable = convertDictByExp(Convert.toStr(value), dictType, separator); + sysDictMap.put(dictType + value, lable); + } + cell.setCellValue(sysDictMap.get(dictType + value)); + } + else if (value instanceof BigDecimal && -1 != attr.scale()) + { + cell.setCellValue((((BigDecimal) value).setScale(attr.scale(), attr.roundingMode())).doubleValue()); + } + else if (!attr.handler().equals(ExcelHandlerAdapter.class)) + { + cell.setCellValue(dataFormatHandlerAdapter(value, attr, cell)); + } + else + { + // 设置列类型 + setCellVo(value, attr, cell); + } + addStatisticsData(column, Convert.toStr(value), attr); + } + } + catch (Exception e) + { + log.error("导出Excel失败{}", e); + } + return cell; + } + + /** + * 使用自定义格式,同时避免样式污染 + * + * @param cellStyle 从此样式复制 + * @param format 格式匹配的字符串 + * @return 格式化后CellStyle对象 + */ + private CellStyle createCellStyle(CellStyle cellStyle, String format) + { + CellStyle style = wb.createCellStyle(); + style.cloneStyleFrom(cellStyle); + style.setDataFormat(wb.getCreationHelper().createDataFormat().getFormat(format)); + return style; + } + + /** + * 设置 POI XSSFSheet 单元格提示或选择框 + * + * @param sheet 表单 + * @param textlist 下拉框显示的内容 + * @param promptContent 提示内容 + * @param firstRow 开始行 + * @param endRow 结束行 + * @param firstCol 开始列 + * @param endCol 结束列 + */ + public void setPromptOrValidation(Sheet sheet, String[] textlist, String promptContent, int firstRow, int endRow, + int firstCol, int endCol) + { + DataValidationHelper helper = sheet.getDataValidationHelper(); + DataValidationConstraint constraint = textlist.length > 0 ? helper.createExplicitListConstraint(textlist) : helper.createCustomConstraint("DD1"); + CellRangeAddressList regions = new CellRangeAddressList(firstRow, endRow, firstCol, endCol); + DataValidation dataValidation = helper.createValidation(constraint, regions); + if (StringUtils.isNotEmpty(promptContent)) + { + // 如果设置了提示信息则鼠标放上去提示 + dataValidation.createPromptBox("", promptContent); + dataValidation.setShowPromptBox(true); + } + // 处理Excel兼容性问题 + if (dataValidation instanceof XSSFDataValidation) + { + dataValidation.setSuppressDropDownArrow(true); + dataValidation.setShowErrorBox(true); + } + else + { + dataValidation.setSuppressDropDownArrow(false); + } + sheet.addValidationData(dataValidation); + } + + /** + * 设置某些列的值只能输入预制的数据,显示下拉框(兼容超出一定数量的下拉框). + * + * @param sheet 要设置的sheet. + * @param textlist 下拉框显示的内容 + * @param promptContent 提示内容 + * @param firstRow 开始行 + * @param endRow 结束行 + * @param firstCol 开始列 + * @param endCol 结束列 + */ + public void setXSSFValidationWithHidden(Sheet sheet, String[] textlist, String promptContent, int firstRow, int endRow, int firstCol, int endCol) + { + String hideSheetName = "combo_" + firstCol + "_" + endCol; + Sheet hideSheet = null; + String hideSheetDataName = hideSheetName + "_data"; + Name name = wb.getName(hideSheetDataName); + if (name != null) + { + // 名称已存在,尝试从名称的引用中找到sheet名称 + String refersToFormula = name.getRefersToFormula(); + if (StringUtils.isNotEmpty(refersToFormula) && refersToFormula.contains("!")) + { + String sheetNameFromFormula = refersToFormula.substring(0, refersToFormula.indexOf("!")); + hideSheet = wb.getSheet(sheetNameFromFormula); + } + } + + if (hideSheet == null) + { + hideSheet = wb.createSheet(hideSheetName); // 用于存储 下拉菜单数据 + for (int i = 0; i < textlist.length; i++) + { + hideSheet.createRow(i).createCell(0).setCellValue(textlist[i]); + } + // 创建名称,可被其他单元格引用 + name = wb.createName(); + name.setNameName(hideSheetDataName); + name.setRefersToFormula(hideSheetName + "!$A$1:$A$" + textlist.length); + } + + DataValidationHelper helper = sheet.getDataValidationHelper(); + // 加载下拉列表内容 + DataValidationConstraint constraint = helper.createFormulaListConstraint(hideSheetDataName); + // 设置数据有效性加载在哪个单元格上,四个参数分别是:起始行、终止行、起始列、终止列 + CellRangeAddressList regions = new CellRangeAddressList(firstRow, endRow, firstCol, endCol); + // 数据有效性对象 + DataValidation dataValidation = helper.createValidation(constraint, regions); + if (StringUtils.isNotEmpty(promptContent)) + { + // 如果设置了提示信息则鼠标放上去提示 + dataValidation.createPromptBox("", promptContent); + dataValidation.setShowPromptBox(true); + } + // 处理Excel兼容性问题 + if (dataValidation instanceof XSSFDataValidation) + { + dataValidation.setSuppressDropDownArrow(true); + dataValidation.setShowErrorBox(true); + } + else + { + dataValidation.setSuppressDropDownArrow(false); + } + + sheet.addValidationData(dataValidation); + // 设置hiddenSheet隐藏 + wb.setSheetHidden(wb.getSheetIndex(hideSheet), true); + } + + /** + * 解析导出值 0=男,1=女,2=未知 + * + * @param propertyValue 参数值 + * @param converterExp 翻译注解 + * @param separator 分隔符 + * @return 解析后值 + */ + public static String convertByExp(String propertyValue, String converterExp, String separator) + { + StringBuilder propertyString = new StringBuilder(); + String[] convertSource = converterExp.split(SEPARATOR); + for (String item : convertSource) + { + String[] itemArray = item.split("="); + if (StringUtils.containsAny(propertyValue, separator)) + { + for (String value : propertyValue.split(separator)) + { + if (itemArray[0].equals(value)) + { + propertyString.append(itemArray[1] + separator); + break; + } + } + } + else + { + if (itemArray[0].equals(propertyValue)) + { + return itemArray[1]; + } + } + } + return StringUtils.stripEnd(propertyString.toString(), separator); + } + + /** + * 反向解析值 男=0,女=1,未知=2 + * + * @param propertyValue 参数值 + * @param converterExp 翻译注解 + * @param separator 分隔符 + * @return 解析后值 + */ + public static String reverseByExp(String propertyValue, String converterExp, String separator) + { + StringBuilder propertyString = new StringBuilder(); + String[] convertSource = converterExp.split(SEPARATOR); + for (String item : convertSource) + { + String[] itemArray = item.split("="); + if (StringUtils.containsAny(propertyValue, separator)) + { + for (String value : propertyValue.split(separator)) + { + if (itemArray[1].equals(value)) + { + propertyString.append(itemArray[0] + separator); + break; + } + } + } + else + { + if (itemArray[1].equals(propertyValue)) + { + return itemArray[0]; + } + } + } + return StringUtils.stripEnd(propertyString.toString(), separator); + } + + /** + * 解析字典值 + * + * @param dictValue 字典值 + * @param dictType 字典类型 + * @param separator 分隔符 + * @return 字典标签 + */ + public static String convertDictByExp(String dictValue, String dictType, String separator) + { + return DictUtils.getDictLabel(dictType, dictValue, separator); + } + + /** + * 反向解析值字典值 + * + * @param dictLabel 字典标签 + * @param dictType 字典类型 + * @param separator 分隔符 + * @return 字典值 + */ + public static String reverseDictByExp(String dictLabel, String dictType, String separator) + { + return DictUtils.getDictValue(dictType, dictLabel, separator); + } + + /** + * 数据处理器 + * + * @param value 数据值 + * @param excel 数据注解 + * @return + */ + public String dataFormatHandlerAdapter(Object value, Excel excel, Cell cell) + { + try + { + Object instance = excel.handler().getDeclaredConstructor().newInstance(); + Method formatMethod = excel.handler().getMethod("format", new Class[] { Object.class, String[].class, Cell.class, Workbook.class }); + value = formatMethod.invoke(instance, value, excel.args(), cell, this.wb); + } + catch (Exception e) + { + log.error("不能格式化数据 " + excel.handler(), e.getMessage()); + } + return Convert.toStr(value); + } + + /** + * 合计统计信息 + */ + private void addStatisticsData(Integer index, String text, Excel entity) + { + if (entity != null && entity.isStatistics()) + { + Double temp = 0D; + if (!statistics.containsKey(index)) + { + statistics.put(index, temp); + } + try + { + temp = Double.valueOf(text); + } + catch (NumberFormatException e) + { + } + statistics.put(index, statistics.get(index) + temp); + } + } + + /** + * 创建统计行 + */ + public void addStatisticsRow() + { + if (statistics.size() > 0) + { + Row row = sheet.createRow(sheet.getLastRowNum() + 1); + Set keys = statistics.keySet(); + Cell cell = row.createCell(0); + cell.setCellStyle(styles.get("total")); + cell.setCellValue("合计"); + + for (Integer key : keys) + { + cell = row.createCell(key); + cell.setCellStyle(styles.get("total")); + cell.setCellValue(statistics.get(key)); + } + statistics.clear(); + } + } + + /** + * 编码文件名 + */ + public String encodingFilename(String filename) + { + return UUID.randomUUID() + "_" + filename + ".xlsx"; + } + + /** + * 获取下载路径 + * + * @param filename 文件名称 + */ + public String getAbsoluteFile(String filename) + { + String downloadPath = ProjectConfig.getDownloadPath() + filename; + File desc = new File(downloadPath); + if (!desc.getParentFile().exists()) + { + desc.getParentFile().mkdirs(); + } + return downloadPath; + } + + /** + * 获取bean中的属性值 + * + * @param vo 实体对象 + * @param field 字段 + * @param excel 注解 + * @return 最终的属性值 + * @throws Exception + */ + private Object getTargetValue(T vo, Field field, Excel excel) throws Exception + { + field.setAccessible(true); + Object o = field.get(vo); + if (StringUtils.isNotEmpty(excel.targetAttr())) + { + String target = excel.targetAttr(); + if (target.contains(".")) + { + String[] targets = target.split("[.]"); + for (String name : targets) + { + o = getValue(o, name); + } + } + else + { + o = getValue(o, target); + } + } + return o; + } + + /** + * 以类的属性的get方法方法形式获取值 + * + * @param o + * @param name + * @return value + * @throws Exception + */ + private Object getValue(Object o, String name) throws Exception + { + if (StringUtils.isNotNull(o) && StringUtils.isNotEmpty(name)) + { + Class clazz = o.getClass(); + Field field = clazz.getDeclaredField(name); + field.setAccessible(true); + o = field.get(o); + } + return o; + } + + /** + * 得到所有定义字段 + */ + private void createExcelField() + { + this.fields = getFields(); + this.fields = this.fields.stream().sorted(Comparator.comparing(objects -> ((Excel) objects[1]).sort())).collect(Collectors.toList()); + this.maxHeight = getRowHeight(); + } + + /** + * 获取字段注解信息 + */ + public List getFields() + { + List fields = new ArrayList(); + List tempFields = new ArrayList<>(); + subFieldsMap = new HashMap<>(); + subMethods = new HashMap<>(); + tempFields.addAll(Arrays.asList(clazz.getSuperclass().getDeclaredFields())); + tempFields.addAll(Arrays.asList(clazz.getDeclaredFields())); + if (StringUtils.isNotEmpty(includeFields)) + { + for (Field field : tempFields) + { + if (ArrayUtils.contains(this.includeFields, field.getName()) || field.isAnnotationPresent(Excels.class)) + { + addField(fields, field); + } + } + } + else if (StringUtils.isNotEmpty(excludeFields)) + { + for (Field field : tempFields) + { + if (!ArrayUtils.contains(this.excludeFields, field.getName())) + { + addField(fields, field); + } + } + } + else + { + for (Field field : tempFields) + { + addField(fields, field); + } + } + return fields; + } + + /** + * 添加字段信息 + */ + public void addField(List fields, Field field) + { + // 单注解 + if (field.isAnnotationPresent(Excel.class)) + { + Excel attr = field.getAnnotation(Excel.class); + if (attr != null && (attr.type() == Type.ALL || attr.type() == type)) + { + fields.add(new Object[] { field, attr }); + } + if (Collection.class.isAssignableFrom(field.getType())) + { + String fieldName = field.getName(); + subMethods.put(fieldName, getSubMethod(fieldName, clazz)); + ParameterizedType pt = (ParameterizedType) field.getGenericType(); + Class subClass = (Class) pt.getActualTypeArguments()[0]; + subFieldsMap.put(fieldName, FieldUtils.getFieldsListWithAnnotation(subClass, Excel.class)); + } + } + + // 多注解 + if (field.isAnnotationPresent(Excels.class)) + { + Excels attrs = field.getAnnotation(Excels.class); + Excel[] excels = attrs.value(); + for (Excel attr : excels) + { + if (StringUtils.isNotEmpty(includeFields)) + { + if (ArrayUtils.contains(this.includeFields, field.getName() + "." + attr.targetAttr()) + && (attr != null && (attr.type() == Type.ALL || attr.type() == type))) + { + fields.add(new Object[] { field, attr }); + } + } + else + { + if (!ArrayUtils.contains(this.excludeFields, field.getName() + "." + attr.targetAttr()) + && (attr != null && (attr.type() == Type.ALL || attr.type() == type))) + { + fields.add(new Object[] { field, attr }); + } + } + } + } + } + + /** + * 根据注解获取最大行高 + */ + public short getRowHeight() + { + double maxHeight = 0; + for (Object[] os : this.fields) + { + Excel excel = (Excel) os[1]; + maxHeight = Math.max(maxHeight, excel.height()); + } + return (short) (maxHeight * 20); + } + + /** + * 创建一个工作簿 + */ + public void createWorkbook() + { + this.wb = new SXSSFWorkbook(500); + this.sheet = wb.createSheet(); + wb.setSheetName(0, sheetName); + this.styles = createStyles(wb); + } + + /** + * 创建工作表 + * + * @param sheetNo sheet数量 + * @param index 序号 + */ + public void createSheet(int sheetNo, int index) + { + // 设置工作表的名称. + if (sheetNo > 1 && index > 0) + { + this.sheet = wb.createSheet(); + this.createTitle(); + int actualIndex = wb.getSheetIndex(this.sheet); + wb.setSheetName(actualIndex, sheetName + index); + } + } + + /** + * 获取单元格值 + * + * @param row 获取的行 + * @param column 获取单元格列号 + * @return 单元格值 + */ + public Object getCellValue(Row row, int column) + { + if (row == null) + { + return row; + } + Object val = ""; + try + { + Cell cell = row.getCell(column); + if (StringUtils.isNotNull(cell)) + { + if (cell.getCellType() == CellType.NUMERIC || cell.getCellType() == CellType.FORMULA) + { + val = cell.getNumericCellValue(); + if (DateUtil.isCellDateFormatted(cell)) + { + val = DateUtil.getJavaDate((Double) val); // POI Excel 日期格式转换 + } + else + { + if ((Double) val % 1 != 0) + { + val = new BigDecimal(val.toString()); + } + else + { + val = new DecimalFormat("0").format(val); + } + } + } + else if (cell.getCellType() == CellType.STRING) + { + val = cell.getStringCellValue(); + } + else if (cell.getCellType() == CellType.BOOLEAN) + { + val = cell.getBooleanCellValue(); + } + else if (cell.getCellType() == CellType.ERROR) + { + val = cell.getErrorCellValue(); + } + + } + } + catch (Exception e) + { + return val; + } + return val; + } + + /** + * 判断是否是空行 + * + * @param row 判断的行 + * @return + */ + private boolean isRowEmpty(Row row) + { + if (row == null) + { + return true; + } + for (int i = row.getFirstCellNum(); i < row.getLastCellNum(); i++) + { + Cell cell = row.getCell(i); + if (cell != null && cell.getCellType() != CellType.BLANK) + { + return false; + } + } + return true; + } + + /** + * 获取Excel2003图片 + * + * @param sheet 当前sheet对象 + * @param workbook 工作簿对象 + * @return Map key:图片单元格索引(1_1)String,value:图片流PictureData + */ + public static Map> getSheetPictures03(HSSFSheet sheet, HSSFWorkbook workbook) + { + Map> sheetIndexPicMap = new HashMap<>(); + List pictures = workbook.getAllPictures(); + if (!pictures.isEmpty() && sheet.getDrawingPatriarch() != null) + { + for (HSSFShape shape : sheet.getDrawingPatriarch().getChildren()) + { + if (shape instanceof HSSFPicture) + { + HSSFPicture pic = (HSSFPicture) shape; + HSSFClientAnchor anchor = (HSSFClientAnchor) pic.getAnchor(); + String picIndex = anchor.getRow1() + "_" + anchor.getCol1(); + sheetIndexPicMap.computeIfAbsent(picIndex, k -> new ArrayList<>()).add(pic.getPictureData()); + } + } + } + return sheetIndexPicMap; + } + + /** + * 获取Excel2007图片 + * + * @param sheet 当前sheet对象 + * @param workbook 工作簿对象 + * @return Map key:图片单元格索引(1_1)String,value:图片流PictureData + */ + public static Map> getSheetPictures07(XSSFSheet sheet, XSSFWorkbook workbook) + { + Map> sheetIndexPicMap = new HashMap<>(); + for (POIXMLDocumentPart dr : sheet.getRelations()) + { + if (dr instanceof XSSFDrawing) + { + XSSFDrawing drawing = (XSSFDrawing) dr; + for (XSSFShape shape : drawing.getShapes()) + { + if (shape instanceof XSSFPicture) + { + XSSFPicture pic = (XSSFPicture) shape; + XSSFClientAnchor anchor = pic.getPreferredSize(); + CTMarker ctMarker = anchor.getFrom(); + String picIndex = ctMarker.getRow() + "_" + ctMarker.getCol(); + sheetIndexPicMap.computeIfAbsent(picIndex, k -> new ArrayList<>()).add(pic.getPictureData()); + } + } + } + } + return sheetIndexPicMap; + } + + /** + * 格式化不同类型的日期对象 + * + * @param dateFormat 日期格式 + * @param val 被格式化的日期对象 + * @return 格式化后的日期字符 + */ + public String parseDateToStr(String dateFormat, Object val) + { + if (val == null) + { + return ""; + } + String str; + if (val instanceof Date) + { + str = DateUtils.parseDateToStr(dateFormat, (Date) val); + } + else if (val instanceof LocalDateTime) + { + str = DateUtils.parseDateToStr(dateFormat, DateUtils.toDate((LocalDateTime) val)); + } + else if (val instanceof LocalDate) + { + str = DateUtils.parseDateToStr(dateFormat, DateUtils.toDate((LocalDate) val)); + } + else + { + str = val.toString(); + } + return str; + } + + /** + * 是否有对象的子列表 + */ + public boolean isSubList() + { + return !StringUtils.isEmpty(subFieldsMap); + } + + /** + * 是否有对象的子列表,集合不为空 + */ + public boolean isSubListValue(T vo) + { + return !StringUtils.isEmpty(subFieldsMap) && getListCellValue(vo) > 0; + } + + /** + * 获取集合的值 + */ + public int getListCellValue(Object obj) + { + Collection value; + int max = 0; + try + { + for (String s : subMethods.keySet()) + { + value = (Collection) subMethods.get(s).invoke(obj); + if (value.size() > max) + { + max = value.size(); + } + } + } + catch (Exception e) + { + return 0; + } + return max; + } + + /** + * 获取对象的子列表方法 + * + * @param name 名称 + * @param pojoClass 类对象 + * @return 子列表方法 + */ + public Method getSubMethod(String name, Class pojoClass) + { + StringBuffer getMethodName = new StringBuffer("get"); + getMethodName.append(name.substring(0, 1).toUpperCase()); + getMethodName.append(name.substring(1)); + Method method = null; + try + { + method = pojoClass.getMethod(getMethodName.toString(), new Class[] {}); + } + catch (Exception e) + { + log.error("获取对象异常{}", e.getMessage()); + } + return method; + } +} diff --git a/portal/backend/portal-common/src/main/java/org/lingniu/portal/common/utils/reflect/ReflectUtils.java b/portal/backend/portal-common/src/main/java/org/lingniu/portal/common/utils/reflect/ReflectUtils.java new file mode 100644 index 0000000..6a5c684 --- /dev/null +++ b/portal/backend/portal-common/src/main/java/org/lingniu/portal/common/utils/reflect/ReflectUtils.java @@ -0,0 +1,412 @@ +package org.lingniu.portal.common.utils.reflect; + +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.Date; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.Validate; +import org.apache.poi.ss.usermodel.DateUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.lingniu.portal.common.core.text.Convert; +import org.lingniu.portal.common.utils.DateUtils; + +/** + * 反射工具类. 提供调用getter/setter方法, 访问私有变量, 调用私有方法, 获取泛型类型Class, 被AOP过的真实类等工具函数. + * + * @author lingniu + */ +@SuppressWarnings("rawtypes") +public class ReflectUtils +{ + private static final String SETTER_PREFIX = "set"; + + private static final String GETTER_PREFIX = "get"; + + private static final String CGLIB_CLASS_SEPARATOR = "$$"; + + private static Logger logger = LoggerFactory.getLogger(ReflectUtils.class); + + /** + * 调用Getter方法. + * 支持多级,如:对象名.对象名.方法 + */ + @SuppressWarnings("unchecked") + public static E invokeGetter(Object obj, String propertyName) + { + Object object = obj; + for (String name : StringUtils.split(propertyName, ".")) + { + String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(name); + object = invokeMethod(object, getterMethodName, new Class[] {}, new Object[] {}); + } + return (E) object; + } + + /** + * 调用Setter方法, 仅匹配方法名。 + * 支持多级,如:对象名.对象名.方法 + */ + public static void invokeSetter(Object obj, String propertyName, E value) + { + Object object = obj; + String[] names = StringUtils.split(propertyName, "."); + for (int i = 0; i < names.length; i++) + { + if (i < names.length - 1) + { + String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(names[i]); + object = invokeMethod(object, getterMethodName, new Class[] {}, new Object[] {}); + } + else + { + String setterMethodName = SETTER_PREFIX + StringUtils.capitalize(names[i]); + invokeMethodByName(object, setterMethodName, new Object[] { value }); + } + } + } + + /** + * 直接读取对象属性值, 无视private/protected修饰符, 不经过getter函数. + */ + @SuppressWarnings("unchecked") + public static E getFieldValue(final Object obj, final String fieldName) + { + Field field = getAccessibleField(obj, fieldName); + if (field == null) + { + logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 "); + return null; + } + E result = null; + try + { + result = (E) field.get(obj); + } + catch (IllegalAccessException e) + { + logger.error("不可能抛出的异常{}", e.getMessage()); + } + return result; + } + + /** + * 直接设置对象属性值, 无视private/protected修饰符, 不经过setter函数. + */ + public static void setFieldValue(final Object obj, final String fieldName, final E value) + { + Field field = getAccessibleField(obj, fieldName); + if (field == null) + { + // throw new IllegalArgumentException("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 "); + logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 "); + return; + } + try + { + field.set(obj, value); + } + catch (IllegalAccessException e) + { + logger.error("不可能抛出的异常: {}", e.getMessage()); + } + } + + /** + * 直接调用对象方法, 无视private/protected修饰符. + * 用于一次性调用的情况,否则应使用getAccessibleMethod()函数获得Method后反复调用. + * 同时匹配方法名+参数类型, + */ + @SuppressWarnings("unchecked") + public static E invokeMethod(final Object obj, final String methodName, final Class[] parameterTypes, + final Object[] args) + { + if (obj == null || methodName == null) + { + return null; + } + Method method = getAccessibleMethod(obj, methodName, parameterTypes); + if (method == null) + { + logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + methodName + "] 方法 "); + return null; + } + try + { + return (E) method.invoke(obj, args); + } + catch (Exception e) + { + String msg = "method: " + method + ", obj: " + obj + ", args: " + args + ""; + throw convertReflectionExceptionToUnchecked(msg, e); + } + } + + /** + * 直接调用对象方法, 无视private/protected修饰符, + * 用于一次性调用的情况,否则应使用getAccessibleMethodByName()函数获得Method后反复调用. + * 只匹配函数名,如果有多个同名函数调用第一个。 + */ + @SuppressWarnings("unchecked") + public static E invokeMethodByName(final Object obj, final String methodName, final Object[] args) + { + Method method = getAccessibleMethodByName(obj, methodName, args.length); + if (method == null) + { + // 如果为空不报错,直接返回空。 + logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + methodName + "] 方法 "); + return null; + } + try + { + // 类型转换(将参数数据类型转换为目标方法参数类型) + Class[] cs = method.getParameterTypes(); + for (int i = 0; i < cs.length; i++) + { + if (args[i] != null && !args[i].getClass().equals(cs[i])) + { + if (cs[i] == String.class) + { + args[i] = Convert.toStr(args[i]); + if (StringUtils.endsWith((String) args[i], ".0")) + { + args[i] = StringUtils.substringBefore((String) args[i], ".0"); + } + } + else if (cs[i] == Integer.class) + { + args[i] = Convert.toInt(args[i]); + } + else if (cs[i] == Long.class) + { + args[i] = Convert.toLong(args[i]); + } + else if (cs[i] == Double.class) + { + args[i] = Convert.toDouble(args[i]); + } + else if (cs[i] == Float.class) + { + args[i] = Convert.toFloat(args[i]); + } + else if (cs[i] == Date.class) + { + if (args[i] instanceof String) + { + args[i] = DateUtils.parseDate(args[i]); + } + else + { + args[i] = DateUtil.getJavaDate((Double) args[i]); + } + } + else if (cs[i] == boolean.class || cs[i] == Boolean.class) + { + args[i] = Convert.toBool(args[i]); + } + } + } + return (E) method.invoke(obj, args); + } + catch (Exception e) + { + String msg = "method: " + method + ", obj: " + obj + ", args: " + args + ""; + throw convertReflectionExceptionToUnchecked(msg, e); + } + } + + /** + * 循环向上转型, 获取对象的DeclaredField, 并强制设置为可访问. + * 如向上转型到Object仍无法找到, 返回null. + */ + public static Field getAccessibleField(final Object obj, final String fieldName) + { + // 为空不报错。直接返回 null + if (obj == null) + { + return null; + } + Validate.notBlank(fieldName, "fieldName can't be blank"); + for (Class superClass = obj.getClass(); superClass != Object.class; superClass = superClass.getSuperclass()) + { + try + { + Field field = superClass.getDeclaredField(fieldName); + makeAccessible(field); + return field; + } + catch (NoSuchFieldException e) + { + continue; + } + } + return null; + } + + /** + * 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问. + * 如向上转型到Object仍无法找到, 返回null. + * 匹配函数名+参数类型。 + * 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args) + */ + public static Method getAccessibleMethod(final Object obj, final String methodName, + final Class... parameterTypes) + { + // 为空不报错。直接返回 null + if (obj == null) + { + return null; + } + Validate.notBlank(methodName, "methodName can't be blank"); + for (Class searchType = obj.getClass(); searchType != Object.class; searchType = searchType.getSuperclass()) + { + try + { + Method method = searchType.getDeclaredMethod(methodName, parameterTypes); + makeAccessible(method); + return method; + } + catch (NoSuchMethodException e) + { + continue; + } + } + return null; + } + + /** + * 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问. + * 如向上转型到Object仍无法找到, 返回null. + * 只匹配函数名。 + * 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args) + */ + public static Method getAccessibleMethodByName(final Object obj, final String methodName, int argsNum) + { + // 为空不报错。直接返回 null + if (obj == null) + { + return null; + } + Validate.notBlank(methodName, "methodName can't be blank"); + for (Class searchType = obj.getClass(); searchType != Object.class; searchType = searchType.getSuperclass()) + { + Method[] methods = searchType.getDeclaredMethods(); + for (Method method : methods) + { + if (method.getName().equals(methodName) && method.getParameterTypes().length == argsNum) + { + makeAccessible(method); + return method; + } + } + } + return null; + } + + /** + * 改变private/protected的方法为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。 + */ + @SuppressWarnings("deprecation") + public static void makeAccessible(Method method) + { + if ((!Modifier.isPublic(method.getModifiers()) || !Modifier.isPublic(method.getDeclaringClass().getModifiers())) + && !method.isAccessible()) + { + method.setAccessible(true); + } + } + + /** + * 改变private/protected的成员变量为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。 + */ + @SuppressWarnings("deprecation") + public static void makeAccessible(Field field) + { + if ((!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers()) + || Modifier.isFinal(field.getModifiers())) && !field.isAccessible()) + { + field.setAccessible(true); + } + } + + /** + * 通过反射, 获得Class定义中声明的泛型参数的类型, 注意泛型必须定义在父类处 + * 如无法找到, 返回Object.class. + */ + @SuppressWarnings("unchecked") + public static Class getClassGenricType(final Class clazz) + { + return getClassGenricType(clazz, 0); + } + + /** + * 通过反射, 获得Class定义中声明的父类的泛型参数的类型. + * 如无法找到, 返回Object.class. + */ + public static Class getClassGenricType(final Class clazz, final int index) + { + Type genType = clazz.getGenericSuperclass(); + + if (!(genType instanceof ParameterizedType)) + { + logger.debug(clazz.getSimpleName() + "'s superclass not ParameterizedType"); + return Object.class; + } + + Type[] params = ((ParameterizedType) genType).getActualTypeArguments(); + + if (index >= params.length || index < 0) + { + logger.debug("Index: " + index + ", Size of " + clazz.getSimpleName() + "'s Parameterized Type: " + + params.length); + return Object.class; + } + if (!(params[index] instanceof Class)) + { + logger.debug(clazz.getSimpleName() + " not set the actual class on superclass generic parameter"); + return Object.class; + } + + return (Class) params[index]; + } + + public static Class getUserClass(Object instance) + { + if (instance == null) + { + throw new RuntimeException("Instance must not be null"); + } + Class clazz = instance.getClass(); + if (clazz != null && clazz.getName().contains(CGLIB_CLASS_SEPARATOR)) + { + Class superClass = clazz.getSuperclass(); + if (superClass != null && !Object.class.equals(superClass)) + { + return superClass; + } + } + return clazz; + + } + + /** + * 将反射时的checked exception转换为unchecked exception. + */ + public static RuntimeException convertReflectionExceptionToUnchecked(String msg, Exception e) + { + if (e instanceof IllegalAccessException || e instanceof IllegalArgumentException + || e instanceof NoSuchMethodException) + { + return new IllegalArgumentException(msg, e); + } + else if (e instanceof InvocationTargetException) + { + return new RuntimeException(msg, ((InvocationTargetException) e).getTargetException()); + } + return new RuntimeException(msg, e); + } +} diff --git a/portal/backend/portal-common/src/main/java/org/lingniu/portal/common/utils/sign/Base64.java b/portal/backend/portal-common/src/main/java/org/lingniu/portal/common/utils/sign/Base64.java new file mode 100644 index 0000000..5b65ccc --- /dev/null +++ b/portal/backend/portal-common/src/main/java/org/lingniu/portal/common/utils/sign/Base64.java @@ -0,0 +1,291 @@ +package org.lingniu.portal.common.utils.sign; + +/** + * Base64工具类 + * + * @author lingniu + */ +public final class Base64 +{ + static private final int BASELENGTH = 128; + static private final int LOOKUPLENGTH = 64; + static private final int TWENTYFOURBITGROUP = 24; + static private final int EIGHTBIT = 8; + static private final int SIXTEENBIT = 16; + static private final int FOURBYTE = 4; + static private final int SIGN = -128; + static private final char PAD = '='; + static final private byte[] base64Alphabet = new byte[BASELENGTH]; + static final private char[] lookUpBase64Alphabet = new char[LOOKUPLENGTH]; + + static + { + for (int i = 0; i < BASELENGTH; ++i) + { + base64Alphabet[i] = -1; + } + for (int i = 'Z'; i >= 'A'; i--) + { + base64Alphabet[i] = (byte) (i - 'A'); + } + for (int i = 'z'; i >= 'a'; i--) + { + base64Alphabet[i] = (byte) (i - 'a' + 26); + } + + for (int i = '9'; i >= '0'; i--) + { + base64Alphabet[i] = (byte) (i - '0' + 52); + } + + base64Alphabet['+'] = 62; + base64Alphabet['/'] = 63; + + for (int i = 0; i <= 25; i++) + { + lookUpBase64Alphabet[i] = (char) ('A' + i); + } + + for (int i = 26, j = 0; i <= 51; i++, j++) + { + lookUpBase64Alphabet[i] = (char) ('a' + j); + } + + for (int i = 52, j = 0; i <= 61; i++, j++) + { + lookUpBase64Alphabet[i] = (char) ('0' + j); + } + lookUpBase64Alphabet[62] = (char) '+'; + lookUpBase64Alphabet[63] = (char) '/'; + } + + private static boolean isWhiteSpace(char octect) + { + return (octect == 0x20 || octect == 0xd || octect == 0xa || octect == 0x9); + } + + private static boolean isPad(char octect) + { + return (octect == PAD); + } + + private static boolean isData(char octect) + { + return (octect < BASELENGTH && base64Alphabet[octect] != -1); + } + + /** + * Encodes hex octects into Base64 + * + * @param binaryData Array containing binaryData + * @return Encoded Base64 array + */ + public static String encode(byte[] binaryData) + { + if (binaryData == null) + { + return null; + } + + int lengthDataBits = binaryData.length * EIGHTBIT; + if (lengthDataBits == 0) + { + return ""; + } + + int fewerThan24bits = lengthDataBits % TWENTYFOURBITGROUP; + int numberTriplets = lengthDataBits / TWENTYFOURBITGROUP; + int numberQuartet = fewerThan24bits != 0 ? numberTriplets + 1 : numberTriplets; + char encodedData[] = null; + + encodedData = new char[numberQuartet * 4]; + + byte k = 0, l = 0, b1 = 0, b2 = 0, b3 = 0; + + int encodedIndex = 0; + int dataIndex = 0; + + for (int i = 0; i < numberTriplets; i++) + { + b1 = binaryData[dataIndex++]; + b2 = binaryData[dataIndex++]; + b3 = binaryData[dataIndex++]; + + l = (byte) (b2 & 0x0f); + k = (byte) (b1 & 0x03); + + byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0); + byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0); + byte val3 = ((b3 & SIGN) == 0) ? (byte) (b3 >> 6) : (byte) ((b3) >> 6 ^ 0xfc); + + encodedData[encodedIndex++] = lookUpBase64Alphabet[val1]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[val2 | (k << 4)]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[(l << 2) | val3]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[b3 & 0x3f]; + } + + // form integral number of 6-bit groups + if (fewerThan24bits == EIGHTBIT) + { + b1 = binaryData[dataIndex]; + k = (byte) (b1 & 0x03); + byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0); + encodedData[encodedIndex++] = lookUpBase64Alphabet[val1]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[k << 4]; + encodedData[encodedIndex++] = PAD; + encodedData[encodedIndex++] = PAD; + } + else if (fewerThan24bits == SIXTEENBIT) + { + b1 = binaryData[dataIndex]; + b2 = binaryData[dataIndex + 1]; + l = (byte) (b2 & 0x0f); + k = (byte) (b1 & 0x03); + + byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0); + byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0); + + encodedData[encodedIndex++] = lookUpBase64Alphabet[val1]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[val2 | (k << 4)]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[l << 2]; + encodedData[encodedIndex++] = PAD; + } + return new String(encodedData); + } + + /** + * Decodes Base64 data into octects + * + * @param encoded string containing Base64 data + * @return Array containind decoded data. + */ + public static byte[] decode(String encoded) + { + if (encoded == null) + { + return null; + } + + char[] base64Data = encoded.toCharArray(); + // remove white spaces + int len = removeWhiteSpace(base64Data); + + if (len % FOURBYTE != 0) + { + return null;// should be divisible by four + } + + int numberQuadruple = (len / FOURBYTE); + + if (numberQuadruple == 0) + { + return new byte[0]; + } + + byte decodedData[] = null; + byte b1 = 0, b2 = 0, b3 = 0, b4 = 0; + char d1 = 0, d2 = 0, d3 = 0, d4 = 0; + + int i = 0; + int encodedIndex = 0; + int dataIndex = 0; + decodedData = new byte[(numberQuadruple) * 3]; + + for (; i < numberQuadruple - 1; i++) + { + + if (!isData((d1 = base64Data[dataIndex++])) || !isData((d2 = base64Data[dataIndex++])) + || !isData((d3 = base64Data[dataIndex++])) || !isData((d4 = base64Data[dataIndex++]))) + { + return null; + } // if found "no data" just return null + + b1 = base64Alphabet[d1]; + b2 = base64Alphabet[d2]; + b3 = base64Alphabet[d3]; + b4 = base64Alphabet[d4]; + + decodedData[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4); + decodedData[encodedIndex++] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf)); + decodedData[encodedIndex++] = (byte) (b3 << 6 | b4); + } + + if (!isData((d1 = base64Data[dataIndex++])) || !isData((d2 = base64Data[dataIndex++]))) + { + return null;// if found "no data" just return null + } + + b1 = base64Alphabet[d1]; + b2 = base64Alphabet[d2]; + + d3 = base64Data[dataIndex++]; + d4 = base64Data[dataIndex++]; + if (!isData((d3)) || !isData((d4))) + {// Check if they are PAD characters + if (isPad(d3) && isPad(d4)) + { + if ((b2 & 0xf) != 0)// last 4 bits should be zero + { + return null; + } + byte[] tmp = new byte[i * 3 + 1]; + System.arraycopy(decodedData, 0, tmp, 0, i * 3); + tmp[encodedIndex] = (byte) (b1 << 2 | b2 >> 4); + return tmp; + } + else if (!isPad(d3) && isPad(d4)) + { + b3 = base64Alphabet[d3]; + if ((b3 & 0x3) != 0)// last 2 bits should be zero + { + return null; + } + byte[] tmp = new byte[i * 3 + 2]; + System.arraycopy(decodedData, 0, tmp, 0, i * 3); + tmp[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4); + tmp[encodedIndex] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf)); + return tmp; + } + else + { + return null; + } + } + else + { // No PAD e.g 3cQl + b3 = base64Alphabet[d3]; + b4 = base64Alphabet[d4]; + decodedData[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4); + decodedData[encodedIndex++] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf)); + decodedData[encodedIndex++] = (byte) (b3 << 6 | b4); + + } + return decodedData; + } + + /** + * remove WhiteSpace from MIME containing encoded Base64 data. + * + * @param data the byte array of base64 data (with WS) + * @return the new length + */ + private static int removeWhiteSpace(char[] data) + { + if (data == null) + { + return 0; + } + + // count characters that's not whitespace + int newSize = 0; + int len = data.length; + for (int i = 0; i < len; i++) + { + if (!isWhiteSpace(data[i])) + { + data[newSize++] = data[i]; + } + } + return newSize; + } +} diff --git a/portal/backend/portal-common/src/main/java/org/lingniu/portal/common/utils/sign/Md5Utils.java b/portal/backend/portal-common/src/main/java/org/lingniu/portal/common/utils/sign/Md5Utils.java new file mode 100644 index 0000000..0881c79 --- /dev/null +++ b/portal/backend/portal-common/src/main/java/org/lingniu/portal/common/utils/sign/Md5Utils.java @@ -0,0 +1,67 @@ +package org.lingniu.portal.common.utils.sign; + +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Md5加密方法 + * + * @author lingniu + */ +public class Md5Utils +{ + private static final Logger log = LoggerFactory.getLogger(Md5Utils.class); + + private static byte[] md5(String s) + { + MessageDigest algorithm; + try + { + algorithm = MessageDigest.getInstance("MD5"); + algorithm.reset(); + algorithm.update(s.getBytes("UTF-8")); + byte[] messageDigest = algorithm.digest(); + return messageDigest; + } + catch (Exception e) + { + log.error("MD5 Error...", e); + } + return null; + } + + private static final String toHex(byte hash[]) + { + if (hash == null) + { + return null; + } + StringBuffer buf = new StringBuffer(hash.length * 2); + int i; + + for (i = 0; i < hash.length; i++) + { + if ((hash[i] & 0xff) < 0x10) + { + buf.append("0"); + } + buf.append(Long.toString(hash[i] & 0xff, 16)); + } + return buf.toString(); + } + + public static String hash(String s) + { + try + { + return new String(toHex(md5(s)).getBytes(StandardCharsets.UTF_8), StandardCharsets.UTF_8); + } + catch (Exception e) + { + log.error("not supported charset...{}", e); + return s; + } + } +} diff --git a/portal/backend/portal-common/src/main/java/org/lingniu/portal/common/utils/spring/SpringUtils.java b/portal/backend/portal-common/src/main/java/org/lingniu/portal/common/utils/spring/SpringUtils.java new file mode 100644 index 0000000..7e2251b --- /dev/null +++ b/portal/backend/portal-common/src/main/java/org/lingniu/portal/common/utils/spring/SpringUtils.java @@ -0,0 +1,164 @@ +package org.lingniu.portal.common.utils.spring; + +import org.springframework.aop.framework.Advised; +import org.springframework.aop.framework.AopContext; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.beans.factory.config.BeanFactoryPostProcessor; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.stereotype.Component; +import org.lingniu.portal.common.utils.StringUtils; + +/** + * spring工具类 方便在非spring管理环境中获取bean + * + * @author lingniu + */ +@Component +public final class SpringUtils implements BeanFactoryPostProcessor, ApplicationContextAware +{ + /** Spring应用上下文环境 */ + private static ConfigurableListableBeanFactory beanFactory; + + private static ApplicationContext applicationContext; + + @Override + public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException + { + SpringUtils.beanFactory = beanFactory; + } + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException + { + SpringUtils.applicationContext = applicationContext; + } + + /** + * 获取对象 + * + * @param name + * @return Object 一个以所给名字注册的bean的实例 + * @throws org.springframework.beans.BeansException + * + */ + @SuppressWarnings("unchecked") + public static T getBean(String name) throws BeansException + { + return (T) beanFactory.getBean(name); + } + + /** + * 获取类型为requiredType的对象 + * + * @param clz + * @return + * @throws org.springframework.beans.BeansException + * + */ + public static T getBean(Class clz) throws BeansException + { + T result = (T) beanFactory.getBean(clz); + return result; + } + + /** + * 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true + * + * @param name + * @return boolean + */ + public static boolean containsBean(String name) + { + return beanFactory.containsBean(name); + } + + /** + * 判断以给定名字注册的bean定义是一个singleton还是一个prototype。 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException) + * + * @param name + * @return boolean + * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException + * + */ + public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException + { + return beanFactory.isSingleton(name); + } + + /** + * @param name + * @return Class 注册对象的类型 + * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException + * + */ + public static Class getType(String name) throws NoSuchBeanDefinitionException + { + return beanFactory.getType(name); + } + + /** + * 如果给定的bean名字在bean定义中有别名,则返回这些别名 + * + * @param name + * @return + * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException + * + */ + public static String[] getAliases(String name) throws NoSuchBeanDefinitionException + { + return beanFactory.getAliases(name); + } + + /** + * 获取aop代理对象 + * + * @param invoker + * @return + */ + @SuppressWarnings("unchecked") + public static T getAopProxy(T invoker) + { + Object proxy = AopContext.currentProxy(); + if (((Advised) proxy).getTargetSource().getTargetClass() == invoker.getClass()) + { + return (T) proxy; + } + return invoker; + } + + /** + * 获取当前的环境配置,无配置返回null + * + * @return 当前的环境配置 + */ + public static String[] getActiveProfiles() + { + return applicationContext.getEnvironment().getActiveProfiles(); + } + + /** + * 获取当前的环境配置,当有多个环境配置时,只获取第一个 + * + * @return 当前的环境配置 + */ + public static String getActiveProfile() + { + final String[] activeProfiles = getActiveProfiles(); + return StringUtils.isNotEmpty(activeProfiles) ? activeProfiles[0] : null; + } + + /** + * 获取配置文件中的值 + * + * @param key 配置文件的key + * @return 当前的配置文件的值 + * + */ + public static String getRequiredProperty(String key) + { + return applicationContext.getEnvironment().getRequiredProperty(key); + } +} diff --git a/portal/backend/portal-common/src/main/java/org/lingniu/portal/common/utils/sql/SqlUtil.java b/portal/backend/portal-common/src/main/java/org/lingniu/portal/common/utils/sql/SqlUtil.java new file mode 100644 index 0000000..d523a53 --- /dev/null +++ b/portal/backend/portal-common/src/main/java/org/lingniu/portal/common/utils/sql/SqlUtil.java @@ -0,0 +1,70 @@ +package org.lingniu.portal.common.utils.sql; + +import org.lingniu.portal.common.exception.UtilException; +import org.lingniu.portal.common.utils.StringUtils; + +/** + * sql操作工具类 + * + * @author lingniu + */ +public class SqlUtil +{ + /** + * 定义常用的 sql关键字 + */ + public static String SQL_REGEX = "\u000B|and |extractvalue|updatexml|sleep|information_schema|exec |insert |select |delete |update |drop |count |chr |mid |master |truncate |char |declare |or |union |like |+|/*|user()"; + + /** + * 仅支持字母、数字、下划线、空格、逗号、小数点(支持多个字段排序) + */ + public static String SQL_PATTERN = "[a-zA-Z0-9_\\ \\,\\.]+"; + + /** + * 限制orderBy最大长度 + */ + private static final int ORDER_BY_MAX_LENGTH = 500; + + /** + * 检查字符,防止注入绕过 + */ + public static String escapeOrderBySql(String value) + { + if (StringUtils.isNotEmpty(value) && !isValidOrderBySql(value)) + { + throw new UtilException("参数不符合规范,不能进行查询"); + } + if (StringUtils.length(value) > ORDER_BY_MAX_LENGTH) + { + throw new UtilException("参数已超过最大限制,不能进行查询"); + } + return value; + } + + /** + * 验证 order by 语法是否符合规范 + */ + public static boolean isValidOrderBySql(String value) + { + return value.matches(SQL_PATTERN); + } + + /** + * SQL关键字检查 + */ + public static void filterKeyword(String value) + { + if (StringUtils.isEmpty(value)) + { + return; + } + String[] sqlKeywords = StringUtils.split(SQL_REGEX, "\\|"); + for (String sqlKeyword : sqlKeywords) + { + if (StringUtils.indexOfIgnoreCase(value, sqlKeyword) > -1) + { + throw new UtilException("参数存在SQL注入风险"); + } + } + } +} diff --git a/portal/backend/portal-common/src/main/java/org/lingniu/portal/common/utils/uuid/IdUtils.java b/portal/backend/portal-common/src/main/java/org/lingniu/portal/common/utils/uuid/IdUtils.java new file mode 100644 index 0000000..b7b63e3 --- /dev/null +++ b/portal/backend/portal-common/src/main/java/org/lingniu/portal/common/utils/uuid/IdUtils.java @@ -0,0 +1,49 @@ +package org.lingniu.portal.common.utils.uuid; + +/** + * ID生成器工具类 + * + * @author lingniu + */ +public class IdUtils +{ + /** + * 获取随机UUID + * + * @return 随机UUID + */ + public static String randomUUID() + { + return UUID.randomUUID().toString(); + } + + /** + * 简化的UUID,去掉了横线 + * + * @return 简化的UUID,去掉了横线 + */ + public static String simpleUUID() + { + return UUID.randomUUID().toString(true); + } + + /** + * 获取随机UUID,使用性能更好的ThreadLocalRandom生成UUID + * + * @return 随机UUID + */ + public static String fastUUID() + { + return UUID.fastUUID().toString(); + } + + /** + * 简化的UUID,去掉了横线,使用性能更好的ThreadLocalRandom生成UUID + * + * @return 简化的UUID,去掉了横线 + */ + public static String fastSimpleUUID() + { + return UUID.fastUUID().toString(true); + } +} diff --git a/portal/backend/portal-common/src/main/java/org/lingniu/portal/common/utils/uuid/Seq.java b/portal/backend/portal-common/src/main/java/org/lingniu/portal/common/utils/uuid/Seq.java new file mode 100644 index 0000000..bbd025e --- /dev/null +++ b/portal/backend/portal-common/src/main/java/org/lingniu/portal/common/utils/uuid/Seq.java @@ -0,0 +1,86 @@ +package org.lingniu.portal.common.utils.uuid; + +import java.util.concurrent.atomic.AtomicInteger; +import org.lingniu.portal.common.utils.DateUtils; +import org.lingniu.portal.common.utils.StringUtils; + +/** + * @author lingniu 序列生成类 + */ +public class Seq +{ + // 通用序列类型 + public static final String commSeqType = "COMMON"; + + // 上传序列类型 + public static final String uploadSeqType = "UPLOAD"; + + // 通用接口序列数 + private static AtomicInteger commSeq = new AtomicInteger(1); + + // 上传接口序列数 + private static AtomicInteger uploadSeq = new AtomicInteger(1); + + // 机器标识 + private static final String machineCode = "A"; + + /** + * 获取通用序列号 + * + * @return 序列值 + */ + public static String getId() + { + return getId(commSeqType); + } + + /** + * 默认16位序列号 yyMMddHHmmss + 一位机器标识 + 3长度循环递增字符串 + * + * @return 序列值 + */ + public static String getId(String type) + { + AtomicInteger atomicInt = commSeq; + if (uploadSeqType.equals(type)) + { + atomicInt = uploadSeq; + } + return getId(atomicInt, 3); + } + + /** + * 通用接口序列号 yyMMddHHmmss + 一位机器标识 + length长度循环递增字符串 + * + * @param atomicInt 序列数 + * @param length 数值长度 + * @return 序列值 + */ + public static String getId(AtomicInteger atomicInt, int length) + { + String result = DateUtils.dateTimeNow(); + result += machineCode; + result += getSeq(atomicInt, length); + return result; + } + + /** + * 序列循环递增字符串[1, 10 的 (length)幂次方), 用0左补齐length位数 + * + * @return 序列值 + */ + private synchronized static String getSeq(AtomicInteger atomicInt, int length) + { + // 先取值再+1 + int value = atomicInt.getAndIncrement(); + + // 如果更新后值>=10 的 (length)幂次方则重置为1 + int maxSeq = (int) Math.pow(10, length); + if (atomicInt.get() >= maxSeq) + { + atomicInt.set(1); + } + // 转字符串,用0左补齐 + return StringUtils.padl(value, length); + } +} diff --git a/portal/backend/portal-common/src/main/java/org/lingniu/portal/common/utils/uuid/UUID.java b/portal/backend/portal-common/src/main/java/org/lingniu/portal/common/utils/uuid/UUID.java new file mode 100644 index 0000000..51c687d --- /dev/null +++ b/portal/backend/portal-common/src/main/java/org/lingniu/portal/common/utils/uuid/UUID.java @@ -0,0 +1,484 @@ +package org.lingniu.portal.common.utils.uuid; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.util.Random; +import java.util.concurrent.ThreadLocalRandom; +import org.lingniu.portal.common.exception.UtilException; + +/** + * 提供通用唯一识别码(universally unique identifier)(UUID)实现 + * + * @author lingniu + */ +public final class UUID implements java.io.Serializable, Comparable +{ + private static final long serialVersionUID = -1185015143654744140L; + + /** + * SecureRandom 的单例 + * + */ + private static class Holder + { + static final SecureRandom numberGenerator = getSecureRandom(); + } + + /** 此UUID的最高64有效位 */ + private final long mostSigBits; + + /** 此UUID的最低64有效位 */ + private final long leastSigBits; + + /** + * 私有构造 + * + * @param data 数据 + */ + private UUID(byte[] data) + { + long msb = 0; + long lsb = 0; + assert data.length == 16 : "data must be 16 bytes in length"; + for (int i = 0; i < 8; i++) + { + msb = (msb << 8) | (data[i] & 0xff); + } + for (int i = 8; i < 16; i++) + { + lsb = (lsb << 8) | (data[i] & 0xff); + } + this.mostSigBits = msb; + this.leastSigBits = lsb; + } + + /** + * 使用指定的数据构造新的 UUID。 + * + * @param mostSigBits 用于 {@code UUID} 的最高有效 64 位 + * @param leastSigBits 用于 {@code UUID} 的最低有效 64 位 + */ + public UUID(long mostSigBits, long leastSigBits) + { + this.mostSigBits = mostSigBits; + this.leastSigBits = leastSigBits; + } + + /** + * 获取类型 4(伪随机生成的)UUID 的静态工厂。 + * + * @return 随机生成的 {@code UUID} + */ + public static UUID fastUUID() + { + return randomUUID(false); + } + + /** + * 获取类型 4(伪随机生成的)UUID 的静态工厂。 使用加密的强伪随机数生成器生成该 UUID。 + * + * @return 随机生成的 {@code UUID} + */ + public static UUID randomUUID() + { + return randomUUID(true); + } + + /** + * 获取类型 4(伪随机生成的)UUID 的静态工厂。 使用加密的强伪随机数生成器生成该 UUID。 + * + * @param isSecure 是否使用{@link SecureRandom}如果是可以获得更安全的随机码,否则可以得到更好的性能 + * @return 随机生成的 {@code UUID} + */ + public static UUID randomUUID(boolean isSecure) + { + final Random ng = isSecure ? Holder.numberGenerator : getRandom(); + + byte[] randomBytes = new byte[16]; + ng.nextBytes(randomBytes); + randomBytes[6] &= 0x0f; /* clear version */ + randomBytes[6] |= 0x40; /* set to version 4 */ + randomBytes[8] &= 0x3f; /* clear variant */ + randomBytes[8] |= 0x80; /* set to IETF variant */ + return new UUID(randomBytes); + } + + /** + * 根据指定的字节数组获取类型 3(基于名称的)UUID 的静态工厂。 + * + * @param name 用于构造 UUID 的字节数组。 + * + * @return 根据指定数组生成的 {@code UUID} + */ + public static UUID nameUUIDFromBytes(byte[] name) + { + MessageDigest md; + try + { + md = MessageDigest.getInstance("MD5"); + } + catch (NoSuchAlgorithmException nsae) + { + throw new InternalError("MD5 not supported"); + } + byte[] md5Bytes = md.digest(name); + md5Bytes[6] &= 0x0f; /* clear version */ + md5Bytes[6] |= 0x30; /* set to version 3 */ + md5Bytes[8] &= 0x3f; /* clear variant */ + md5Bytes[8] |= 0x80; /* set to IETF variant */ + return new UUID(md5Bytes); + } + + /** + * 根据 {@link #toString()} 方法中描述的字符串标准表示形式创建{@code UUID}。 + * + * @param name 指定 {@code UUID} 字符串 + * @return 具有指定值的 {@code UUID} + * @throws IllegalArgumentException 如果 name 与 {@link #toString} 中描述的字符串表示形式不符抛出此异常 + * + */ + public static UUID fromString(String name) + { + String[] components = name.split("-"); + if (components.length != 5) + { + throw new IllegalArgumentException("Invalid UUID string: " + name); + } + for (int i = 0; i < 5; i++) + { + components[i] = "0x" + components[i]; + } + + long mostSigBits = Long.decode(components[0]).longValue(); + mostSigBits <<= 16; + mostSigBits |= Long.decode(components[1]).longValue(); + mostSigBits <<= 16; + mostSigBits |= Long.decode(components[2]).longValue(); + + long leastSigBits = Long.decode(components[3]).longValue(); + leastSigBits <<= 48; + leastSigBits |= Long.decode(components[4]).longValue(); + + return new UUID(mostSigBits, leastSigBits); + } + + /** + * 返回此 UUID 的 128 位值中的最低有效 64 位。 + * + * @return 此 UUID 的 128 位值中的最低有效 64 位。 + */ + public long getLeastSignificantBits() + { + return leastSigBits; + } + + /** + * 返回此 UUID 的 128 位值中的最高有效 64 位。 + * + * @return 此 UUID 的 128 位值中最高有效 64 位。 + */ + public long getMostSignificantBits() + { + return mostSigBits; + } + + /** + * 与此 {@code UUID} 相关联的版本号. 版本号描述此 {@code UUID} 是如何生成的。 + *

+ * 版本号具有以下含意: + *

    + *
  • 1 基于时间的 UUID + *
  • 2 DCE 安全 UUID + *
  • 3 基于名称的 UUID + *
  • 4 随机生成的 UUID + *
+ * + * @return 此 {@code UUID} 的版本号 + */ + public int version() + { + // Version is bits masked by 0x000000000000F000 in MS long + return (int) ((mostSigBits >> 12) & 0x0f); + } + + /** + * 与此 {@code UUID} 相关联的变体号。变体号描述 {@code UUID} 的布局。 + *

+ * 变体号具有以下含意: + *

    + *
  • 0 为 NCS 向后兼容保留 + *
  • 2 IETF RFC 4122(Leach-Salz), 用于此类 + *
  • 6 保留,微软向后兼容 + *
  • 7 保留供以后定义使用 + *
+ * + * @return 此 {@code UUID} 相关联的变体号 + */ + public int variant() + { + // This field is composed of a varying number of bits. + // 0 - - Reserved for NCS backward compatibility + // 1 0 - The IETF aka Leach-Salz variant (used by this class) + // 1 1 0 Reserved, Microsoft backward compatibility + // 1 1 1 Reserved for future definition. + return (int) ((leastSigBits >>> (64 - (leastSigBits >>> 62))) & (leastSigBits >> 63)); + } + + /** + * 与此 UUID 相关联的时间戳值。 + * + *

+ * 60 位的时间戳值根据此 {@code UUID} 的 time_low、time_mid 和 time_hi 字段构造。
+ * 所得到的时间戳以 100 毫微秒为单位,从 UTC(通用协调时间) 1582 年 10 月 15 日零时开始。 + * + *

+ * 时间戳值仅在在基于时间的 UUID(其 version 类型为 1)中才有意义。
+ * 如果此 {@code UUID} 不是基于时间的 UUID,则此方法抛出 UnsupportedOperationException。 + * + * @throws UnsupportedOperationException 如果此 {@code UUID} 不是 version 为 1 的 UUID。 + */ + public long timestamp() throws UnsupportedOperationException + { + checkTimeBase(); + return (mostSigBits & 0x0FFFL) << 48// + | ((mostSigBits >> 16) & 0x0FFFFL) << 32// + | mostSigBits >>> 32; + } + + /** + * 与此 UUID 相关联的时钟序列值。 + * + *

+ * 14 位的时钟序列值根据此 UUID 的 clock_seq 字段构造。clock_seq 字段用于保证在基于时间的 UUID 中的时间唯一性。 + *

+ * {@code clockSequence} 值仅在基于时间的 UUID(其 version 类型为 1)中才有意义。 如果此 UUID 不是基于时间的 UUID,则此方法抛出 + * UnsupportedOperationException。 + * + * @return 此 {@code UUID} 的时钟序列 + * + * @throws UnsupportedOperationException 如果此 UUID 的 version 不为 1 + */ + public int clockSequence() throws UnsupportedOperationException + { + checkTimeBase(); + return (int) ((leastSigBits & 0x3FFF000000000000L) >>> 48); + } + + /** + * 与此 UUID 相关的节点值。 + * + *

+ * 48 位的节点值根据此 UUID 的 node 字段构造。此字段旨在用于保存机器的 IEEE 802 地址,该地址用于生成此 UUID 以保证空间唯一性。 + *

+ * 节点值仅在基于时间的 UUID(其 version 类型为 1)中才有意义。
+ * 如果此 UUID 不是基于时间的 UUID,则此方法抛出 UnsupportedOperationException。 + * + * @return 此 {@code UUID} 的节点值 + * + * @throws UnsupportedOperationException 如果此 UUID 的 version 不为 1 + */ + public long node() throws UnsupportedOperationException + { + checkTimeBase(); + return leastSigBits & 0x0000FFFFFFFFFFFFL; + } + + /** + * 返回此{@code UUID} 的字符串表现形式。 + * + *

+ * UUID 的字符串表示形式由此 BNF 描述: + * + *

+     * {@code
+     * UUID                   = ----
+     * time_low               = 4*
+     * time_mid               = 2*
+     * time_high_and_version  = 2*
+     * variant_and_sequence   = 2*
+     * node                   = 6*
+     * hexOctet               = 
+     * hexDigit               = [0-9a-fA-F]
+     * }
+     * 
+ * + * + * + * @return 此{@code UUID} 的字符串表现形式 + * @see #toString(boolean) + */ + @Override + public String toString() + { + return toString(false); + } + + /** + * 返回此{@code UUID} 的字符串表现形式。 + * + *

+ * UUID 的字符串表示形式由此 BNF 描述: + * + *

+     * {@code
+     * UUID                   = ----
+     * time_low               = 4*
+     * time_mid               = 2*
+     * time_high_and_version  = 2*
+     * variant_and_sequence   = 2*
+     * node                   = 6*
+     * hexOctet               = 
+     * hexDigit               = [0-9a-fA-F]
+     * }
+     * 
+ * + * + * + * @param isSimple 是否简单模式,简单模式为不带'-'的UUID字符串 + * @return 此{@code UUID} 的字符串表现形式 + */ + public String toString(boolean isSimple) + { + final StringBuilder builder = new StringBuilder(isSimple ? 32 : 36); + // time_low + builder.append(digits(mostSigBits >> 32, 8)); + if (!isSimple) + { + builder.append('-'); + } + // time_mid + builder.append(digits(mostSigBits >> 16, 4)); + if (!isSimple) + { + builder.append('-'); + } + // time_high_and_version + builder.append(digits(mostSigBits, 4)); + if (!isSimple) + { + builder.append('-'); + } + // variant_and_sequence + builder.append(digits(leastSigBits >> 48, 4)); + if (!isSimple) + { + builder.append('-'); + } + // node + builder.append(digits(leastSigBits, 12)); + + return builder.toString(); + } + + /** + * 返回此 UUID 的哈希码。 + * + * @return UUID 的哈希码值。 + */ + @Override + public int hashCode() + { + long hilo = mostSigBits ^ leastSigBits; + return ((int) (hilo >> 32)) ^ (int) hilo; + } + + /** + * 将此对象与指定对象比较。 + *

+ * 当且仅当参数不为 {@code null}、而是一个 UUID 对象、具有与此 UUID 相同的 varriant、包含相同的值(每一位均相同)时,结果才为 {@code true}。 + * + * @param obj 要与之比较的对象 + * + * @return 如果对象相同,则返回 {@code true};否则返回 {@code false} + */ + @Override + public boolean equals(Object obj) + { + if ((null == obj) || (obj.getClass() != UUID.class)) + { + return false; + } + UUID id = (UUID) obj; + return (mostSigBits == id.mostSigBits && leastSigBits == id.leastSigBits); + } + + // Comparison Operations + + /** + * 将此 UUID 与指定的 UUID 比较。 + * + *

+ * 如果两个 UUID 不同,且第一个 UUID 的最高有效字段大于第二个 UUID 的对应字段,则第一个 UUID 大于第二个 UUID。 + * + * @param val 与此 UUID 比较的 UUID + * + * @return 在此 UUID 小于、等于或大于 val 时,分别返回 -1、0 或 1。 + * + */ + @Override + public int compareTo(UUID val) + { + // The ordering is intentionally set up so that the UUIDs + // can simply be numerically compared as two numbers + return (this.mostSigBits < val.mostSigBits ? -1 : // + (this.mostSigBits > val.mostSigBits ? 1 : // + (this.leastSigBits < val.leastSigBits ? -1 : // + (this.leastSigBits > val.leastSigBits ? 1 : // + 0)))); + } + + // ------------------------------------------------------------------------------------------------------------------- + // Private method start + /** + * 返回指定数字对应的hex值 + * + * @param val 值 + * @param digits 位 + * @return 值 + */ + private static String digits(long val, int digits) + { + long hi = 1L << (digits * 4); + return Long.toHexString(hi | (val & (hi - 1))).substring(1); + } + + /** + * 检查是否为time-based版本UUID + */ + private void checkTimeBase() + { + if (version() != 1) + { + throw new UnsupportedOperationException("Not a time-based UUID"); + } + } + + /** + * 获取{@link SecureRandom},类提供加密的强随机数生成器 (RNG) + * + * @return {@link SecureRandom} + */ + public static SecureRandom getSecureRandom() + { + try + { + return SecureRandom.getInstance("SHA1PRNG"); + } + catch (NoSuchAlgorithmException e) + { + throw new UtilException(e); + } + } + + /** + * 获取随机数生成器对象
+ * ThreadLocalRandom是JDK 7之后提供并发产生随机数,能够解决多个线程发生的竞争争夺。 + * + * @return {@link ThreadLocalRandom} + */ + public static ThreadLocalRandom getRandom() + { + return ThreadLocalRandom.current(); + } +} diff --git a/portal/backend/portal-common/src/main/java/org/lingniu/portal/common/xss/Xss.java b/portal/backend/portal-common/src/main/java/org/lingniu/portal/common/xss/Xss.java new file mode 100644 index 0000000..79a73ec --- /dev/null +++ b/portal/backend/portal-common/src/main/java/org/lingniu/portal/common/xss/Xss.java @@ -0,0 +1,27 @@ +package org.lingniu.portal.common.xss; + +import jakarta.validation.Constraint; +import jakarta.validation.Payload; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 自定义xss校验注解 + * + * @author lingniu + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(value = { ElementType.METHOD, ElementType.FIELD, ElementType.CONSTRUCTOR, ElementType.PARAMETER }) +@Constraint(validatedBy = { XssValidator.class }) +public @interface Xss +{ + String message() + + default "不允许任何脚本运行"; + + Class[] groups() default {}; + + Class[] payload() default {}; +} diff --git a/portal/backend/portal-common/src/main/java/org/lingniu/portal/common/xss/XssValidator.java b/portal/backend/portal-common/src/main/java/org/lingniu/portal/common/xss/XssValidator.java new file mode 100644 index 0000000..34e50e3 --- /dev/null +++ b/portal/backend/portal-common/src/main/java/org/lingniu/portal/common/xss/XssValidator.java @@ -0,0 +1,39 @@ +package org.lingniu.portal.common.xss; + +import org.lingniu.portal.common.utils.StringUtils; +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * 自定义xss校验注解实现 + * + * @author lingniu + */ +public class XssValidator implements ConstraintValidator +{ + private static final String HTML_PATTERN = "<(\\S*?)[^>]*>.*?|<.*? />"; + + @Override + public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) + { + if (StringUtils.isBlank(value)) + { + return true; + } + return !containsHtml(value); + } + + public static boolean containsHtml(String value) + { + StringBuilder sHtml = new StringBuilder(); + Pattern pattern = Pattern.compile(HTML_PATTERN); + Matcher matcher = pattern.matcher(value); + while (matcher.find()) + { + sHtml.append(matcher.group()); + } + return pattern.matcher(sHtml).matches(); + } +} \ No newline at end of file diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/annotation/Anonymous.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/annotation/Anonymous.class new file mode 100644 index 0000000..f1d0c8c Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/annotation/Anonymous.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/annotation/DataScope.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/annotation/DataScope.class new file mode 100644 index 0000000..5ca4262 Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/annotation/DataScope.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/annotation/DataSource.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/annotation/DataSource.class new file mode 100644 index 0000000..a46a35a Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/annotation/DataSource.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/annotation/Excel$ColumnType.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/annotation/Excel$ColumnType.class new file mode 100644 index 0000000..703a2f6 Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/annotation/Excel$ColumnType.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/annotation/Excel$Type.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/annotation/Excel$Type.class new file mode 100644 index 0000000..932283e Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/annotation/Excel$Type.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/annotation/Excel.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/annotation/Excel.class new file mode 100644 index 0000000..cb9e3c8 Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/annotation/Excel.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/annotation/Excels.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/annotation/Excels.class new file mode 100644 index 0000000..4ecfc0c Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/annotation/Excels.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/annotation/Log.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/annotation/Log.class new file mode 100644 index 0000000..5ab2814 Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/annotation/Log.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/annotation/RateLimiter.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/annotation/RateLimiter.class new file mode 100644 index 0000000..e2bda23 Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/annotation/RateLimiter.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/annotation/RepeatSubmit.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/annotation/RepeatSubmit.class new file mode 100644 index 0000000..f3fd763 Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/annotation/RepeatSubmit.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/annotation/Sensitive.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/annotation/Sensitive.class new file mode 100644 index 0000000..8b0e59c Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/annotation/Sensitive.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/config/ProjectConfig.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/config/ProjectConfig.class new file mode 100644 index 0000000..4acc035 Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/config/ProjectConfig.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/config/serializer/SensitiveJsonSerializer.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/config/serializer/SensitiveJsonSerializer.class new file mode 100644 index 0000000..5ae5073 Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/config/serializer/SensitiveJsonSerializer.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/constant/CacheConstants.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/constant/CacheConstants.class new file mode 100644 index 0000000..8ad1cea Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/constant/CacheConstants.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/constant/Constants.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/constant/Constants.class new file mode 100644 index 0000000..00e34d3 Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/constant/Constants.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/constant/GenConstants.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/constant/GenConstants.class new file mode 100644 index 0000000..e345f75 Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/constant/GenConstants.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/constant/HttpStatus.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/constant/HttpStatus.class new file mode 100644 index 0000000..38be540 Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/constant/HttpStatus.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/constant/ScheduleConstants$Status.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/constant/ScheduleConstants$Status.class new file mode 100644 index 0000000..c8ae5ec Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/constant/ScheduleConstants$Status.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/constant/ScheduleConstants.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/constant/ScheduleConstants.class new file mode 100644 index 0000000..0cc4034 Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/constant/ScheduleConstants.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/constant/UserConstants.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/constant/UserConstants.class new file mode 100644 index 0000000..d88942b Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/constant/UserConstants.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/core/controller/BaseController$1.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/core/controller/BaseController$1.class new file mode 100644 index 0000000..25ebcdd Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/core/controller/BaseController$1.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/core/controller/BaseController.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/core/controller/BaseController.class new file mode 100644 index 0000000..8286117 Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/core/controller/BaseController.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/core/domain/AjaxResult.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/core/domain/AjaxResult.class new file mode 100644 index 0000000..a844d1f Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/core/domain/AjaxResult.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/core/domain/BaseEntity.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/core/domain/BaseEntity.class new file mode 100644 index 0000000..23c64d6 Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/core/domain/BaseEntity.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/core/domain/R.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/core/domain/R.class new file mode 100644 index 0000000..71358f7 Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/core/domain/R.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/core/domain/TreeEntity.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/core/domain/TreeEntity.class new file mode 100644 index 0000000..9774dd9 Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/core/domain/TreeEntity.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/core/domain/TreeSelect.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/core/domain/TreeSelect.class new file mode 100644 index 0000000..19f9f95 Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/core/domain/TreeSelect.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/core/domain/entity/SysDept.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/core/domain/entity/SysDept.class new file mode 100644 index 0000000..45897e3 Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/core/domain/entity/SysDept.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/core/domain/entity/SysDictData.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/core/domain/entity/SysDictData.class new file mode 100644 index 0000000..a46b714 Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/core/domain/entity/SysDictData.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/core/domain/entity/SysDictType.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/core/domain/entity/SysDictType.class new file mode 100644 index 0000000..82aff7b Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/core/domain/entity/SysDictType.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/core/domain/entity/SysMenu.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/core/domain/entity/SysMenu.class new file mode 100644 index 0000000..8d89486 Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/core/domain/entity/SysMenu.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/core/domain/entity/SysRole.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/core/domain/entity/SysRole.class new file mode 100644 index 0000000..5ab84b7 Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/core/domain/entity/SysRole.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/core/domain/entity/SysUser.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/core/domain/entity/SysUser.class new file mode 100644 index 0000000..1452740 Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/core/domain/entity/SysUser.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/core/domain/model/LoginBody.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/core/domain/model/LoginBody.class new file mode 100644 index 0000000..1fc64fd Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/core/domain/model/LoginBody.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/core/domain/model/LoginUser.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/core/domain/model/LoginUser.class new file mode 100644 index 0000000..b73fdac Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/core/domain/model/LoginUser.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/core/domain/model/RegisterBody.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/core/domain/model/RegisterBody.class new file mode 100644 index 0000000..5528148 Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/core/domain/model/RegisterBody.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/core/page/PageDomain.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/core/page/PageDomain.class new file mode 100644 index 0000000..3ed153d Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/core/page/PageDomain.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/core/page/TableDataInfo.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/core/page/TableDataInfo.class new file mode 100644 index 0000000..fb2ac6b Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/core/page/TableDataInfo.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/core/page/TableSupport.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/core/page/TableSupport.class new file mode 100644 index 0000000..123103b Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/core/page/TableSupport.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/core/redis/RedisCache.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/core/redis/RedisCache.class new file mode 100644 index 0000000..252d2ff Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/core/redis/RedisCache.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/core/text/CharsetKit.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/core/text/CharsetKit.class new file mode 100644 index 0000000..3881e30 Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/core/text/CharsetKit.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/core/text/Convert.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/core/text/Convert.class new file mode 100644 index 0000000..2d91caf Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/core/text/Convert.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/core/text/StrFormatter.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/core/text/StrFormatter.class new file mode 100644 index 0000000..5469acd Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/core/text/StrFormatter.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/enums/BusinessStatus.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/enums/BusinessStatus.class new file mode 100644 index 0000000..feb5233 Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/enums/BusinessStatus.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/enums/BusinessType.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/enums/BusinessType.class new file mode 100644 index 0000000..74eed1d Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/enums/BusinessType.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/enums/DataSourceType.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/enums/DataSourceType.class new file mode 100644 index 0000000..4325eed Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/enums/DataSourceType.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/enums/DesensitizedType.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/enums/DesensitizedType.class new file mode 100644 index 0000000..b550e4c Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/enums/DesensitizedType.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/enums/HttpMethod.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/enums/HttpMethod.class new file mode 100644 index 0000000..8130adb Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/enums/HttpMethod.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/enums/LimitType.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/enums/LimitType.class new file mode 100644 index 0000000..986612f Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/enums/LimitType.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/enums/OperatorType.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/enums/OperatorType.class new file mode 100644 index 0000000..3f09f24 Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/enums/OperatorType.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/enums/UserStatus.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/enums/UserStatus.class new file mode 100644 index 0000000..69c758c Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/enums/UserStatus.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/exception/DemoModeException.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/exception/DemoModeException.class new file mode 100644 index 0000000..6756c1c Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/exception/DemoModeException.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/exception/GlobalException.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/exception/GlobalException.class new file mode 100644 index 0000000..e8f0d2c Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/exception/GlobalException.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/exception/ServiceException.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/exception/ServiceException.class new file mode 100644 index 0000000..378e98c Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/exception/ServiceException.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/exception/UtilException.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/exception/UtilException.class new file mode 100644 index 0000000..7d2f8d1 Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/exception/UtilException.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/exception/base/BaseException.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/exception/base/BaseException.class new file mode 100644 index 0000000..31d4d09 Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/exception/base/BaseException.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/exception/file/FileException.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/exception/file/FileException.class new file mode 100644 index 0000000..4cb450a Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/exception/file/FileException.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/exception/file/FileNameLengthLimitExceededException.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/exception/file/FileNameLengthLimitExceededException.class new file mode 100644 index 0000000..f56b144 Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/exception/file/FileNameLengthLimitExceededException.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/exception/file/FileSizeLimitExceededException.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/exception/file/FileSizeLimitExceededException.class new file mode 100644 index 0000000..b091728 Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/exception/file/FileSizeLimitExceededException.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/exception/file/FileUploadException.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/exception/file/FileUploadException.class new file mode 100644 index 0000000..f042876 Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/exception/file/FileUploadException.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/exception/file/InvalidExtensionException$InvalidFlashExtensionException.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/exception/file/InvalidExtensionException$InvalidFlashExtensionException.class new file mode 100644 index 0000000..d4fbf17 Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/exception/file/InvalidExtensionException$InvalidFlashExtensionException.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/exception/file/InvalidExtensionException$InvalidImageExtensionException.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/exception/file/InvalidExtensionException$InvalidImageExtensionException.class new file mode 100644 index 0000000..762494b Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/exception/file/InvalidExtensionException$InvalidImageExtensionException.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/exception/file/InvalidExtensionException$InvalidMediaExtensionException.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/exception/file/InvalidExtensionException$InvalidMediaExtensionException.class new file mode 100644 index 0000000..5d85d4f Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/exception/file/InvalidExtensionException$InvalidMediaExtensionException.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/exception/file/InvalidExtensionException$InvalidVideoExtensionException.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/exception/file/InvalidExtensionException$InvalidVideoExtensionException.class new file mode 100644 index 0000000..efb2d15 Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/exception/file/InvalidExtensionException$InvalidVideoExtensionException.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/exception/file/InvalidExtensionException.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/exception/file/InvalidExtensionException.class new file mode 100644 index 0000000..1d3ce82 Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/exception/file/InvalidExtensionException.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/exception/job/TaskException$Code.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/exception/job/TaskException$Code.class new file mode 100644 index 0000000..a402d4b Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/exception/job/TaskException$Code.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/exception/job/TaskException.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/exception/job/TaskException.class new file mode 100644 index 0000000..d82dfe4 Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/exception/job/TaskException.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/exception/user/BlackListException.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/exception/user/BlackListException.class new file mode 100644 index 0000000..32f865a Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/exception/user/BlackListException.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/exception/user/CaptchaException.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/exception/user/CaptchaException.class new file mode 100644 index 0000000..fafc0e6 Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/exception/user/CaptchaException.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/exception/user/CaptchaExpireException.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/exception/user/CaptchaExpireException.class new file mode 100644 index 0000000..7c8fb3e Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/exception/user/CaptchaExpireException.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/exception/user/UserException.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/exception/user/UserException.class new file mode 100644 index 0000000..cdcc06c Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/exception/user/UserException.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/exception/user/UserNotExistsException.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/exception/user/UserNotExistsException.class new file mode 100644 index 0000000..2474091 Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/exception/user/UserNotExistsException.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/exception/user/UserPasswordNotMatchException.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/exception/user/UserPasswordNotMatchException.class new file mode 100644 index 0000000..e6000e6 Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/exception/user/UserPasswordNotMatchException.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/exception/user/UserPasswordRetryLimitExceedException.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/exception/user/UserPasswordRetryLimitExceedException.class new file mode 100644 index 0000000..c464cd8 Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/exception/user/UserPasswordRetryLimitExceedException.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/filter/PropertyPreExcludeFilter.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/filter/PropertyPreExcludeFilter.class new file mode 100644 index 0000000..05381ee Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/filter/PropertyPreExcludeFilter.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/filter/RefererFilter.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/filter/RefererFilter.class new file mode 100644 index 0000000..fb640fe Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/filter/RefererFilter.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/filter/RepeatableFilter.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/filter/RepeatableFilter.class new file mode 100644 index 0000000..61da44a Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/filter/RepeatableFilter.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/filter/RepeatedlyRequestWrapper$1.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/filter/RepeatedlyRequestWrapper$1.class new file mode 100644 index 0000000..38d6e3d Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/filter/RepeatedlyRequestWrapper$1.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/filter/RepeatedlyRequestWrapper.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/filter/RepeatedlyRequestWrapper.class new file mode 100644 index 0000000..739649d Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/filter/RepeatedlyRequestWrapper.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/filter/XssFilter.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/filter/XssFilter.class new file mode 100644 index 0000000..0aed98d Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/filter/XssFilter.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/filter/XssHttpServletRequestWrapper$1.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/filter/XssHttpServletRequestWrapper$1.class new file mode 100644 index 0000000..1f89b09 Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/filter/XssHttpServletRequestWrapper$1.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/filter/XssHttpServletRequestWrapper.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/filter/XssHttpServletRequestWrapper.class new file mode 100644 index 0000000..9b91270 Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/filter/XssHttpServletRequestWrapper.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/Arith.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/Arith.class new file mode 100644 index 0000000..ab93c53 Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/Arith.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/DateUtils.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/DateUtils.class new file mode 100644 index 0000000..21db6dc Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/DateUtils.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/DesensitizedUtil.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/DesensitizedUtil.class new file mode 100644 index 0000000..9b2cc23 Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/DesensitizedUtil.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/DictUtils.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/DictUtils.class new file mode 100644 index 0000000..90f81cd Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/DictUtils.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/ExceptionUtil.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/ExceptionUtil.class new file mode 100644 index 0000000..15a0db6 Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/ExceptionUtil.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/LogUtils.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/LogUtils.class new file mode 100644 index 0000000..0310b1d Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/LogUtils.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/MessageUtils.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/MessageUtils.class new file mode 100644 index 0000000..30f8f10 Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/MessageUtils.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/PageUtils.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/PageUtils.class new file mode 100644 index 0000000..efeb29b Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/PageUtils.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/SecurityUtils.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/SecurityUtils.class new file mode 100644 index 0000000..1ce0f6e Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/SecurityUtils.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/ServletUtils.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/ServletUtils.class new file mode 100644 index 0000000..cb544a4 Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/ServletUtils.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/StringUtils.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/StringUtils.class new file mode 100644 index 0000000..68518fa Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/StringUtils.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/Threads.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/Threads.class new file mode 100644 index 0000000..4dc8556 Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/Threads.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/bean/BeanUtils.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/bean/BeanUtils.class new file mode 100644 index 0000000..98e4096 Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/bean/BeanUtils.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/bean/BeanValidators.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/bean/BeanValidators.class new file mode 100644 index 0000000..f38d75f Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/bean/BeanValidators.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/file/FileTypeUtils.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/file/FileTypeUtils.class new file mode 100644 index 0000000..5ffa50e Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/file/FileTypeUtils.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/file/FileUploadUtils.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/file/FileUploadUtils.class new file mode 100644 index 0000000..dc1d6d3 Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/file/FileUploadUtils.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/file/FileUtils.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/file/FileUtils.class new file mode 100644 index 0000000..c175306 Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/file/FileUtils.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/file/ImageUtils.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/file/ImageUtils.class new file mode 100644 index 0000000..b7741be Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/file/ImageUtils.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/file/MimeTypeUtils.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/file/MimeTypeUtils.class new file mode 100644 index 0000000..4e15958 Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/file/MimeTypeUtils.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/html/EscapeUtil.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/html/EscapeUtil.class new file mode 100644 index 0000000..30e5162 Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/html/EscapeUtil.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/html/HTMLFilter.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/html/HTMLFilter.class new file mode 100644 index 0000000..23a804c Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/html/HTMLFilter.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/http/HttpHelper.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/http/HttpHelper.class new file mode 100644 index 0000000..761a4f1 Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/http/HttpHelper.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/http/HttpUtils$TrustAnyHostnameVerifier.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/http/HttpUtils$TrustAnyHostnameVerifier.class new file mode 100644 index 0000000..e03aa75 Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/http/HttpUtils$TrustAnyHostnameVerifier.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/http/HttpUtils$TrustAnyTrustManager.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/http/HttpUtils$TrustAnyTrustManager.class new file mode 100644 index 0000000..e34312d Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/http/HttpUtils$TrustAnyTrustManager.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/http/HttpUtils.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/http/HttpUtils.class new file mode 100644 index 0000000..b2d9ac0 Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/http/HttpUtils.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/http/UserAgentUtils.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/http/UserAgentUtils.class new file mode 100644 index 0000000..ae82229 Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/http/UserAgentUtils.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/ip/AddressUtils.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/ip/AddressUtils.class new file mode 100644 index 0000000..23b42fd Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/ip/AddressUtils.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/ip/IpUtils.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/ip/IpUtils.class new file mode 100644 index 0000000..ce70ae3 Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/ip/IpUtils.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/poi/ExcelHandlerAdapter.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/poi/ExcelHandlerAdapter.class new file mode 100644 index 0000000..9335135 Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/poi/ExcelHandlerAdapter.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/poi/ExcelUtil.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/poi/ExcelUtil.class new file mode 100644 index 0000000..859ffc1 Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/poi/ExcelUtil.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/reflect/ReflectUtils.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/reflect/ReflectUtils.class new file mode 100644 index 0000000..0808c90 Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/reflect/ReflectUtils.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/sign/Base64.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/sign/Base64.class new file mode 100644 index 0000000..91bd0d2 Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/sign/Base64.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/sign/Md5Utils.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/sign/Md5Utils.class new file mode 100644 index 0000000..943db2f Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/sign/Md5Utils.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/spring/SpringUtils.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/spring/SpringUtils.class new file mode 100644 index 0000000..b63d3a4 Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/spring/SpringUtils.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/sql/SqlUtil.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/sql/SqlUtil.class new file mode 100644 index 0000000..353ba4e Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/sql/SqlUtil.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/uuid/IdUtils.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/uuid/IdUtils.class new file mode 100644 index 0000000..27a7835 Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/uuid/IdUtils.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/uuid/Seq.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/uuid/Seq.class new file mode 100644 index 0000000..ff53d37 Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/uuid/Seq.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/uuid/UUID$Holder.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/uuid/UUID$Holder.class new file mode 100644 index 0000000..a9d95fa Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/uuid/UUID$Holder.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/uuid/UUID.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/uuid/UUID.class new file mode 100644 index 0000000..3f4f78e Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/utils/uuid/UUID.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/xss/Xss.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/xss/Xss.class new file mode 100644 index 0000000..79837ac Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/xss/Xss.class differ diff --git a/portal/backend/portal-common/target/classes/org/lingniu/portal/common/xss/XssValidator.class b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/xss/XssValidator.class new file mode 100644 index 0000000..e9fc620 Binary files /dev/null and b/portal/backend/portal-common/target/classes/org/lingniu/portal/common/xss/XssValidator.class differ diff --git a/portal/backend/portal-common/target/maven-archiver/pom.properties b/portal/backend/portal-common/target/maven-archiver/pom.properties new file mode 100644 index 0000000..5273a28 --- /dev/null +++ b/portal/backend/portal-common/target/maven-archiver/pom.properties @@ -0,0 +1,5 @@ +#Generated by Maven +#Tue Feb 10 15:13:40 CST 2026 +groupId=org.lingniu +artifactId=portal-common +version=1.0.0 diff --git a/portal/backend/portal-common/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst b/portal/backend/portal-common/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst new file mode 100644 index 0000000..ca3f95b --- /dev/null +++ b/portal/backend/portal-common/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst @@ -0,0 +1,123 @@ +org\lingniu\portal\common\constant\GenConstants.class +org\lingniu\portal\common\exception\file\InvalidExtensionException$InvalidVideoExtensionException.class +org\lingniu\portal\common\core\text\StrFormatter.class +org\lingniu\portal\common\enums\DesensitizedType.class +org\lingniu\portal\common\constant\Constants.class +org\lingniu\portal\common\utils\DateUtils.class +org\lingniu\portal\common\exception\user\UserPasswordNotMatchException.class +org\lingniu\portal\common\utils\MessageUtils.class +org\lingniu\portal\common\core\domain\entity\SysDictData.class +org\lingniu\portal\common\utils\DesensitizedUtil.class +org\lingniu\portal\common\utils\uuid\UUID.class +org\lingniu\portal\common\core\page\TableDataInfo.class +org\lingniu\portal\common\constant\CacheConstants.class +org\lingniu\portal\common\exception\file\FileSizeLimitExceededException.class +org\lingniu\portal\common\utils\sign\Base64.class +org\lingniu\portal\common\exception\file\FileException.class +org\lingniu\portal\common\enums\OperatorType.class +org\lingniu\portal\common\utils\StringUtils.class +org\lingniu\portal\common\utils\uuid\UUID$Holder.class +org\lingniu\portal\common\xss\XssValidator.class +org\lingniu\portal\common\annotation\Excel.class +org\lingniu\portal\common\enums\UserStatus.class +org\lingniu\portal\common\utils\SecurityUtils.class +org\lingniu\portal\common\filter\RepeatedlyRequestWrapper.class +org\lingniu\portal\common\utils\LogUtils.class +org\lingniu\portal\common\core\domain\TreeEntity.class +org\lingniu\portal\common\core\page\TableSupport.class +org\lingniu\portal\common\exception\UtilException.class +org\lingniu\portal\common\utils\html\EscapeUtil.class +org\lingniu\portal\common\core\domain\model\RegisterBody.class +org\lingniu\portal\common\exception\user\UserPasswordRetryLimitExceedException.class +org\lingniu\portal\common\exception\user\CaptchaExpireException.class +org\lingniu\portal\common\exception\GlobalException.class +org\lingniu\portal\common\utils\DictUtils.class +org\lingniu\portal\common\utils\http\HttpUtils.class +org\lingniu\portal\common\core\domain\TreeSelect.class +org\lingniu\portal\common\enums\HttpMethod.class +org\lingniu\portal\common\constant\ScheduleConstants$Status.class +org\lingniu\portal\common\core\controller\BaseController$1.class +org\lingniu\portal\common\enums\DataSourceType.class +org\lingniu\portal\common\core\domain\R.class +org\lingniu\portal\common\filter\XssHttpServletRequestWrapper$1.class +org\lingniu\portal\common\core\text\Convert.class +org\lingniu\portal\common\core\page\PageDomain.class +org\lingniu\portal\common\utils\poi\ExcelHandlerAdapter.class +org\lingniu\portal\common\utils\http\UserAgentUtils.class +org\lingniu\portal\common\utils\file\ImageUtils.class +org\lingniu\portal\common\exception\user\BlackListException.class +org\lingniu\portal\common\annotation\Sensitive.class +org\lingniu\portal\common\core\domain\model\LoginUser.class +org\lingniu\portal\common\utils\Threads.class +org\lingniu\portal\common\exception\file\InvalidExtensionException.class +org\lingniu\portal\common\utils\file\FileUploadUtils.class +org\lingniu\portal\common\utils\bean\BeanUtils.class +org\lingniu\portal\common\exception\file\InvalidExtensionException$InvalidFlashExtensionException.class +org\lingniu\portal\common\core\domain\AjaxResult.class +org\lingniu\portal\common\exception\base\BaseException.class +org\lingniu\portal\common\utils\file\FileTypeUtils.class +org\lingniu\portal\common\annotation\DataScope.class +org\lingniu\portal\common\utils\sql\SqlUtil.class +org\lingniu\portal\common\utils\Arith.class +org\lingniu\portal\common\enums\BusinessType.class +org\lingniu\portal\common\utils\sign\Md5Utils.class +org\lingniu\portal\common\exception\ServiceException.class +org\lingniu\portal\common\constant\HttpStatus.class +org\lingniu\portal\common\utils\file\MimeTypeUtils.class +org\lingniu\portal\common\utils\http\HttpUtils$TrustAnyTrustManager.class +org\lingniu\portal\common\exception\job\TaskException$Code.class +org\lingniu\portal\common\annotation\RateLimiter.class +org\lingniu\portal\common\core\domain\BaseEntity.class +org\lingniu\portal\common\enums\LimitType.class +org\lingniu\portal\common\core\redis\RedisCache.class +org\lingniu\portal\common\core\domain\entity\SysDictType.class +org\lingniu\portal\common\exception\user\UserNotExistsException.class +org\lingniu\portal\common\annotation\RepeatSubmit.class +org\lingniu\portal\common\utils\spring\SpringUtils.class +org\lingniu\portal\common\config\serializer\SensitiveJsonSerializer.class +org\lingniu\portal\common\enums\BusinessStatus.class +org\lingniu\portal\common\utils\html\HTMLFilter.class +org\lingniu\portal\common\annotation\Excels.class +org\lingniu\portal\common\filter\XssHttpServletRequestWrapper.class +org\lingniu\portal\common\utils\file\FileUtils.class +org\lingniu\portal\common\exception\job\TaskException.class +org\lingniu\portal\common\annotation\Excel$Type.class +org\lingniu\portal\common\annotation\DataSource.class +org\lingniu\portal\common\exception\user\CaptchaException.class +org\lingniu\portal\common\utils\ServletUtils.class +org\lingniu\portal\common\exception\file\InvalidExtensionException$InvalidMediaExtensionException.class +org\lingniu\portal\common\exception\file\FileUploadException.class +org\lingniu\portal\common\filter\PropertyPreExcludeFilter.class +org\lingniu\portal\common\core\domain\entity\SysDept.class +org\lingniu\portal\common\core\domain\entity\SysMenu.class +org\lingniu\portal\common\filter\RefererFilter.class +org\lingniu\portal\common\exception\file\InvalidExtensionException$InvalidImageExtensionException.class +org\lingniu\portal\common\utils\reflect\ReflectUtils.class +org\lingniu\portal\common\xss\Xss.class +org\lingniu\portal\common\utils\ip\IpUtils.class +org\lingniu\portal\common\utils\http\HttpUtils$TrustAnyHostnameVerifier.class +org\lingniu\portal\common\constant\ScheduleConstants.class +org\lingniu\portal\common\core\domain\model\LoginBody.class +org\lingniu\portal\common\utils\uuid\Seq.class +org\lingniu\portal\common\filter\RepeatableFilter.class +org\lingniu\portal\common\exception\file\FileNameLengthLimitExceededException.class +org\lingniu\portal\common\filter\RepeatedlyRequestWrapper$1.class +org\lingniu\portal\common\utils\uuid\IdUtils.class +org\lingniu\portal\common\config\ProjectConfig.class +org\lingniu\portal\common\utils\poi\ExcelUtil.class +org\lingniu\portal\common\annotation\Log.class +org\lingniu\portal\common\annotation\Anonymous.class +org\lingniu\portal\common\constant\UserConstants.class +org\lingniu\portal\common\exception\DemoModeException.class +org\lingniu\portal\common\utils\PageUtils.class +org\lingniu\portal\common\core\domain\entity\SysRole.class +org\lingniu\portal\common\utils\bean\BeanValidators.class +org\lingniu\portal\common\core\controller\BaseController.class +org\lingniu\portal\common\core\domain\entity\SysUser.class +org\lingniu\portal\common\exception\user\UserException.class +org\lingniu\portal\common\utils\ExceptionUtil.class +org\lingniu\portal\common\utils\http\HttpHelper.class +org\lingniu\portal\common\annotation\Excel$ColumnType.class +org\lingniu\portal\common\core\text\CharsetKit.class +org\lingniu\portal\common\utils\ip\AddressUtils.class +org\lingniu\portal\common\filter\XssFilter.class diff --git a/portal/backend/portal-common/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst b/portal/backend/portal-common/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst new file mode 100644 index 0000000..265114c --- /dev/null +++ b/portal/backend/portal-common/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst @@ -0,0 +1,109 @@ +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\annotation\Anonymous.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\annotation\DataScope.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\annotation\DataSource.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\annotation\Excel.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\annotation\Excels.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\annotation\Log.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\annotation\RateLimiter.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\annotation\RepeatSubmit.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\annotation\Sensitive.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\config\ProjectConfig.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\config\serializer\SensitiveJsonSerializer.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\constant\CacheConstants.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\constant\Constants.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\constant\GenConstants.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\constant\HttpStatus.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\constant\ScheduleConstants.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\constant\UserConstants.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\core\controller\BaseController.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\core\domain\AjaxResult.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\core\domain\BaseEntity.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\core\domain\entity\SysDept.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\core\domain\entity\SysDictData.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\core\domain\entity\SysDictType.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\core\domain\entity\SysMenu.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\core\domain\entity\SysRole.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\core\domain\entity\SysUser.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\core\domain\model\LoginBody.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\core\domain\model\LoginUser.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\core\domain\model\RegisterBody.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\core\domain\R.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\core\domain\TreeEntity.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\core\domain\TreeSelect.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\core\page\PageDomain.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\core\page\TableDataInfo.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\core\page\TableSupport.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\core\redis\RedisCache.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\core\text\CharsetKit.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\core\text\Convert.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\core\text\StrFormatter.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\enums\BusinessStatus.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\enums\BusinessType.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\enums\DataSourceType.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\enums\DesensitizedType.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\enums\HttpMethod.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\enums\LimitType.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\enums\OperatorType.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\enums\UserStatus.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\exception\base\BaseException.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\exception\DemoModeException.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\exception\file\FileException.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\exception\file\FileNameLengthLimitExceededException.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\exception\file\FileSizeLimitExceededException.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\exception\file\FileUploadException.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\exception\file\InvalidExtensionException.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\exception\GlobalException.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\exception\job\TaskException.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\exception\ServiceException.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\exception\user\BlackListException.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\exception\user\CaptchaException.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\exception\user\CaptchaExpireException.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\exception\user\UserException.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\exception\user\UserNotExistsException.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\exception\user\UserPasswordNotMatchException.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\exception\user\UserPasswordRetryLimitExceedException.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\exception\UtilException.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\filter\PropertyPreExcludeFilter.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\filter\RefererFilter.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\filter\RepeatableFilter.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\filter\RepeatedlyRequestWrapper.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\filter\XssFilter.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\filter\XssHttpServletRequestWrapper.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\utils\Arith.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\utils\bean\BeanUtils.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\utils\bean\BeanValidators.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\utils\DateUtils.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\utils\DesensitizedUtil.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\utils\DictUtils.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\utils\ExceptionUtil.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\utils\file\FileTypeUtils.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\utils\file\FileUploadUtils.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\utils\file\FileUtils.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\utils\file\ImageUtils.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\utils\file\MimeTypeUtils.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\utils\html\EscapeUtil.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\utils\html\HTMLFilter.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\utils\http\HttpHelper.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\utils\http\HttpUtils.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\utils\http\UserAgentUtils.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\utils\ip\AddressUtils.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\utils\ip\IpUtils.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\utils\LogUtils.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\utils\MessageUtils.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\utils\PageUtils.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\utils\poi\ExcelHandlerAdapter.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\utils\poi\ExcelUtil.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\utils\reflect\ReflectUtils.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\utils\SecurityUtils.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\utils\ServletUtils.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\utils\sign\Base64.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\utils\sign\Md5Utils.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\utils\spring\SpringUtils.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\utils\sql\SqlUtil.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\utils\StringUtils.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\utils\Threads.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\utils\uuid\IdUtils.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\utils\uuid\Seq.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\utils\uuid\UUID.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\xss\Xss.java +D:\RuoYi-Vue\portal-common\src\main\java\org\lingniu\portal\common\xss\XssValidator.java diff --git a/portal/backend/portal-common/target/portal-common-1.0.0.jar b/portal/backend/portal-common/target/portal-common-1.0.0.jar new file mode 100644 index 0000000..2a5906f Binary files /dev/null and b/portal/backend/portal-common/target/portal-common-1.0.0.jar differ diff --git a/portal/backend/portal-framework/pom.xml b/portal/backend/portal-framework/pom.xml new file mode 100644 index 0000000..06804eb --- /dev/null +++ b/portal/backend/portal-framework/pom.xml @@ -0,0 +1,64 @@ + + + + portal + org.lingniu + 1.0.0 + + 4.0.0 + + portal-framework + + + framework框架核心 + + + + + + + org.springframework.boot + spring-boot-starter-web + + + + + org.springframework.boot + spring-boot-starter-aop + + + + + com.alibaba + druid-spring-boot-3-starter + + + + + pro.fessional + kaptcha + + + servlet-api + javax.servlet + + + + + + + com.github.oshi + oshi-core + + + + + org.lingniu + portal-system + + + + + \ No newline at end of file diff --git a/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/aspectj/DataScopeAspect.java b/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/aspectj/DataScopeAspect.java new file mode 100644 index 0000000..f556f4c --- /dev/null +++ b/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/aspectj/DataScopeAspect.java @@ -0,0 +1,184 @@ +package org.lingniu.portal.framework.aspectj; + +import java.util.ArrayList; +import java.util.List; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.springframework.stereotype.Component; +import org.lingniu.portal.common.annotation.DataScope; +import org.lingniu.portal.common.constant.UserConstants; +import org.lingniu.portal.common.core.domain.BaseEntity; +import org.lingniu.portal.common.core.domain.entity.SysRole; +import org.lingniu.portal.common.core.domain.entity.SysUser; +import org.lingniu.portal.common.core.domain.model.LoginUser; +import org.lingniu.portal.common.core.text.Convert; +import org.lingniu.portal.common.utils.SecurityUtils; +import org.lingniu.portal.common.utils.StringUtils; +import org.lingniu.portal.framework.security.context.PermissionContextHolder; + +/** + * 数据过滤处理 + * + * @author lingniu + */ +@Aspect +@Component +public class DataScopeAspect +{ + /** + * 全部数据权限 + */ + public static final String DATA_SCOPE_ALL = "1"; + + /** + * 自定数据权限 + */ + public static final String DATA_SCOPE_CUSTOM = "2"; + + /** + * 部门数据权限 + */ + public static final String DATA_SCOPE_DEPT = "3"; + + /** + * 部门及以下数据权限 + */ + public static final String DATA_SCOPE_DEPT_AND_CHILD = "4"; + + /** + * 仅本人数据权限 + */ + public static final String DATA_SCOPE_SELF = "5"; + + /** + * 数据权限过滤关键字 + */ + public static final String DATA_SCOPE = "dataScope"; + + @Before("@annotation(controllerDataScope)") + public void doBefore(JoinPoint point, DataScope controllerDataScope) throws Throwable + { + clearDataScope(point); + handleDataScope(point, controllerDataScope); + } + + protected void handleDataScope(final JoinPoint joinPoint, DataScope controllerDataScope) + { + // 获取当前的用户 + LoginUser loginUser = SecurityUtils.getLoginUser(); + if (StringUtils.isNotNull(loginUser)) + { + SysUser currentUser = loginUser.getUser(); + // 如果是超级管理员,则不过滤数据 + if (StringUtils.isNotNull(currentUser) && !currentUser.isAdmin()) + { + String permission = StringUtils.defaultIfEmpty(controllerDataScope.permission(), PermissionContextHolder.getContext()); + dataScopeFilter(joinPoint, currentUser, controllerDataScope.deptAlias(), controllerDataScope.userAlias(), permission); + } + } + } + + /** + * 数据范围过滤 + * + * @param joinPoint 切点 + * @param user 用户 + * @param deptAlias 部门别名 + * @param userAlias 用户别名 + * @param permission 权限字符 + */ + public static void dataScopeFilter(JoinPoint joinPoint, SysUser user, String deptAlias, String userAlias, String permission) + { + StringBuilder sqlString = new StringBuilder(); + List conditions = new ArrayList(); + List scopeCustomIds = new ArrayList(); + user.getRoles().forEach(role -> { + if (DATA_SCOPE_CUSTOM.equals(role.getDataScope()) && StringUtils.equals(role.getStatus(), UserConstants.ROLE_NORMAL) && (StringUtils.isEmpty(permission) || StringUtils.containsAny(role.getPermissions(), Convert.toStrArray(permission)))) + { + scopeCustomIds.add(Convert.toStr(role.getRoleId())); + } + }); + + for (SysRole role : user.getRoles()) + { + String dataScope = role.getDataScope(); + if (conditions.contains(dataScope) || StringUtils.equals(role.getStatus(), UserConstants.ROLE_DISABLE)) + { + continue; + } + if (StringUtils.isNotEmpty(permission) && !StringUtils.containsAny(role.getPermissions(), Convert.toStrArray(permission))) + { + continue; + } + if (DATA_SCOPE_ALL.equals(dataScope)) + { + sqlString = new StringBuilder(); + conditions.add(dataScope); + break; + } + else if (DATA_SCOPE_CUSTOM.equals(dataScope)) + { + if (scopeCustomIds.size() > 1) + { + // 多个自定数据权限使用in查询,避免多次拼接。 + sqlString.append(StringUtils.format(" OR {}.dept_id IN ( SELECT dept_id FROM sys_role_dept WHERE role_id in ({}) ) ", deptAlias, String.join(",", scopeCustomIds))); + } + else + { + sqlString.append(StringUtils.format(" OR {}.dept_id IN ( SELECT dept_id FROM sys_role_dept WHERE role_id = {} ) ", deptAlias, role.getRoleId())); + } + } + else if (DATA_SCOPE_DEPT.equals(dataScope)) + { + sqlString.append(StringUtils.format(" OR {}.dept_id = {} ", deptAlias, user.getDeptId())); + } + else if (DATA_SCOPE_DEPT_AND_CHILD.equals(dataScope)) + { + sqlString.append(StringUtils.format(" OR {}.dept_id IN ( SELECT dept_id FROM sys_dept WHERE dept_id = {} or find_in_set( {} , ancestors ) )", deptAlias, user.getDeptId(), user.getDeptId())); + } + else if (DATA_SCOPE_SELF.equals(dataScope)) + { + if (StringUtils.isNotBlank(userAlias)) + { + sqlString.append(StringUtils.format(" OR {}.user_id = {} ", userAlias, user.getUserId())); + } + else + { + // 数据权限为仅本人且没有userAlias别名不查询任何数据 + sqlString.append(StringUtils.format(" OR {}.dept_id = 0 ", deptAlias)); + } + } + conditions.add(dataScope); + } + + // 角色都不包含传递过来的权限字符,这个时候sqlString也会为空,所以要限制一下,不查询任何数据 + if (StringUtils.isEmpty(conditions)) + { + sqlString.append(StringUtils.format(" OR {}.dept_id = 0 ", deptAlias)); + } + + if (StringUtils.isNotBlank(sqlString.toString())) + { + Object params = joinPoint.getArgs()[0]; + if (StringUtils.isNotNull(params) && params instanceof BaseEntity) + { + BaseEntity baseEntity = (BaseEntity) params; + baseEntity.getParams().put(DATA_SCOPE, " AND (" + sqlString.substring(4) + ")"); + } + } + } + + /** + * 拼接权限sql前先清空params.dataScope参数防止注入 + */ + private void clearDataScope(final JoinPoint joinPoint) + { + Object params = joinPoint.getArgs()[0]; + if (StringUtils.isNotNull(params) && params instanceof BaseEntity) + { + BaseEntity baseEntity = (BaseEntity) params; + baseEntity.getParams().put(DATA_SCOPE, ""); + } + } +} diff --git a/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/aspectj/DataSourceAspect.java b/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/aspectj/DataSourceAspect.java new file mode 100644 index 0000000..149f12b --- /dev/null +++ b/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/aspectj/DataSourceAspect.java @@ -0,0 +1,72 @@ +package org.lingniu.portal.framework.aspectj; + +import java.util.Objects; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Pointcut; +import org.aspectj.lang.reflect.MethodSignature; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; +import org.lingniu.portal.common.annotation.DataSource; +import org.lingniu.portal.common.utils.StringUtils; +import org.lingniu.portal.framework.datasource.DynamicDataSourceContextHolder; + +/** + * 多数据源处理 + * + * @author lingniu + */ +@Aspect +@Order(1) +@Component +public class DataSourceAspect +{ + protected Logger logger = LoggerFactory.getLogger(getClass()); + + @Pointcut("@annotation(org.lingniu.portal.common.annotation.DataSource)" + + "|| @within(org.lingniu.portal.common.annotation.DataSource)") + public void dsPointCut() + { + + } + + @Around("dsPointCut()") + public Object around(ProceedingJoinPoint point) throws Throwable + { + DataSource dataSource = getDataSource(point); + + if (StringUtils.isNotNull(dataSource)) + { + DynamicDataSourceContextHolder.setDataSourceType(dataSource.value().name()); + } + + try + { + return point.proceed(); + } + finally + { + // 销毁数据源 在执行方法之后 + DynamicDataSourceContextHolder.clearDataSourceType(); + } + } + + /** + * 获取需要切换的数据源 + */ + public DataSource getDataSource(ProceedingJoinPoint point) + { + MethodSignature signature = (MethodSignature) point.getSignature(); + DataSource dataSource = AnnotationUtils.findAnnotation(signature.getMethod(), DataSource.class); + if (Objects.nonNull(dataSource)) + { + return dataSource; + } + + return AnnotationUtils.findAnnotation(signature.getDeclaringType(), DataSource.class); + } +} diff --git a/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/aspectj/LogAspect.java b/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/aspectj/LogAspect.java new file mode 100644 index 0000000..73749af --- /dev/null +++ b/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/aspectj/LogAspect.java @@ -0,0 +1,264 @@ +package org.lingniu.portal.framework.aspectj; + +import java.util.Collection; +import java.util.Map; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.apache.commons.lang3.ArrayUtils; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.AfterReturning; +import org.aspectj.lang.annotation.AfterThrowing; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.NamedThreadLocal; +import org.springframework.stereotype.Component; +import org.springframework.validation.BindingResult; +import org.springframework.web.multipart.MultipartFile; +import com.alibaba.fastjson2.JSON; +import org.lingniu.portal.common.annotation.Log; +import org.lingniu.portal.common.core.domain.entity.SysUser; +import org.lingniu.portal.common.core.domain.model.LoginUser; +import org.lingniu.portal.common.core.text.Convert; +import org.lingniu.portal.common.enums.BusinessStatus; +import org.lingniu.portal.common.enums.HttpMethod; +import org.lingniu.portal.common.filter.PropertyPreExcludeFilter; +import org.lingniu.portal.common.utils.ExceptionUtil; +import org.lingniu.portal.common.utils.SecurityUtils; +import org.lingniu.portal.common.utils.ServletUtils; +import org.lingniu.portal.common.utils.StringUtils; +import org.lingniu.portal.common.utils.ip.IpUtils; +import org.lingniu.portal.framework.manager.AsyncManager; +import org.lingniu.portal.framework.manager.factory.AsyncFactory; +import org.lingniu.portal.system.domain.SysOperLog; + +/** + * 操作日志记录处理 + * + * @author lingniu + */ +@Aspect +@Component +public class LogAspect +{ + private static final Logger log = LoggerFactory.getLogger(LogAspect.class); + + /** 排除敏感属性字段 */ + public static final String[] EXCLUDE_PROPERTIES = { "password", "oldPassword", "newPassword", "confirmPassword" }; + + /** 计算操作消耗时间 */ + private static final ThreadLocal TIME_THREADLOCAL = new NamedThreadLocal("Cost Time"); + + /** 参数最大长度限制 */ + private static final int PARAM_MAX_LENGTH = 2000; + + /** + * 处理请求前执行 + */ + @Before(value = "@annotation(controllerLog)") + public void doBefore(JoinPoint joinPoint, Log controllerLog) + { + TIME_THREADLOCAL.set(System.currentTimeMillis()); + } + + /** + * 处理完请求后执行 + * + * @param joinPoint 切点 + */ + @AfterReturning(pointcut = "@annotation(controllerLog)", returning = "jsonResult") + public void doAfterReturning(JoinPoint joinPoint, Log controllerLog, Object jsonResult) + { + handleLog(joinPoint, controllerLog, null, jsonResult); + } + + /** + * 拦截异常操作 + * + * @param joinPoint 切点 + * @param e 异常 + */ + @AfterThrowing(value = "@annotation(controllerLog)", throwing = "e") + public void doAfterThrowing(JoinPoint joinPoint, Log controllerLog, Exception e) + { + handleLog(joinPoint, controllerLog, e, null); + } + + protected void handleLog(final JoinPoint joinPoint, Log controllerLog, final Exception e, Object jsonResult) + { + try + { + // 获取当前的用户 + LoginUser loginUser = SecurityUtils.getLoginUser(); + + // *========数据库日志=========*// + SysOperLog operLog = new SysOperLog(); + operLog.setStatus(BusinessStatus.SUCCESS.ordinal()); + // 请求的地址 + String ip = IpUtils.getIpAddr(); + operLog.setOperIp(ip); + operLog.setOperUrl(StringUtils.substring(ServletUtils.getRequest().getRequestURI(), 0, 255)); + if (loginUser != null) + { + operLog.setOperName(loginUser.getUsername()); + SysUser currentUser = loginUser.getUser(); + if (StringUtils.isNotNull(currentUser) && StringUtils.isNotNull(currentUser.getDept())) + { + operLog.setDeptName(currentUser.getDept().getDeptName()); + } + } + + if (e != null) + { + operLog.setStatus(BusinessStatus.FAIL.ordinal()); + operLog.setErrorMsg(StringUtils.substring(Convert.toStr(e.getMessage(), ExceptionUtil.getExceptionMessage(e)), 0, 2000)); + } + // 设置方法名称 + String className = joinPoint.getTarget().getClass().getName(); + String methodName = joinPoint.getSignature().getName(); + operLog.setMethod(className + "." + methodName + "()"); + // 设置请求方式 + operLog.setRequestMethod(ServletUtils.getRequest().getMethod()); + // 处理设置注解上的参数 + getControllerMethodDescription(joinPoint, controllerLog, operLog, jsonResult); + // 设置消耗时间 + operLog.setCostTime(System.currentTimeMillis() - TIME_THREADLOCAL.get()); + // 保存数据库 + AsyncManager.me().execute(AsyncFactory.recordOper(operLog)); + } + catch (Exception exp) + { + // 记录本地异常日志 + log.error("异常信息:{}", exp.getMessage()); + exp.printStackTrace(); + } + finally + { + TIME_THREADLOCAL.remove(); + } + } + + /** + * 获取注解中对方法的描述信息 用于Controller层注解 + * + * @param log 日志 + * @param operLog 操作日志 + * @throws Exception + */ + public void getControllerMethodDescription(JoinPoint joinPoint, Log log, SysOperLog operLog, Object jsonResult) throws Exception + { + // 设置action动作 + operLog.setBusinessType(log.businessType().ordinal()); + // 设置标题 + operLog.setTitle(log.title()); + // 设置操作人类别 + operLog.setOperatorType(log.operatorType().ordinal()); + // 是否需要保存request,参数和值 + if (log.isSaveRequestData()) + { + // 获取参数的信息,传入到数据库中。 + setRequestValue(joinPoint, operLog, log.excludeParamNames()); + } + // 是否需要保存response,参数和值 + if (log.isSaveResponseData() && StringUtils.isNotNull(jsonResult)) + { + operLog.setJsonResult(StringUtils.substring(JSON.toJSONString(jsonResult), 0, 2000)); + } + } + + /** + * 获取请求的参数,放到log中 + * + * @param operLog 操作日志 + * @throws Exception 异常 + */ + private void setRequestValue(JoinPoint joinPoint, SysOperLog operLog, String[] excludeParamNames) throws Exception + { + String requestMethod = operLog.getRequestMethod(); + Map paramsMap = ServletUtils.getParamMap(ServletUtils.getRequest()); + if (StringUtils.isEmpty(paramsMap) && StringUtils.equalsAny(requestMethod, HttpMethod.PUT.name(), HttpMethod.POST.name(), HttpMethod.DELETE.name())) + { + String params = argsArrayToString(joinPoint.getArgs(), excludeParamNames); + operLog.setOperParam(params); + } + else + { + operLog.setOperParam(StringUtils.substring(JSON.toJSONString(paramsMap, excludePropertyPreFilter(excludeParamNames)), 0, PARAM_MAX_LENGTH)); + } + } + + /** + * 参数拼装 + */ + private String argsArrayToString(Object[] paramsArray, String[] excludeParamNames) + { + StringBuilder params = new StringBuilder(); + if (paramsArray != null && paramsArray.length > 0) + { + for (Object o : paramsArray) + { + if (StringUtils.isNotNull(o) && !isFilterObject(o)) + { + try + { + String jsonObj = JSON.toJSONString(o, excludePropertyPreFilter(excludeParamNames)); + params.append(jsonObj).append(" "); + if (params.length() >= PARAM_MAX_LENGTH) + { + return StringUtils.substring(params.toString(), 0, PARAM_MAX_LENGTH); + } + } + catch (Exception e) + { + log.error("请求参数拼装异常 msg:{}, 参数:{}", e.getMessage(), paramsArray, e); + } + } + } + } + return params.toString(); + } + + /** + * 忽略敏感属性 + */ + public PropertyPreExcludeFilter excludePropertyPreFilter(String[] excludeParamNames) + { + return new PropertyPreExcludeFilter().addExcludes(ArrayUtils.addAll(EXCLUDE_PROPERTIES, excludeParamNames)); + } + + /** + * 判断是否需要过滤的对象。 + * + * @param o 对象信息。 + * @return 如果是需要过滤的对象,则返回true;否则返回false。 + */ + @SuppressWarnings("rawtypes") + public boolean isFilterObject(final Object o) + { + Class clazz = o.getClass(); + if (clazz.isArray()) + { + return clazz.getComponentType().isAssignableFrom(MultipartFile.class); + } + else if (Collection.class.isAssignableFrom(clazz)) + { + Collection collection = (Collection) o; + for (Object value : collection) + { + return value instanceof MultipartFile; + } + } + else if (Map.class.isAssignableFrom(clazz)) + { + Map map = (Map) o; + for (Object value : map.entrySet()) + { + Map.Entry entry = (Map.Entry) value; + return entry.getValue() instanceof MultipartFile; + } + } + return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse + || o instanceof BindingResult; + } +} diff --git a/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/aspectj/RateLimiterAspect.java b/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/aspectj/RateLimiterAspect.java new file mode 100644 index 0000000..a28c4f7 --- /dev/null +++ b/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/aspectj/RateLimiterAspect.java @@ -0,0 +1,89 @@ +package org.lingniu.portal.framework.aspectj; + +import java.lang.reflect.Method; +import java.util.Collections; +import java.util.List; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.aspectj.lang.reflect.MethodSignature; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.script.RedisScript; +import org.springframework.stereotype.Component; +import org.lingniu.portal.common.annotation.RateLimiter; +import org.lingniu.portal.common.enums.LimitType; +import org.lingniu.portal.common.exception.ServiceException; +import org.lingniu.portal.common.utils.StringUtils; +import org.lingniu.portal.common.utils.ip.IpUtils; + +/** + * 限流处理 + * + * @author lingniu + */ +@Aspect +@Component +public class RateLimiterAspect +{ + private static final Logger log = LoggerFactory.getLogger(RateLimiterAspect.class); + + private RedisTemplate redisTemplate; + + private RedisScript limitScript; + + @Autowired + public void setRedisTemplate1(RedisTemplate redisTemplate) + { + this.redisTemplate = redisTemplate; + } + + @Autowired + public void setLimitScript(RedisScript limitScript) + { + this.limitScript = limitScript; + } + + @Before("@annotation(rateLimiter)") + public void doBefore(JoinPoint point, RateLimiter rateLimiter) throws Throwable + { + int time = rateLimiter.time(); + int count = rateLimiter.count(); + + String combineKey = getCombineKey(rateLimiter, point); + List keys = Collections.singletonList(combineKey); + try + { + Long number = redisTemplate.execute(limitScript, keys, count, time); + if (StringUtils.isNull(number) || number.intValue() > count) + { + throw new ServiceException("访问过于频繁,请稍候再试"); + } + log.info("限制请求'{}',当前请求'{}',缓存key'{}'", count, number.intValue(), combineKey); + } + catch (ServiceException e) + { + throw e; + } + catch (Exception e) + { + throw new RuntimeException("服务器限流异常,请稍候再试"); + } + } + + public String getCombineKey(RateLimiter rateLimiter, JoinPoint point) + { + StringBuffer stringBuffer = new StringBuffer(rateLimiter.key()); + if (rateLimiter.limitType() == LimitType.IP) + { + stringBuffer.append(IpUtils.getIpAddr()).append("-"); + } + MethodSignature signature = (MethodSignature) point.getSignature(); + Method method = signature.getMethod(); + Class targetClass = method.getDeclaringClass(); + stringBuffer.append(targetClass.getName()).append("-").append(method.getName()); + return stringBuffer.toString(); + } +} diff --git a/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/config/ApplicationConfig.java b/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/config/ApplicationConfig.java new file mode 100644 index 0000000..683e975 --- /dev/null +++ b/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/config/ApplicationConfig.java @@ -0,0 +1,30 @@ +package org.lingniu.portal.framework.config; + +import java.util.TimeZone; +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.EnableAspectJAutoProxy; + +/** + * 程序注解配置 + * + * @author lingniu + */ +@Configuration +// 表示通过aop框架暴露该代理对象,AopContext能够访问 +@EnableAspectJAutoProxy(exposeProxy = true) +// 指定要扫描的Mapper类的包的路径 +@MapperScan("org.lingniu.**.mapper") +public class ApplicationConfig +{ + /** + * 时区配置 + */ + @Bean + public Jackson2ObjectMapperBuilderCustomizer jacksonObjectMapperCustomization() + { + return jacksonObjectMapperBuilder -> jacksonObjectMapperBuilder.timeZone(TimeZone.getDefault()); + } +} diff --git a/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/config/CaptchaConfig.java b/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/config/CaptchaConfig.java new file mode 100644 index 0000000..de995d2 --- /dev/null +++ b/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/config/CaptchaConfig.java @@ -0,0 +1,83 @@ +package org.lingniu.portal.framework.config; + +import java.util.Properties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import com.google.code.kaptcha.impl.DefaultKaptcha; +import com.google.code.kaptcha.util.Config; +import static com.google.code.kaptcha.Constants.*; + +/** + * 验证码配置 + * + * @author lingniu + */ +@Configuration +public class CaptchaConfig +{ + @Bean(name = "captchaProducer") + public DefaultKaptcha getKaptchaBean() + { + DefaultKaptcha defaultKaptcha = new DefaultKaptcha(); + Properties properties = new Properties(); + // 是否有边框 默认为true 我们可以自己设置yes,no + properties.setProperty(KAPTCHA_BORDER, "yes"); + // 验证码文本字符颜色 默认为Color.BLACK + properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_COLOR, "black"); + // 验证码图片宽度 默认为200 + properties.setProperty(KAPTCHA_IMAGE_WIDTH, "160"); + // 验证码图片高度 默认为50 + properties.setProperty(KAPTCHA_IMAGE_HEIGHT, "60"); + // 验证码文本字符大小 默认为40 + properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_SIZE, "38"); + // KAPTCHA_SESSION_KEY + properties.setProperty(KAPTCHA_SESSION_CONFIG_KEY, "kaptchaCode"); + // 验证码文本字符长度 默认为5 + properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, "4"); + // 验证码文本字体样式 默认为new Font("Arial", 1, fontSize), new Font("Courier", 1, fontSize) + properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_NAMES, "Arial,Courier"); + // 图片样式 水纹com.google.code.kaptcha.impl.WaterRipple 鱼眼com.google.code.kaptcha.impl.FishEyeGimpy 阴影com.google.code.kaptcha.impl.ShadowGimpy + properties.setProperty(KAPTCHA_OBSCURIFICATOR_IMPL, "com.google.code.kaptcha.impl.ShadowGimpy"); + Config config = new Config(properties); + defaultKaptcha.setConfig(config); + return defaultKaptcha; + } + + @Bean(name = "captchaProducerMath") + public DefaultKaptcha getKaptchaBeanMath() + { + DefaultKaptcha defaultKaptcha = new DefaultKaptcha(); + Properties properties = new Properties(); + // 是否有边框 默认为true 我们可以自己设置yes,no + properties.setProperty(KAPTCHA_BORDER, "yes"); + // 边框颜色 默认为Color.BLACK + properties.setProperty(KAPTCHA_BORDER_COLOR, "105,179,90"); + // 验证码文本字符颜色 默认为Color.BLACK + properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_COLOR, "blue"); + // 验证码图片宽度 默认为200 + properties.setProperty(KAPTCHA_IMAGE_WIDTH, "160"); + // 验证码图片高度 默认为50 + properties.setProperty(KAPTCHA_IMAGE_HEIGHT, "60"); + // 验证码文本字符大小 默认为40 + properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_SIZE, "35"); + // KAPTCHA_SESSION_KEY + properties.setProperty(KAPTCHA_SESSION_CONFIG_KEY, "kaptchaCodeMath"); + // 验证码文本生成器 + properties.setProperty(KAPTCHA_TEXTPRODUCER_IMPL, "org.lingniu.portal.framework.config.KaptchaTextCreator"); + // 验证码文本字符间距 默认为2 + properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_SPACE, "3"); + // 验证码文本字符长度 默认为5 + properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, "6"); + // 验证码文本字体样式 默认为new Font("Arial", 1, fontSize), new Font("Courier", 1, fontSize) + properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_NAMES, "Arial,Courier"); + // 验证码噪点颜色 默认为Color.BLACK + properties.setProperty(KAPTCHA_NOISE_COLOR, "white"); + // 干扰实现类 + properties.setProperty(KAPTCHA_NOISE_IMPL, "com.google.code.kaptcha.impl.NoNoise"); + // 图片样式 水纹com.google.code.kaptcha.impl.WaterRipple 鱼眼com.google.code.kaptcha.impl.FishEyeGimpy 阴影com.google.code.kaptcha.impl.ShadowGimpy + properties.setProperty(KAPTCHA_OBSCURIFICATOR_IMPL, "com.google.code.kaptcha.impl.ShadowGimpy"); + Config config = new Config(properties); + defaultKaptcha.setConfig(config); + return defaultKaptcha; + } +} diff --git a/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/config/DruidConfig.java b/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/config/DruidConfig.java new file mode 100644 index 0000000..11e49be --- /dev/null +++ b/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/config/DruidConfig.java @@ -0,0 +1,126 @@ +package org.lingniu.portal.framework.config; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import javax.sql.DataSource; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; +import com.alibaba.druid.pool.DruidDataSource; +import com.alibaba.druid.spring.boot3.autoconfigure.DruidDataSourceBuilder; +import com.alibaba.druid.spring.boot3.autoconfigure.properties.DruidStatProperties; +import com.alibaba.druid.util.Utils; +import org.lingniu.portal.common.enums.DataSourceType; +import org.lingniu.portal.common.utils.spring.SpringUtils; +import org.lingniu.portal.framework.config.properties.DruidProperties; +import org.lingniu.portal.framework.datasource.DynamicDataSource; +import jakarta.servlet.Filter; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.ServletResponse; + +/** + * druid 配置多数据源 + * + * @author lingniu + */ +@Configuration +public class DruidConfig +{ + @Bean + @ConfigurationProperties("spring.datasource.druid.master") + public DataSource masterDataSource(DruidProperties druidProperties) + { + DruidDataSource dataSource = DruidDataSourceBuilder.create().build(); + return druidProperties.dataSource(dataSource); + } + + @Bean + @ConfigurationProperties("spring.datasource.druid.slave") + @ConditionalOnProperty(prefix = "spring.datasource.druid.slave", name = "enabled", havingValue = "true") + public DataSource slaveDataSource(DruidProperties druidProperties) + { + DruidDataSource dataSource = DruidDataSourceBuilder.create().build(); + return druidProperties.dataSource(dataSource); + } + + @Bean(name = "dynamicDataSource") + @Primary + public DynamicDataSource dataSource(DataSource masterDataSource) + { + Map targetDataSources = new HashMap<>(); + targetDataSources.put(DataSourceType.MASTER.name(), masterDataSource); + setDataSource(targetDataSources, DataSourceType.SLAVE.name(), "slaveDataSource"); + return new DynamicDataSource(masterDataSource, targetDataSources); + } + + /** + * 设置数据源 + * + * @param targetDataSources 备选数据源集合 + * @param sourceName 数据源名称 + * @param beanName bean名称 + */ + public void setDataSource(Map targetDataSources, String sourceName, String beanName) + { + try + { + DataSource dataSource = SpringUtils.getBean(beanName); + targetDataSources.put(sourceName, dataSource); + } + catch (Exception e) + { + } + } + + /** + * 去除监控页面底部的广告 + */ + @SuppressWarnings({ "rawtypes", "unchecked" }) + @Bean + @ConditionalOnProperty(name = "spring.datasource.druid.statViewServlet.enabled", havingValue = "true") + public FilterRegistrationBean removeDruidFilterRegistrationBean(DruidStatProperties properties) + { + // 获取web监控页面的参数 + DruidStatProperties.StatViewServlet config = properties.getStatViewServlet(); + // 提取common.js的配置路径 + String pattern = config.getUrlPattern() != null ? config.getUrlPattern() : "/druid/*"; + String commonJsPattern = pattern.replaceAll("\\*", "js/common.js"); + final String filePath = "support/http/resources/js/common.js"; + // 创建filter进行过滤 + Filter filter = new Filter() + { + @Override + public void init(jakarta.servlet.FilterConfig filterConfig) throws ServletException + { + } + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException + { + chain.doFilter(request, response); + // 重置缓冲区,响应头不会被重置 + response.resetBuffer(); + // 获取common.js + String text = Utils.readFromResource(filePath); + // 正则替换banner, 除去底部的广告信息 + text = text.replaceAll("
", ""); + text = text.replaceAll("powered.*?shrek.wang", ""); + response.getWriter().write(text); + } + @Override + public void destroy() + { + } + }; + FilterRegistrationBean registrationBean = new FilterRegistrationBean(); + registrationBean.setFilter(filter); + registrationBean.addUrlPatterns(commonJsPattern); + return registrationBean; + } +} diff --git a/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/config/FastJson2JsonRedisSerializer.java b/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/config/FastJson2JsonRedisSerializer.java new file mode 100644 index 0000000..cea1893 --- /dev/null +++ b/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/config/FastJson2JsonRedisSerializer.java @@ -0,0 +1,52 @@ +package org.lingniu.portal.framework.config; + +import java.nio.charset.Charset; +import org.springframework.data.redis.serializer.RedisSerializer; +import org.springframework.data.redis.serializer.SerializationException; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONReader; +import com.alibaba.fastjson2.JSONWriter; +import com.alibaba.fastjson2.filter.Filter; +import org.lingniu.portal.common.constant.Constants; + +/** + * Redis使用FastJson序列化 + * + * @author lingniu + */ +public class FastJson2JsonRedisSerializer implements RedisSerializer +{ + public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8"); + + static final Filter AUTO_TYPE_FILTER = JSONReader.autoTypeFilter(Constants.JSON_WHITELIST_STR); + + private Class clazz; + + public FastJson2JsonRedisSerializer(Class clazz) + { + super(); + this.clazz = clazz; + } + + @Override + public byte[] serialize(T t) throws SerializationException + { + if (t == null) + { + return new byte[0]; + } + return JSON.toJSONString(t, JSONWriter.Feature.WriteClassName).getBytes(DEFAULT_CHARSET); + } + + @Override + public T deserialize(byte[] bytes) throws SerializationException + { + if (bytes == null || bytes.length <= 0) + { + return null; + } + String str = new String(bytes, DEFAULT_CHARSET); + + return JSON.parseObject(str, clazz, AUTO_TYPE_FILTER); + } +} diff --git a/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/config/FilterConfig.java b/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/config/FilterConfig.java new file mode 100644 index 0000000..95d5dee --- /dev/null +++ b/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/config/FilterConfig.java @@ -0,0 +1,80 @@ +package org.lingniu.portal.framework.config; + +import java.util.HashMap; +import java.util.Map; +import jakarta.servlet.DispatcherType; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.lingniu.portal.common.constant.Constants; +import org.lingniu.portal.common.filter.RefererFilter; +import org.lingniu.portal.common.filter.RepeatableFilter; +import org.lingniu.portal.common.filter.XssFilter; +import org.lingniu.portal.common.utils.StringUtils; + +/** + * Filter配置 + * + * @author lingniu + */ +@Configuration +public class FilterConfig +{ + @Value("${xss.excludes}") + private String excludes; + + @Value("${xss.urlPatterns}") + private String urlPatterns; + + @Value("${referer.allowed-domains}") + private String allowedDomains; + + @SuppressWarnings({ "rawtypes", "unchecked" }) + @Bean + @ConditionalOnProperty(value = "xss.enabled", havingValue = "true") + public FilterRegistrationBean xssFilterRegistration() + { + FilterRegistrationBean registration = new FilterRegistrationBean(); + registration.setDispatcherTypes(DispatcherType.REQUEST); + registration.setFilter(new XssFilter()); + registration.addUrlPatterns(StringUtils.split(urlPatterns, ",")); + registration.setName("xssFilter"); + registration.setOrder(FilterRegistrationBean.HIGHEST_PRECEDENCE); + Map initParameters = new HashMap(); + initParameters.put("excludes", excludes); + registration.setInitParameters(initParameters); + return registration; + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + @Bean + @ConditionalOnProperty(value = "referer.enabled", havingValue = "true") + public FilterRegistrationBean refererFilterRegistration() + { + FilterRegistrationBean registration = new FilterRegistrationBean(); + registration.setDispatcherTypes(DispatcherType.REQUEST); + registration.setFilter(new RefererFilter()); + registration.addUrlPatterns(Constants.RESOURCE_PREFIX + "/*"); + registration.setName("refererFilter"); + registration.setOrder(FilterRegistrationBean.HIGHEST_PRECEDENCE); + Map initParameters = new HashMap(); + initParameters.put("allowedDomains", allowedDomains); + registration.setInitParameters(initParameters); + return registration; + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + @Bean + public FilterRegistrationBean someFilterRegistration() + { + FilterRegistrationBean registration = new FilterRegistrationBean(); + registration.setFilter(new RepeatableFilter()); + registration.addUrlPatterns("/*"); + registration.setName("repeatableFilter"); + registration.setOrder(FilterRegistrationBean.LOWEST_PRECEDENCE); + return registration; + } + +} diff --git a/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/config/I18nConfig.java b/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/config/I18nConfig.java new file mode 100644 index 0000000..77e854f --- /dev/null +++ b/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/config/I18nConfig.java @@ -0,0 +1,43 @@ +package org.lingniu.portal.framework.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.LocaleResolver; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; +import org.springframework.web.servlet.i18n.LocaleChangeInterceptor; +import org.springframework.web.servlet.i18n.SessionLocaleResolver; +import org.lingniu.portal.common.constant.Constants; + +/** + * 资源文件配置加载 + * + * @author lingniu + */ +@Configuration +public class I18nConfig implements WebMvcConfigurer +{ + @Bean + public LocaleResolver localeResolver() + { + SessionLocaleResolver slr = new SessionLocaleResolver(); + // 默认语言 + slr.setDefaultLocale(Constants.DEFAULT_LOCALE); + return slr; + } + + @Bean + public LocaleChangeInterceptor localeChangeInterceptor() + { + LocaleChangeInterceptor lci = new LocaleChangeInterceptor(); + // 参数名 + lci.setParamName("lang"); + return lci; + } + + @Override + public void addInterceptors(InterceptorRegistry registry) + { + registry.addInterceptor(localeChangeInterceptor()); + } +} diff --git a/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/config/KaptchaTextCreator.java b/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/config/KaptchaTextCreator.java new file mode 100644 index 0000000..6dcb50f --- /dev/null +++ b/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/config/KaptchaTextCreator.java @@ -0,0 +1,68 @@ +package org.lingniu.portal.framework.config; + +import java.util.Random; +import com.google.code.kaptcha.text.impl.DefaultTextCreator; + +/** + * 验证码文本生成器 + * + * @author lingniu + */ +public class KaptchaTextCreator extends DefaultTextCreator +{ + private static final String[] CNUMBERS = "0,1,2,3,4,5,6,7,8,9,10".split(","); + + @Override + public String getText() + { + Integer result = 0; + Random random = new Random(); + int x = random.nextInt(10); + int y = random.nextInt(10); + StringBuilder suChinese = new StringBuilder(); + int randomoperands = random.nextInt(3); + if (randomoperands == 0) + { + result = x * y; + suChinese.append(CNUMBERS[x]); + suChinese.append("*"); + suChinese.append(CNUMBERS[y]); + } + else if (randomoperands == 1) + { + if ((x != 0) && y % x == 0) + { + result = y / x; + suChinese.append(CNUMBERS[y]); + suChinese.append("/"); + suChinese.append(CNUMBERS[x]); + } + else + { + result = x + y; + suChinese.append(CNUMBERS[x]); + suChinese.append("+"); + suChinese.append(CNUMBERS[y]); + } + } + else + { + if (x >= y) + { + result = x - y; + suChinese.append(CNUMBERS[x]); + suChinese.append("-"); + suChinese.append(CNUMBERS[y]); + } + else + { + result = y - x; + suChinese.append(CNUMBERS[y]); + suChinese.append("-"); + suChinese.append(CNUMBERS[x]); + } + } + suChinese.append("=?@" + result); + return suChinese.toString(); + } +} \ No newline at end of file diff --git a/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/config/MyBatisConfig.java b/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/config/MyBatisConfig.java new file mode 100644 index 0000000..5e0d39c --- /dev/null +++ b/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/config/MyBatisConfig.java @@ -0,0 +1,132 @@ +package org.lingniu.portal.framework.config; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import javax.sql.DataSource; +import org.apache.ibatis.io.VFS; +import org.apache.ibatis.session.SqlSessionFactory; +import org.mybatis.spring.SqlSessionFactoryBean; +import org.mybatis.spring.boot.autoconfigure.SpringBootVFS; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; +import org.springframework.core.io.DefaultResourceLoader; +import org.springframework.core.io.Resource; +import org.springframework.core.io.support.PathMatchingResourcePatternResolver; +import org.springframework.core.io.support.ResourcePatternResolver; +import org.springframework.core.type.classreading.CachingMetadataReaderFactory; +import org.springframework.core.type.classreading.MetadataReader; +import org.springframework.core.type.classreading.MetadataReaderFactory; +import org.springframework.util.ClassUtils; +import org.lingniu.portal.common.utils.StringUtils; + +/** + * Mybatis支持*匹配扫描包 + * + * @author lingniu + */ +@Configuration +public class MyBatisConfig +{ + @Autowired + private Environment env; + + static final String DEFAULT_RESOURCE_PATTERN = "**/*.class"; + + public static String setTypeAliasesPackage(String typeAliasesPackage) + { + ResourcePatternResolver resolver = (ResourcePatternResolver) new PathMatchingResourcePatternResolver(); + MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(resolver); + List allResult = new ArrayList(); + try + { + for (String aliasesPackage : typeAliasesPackage.split(",")) + { + List result = new ArrayList(); + aliasesPackage = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + + ClassUtils.convertClassNameToResourcePath(aliasesPackage.trim()) + "/" + DEFAULT_RESOURCE_PATTERN; + Resource[] resources = resolver.getResources(aliasesPackage); + if (resources != null && resources.length > 0) + { + MetadataReader metadataReader = null; + for (Resource resource : resources) + { + if (resource.isReadable()) + { + metadataReader = metadataReaderFactory.getMetadataReader(resource); + try + { + result.add(Class.forName(metadataReader.getClassMetadata().getClassName()).getPackage().getName()); + } + catch (ClassNotFoundException e) + { + e.printStackTrace(); + } + } + } + } + if (result.size() > 0) + { + HashSet hashResult = new HashSet(result); + allResult.addAll(hashResult); + } + } + if (allResult.size() > 0) + { + typeAliasesPackage = String.join(",", (String[]) allResult.toArray(new String[0])); + } + else + { + throw new RuntimeException("mybatis typeAliasesPackage 路径扫描错误,参数typeAliasesPackage:" + typeAliasesPackage + "未找到任何包"); + } + } + catch (IOException e) + { + e.printStackTrace(); + } + return typeAliasesPackage; + } + + public Resource[] resolveMapperLocations(String[] mapperLocations) + { + ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver(); + List resources = new ArrayList(); + if (mapperLocations != null) + { + for (String mapperLocation : mapperLocations) + { + try + { + Resource[] mappers = resourceResolver.getResources(mapperLocation); + resources.addAll(Arrays.asList(mappers)); + } + catch (IOException e) + { + // ignore + } + } + } + return resources.toArray(new Resource[resources.size()]); + } + + @Bean + public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception + { + String typeAliasesPackage = env.getProperty("mybatis.typeAliasesPackage"); + String mapperLocations = env.getProperty("mybatis.mapperLocations"); + String configLocation = env.getProperty("mybatis.configLocation"); + typeAliasesPackage = setTypeAliasesPackage(typeAliasesPackage); + VFS.addImplClass(SpringBootVFS.class); + + final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean(); + sessionFactory.setDataSource(dataSource); + sessionFactory.setTypeAliasesPackage(typeAliasesPackage); + sessionFactory.setMapperLocations(resolveMapperLocations(StringUtils.split(mapperLocations, ","))); + sessionFactory.setConfigLocation(new DefaultResourceLoader().getResource(configLocation)); + return sessionFactory.getObject(); + } +} \ No newline at end of file diff --git a/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/config/RedisConfig.java b/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/config/RedisConfig.java new file mode 100644 index 0000000..655a212 --- /dev/null +++ b/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/config/RedisConfig.java @@ -0,0 +1,70 @@ +package org.lingniu.portal.framework.config; + +import org.springframework.cache.annotation.CachingConfigurerSupport; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.script.DefaultRedisScript; +import org.springframework.data.redis.serializer.StringRedisSerializer; + +/** + * redis配置 + * + * @author lingniu + */ +@SuppressWarnings("deprecation") +@Configuration +@EnableCaching +public class RedisConfig extends CachingConfigurerSupport +{ + @Bean + @SuppressWarnings(value = { "unchecked", "rawtypes" }) + public RedisTemplate redisTemplate(RedisConnectionFactory connectionFactory) + { + RedisTemplate template = new RedisTemplate<>(); + template.setConnectionFactory(connectionFactory); + + FastJson2JsonRedisSerializer serializer = new FastJson2JsonRedisSerializer(Object.class); + + // 使用StringRedisSerializer来序列化和反序列化redis的key值 + template.setKeySerializer(new StringRedisSerializer()); + template.setValueSerializer(serializer); + + // Hash的key也采用StringRedisSerializer的序列化方式 + template.setHashKeySerializer(new StringRedisSerializer()); + template.setHashValueSerializer(serializer); + + template.afterPropertiesSet(); + return template; + } + + @Bean + public DefaultRedisScript limitScript() + { + DefaultRedisScript redisScript = new DefaultRedisScript<>(); + redisScript.setScriptText(limitScriptText()); + redisScript.setResultType(Long.class); + return redisScript; + } + + /** + * 限流脚本 + */ + private String limitScriptText() + { + return "local key = KEYS[1]\n" + + "local count = tonumber(ARGV[1])\n" + + "local time = tonumber(ARGV[2])\n" + + "local current = redis.call('get', key);\n" + + "if current and tonumber(current) > count then\n" + + " return tonumber(current);\n" + + "end\n" + + "current = redis.call('incr', key)\n" + + "if tonumber(current) == 1 then\n" + + " redis.call('expire', key, time)\n" + + "end\n" + + "return tonumber(current);"; + } +} diff --git a/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/config/ResourcesConfig.java b/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/config/ResourcesConfig.java new file mode 100644 index 0000000..623604c --- /dev/null +++ b/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/config/ResourcesConfig.java @@ -0,0 +1,72 @@ +package org.lingniu.portal.framework.config; + +import java.util.concurrent.TimeUnit; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.CacheControl; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; +import org.springframework.web.filter.CorsFilter; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; +import org.lingniu.portal.common.config.ProjectConfig; +import org.lingniu.portal.common.constant.Constants; +import org.lingniu.portal.framework.interceptor.RepeatSubmitInterceptor; + +/** + * 通用配置 + * + * @author lingniu + */ +@Configuration +public class ResourcesConfig implements WebMvcConfigurer +{ + @Autowired + private RepeatSubmitInterceptor repeatSubmitInterceptor; + + @Override + public void addResourceHandlers(ResourceHandlerRegistry registry) + { + /** 本地文件上传路径 */ + registry.addResourceHandler(Constants.RESOURCE_PREFIX + "/**") + .addResourceLocations("file:" + ProjectConfig.getProfile() + "/"); + + /** swagger配置 */ + registry.addResourceHandler("/swagger-ui/**") + .addResourceLocations("classpath:/META-INF/resources/webjars/springfox-swagger-ui/") + .setCacheControl(CacheControl.maxAge(5, TimeUnit.HOURS).cachePublic()); + } + + /** + * 自定义拦截规则 + */ + @Override + public void addInterceptors(InterceptorRegistry registry) + { + registry.addInterceptor(repeatSubmitInterceptor).addPathPatterns("/**"); + } + + /** + * 跨域配置 + */ + @Bean + public CorsFilter corsFilter() + { + CorsConfiguration config = new CorsConfiguration(); + // 设置访问源地址 + config.addAllowedOriginPattern("*"); + // 设置访问源请求头 + config.addAllowedHeader("*"); + // 设置访问源请求方法 + config.addAllowedMethod("*"); + // 有效期 1800秒 + config.setMaxAge(1800L); + // 添加映射路径,拦截一切请求 + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + source.registerCorsConfiguration("/**", config); + // 返回新的CorsFilter + return new CorsFilter(source); + } +} \ No newline at end of file diff --git a/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/config/SecurityConfig.java b/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/config/SecurityConfig.java new file mode 100644 index 0000000..de3619d --- /dev/null +++ b/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/config/SecurityConfig.java @@ -0,0 +1,128 @@ +//package org.lingniu.portal.framework.config; +// +//import org.springframework.beans.factory.annotation.Autowired; +//import org.springframework.context.annotation.Bean; +//import org.springframework.context.annotation.Configuration; +//import org.springframework.http.HttpMethod; +//import org.springframework.security.authentication.AuthenticationManager; +//import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; +//import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; +//import org.springframework.security.config.annotation.web.builders.HttpSecurity; +//import org.springframework.security.config.http.SessionCreationPolicy; +//import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +//import org.springframework.security.web.SecurityFilterChain; +//import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +//import org.springframework.security.web.authentication.logout.LogoutFilter; +//import org.springframework.web.filter.CorsFilter; +//import org.lingniu.portal.framework.config.properties.PermitAllUrlProperties; +//import org.lingniu.portal.framework.security.filter.JwtAuthenticationTokenFilter; +//import org.lingniu.portal.framework.security.handle.AuthenticationEntryPointImpl; +//import org.lingniu.portal.framework.security.handle.LogoutSuccessHandlerImpl; +// +///** +// * spring security配置 +// * +// * @author lingniu +// */ +//@EnableMethodSecurity(prePostEnabled = true, securedEnabled = true) +//@Configuration +//public class SecurityConfig +//{ +// /** +// * 认证失败处理类 +// */ +// @Autowired +// private AuthenticationEntryPointImpl unauthorizedHandler; +// +// /** +// * 退出处理类 +// */ +// @Autowired +// private LogoutSuccessHandlerImpl logoutSuccessHandler; +// +// /** +// * token认证过滤器 +// */ +// @Autowired +// private JwtAuthenticationTokenFilter authenticationTokenFilter; +// +// /** +// * 跨域过滤器 +// */ +// @Autowired +// private CorsFilter corsFilter; +// +// /** +// * 允许匿名访问的地址 +// */ +// @Autowired +// private PermitAllUrlProperties permitAllUrl; +// +// /** +// * 身份验证实现 +// */ +// @Bean +// public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception +// { +// return authenticationConfiguration.getAuthenticationManager(); +// } +// +// /** +// * anyRequest | 匹配所有请求路径 +// * access | SpringEl表达式结果为true时可以访问 +// * anonymous | 匿名可以访问 +// * denyAll | 用户不能访问 +// * fullyAuthenticated | 用户完全认证可以访问(非remember-me下自动登录) +// * hasAnyAuthority | 如果有参数,参数表示权限,则其中任何一个权限可以访问 +// * hasAnyRole | 如果有参数,参数表示角色,则其中任何一个角色可以访问 +// * hasAuthority | 如果有参数,参数表示权限,则其权限可以访问 +// * hasIpAddress | 如果有参数,参数表示IP地址,如果用户IP和参数匹配,则可以访问 +// * hasRole | 如果有参数,参数表示角色,则其角色可以访问 +// * permitAll | 用户可以任意访问 +// * rememberMe | 允许通过remember-me登录的用户访问 +// * authenticated | 用户登录后可访问 +// */ +// @Bean +// protected SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception +// { +// return httpSecurity +// // CSRF禁用,因为不使用session +// .csrf(csrf -> csrf.disable()) +// // 禁用HTTP响应标头 +// .headers((headersCustomizer) -> { +// headersCustomizer.cacheControl(cache -> cache.disable()).frameOptions(options -> options.sameOrigin()); +// }) +// // 认证失败处理类 +// .exceptionHandling(exception -> exception.authenticationEntryPoint(unauthorizedHandler)) +// // 基于token,所以不需要session +// .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) +// // 注解标记允许匿名访问的url +// .authorizeHttpRequests((requests) -> { +// permitAllUrl.getUrls().forEach(url -> requests.requestMatchers(url).permitAll()); +// // 对于登录login 注册register 验证码captchaImage 允许匿名访问 +// requests.requestMatchers("/login", "/register", "/captchaImage").permitAll() +// // 静态资源,可匿名访问 +// .requestMatchers(HttpMethod.GET, "/", "/*.html", "/**.html", "/**.css", "/**.js", "/profile/**").permitAll() +// .requestMatchers("/swagger-ui.html", "/v3/api-docs/**", "/swagger-ui/**", "/druid/**").permitAll() +// // 除上面外的所有请求全部需要鉴权认证 +// .anyRequest().authenticated(); +// }) +// // 添加Logout filter +// .logout(logout -> logout.logoutUrl("/logout").logoutSuccessHandler(logoutSuccessHandler)) +// // 添加JWT filter +// .addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class) +// // 添加CORS filter +// .addFilterBefore(corsFilter, JwtAuthenticationTokenFilter.class) +// .addFilterBefore(corsFilter, LogoutFilter.class) +// .build(); +// } +// +// /** +// * 强散列哈希加密实现 +// */ +// @Bean +// public BCryptPasswordEncoder bCryptPasswordEncoder() +// { +// return new BCryptPasswordEncoder(); +// } +//} diff --git a/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/config/ServerConfig.java b/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/config/ServerConfig.java new file mode 100644 index 0000000..194cfee --- /dev/null +++ b/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/config/ServerConfig.java @@ -0,0 +1,32 @@ +package org.lingniu.portal.framework.config; + +import jakarta.servlet.http.HttpServletRequest; +import org.springframework.stereotype.Component; +import org.lingniu.portal.common.utils.ServletUtils; + +/** + * 服务相关配置 + * + * @author lingniu + */ +@Component +public class ServerConfig +{ + /** + * 获取完整的请求路径,包括:域名,端口,上下文访问路径 + * + * @return 服务地址 + */ + public String getUrl() + { + HttpServletRequest request = ServletUtils.getRequest(); + return getDomain(request); + } + + public static String getDomain(HttpServletRequest request) + { + StringBuffer url = request.getRequestURL(); + String contextPath = request.getServletContext().getContextPath(); + return url.delete(url.length() - request.getRequestURI().length(), url.length()).append(contextPath).toString(); + } +} diff --git a/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/config/ThreadPoolConfig.java b/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/config/ThreadPoolConfig.java new file mode 100644 index 0000000..9116be4 --- /dev/null +++ b/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/config/ThreadPoolConfig.java @@ -0,0 +1,63 @@ +package org.lingniu.portal.framework.config; + +import org.lingniu.portal.common.utils.Threads; +import org.apache.commons.lang3.concurrent.BasicThreadFactory; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.ThreadPoolExecutor; + +/** + * 线程池配置 + * + * @author lingniu + **/ +@Configuration +public class ThreadPoolConfig +{ + // 核心线程池大小 + private int corePoolSize = 50; + + // 最大可创建的线程数 + private int maxPoolSize = 200; + + // 队列最大长度 + private int queueCapacity = 1000; + + // 线程池维护线程所允许的空闲时间 + private int keepAliveSeconds = 300; + + @Bean(name = "threadPoolTaskExecutor") + public ThreadPoolTaskExecutor threadPoolTaskExecutor() + { + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + executor.setMaxPoolSize(maxPoolSize); + executor.setCorePoolSize(corePoolSize); + executor.setQueueCapacity(queueCapacity); + executor.setKeepAliveSeconds(keepAliveSeconds); + // 线程池对拒绝任务(无线程可用)的处理策略 + executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); + return executor; + } + + /** + * 执行周期性或定时任务 + */ + @Bean(name = "scheduledExecutorService") + protected ScheduledExecutorService scheduledExecutorService() + { + return new ScheduledThreadPoolExecutor(corePoolSize, + new BasicThreadFactory.Builder().namingPattern("schedule-pool-%d").daemon(true).build(), + new ThreadPoolExecutor.CallerRunsPolicy()) + { + @Override + protected void afterExecute(Runnable r, Throwable t) + { + super.afterExecute(r, t); + Threads.printException(r, t); + } + }; + } +} diff --git a/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/config/properties/DruidProperties.java b/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/config/properties/DruidProperties.java new file mode 100644 index 0000000..1f2d8b8 --- /dev/null +++ b/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/config/properties/DruidProperties.java @@ -0,0 +1,89 @@ +package org.lingniu.portal.framework.config.properties; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; +import com.alibaba.druid.pool.DruidDataSource; + +/** + * druid 配置属性 + * + * @author lingniu + */ +@Configuration +public class DruidProperties +{ + @Value("${spring.datasource.druid.initialSize}") + private int initialSize; + + @Value("${spring.datasource.druid.minIdle}") + private int minIdle; + + @Value("${spring.datasource.druid.maxActive}") + private int maxActive; + + @Value("${spring.datasource.druid.maxWait}") + private int maxWait; + + @Value("${spring.datasource.druid.connectTimeout}") + private int connectTimeout; + + @Value("${spring.datasource.druid.socketTimeout}") + private int socketTimeout; + + @Value("${spring.datasource.druid.timeBetweenEvictionRunsMillis}") + private int timeBetweenEvictionRunsMillis; + + @Value("${spring.datasource.druid.minEvictableIdleTimeMillis}") + private int minEvictableIdleTimeMillis; + + @Value("${spring.datasource.druid.maxEvictableIdleTimeMillis}") + private int maxEvictableIdleTimeMillis; + + @Value("${spring.datasource.druid.validationQuery}") + private String validationQuery; + + @Value("${spring.datasource.druid.testWhileIdle}") + private boolean testWhileIdle; + + @Value("${spring.datasource.druid.testOnBorrow}") + private boolean testOnBorrow; + + @Value("${spring.datasource.druid.testOnReturn}") + private boolean testOnReturn; + + public DruidDataSource dataSource(DruidDataSource datasource) + { + /** 配置初始化大小、最小、最大 */ + datasource.setInitialSize(initialSize); + datasource.setMaxActive(maxActive); + datasource.setMinIdle(minIdle); + + /** 配置获取连接等待超时的时间 */ + datasource.setMaxWait(maxWait); + + /** 配置驱动连接超时时间,检测数据库建立连接的超时时间,单位是毫秒 */ + datasource.setConnectTimeout(connectTimeout); + + /** 配置网络超时时间,等待数据库操作完成的网络超时时间,单位是毫秒 */ + datasource.setSocketTimeout(socketTimeout); + + /** 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 */ + datasource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis); + + /** 配置一个连接在池中最小、最大生存的时间,单位是毫秒 */ + datasource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis); + datasource.setMaxEvictableIdleTimeMillis(maxEvictableIdleTimeMillis); + + /** + * 用来检测连接是否有效的sql,要求是一个查询语句,常用select 'x'。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会起作用。 + */ + datasource.setValidationQuery(validationQuery); + /** 建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。 */ + datasource.setTestWhileIdle(testWhileIdle); + /** 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 */ + datasource.setTestOnBorrow(testOnBorrow); + /** 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 */ + datasource.setTestOnReturn(testOnReturn); + return datasource; + } +} diff --git a/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/config/properties/PermitAllUrlProperties.java b/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/config/properties/PermitAllUrlProperties.java new file mode 100644 index 0000000..1dda061 --- /dev/null +++ b/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/config/properties/PermitAllUrlProperties.java @@ -0,0 +1,73 @@ +package org.lingniu.portal.framework.config.properties; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.regex.Pattern; +import org.apache.commons.lang3.RegExUtils; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.web.method.HandlerMethod; +import org.springframework.web.servlet.mvc.method.RequestMappingInfo; +import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; +import org.lingniu.portal.common.annotation.Anonymous; + +/** + * 设置Anonymous注解允许匿名访问的url + * + * @author lingniu + */ +@Configuration +public class PermitAllUrlProperties implements InitializingBean, ApplicationContextAware +{ + private static final Pattern PATTERN = Pattern.compile("\\{(.*?)\\}"); + + private ApplicationContext applicationContext; + + private List urls = new ArrayList<>(); + + public String ASTERISK = "*"; + + @Override + public void afterPropertiesSet() + { + RequestMappingHandlerMapping mapping = applicationContext.getBean("requestMappingHandlerMapping", RequestMappingHandlerMapping.class); + Map map = mapping.getHandlerMethods(); + + map.keySet().forEach(info -> { + HandlerMethod handlerMethod = map.get(info); + + // 获取方法上边的注解 替代path variable 为 * + Anonymous method = AnnotationUtils.findAnnotation(handlerMethod.getMethod(), Anonymous.class); + Optional.ofNullable(method).ifPresent(anonymous -> Objects.requireNonNull(info.getPathPatternsCondition().getPatternValues()) // + .forEach(url -> urls.add(RegExUtils.replaceAll(url, PATTERN, ASTERISK)))); + + // 获取类上边的注解, 替代path variable 为 * + Anonymous controller = AnnotationUtils.findAnnotation(handlerMethod.getBeanType(), Anonymous.class); + Optional.ofNullable(controller).ifPresent(anonymous -> Objects.requireNonNull(info.getPathPatternsCondition().getPatternValues()) + .forEach(url -> urls.add(RegExUtils.replaceAll(url, PATTERN, ASTERISK)))); + }); + } + + @Override + public void setApplicationContext(ApplicationContext context) throws BeansException + { + this.applicationContext = context; + } + + public List getUrls() + { + return urls; + } + + public void setUrls(List urls) + { + this.urls = urls; + } +} diff --git a/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/datasource/DynamicDataSource.java b/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/datasource/DynamicDataSource.java new file mode 100644 index 0000000..8c54859 --- /dev/null +++ b/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/datasource/DynamicDataSource.java @@ -0,0 +1,26 @@ +package org.lingniu.portal.framework.datasource; + +import java.util.Map; +import javax.sql.DataSource; +import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; + +/** + * 动态数据源 + * + * @author lingniu + */ +public class DynamicDataSource extends AbstractRoutingDataSource +{ + public DynamicDataSource(DataSource defaultTargetDataSource, Map targetDataSources) + { + super.setDefaultTargetDataSource(defaultTargetDataSource); + super.setTargetDataSources(targetDataSources); + super.afterPropertiesSet(); + } + + @Override + protected Object determineCurrentLookupKey() + { + return DynamicDataSourceContextHolder.getDataSourceType(); + } +} \ No newline at end of file diff --git a/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/datasource/DynamicDataSourceContextHolder.java b/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/datasource/DynamicDataSourceContextHolder.java new file mode 100644 index 0000000..e098ad8 --- /dev/null +++ b/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/datasource/DynamicDataSourceContextHolder.java @@ -0,0 +1,45 @@ +package org.lingniu.portal.framework.datasource; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 数据源切换处理 + * + * @author lingniu + */ +public class DynamicDataSourceContextHolder +{ + public static final Logger log = LoggerFactory.getLogger(DynamicDataSourceContextHolder.class); + + /** + * 使用ThreadLocal维护变量,ThreadLocal为每个使用该变量的线程提供独立的变量副本, + * 所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。 + */ + private static final ThreadLocal CONTEXT_HOLDER = new ThreadLocal<>(); + + /** + * 设置数据源的变量 + */ + public static void setDataSourceType(String dsType) + { + log.info("切换到{}数据源", dsType); + CONTEXT_HOLDER.set(dsType); + } + + /** + * 获得数据源的变量 + */ + public static String getDataSourceType() + { + return CONTEXT_HOLDER.get(); + } + + /** + * 清空数据源变量 + */ + public static void clearDataSourceType() + { + CONTEXT_HOLDER.remove(); + } +} diff --git a/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/interceptor/RepeatSubmitInterceptor.java b/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/interceptor/RepeatSubmitInterceptor.java new file mode 100644 index 0000000..35f8c89 --- /dev/null +++ b/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/interceptor/RepeatSubmitInterceptor.java @@ -0,0 +1,56 @@ +package org.lingniu.portal.framework.interceptor; + +import java.lang.reflect.Method; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.stereotype.Component; +import org.springframework.web.method.HandlerMethod; +import org.springframework.web.servlet.HandlerInterceptor; +import com.alibaba.fastjson2.JSON; +import org.lingniu.portal.common.annotation.RepeatSubmit; +import org.lingniu.portal.common.core.domain.AjaxResult; +import org.lingniu.portal.common.utils.ServletUtils; + +/** + * 防止重复提交拦截器 + * + * @author lingniu + */ +@Component +public abstract class RepeatSubmitInterceptor implements HandlerInterceptor +{ + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception + { + if (handler instanceof HandlerMethod) + { + HandlerMethod handlerMethod = (HandlerMethod) handler; + Method method = handlerMethod.getMethod(); + RepeatSubmit annotation = method.getAnnotation(RepeatSubmit.class); + if (annotation != null) + { + if (this.isRepeatSubmit(request, annotation)) + { + AjaxResult ajaxResult = AjaxResult.error(annotation.message()); + ServletUtils.renderString(response, JSON.toJSONString(ajaxResult)); + return false; + } + } + return true; + } + else + { + return true; + } + } + + /** + * 验证是否重复提交由子类实现具体的防重复提交的规则 + * + * @param request 请求信息 + * @param annotation 防重复注解参数 + * @return 结果 + * @throws Exception + */ + public abstract boolean isRepeatSubmit(HttpServletRequest request, RepeatSubmit annotation); +} diff --git a/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/interceptor/impl/SameUrlDataInterceptor.java b/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/interceptor/impl/SameUrlDataInterceptor.java new file mode 100644 index 0000000..1559834 --- /dev/null +++ b/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/interceptor/impl/SameUrlDataInterceptor.java @@ -0,0 +1,110 @@ +package org.lingniu.portal.framework.interceptor.impl; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import jakarta.servlet.http.HttpServletRequest; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import com.alibaba.fastjson2.JSON; +import org.lingniu.portal.common.annotation.RepeatSubmit; +import org.lingniu.portal.common.constant.CacheConstants; +import org.lingniu.portal.common.core.redis.RedisCache; +import org.lingniu.portal.common.filter.RepeatedlyRequestWrapper; +import org.lingniu.portal.common.utils.StringUtils; +import org.lingniu.portal.common.utils.http.HttpHelper; +import org.lingniu.portal.framework.interceptor.RepeatSubmitInterceptor; + +/** + * 判断请求url和数据是否和上一次相同, + * 如果和上次相同,则是重复提交表单。 有效时间为10秒内。 + * + * @author lingniu + */ +@Component +public class SameUrlDataInterceptor extends RepeatSubmitInterceptor +{ + public final String REPEAT_PARAMS = "repeatParams"; + + public final String REPEAT_TIME = "repeatTime"; + + // 令牌自定义标识 + @Value("${token.header}") + private String header; + + @Autowired + private RedisCache redisCache; + + @SuppressWarnings("unchecked") + @Override + public boolean isRepeatSubmit(HttpServletRequest request, RepeatSubmit annotation) + { + String nowParams = ""; + if (request instanceof RepeatedlyRequestWrapper) + { + RepeatedlyRequestWrapper repeatedlyRequest = (RepeatedlyRequestWrapper) request; + nowParams = HttpHelper.getBodyString(repeatedlyRequest); + } + + // body参数为空,获取Parameter的数据 + if (StringUtils.isEmpty(nowParams)) + { + nowParams = JSON.toJSONString(request.getParameterMap()); + } + Map nowDataMap = new HashMap(); + nowDataMap.put(REPEAT_PARAMS, nowParams); + nowDataMap.put(REPEAT_TIME, System.currentTimeMillis()); + + // 请求地址(作为存放cache的key值) + String url = request.getRequestURI(); + + // 唯一值(没有消息头则使用请求地址) + String submitKey = StringUtils.trimToEmpty(request.getHeader(header)); + + // 唯一标识(指定key + url + 消息头) + String cacheRepeatKey = CacheConstants.REPEAT_SUBMIT_KEY + url + submitKey; + + Object sessionObj = redisCache.getCacheObject(cacheRepeatKey); + if (sessionObj != null) + { + Map sessionMap = (Map) sessionObj; + if (sessionMap.containsKey(url)) + { + Map preDataMap = (Map) sessionMap.get(url); + if (compareParams(nowDataMap, preDataMap) && compareTime(nowDataMap, preDataMap, annotation.interval())) + { + return true; + } + } + } + Map cacheMap = new HashMap(); + cacheMap.put(url, nowDataMap); + redisCache.setCacheObject(cacheRepeatKey, cacheMap, annotation.interval(), TimeUnit.MILLISECONDS); + return false; + } + + /** + * 判断参数是否相同 + */ + private boolean compareParams(Map nowMap, Map preMap) + { + String nowParams = (String) nowMap.get(REPEAT_PARAMS); + String preParams = (String) preMap.get(REPEAT_PARAMS); + return nowParams.equals(preParams); + } + + /** + * 判断两次间隔时间 + */ + private boolean compareTime(Map nowMap, Map preMap, int interval) + { + long time1 = (Long) nowMap.get(REPEAT_TIME); + long time2 = (Long) preMap.get(REPEAT_TIME); + if ((time1 - time2) < interval) + { + return true; + } + return false; + } +} diff --git a/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/manager/AsyncManager.java b/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/manager/AsyncManager.java new file mode 100644 index 0000000..88153f1 --- /dev/null +++ b/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/manager/AsyncManager.java @@ -0,0 +1,55 @@ +package org.lingniu.portal.framework.manager; + +import java.util.TimerTask; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import org.lingniu.portal.common.utils.Threads; +import org.lingniu.portal.common.utils.spring.SpringUtils; + +/** + * 异步任务管理器 + * + * @author lingniu + */ +public class AsyncManager +{ + /** + * 操作延迟10毫秒 + */ + private final int OPERATE_DELAY_TIME = 10; + + /** + * 异步操作任务调度线程池 + */ + private ScheduledExecutorService executor = SpringUtils.getBean("scheduledExecutorService"); + + /** + * 单例模式 + */ + private AsyncManager(){} + + private static AsyncManager me = new AsyncManager(); + + public static AsyncManager me() + { + return me; + } + + /** + * 执行任务 + * + * @param task 任务 + */ + public void execute(TimerTask task) + { + executor.schedule(task, OPERATE_DELAY_TIME, TimeUnit.MILLISECONDS); + } + + /** + * 停止任务线程池 + */ + public void shutdown() + { + Threads.shutdownAndAwaitTermination(executor); + } +} diff --git a/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/manager/ShutdownManager.java b/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/manager/ShutdownManager.java new file mode 100644 index 0000000..ed74f33 --- /dev/null +++ b/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/manager/ShutdownManager.java @@ -0,0 +1,39 @@ +package org.lingniu.portal.framework.manager; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; +import jakarta.annotation.PreDestroy; + +/** + * 确保应用退出时能关闭后台线程 + * + * @author lingniu + */ +@Component +public class ShutdownManager +{ + private static final Logger logger = LoggerFactory.getLogger("sys-user"); + + @PreDestroy + public void destroy() + { + shutdownAsyncManager(); + } + + /** + * 停止异步执行任务 + */ + private void shutdownAsyncManager() + { + try + { + logger.info("====关闭后台任务任务线程池===="); + AsyncManager.me().shutdown(); + } + catch (Exception e) + { + logger.error(e.getMessage(), e); + } + } +} diff --git a/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/manager/factory/AsyncFactory.java b/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/manager/factory/AsyncFactory.java new file mode 100644 index 0000000..5b15312 --- /dev/null +++ b/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/manager/factory/AsyncFactory.java @@ -0,0 +1,102 @@ +package org.lingniu.portal.framework.manager.factory; + +import java.util.TimerTask; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.lingniu.portal.common.constant.Constants; +import org.lingniu.portal.common.utils.LogUtils; +import org.lingniu.portal.common.utils.ServletUtils; +import org.lingniu.portal.common.utils.StringUtils; +import org.lingniu.portal.common.utils.http.UserAgentUtils; +import org.lingniu.portal.common.utils.ip.AddressUtils; +import org.lingniu.portal.common.utils.ip.IpUtils; +import org.lingniu.portal.common.utils.spring.SpringUtils; +import org.lingniu.portal.system.domain.SysLogininfor; +import org.lingniu.portal.system.domain.SysOperLog; +import org.lingniu.portal.system.service.ISysLogininforService; +import org.lingniu.portal.system.service.ISysOperLogService; + +/** + * 异步工厂(产生任务用) + * + * @author lingniu + */ +public class AsyncFactory +{ + private static final Logger sys_user_logger = LoggerFactory.getLogger("sys-user"); + + /** + * 记录登录信息 + * + * @param username 用户名 + * @param status 状态 + * @param message 消息 + * @param args 列表 + * @return 任务task + */ + public static TimerTask recordLogininfor(final String username, final String status, final String message, + final Object... args) + { + final String userAgent = ServletUtils.getRequest().getHeader("User-Agent"); + final String ip = IpUtils.getIpAddr(); + return new TimerTask() + { + @Override + public void run() + { + String address = AddressUtils.getRealAddressByIP(ip); + StringBuilder s = new StringBuilder(); + s.append(LogUtils.getBlock(ip)); + s.append(address); + s.append(LogUtils.getBlock(username)); + s.append(LogUtils.getBlock(status)); + s.append(LogUtils.getBlock(message)); + // 打印信息到日志 + sys_user_logger.info(s.toString(), args); + // 获取客户端操作系统 + String os = UserAgentUtils.getOperatingSystem(userAgent); + // 获取客户端浏览器 + String browser = UserAgentUtils.getBrowser(userAgent); + // 封装对象 + SysLogininfor logininfor = new SysLogininfor(); + logininfor.setUserName(username); + logininfor.setIpaddr(ip); + logininfor.setLoginLocation(address); + logininfor.setBrowser(browser); + logininfor.setOs(os); + logininfor.setMsg(message); + // 日志状态 + if (StringUtils.equalsAny(status, Constants.LOGIN_SUCCESS, Constants.LOGOUT, Constants.REGISTER)) + { + logininfor.setStatus(Constants.SUCCESS); + } + else if (Constants.LOGIN_FAIL.equals(status)) + { + logininfor.setStatus(Constants.FAIL); + } + // 插入数据 + SpringUtils.getBean(ISysLogininforService.class).insertLogininfor(logininfor); + } + }; + } + + /** + * 操作日志记录 + * + * @param operLog 操作日志信息 + * @return 任务task + */ + public static TimerTask recordOper(final SysOperLog operLog) + { + return new TimerTask() + { + @Override + public void run() + { + // 远程查询操作地点 + operLog.setOperLocation(AddressUtils.getRealAddressByIP(operLog.getOperIp())); + SpringUtils.getBean(ISysOperLogService.class).insertOperlog(operLog); + } + }; + } +} diff --git a/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/security/context/AuthenticationContextHolder.java b/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/security/context/AuthenticationContextHolder.java new file mode 100644 index 0000000..bef94c3 --- /dev/null +++ b/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/security/context/AuthenticationContextHolder.java @@ -0,0 +1,28 @@ +package org.lingniu.portal.framework.security.context; + +import org.springframework.security.core.Authentication; + +/** + * 身份验证信息 + * + * @author lingniu + */ +public class AuthenticationContextHolder +{ + private static final ThreadLocal contextHolder = new ThreadLocal<>(); + + public static Authentication getContext() + { + return contextHolder.get(); + } + + public static void setContext(Authentication context) + { + contextHolder.set(context); + } + + public static void clearContext() + { + contextHolder.remove(); + } +} diff --git a/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/security/context/PermissionContextHolder.java b/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/security/context/PermissionContextHolder.java new file mode 100644 index 0000000..03da446 --- /dev/null +++ b/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/security/context/PermissionContextHolder.java @@ -0,0 +1,27 @@ +package org.lingniu.portal.framework.security.context; + +import org.springframework.web.context.request.RequestAttributes; +import org.springframework.web.context.request.RequestContextHolder; +import org.lingniu.portal.common.core.text.Convert; + +/** + * 权限信息 + * + * @author lingniu + */ +public class PermissionContextHolder +{ + private static final String PERMISSION_CONTEXT_ATTRIBUTES = "PERMISSION_CONTEXT"; + + public static void setContext(String permission) + { + RequestContextHolder.currentRequestAttributes().setAttribute(PERMISSION_CONTEXT_ATTRIBUTES, permission, + RequestAttributes.SCOPE_REQUEST); + } + + public static String getContext() + { + return Convert.toStr(RequestContextHolder.currentRequestAttributes().getAttribute(PERMISSION_CONTEXT_ATTRIBUTES, + RequestAttributes.SCOPE_REQUEST)); + } +} diff --git a/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/web/domain/Server.java b/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/web/domain/Server.java new file mode 100644 index 0000000..4d7e3c0 --- /dev/null +++ b/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/web/domain/Server.java @@ -0,0 +1,240 @@ +package org.lingniu.portal.framework.web.domain; + +import java.net.UnknownHostException; +import java.util.LinkedList; +import java.util.List; +import java.util.Properties; +import org.lingniu.portal.common.utils.Arith; +import org.lingniu.portal.common.utils.ip.IpUtils; +import org.lingniu.portal.framework.web.domain.server.Cpu; +import org.lingniu.portal.framework.web.domain.server.Jvm; +import org.lingniu.portal.framework.web.domain.server.Mem; +import org.lingniu.portal.framework.web.domain.server.Sys; +import org.lingniu.portal.framework.web.domain.server.SysFile; +import oshi.SystemInfo; +import oshi.hardware.CentralProcessor; +import oshi.hardware.CentralProcessor.TickType; +import oshi.hardware.GlobalMemory; +import oshi.hardware.HardwareAbstractionLayer; +import oshi.software.os.FileSystem; +import oshi.software.os.OSFileStore; +import oshi.software.os.OperatingSystem; +import oshi.util.Util; + +/** + * 服务器相关信息 + * + * @author lingniu + */ +public class Server +{ + private static final int OSHI_WAIT_SECOND = 1000; + + /** + * CPU相关信息 + */ + private Cpu cpu = new Cpu(); + + /** + * 內存相关信息 + */ + private Mem mem = new Mem(); + + /** + * JVM相关信息 + */ + private Jvm jvm = new Jvm(); + + /** + * 服务器相关信息 + */ + private Sys sys = new Sys(); + + /** + * 磁盘相关信息 + */ + private List sysFiles = new LinkedList(); + + public Cpu getCpu() + { + return cpu; + } + + public void setCpu(Cpu cpu) + { + this.cpu = cpu; + } + + public Mem getMem() + { + return mem; + } + + public void setMem(Mem mem) + { + this.mem = mem; + } + + public Jvm getJvm() + { + return jvm; + } + + public void setJvm(Jvm jvm) + { + this.jvm = jvm; + } + + public Sys getSys() + { + return sys; + } + + public void setSys(Sys sys) + { + this.sys = sys; + } + + public List getSysFiles() + { + return sysFiles; + } + + public void setSysFiles(List sysFiles) + { + this.sysFiles = sysFiles; + } + + public void copyTo() throws Exception + { + SystemInfo si = new SystemInfo(); + HardwareAbstractionLayer hal = si.getHardware(); + + setCpuInfo(hal.getProcessor()); + + setMemInfo(hal.getMemory()); + + setSysInfo(); + + setJvmInfo(); + + setSysFiles(si.getOperatingSystem()); + } + + /** + * 设置CPU信息 + */ + private void setCpuInfo(CentralProcessor processor) + { + // CPU信息 + long[] prevTicks = processor.getSystemCpuLoadTicks(); + Util.sleep(OSHI_WAIT_SECOND); + long[] ticks = processor.getSystemCpuLoadTicks(); + long nice = ticks[TickType.NICE.getIndex()] - prevTicks[TickType.NICE.getIndex()]; + long irq = ticks[TickType.IRQ.getIndex()] - prevTicks[TickType.IRQ.getIndex()]; + long softirq = ticks[TickType.SOFTIRQ.getIndex()] - prevTicks[TickType.SOFTIRQ.getIndex()]; + long steal = ticks[TickType.STEAL.getIndex()] - prevTicks[TickType.STEAL.getIndex()]; + long cSys = ticks[TickType.SYSTEM.getIndex()] - prevTicks[TickType.SYSTEM.getIndex()]; + long user = ticks[TickType.USER.getIndex()] - prevTicks[TickType.USER.getIndex()]; + long iowait = ticks[TickType.IOWAIT.getIndex()] - prevTicks[TickType.IOWAIT.getIndex()]; + long idle = ticks[TickType.IDLE.getIndex()] - prevTicks[TickType.IDLE.getIndex()]; + long totalCpu = user + nice + cSys + idle + iowait + irq + softirq + steal; + cpu.setCpuNum(processor.getLogicalProcessorCount()); + cpu.setTotal(totalCpu); + cpu.setSys(cSys); + cpu.setUsed(user); + cpu.setWait(iowait); + cpu.setFree(idle); + } + + /** + * 设置内存信息 + */ + private void setMemInfo(GlobalMemory memory) + { + mem.setTotal(memory.getTotal()); + mem.setUsed(memory.getTotal() - memory.getAvailable()); + mem.setFree(memory.getAvailable()); + } + + /** + * 设置服务器信息 + */ + private void setSysInfo() + { + Properties props = System.getProperties(); + sys.setComputerName(IpUtils.getHostName()); + sys.setComputerIp(IpUtils.getHostIp()); + sys.setOsName(props.getProperty("os.name")); + sys.setOsArch(props.getProperty("os.arch")); + sys.setUserDir(props.getProperty("user.dir")); + } + + /** + * 设置Java虚拟机 + */ + private void setJvmInfo() throws UnknownHostException + { + Properties props = System.getProperties(); + jvm.setTotal(Runtime.getRuntime().totalMemory()); + jvm.setMax(Runtime.getRuntime().maxMemory()); + jvm.setFree(Runtime.getRuntime().freeMemory()); + jvm.setVersion(props.getProperty("java.version")); + jvm.setHome(props.getProperty("java.home")); + } + + /** + * 设置磁盘信息 + */ + private void setSysFiles(OperatingSystem os) + { + FileSystem fileSystem = os.getFileSystem(); + List fsArray = fileSystem.getFileStores(); + for (OSFileStore fs : fsArray) + { + long free = fs.getUsableSpace(); + long total = fs.getTotalSpace(); + long used = total - free; + SysFile sysFile = new SysFile(); + sysFile.setDirName(fs.getMount()); + sysFile.setSysTypeName(fs.getType()); + sysFile.setTypeName(fs.getName()); + sysFile.setTotal(convertFileSize(total)); + sysFile.setFree(convertFileSize(free)); + sysFile.setUsed(convertFileSize(used)); + sysFile.setUsage(Arith.mul(Arith.div(used, total, 4), 100)); + sysFiles.add(sysFile); + } + } + + /** + * 字节转换 + * + * @param size 字节大小 + * @return 转换后值 + */ + public String convertFileSize(long size) + { + long kb = 1024; + long mb = kb * 1024; + long gb = mb * 1024; + if (size >= gb) + { + return String.format("%.1f GB", (float) size / gb); + } + else if (size >= mb) + { + float f = (float) size / mb; + return String.format(f > 100 ? "%.0f MB" : "%.1f MB", f); + } + else if (size >= kb) + { + float f = (float) size / kb; + return String.format(f > 100 ? "%.0f KB" : "%.1f KB", f); + } + else + { + return String.format("%d B", size); + } + } +} diff --git a/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/web/domain/server/Cpu.java b/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/web/domain/server/Cpu.java new file mode 100644 index 0000000..4907b62 --- /dev/null +++ b/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/web/domain/server/Cpu.java @@ -0,0 +1,101 @@ +package org.lingniu.portal.framework.web.domain.server; + +import org.lingniu.portal.common.utils.Arith; + +/** + * CPU相关信息 + * + * @author lingniu + */ +public class Cpu +{ + /** + * 核心数 + */ + private int cpuNum; + + /** + * CPU总的使用率 + */ + private double total; + + /** + * CPU系统使用率 + */ + private double sys; + + /** + * CPU用户使用率 + */ + private double used; + + /** + * CPU当前等待率 + */ + private double wait; + + /** + * CPU当前空闲率 + */ + private double free; + + public int getCpuNum() + { + return cpuNum; + } + + public void setCpuNum(int cpuNum) + { + this.cpuNum = cpuNum; + } + + public double getTotal() + { + return Arith.round(Arith.mul(total, 100), 2); + } + + public void setTotal(double total) + { + this.total = total; + } + + public double getSys() + { + return Arith.round(Arith.mul(sys / total, 100), 2); + } + + public void setSys(double sys) + { + this.sys = sys; + } + + public double getUsed() + { + return Arith.round(Arith.mul(used / total, 100), 2); + } + + public void setUsed(double used) + { + this.used = used; + } + + public double getWait() + { + return Arith.round(Arith.mul(wait / total, 100), 2); + } + + public void setWait(double wait) + { + this.wait = wait; + } + + public double getFree() + { + return Arith.round(Arith.mul(free / total, 100), 2); + } + + public void setFree(double free) + { + this.free = free; + } +} diff --git a/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/web/domain/server/Jvm.java b/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/web/domain/server/Jvm.java new file mode 100644 index 0000000..18fdacd --- /dev/null +++ b/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/web/domain/server/Jvm.java @@ -0,0 +1,130 @@ +package org.lingniu.portal.framework.web.domain.server; + +import java.lang.management.ManagementFactory; +import org.lingniu.portal.common.utils.Arith; +import org.lingniu.portal.common.utils.DateUtils; + +/** + * JVM相关信息 + * + * @author lingniu + */ +public class Jvm +{ + /** + * 当前JVM占用的内存总数(M) + */ + private double total; + + /** + * JVM最大可用内存总数(M) + */ + private double max; + + /** + * JVM空闲内存(M) + */ + private double free; + + /** + * JDK版本 + */ + private String version; + + /** + * JDK路径 + */ + private String home; + + public double getTotal() + { + return Arith.div(total, (1024 * 1024), 2); + } + + public void setTotal(double total) + { + this.total = total; + } + + public double getMax() + { + return Arith.div(max, (1024 * 1024), 2); + } + + public void setMax(double max) + { + this.max = max; + } + + public double getFree() + { + return Arith.div(free, (1024 * 1024), 2); + } + + public void setFree(double free) + { + this.free = free; + } + + public double getUsed() + { + return Arith.div(total - free, (1024 * 1024), 2); + } + + public double getUsage() + { + return Arith.mul(Arith.div(total - free, total, 4), 100); + } + + /** + * 获取JDK名称 + */ + public String getName() + { + return ManagementFactory.getRuntimeMXBean().getVmName(); + } + + public String getVersion() + { + return version; + } + + public void setVersion(String version) + { + this.version = version; + } + + public String getHome() + { + return home; + } + + public void setHome(String home) + { + this.home = home; + } + + /** + * JDK启动时间 + */ + public String getStartTime() + { + return DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, DateUtils.getServerStartDate()); + } + + /** + * JDK运行时间 + */ + public String getRunTime() + { + return DateUtils.timeDistance(DateUtils.getNowDate(), DateUtils.getServerStartDate()); + } + + /** + * 运行参数 + */ + public String getInputArgs() + { + return ManagementFactory.getRuntimeMXBean().getInputArguments().toString(); + } +} diff --git a/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/web/domain/server/Mem.java b/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/web/domain/server/Mem.java new file mode 100644 index 0000000..968c685 --- /dev/null +++ b/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/web/domain/server/Mem.java @@ -0,0 +1,61 @@ +package org.lingniu.portal.framework.web.domain.server; + +import org.lingniu.portal.common.utils.Arith; + +/** + * 內存相关信息 + * + * @author lingniu + */ +public class Mem +{ + /** + * 内存总量 + */ + private double total; + + /** + * 已用内存 + */ + private double used; + + /** + * 剩余内存 + */ + private double free; + + public double getTotal() + { + return Arith.div(total, (1024 * 1024 * 1024), 2); + } + + public void setTotal(long total) + { + this.total = total; + } + + public double getUsed() + { + return Arith.div(used, (1024 * 1024 * 1024), 2); + } + + public void setUsed(long used) + { + this.used = used; + } + + public double getFree() + { + return Arith.div(free, (1024 * 1024 * 1024), 2); + } + + public void setFree(long free) + { + this.free = free; + } + + public double getUsage() + { + return Arith.mul(Arith.div(used, total, 4), 100); + } +} diff --git a/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/web/domain/server/Sys.java b/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/web/domain/server/Sys.java new file mode 100644 index 0000000..2ef22d5 --- /dev/null +++ b/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/web/domain/server/Sys.java @@ -0,0 +1,84 @@ +package org.lingniu.portal.framework.web.domain.server; + +/** + * 系统相关信息 + * + * @author lingniu + */ +public class Sys +{ + /** + * 服务器名称 + */ + private String computerName; + + /** + * 服务器Ip + */ + private String computerIp; + + /** + * 项目路径 + */ + private String userDir; + + /** + * 操作系统 + */ + private String osName; + + /** + * 系统架构 + */ + private String osArch; + + public String getComputerName() + { + return computerName; + } + + public void setComputerName(String computerName) + { + this.computerName = computerName; + } + + public String getComputerIp() + { + return computerIp; + } + + public void setComputerIp(String computerIp) + { + this.computerIp = computerIp; + } + + public String getUserDir() + { + return userDir; + } + + public void setUserDir(String userDir) + { + this.userDir = userDir; + } + + public String getOsName() + { + return osName; + } + + public void setOsName(String osName) + { + this.osName = osName; + } + + public String getOsArch() + { + return osArch; + } + + public void setOsArch(String osArch) + { + this.osArch = osArch; + } +} diff --git a/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/web/domain/server/SysFile.java b/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/web/domain/server/SysFile.java new file mode 100644 index 0000000..4e5b8a4 --- /dev/null +++ b/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/web/domain/server/SysFile.java @@ -0,0 +1,114 @@ +package org.lingniu.portal.framework.web.domain.server; + +/** + * 系统文件相关信息 + * + * @author lingniu + */ +public class SysFile +{ + /** + * 盘符路径 + */ + private String dirName; + + /** + * 盘符类型 + */ + private String sysTypeName; + + /** + * 文件类型 + */ + private String typeName; + + /** + * 总大小 + */ + private String total; + + /** + * 剩余大小 + */ + private String free; + + /** + * 已经使用量 + */ + private String used; + + /** + * 资源的使用率 + */ + private double usage; + + public String getDirName() + { + return dirName; + } + + public void setDirName(String dirName) + { + this.dirName = dirName; + } + + public String getSysTypeName() + { + return sysTypeName; + } + + public void setSysTypeName(String sysTypeName) + { + this.sysTypeName = sysTypeName; + } + + public String getTypeName() + { + return typeName; + } + + public void setTypeName(String typeName) + { + this.typeName = typeName; + } + + public String getTotal() + { + return total; + } + + public void setTotal(String total) + { + this.total = total; + } + + public String getFree() + { + return free; + } + + public void setFree(String free) + { + this.free = free; + } + + public String getUsed() + { + return used; + } + + public void setUsed(String used) + { + this.used = used; + } + + public double getUsage() + { + return usage; + } + + public void setUsage(double usage) + { + this.usage = usage; + } +} diff --git a/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/web/exception/GlobalExceptionHandler.java b/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/web/exception/GlobalExceptionHandler.java new file mode 100644 index 0000000..1b9abab --- /dev/null +++ b/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/web/exception/GlobalExceptionHandler.java @@ -0,0 +1,145 @@ +package org.lingniu.portal.framework.web.exception; + +import jakarta.servlet.http.HttpServletRequest; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.validation.BindException; +import org.springframework.web.HttpRequestMethodNotSupportedException; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.MissingPathVariableException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException; +import org.lingniu.portal.common.constant.HttpStatus; +import org.lingniu.portal.common.core.domain.AjaxResult; +import org.lingniu.portal.common.core.text.Convert; +import org.lingniu.portal.common.exception.DemoModeException; +import org.lingniu.portal.common.exception.ServiceException; +import org.lingniu.portal.common.utils.StringUtils; +import org.lingniu.portal.common.utils.html.EscapeUtil; + +/** + * 全局异常处理器 + * + * @author lingniu + */ +@RestControllerAdvice +public class GlobalExceptionHandler +{ + private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class); + + /** + * 权限校验异常 + */ + @ExceptionHandler(AccessDeniedException.class) + public AjaxResult handleAccessDeniedException(AccessDeniedException e, HttpServletRequest request) + { + String requestURI = request.getRequestURI(); + log.error("请求地址'{}',权限校验失败'{}'", requestURI, e.getMessage()); + return AjaxResult.error(HttpStatus.FORBIDDEN, "没有权限,请联系管理员授权"); + } + + /** + * 请求方式不支持 + */ + @ExceptionHandler(HttpRequestMethodNotSupportedException.class) + public AjaxResult handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException e, + HttpServletRequest request) + { + String requestURI = request.getRequestURI(); + log.error("请求地址'{}',不支持'{}'请求", requestURI, e.getMethod()); + return AjaxResult.error(e.getMessage()); + } + + /** + * 业务异常 + */ + @ExceptionHandler(ServiceException.class) + public AjaxResult handleServiceException(ServiceException e, HttpServletRequest request) + { + log.error(e.getMessage(), e); + Integer code = e.getCode(); + return StringUtils.isNotNull(code) ? AjaxResult.error(code, e.getMessage()) : AjaxResult.error(e.getMessage()); + } + + /** + * 请求路径中缺少必需的路径变量 + */ + @ExceptionHandler(MissingPathVariableException.class) + public AjaxResult handleMissingPathVariableException(MissingPathVariableException e, HttpServletRequest request) + { + String requestURI = request.getRequestURI(); + log.error("请求路径中缺少必需的路径变量'{}',发生系统异常.", requestURI, e); + return AjaxResult.error(String.format("请求路径中缺少必需的路径变量[%s]", e.getVariableName())); + } + + /** + * 请求参数类型不匹配 + */ + @ExceptionHandler(MethodArgumentTypeMismatchException.class) + public AjaxResult handleMethodArgumentTypeMismatchException(MethodArgumentTypeMismatchException e, HttpServletRequest request) + { + String requestURI = request.getRequestURI(); + String value = Convert.toStr(e.getValue()); + if (StringUtils.isNotEmpty(value)) + { + value = EscapeUtil.clean(value); + } + log.error("请求参数类型不匹配'{}',发生系统异常.", requestURI, e); + return AjaxResult.error(String.format("请求参数类型不匹配,参数[%s]要求类型为:'%s',但输入值为:'%s'", e.getName(), e.getRequiredType().getName(), value)); + } + + /** + * 拦截未知的运行时异常 + */ + @ExceptionHandler(RuntimeException.class) + public AjaxResult handleRuntimeException(RuntimeException e, HttpServletRequest request) + { + String requestURI = request.getRequestURI(); + log.error("请求地址'{}',发生未知异常.", requestURI, e); + return AjaxResult.error(e.getMessage()); + } + + /** + * 系统异常 + */ + @ExceptionHandler(Exception.class) + public AjaxResult handleException(Exception e, HttpServletRequest request) + { + String requestURI = request.getRequestURI(); + log.error("请求地址'{}',发生系统异常.", requestURI, e); + return AjaxResult.error(e.getMessage()); + } + + /** + * 自定义验证异常 + */ + @ExceptionHandler(BindException.class) + public AjaxResult handleBindException(BindException e) + { + log.error(e.getMessage(), e); + String message = e.getAllErrors().get(0).getDefaultMessage(); + return AjaxResult.error(message); + } + + /** + * 自定义验证异常 + */ + @ExceptionHandler(MethodArgumentNotValidException.class) + public Object handleMethodArgumentNotValidException(MethodArgumentNotValidException e) + { + log.error(e.getMessage(), e); + String message = e.getBindingResult().getFieldError().getDefaultMessage(); + return AjaxResult.error(message); + } + + /** + * 演示模式异常 + */ + @ExceptionHandler(DemoModeException.class) + public AjaxResult handleDemoModeException(DemoModeException e) + { + return AjaxResult.error("演示模式,不允许操作"); + } +} diff --git a/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/web/service/SysPasswordService.java b/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/web/service/SysPasswordService.java new file mode 100644 index 0000000..5198b32 --- /dev/null +++ b/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/web/service/SysPasswordService.java @@ -0,0 +1,86 @@ +package org.lingniu.portal.framework.web.service; + +import java.util.concurrent.TimeUnit; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.core.Authentication; +import org.springframework.stereotype.Component; +import org.lingniu.portal.common.constant.CacheConstants; +import org.lingniu.portal.common.core.domain.entity.SysUser; +import org.lingniu.portal.common.core.redis.RedisCache; +import org.lingniu.portal.common.exception.user.UserPasswordNotMatchException; +import org.lingniu.portal.common.exception.user.UserPasswordRetryLimitExceedException; +import org.lingniu.portal.common.utils.SecurityUtils; +import org.lingniu.portal.framework.security.context.AuthenticationContextHolder; + +/** + * 登录密码方法 + * + * @author lingniu + */ +@Component +public class SysPasswordService +{ + @Autowired + private RedisCache redisCache; + + @Value(value = "${user.password.maxRetryCount}") + private int maxRetryCount; + + @Value(value = "${user.password.lockTime}") + private int lockTime; + + /** + * 登录账户密码错误次数缓存键名 + * + * @param username 用户名 + * @return 缓存键key + */ + private String getCacheKey(String username) + { + return CacheConstants.PWD_ERR_CNT_KEY + username; + } + + public void validate(SysUser user) + { + Authentication usernamePasswordAuthenticationToken = AuthenticationContextHolder.getContext(); + String username = usernamePasswordAuthenticationToken.getName(); + String password = usernamePasswordAuthenticationToken.getCredentials().toString(); + + Integer retryCount = redisCache.getCacheObject(getCacheKey(username)); + + if (retryCount == null) + { + retryCount = 0; + } + + if (retryCount >= Integer.valueOf(maxRetryCount).intValue()) + { + throw new UserPasswordRetryLimitExceedException(maxRetryCount, lockTime); + } + + if (!matches(user, password)) + { + retryCount = retryCount + 1; + redisCache.setCacheObject(getCacheKey(username), retryCount, lockTime, TimeUnit.MINUTES); + throw new UserPasswordNotMatchException(); + } + else + { + clearLoginRecordCache(username); + } + } + + public boolean matches(SysUser user, String rawPassword) + { + return SecurityUtils.matchesPassword(rawPassword, user.getPassword()); + } + + public void clearLoginRecordCache(String loginName) + { + if (redisCache.hasKey(getCacheKey(loginName))) + { + redisCache.deleteObject(getCacheKey(loginName)); + } + } +} diff --git a/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/web/service/SysPermissionService.java b/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/web/service/SysPermissionService.java new file mode 100644 index 0000000..3ca5dd6 --- /dev/null +++ b/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/web/service/SysPermissionService.java @@ -0,0 +1,89 @@ +package org.lingniu.portal.framework.web.service; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; +import org.lingniu.portal.common.constant.Constants; +import org.lingniu.portal.common.constant.UserConstants; +import org.lingniu.portal.common.core.domain.entity.SysRole; +import org.lingniu.portal.common.core.domain.entity.SysUser; +import org.lingniu.portal.common.utils.StringUtils; +import org.lingniu.portal.system.service.ISysMenuService; +import org.lingniu.portal.system.service.ISysRoleService; + +/** + * 用户权限处理 + * + * @author lingniu + */ +@Component +public class SysPermissionService +{ + @Autowired + private ISysRoleService roleService; + + @Autowired + private ISysMenuService menuService; + + /** + * 获取角色数据权限 + * + * @param user 用户信息 + * @return 角色权限信息 + */ + public Set getRolePermission(SysUser user) + { + Set roles = new HashSet(); + // 管理员拥有所有权限 + if (user.isAdmin()) + { + roles.add(Constants.SUPER_ADMIN); + } + else + { + roles.addAll(roleService.selectRolePermissionByUserId(user.getUserId())); + } + return roles; + } + + /** + * 获取菜单数据权限 + * + * @param user 用户信息 + * @return 菜单权限信息 + */ + public Set getMenuPermission(SysUser user) + { + Set perms = new HashSet(); + // 管理员拥有所有权限 + if (user.isAdmin()) + { + perms.add(Constants.ALL_PERMISSION); + } + else + { + List roles = user.getRoles(); + if (!CollectionUtils.isEmpty(roles)) + { + // 多角色设置permissions属性,以便数据权限匹配权限 + for (SysRole role : roles) + { + if (StringUtils.equals(role.getStatus(), UserConstants.ROLE_NORMAL) && !role.isAdmin()) + { + Set rolePerms = menuService.selectMenuPermsByRoleId(role.getRoleId()); + role.setPermissions(rolePerms); + perms.addAll(rolePerms); + } + } + } + else + { + perms.addAll(menuService.selectMenuPermsByUserId(user.getUserId())); + } + } + return perms; + } +} diff --git a/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/web/service/SysRegisterService.java b/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/web/service/SysRegisterService.java new file mode 100644 index 0000000..db49873 --- /dev/null +++ b/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/web/service/SysRegisterService.java @@ -0,0 +1,117 @@ +package org.lingniu.portal.framework.web.service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.lingniu.portal.common.constant.CacheConstants; +import org.lingniu.portal.common.constant.Constants; +import org.lingniu.portal.common.constant.UserConstants; +import org.lingniu.portal.common.core.domain.entity.SysUser; +import org.lingniu.portal.common.core.domain.model.RegisterBody; +import org.lingniu.portal.common.core.redis.RedisCache; +import org.lingniu.portal.common.exception.user.CaptchaException; +import org.lingniu.portal.common.exception.user.CaptchaExpireException; +import org.lingniu.portal.common.utils.DateUtils; +import org.lingniu.portal.common.utils.MessageUtils; +import org.lingniu.portal.common.utils.SecurityUtils; +import org.lingniu.portal.common.utils.StringUtils; +import org.lingniu.portal.framework.manager.AsyncManager; +import org.lingniu.portal.framework.manager.factory.AsyncFactory; +import org.lingniu.portal.system.service.ISysConfigService; +import org.lingniu.portal.system.service.ISysUserService; + +/** + * 注册校验方法 + * + * @author lingniu + */ +@Component +public class SysRegisterService +{ + @Autowired + private ISysUserService userService; + + @Autowired + private ISysConfigService configService; + + @Autowired + private RedisCache redisCache; + + /** + * 注册 + */ + public String register(RegisterBody registerBody) + { + String msg = "", username = registerBody.getUsername(), password = registerBody.getPassword(); + SysUser sysUser = new SysUser(); + sysUser.setUserName(username); + + // 验证码开关 + boolean captchaEnabled = configService.selectCaptchaEnabled(); + if (captchaEnabled) + { + validateCaptcha(username, registerBody.getCode(), registerBody.getUuid()); + } + + if (StringUtils.isEmpty(username)) + { + msg = "用户名不能为空"; + } + else if (StringUtils.isEmpty(password)) + { + msg = "用户密码不能为空"; + } + else if (username.length() < UserConstants.USERNAME_MIN_LENGTH + || username.length() > UserConstants.USERNAME_MAX_LENGTH) + { + msg = "账户长度必须在2到20个字符之间"; + } + else if (password.length() < UserConstants.PASSWORD_MIN_LENGTH + || password.length() > UserConstants.PASSWORD_MAX_LENGTH) + { + msg = "密码长度必须在5到20个字符之间"; + } + else if (!userService.checkUserNameUnique(sysUser)) + { + msg = "保存用户'" + username + "'失败,注册账号已存在"; + } + else + { + sysUser.setNickName(username); + sysUser.setPwdUpdateDate(DateUtils.getNowDate()); + sysUser.setPassword(SecurityUtils.encryptPassword(password)); + boolean regFlag = userService.registerUser(sysUser); + if (!regFlag) + { + msg = "注册失败,请联系系统管理人员"; + } + else + { + AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.REGISTER, MessageUtils.message("user.register.success"))); + } + } + return msg; + } + + /** + * 校验验证码 + * + * @param username 用户名 + * @param code 验证码 + * @param uuid 唯一标识 + * @return 结果 + */ + public void validateCaptcha(String username, String code, String uuid) + { + String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + StringUtils.nvl(uuid, ""); + String captcha = redisCache.getCacheObject(verifyKey); + redisCache.deleteObject(verifyKey); + if (captcha == null) + { + throw new CaptchaExpireException(); + } + if (!code.equalsIgnoreCase(captcha)) + { + throw new CaptchaException(); + } + } +} diff --git a/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/web/service/UserDetailsServiceImpl.java b/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/web/service/UserDetailsServiceImpl.java new file mode 100644 index 0000000..bd40011 --- /dev/null +++ b/portal/backend/portal-framework/src/main/java/org/lingniu/portal/framework/web/service/UserDetailsServiceImpl.java @@ -0,0 +1,66 @@ +package org.lingniu.portal.framework.web.service; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.stereotype.Service; +import org.lingniu.portal.common.core.domain.entity.SysUser; +import org.lingniu.portal.common.core.domain.model.LoginUser; +import org.lingniu.portal.common.enums.UserStatus; +import org.lingniu.portal.common.exception.ServiceException; +import org.lingniu.portal.common.utils.MessageUtils; +import org.lingniu.portal.common.utils.StringUtils; +import org.lingniu.portal.system.service.ISysUserService; + +/** + * 用户验证处理 + * + * @author lingniu + */ +@Service +public class UserDetailsServiceImpl implements UserDetailsService +{ + private static final Logger log = LoggerFactory.getLogger(UserDetailsServiceImpl.class); + + @Autowired + private ISysUserService userService; + + @Autowired + private SysPasswordService passwordService; + + @Autowired + private SysPermissionService permissionService; + + @Override + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException + { + SysUser user = userService.selectUserByUserName(username); + if (StringUtils.isNull(user)) + { + log.info("登录用户:{} 不存在.", username); + throw new ServiceException(MessageUtils.message("user.not.exists")); + } + else if (UserStatus.DELETED.getCode().equals(user.getDelFlag())) + { + log.info("登录用户:{} 已被删除.", username); + throw new ServiceException(MessageUtils.message("user.password.delete")); + } + else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) + { + log.info("登录用户:{} 已被停用.", username); + throw new ServiceException(MessageUtils.message("user.blocked")); + } + + passwordService.validate(user); + + return createLoginUser(user); + } + + public UserDetails createLoginUser(SysUser user) + { + return new LoginUser(user.getUserId(), user.getDeptId(), user, permissionService.getMenuPermission(user)); + } +} diff --git a/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/aspectj/DataScopeAspect.class b/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/aspectj/DataScopeAspect.class new file mode 100644 index 0000000..8b7a43c Binary files /dev/null and b/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/aspectj/DataScopeAspect.class differ diff --git a/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/aspectj/DataSourceAspect.class b/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/aspectj/DataSourceAspect.class new file mode 100644 index 0000000..3cbf305 Binary files /dev/null and b/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/aspectj/DataSourceAspect.class differ diff --git a/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/aspectj/LogAspect.class b/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/aspectj/LogAspect.class new file mode 100644 index 0000000..a0ca77d Binary files /dev/null and b/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/aspectj/LogAspect.class differ diff --git a/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/aspectj/RateLimiterAspect.class b/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/aspectj/RateLimiterAspect.class new file mode 100644 index 0000000..a1e0317 Binary files /dev/null and b/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/aspectj/RateLimiterAspect.class differ diff --git a/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/config/ApplicationConfig.class b/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/config/ApplicationConfig.class new file mode 100644 index 0000000..a8f5fa8 Binary files /dev/null and b/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/config/ApplicationConfig.class differ diff --git a/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/config/CaptchaConfig.class b/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/config/CaptchaConfig.class new file mode 100644 index 0000000..ef440ce Binary files /dev/null and b/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/config/CaptchaConfig.class differ diff --git a/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/config/DruidConfig$1.class b/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/config/DruidConfig$1.class new file mode 100644 index 0000000..e3aa5b1 Binary files /dev/null and b/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/config/DruidConfig$1.class differ diff --git a/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/config/DruidConfig.class b/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/config/DruidConfig.class new file mode 100644 index 0000000..83e78dd Binary files /dev/null and b/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/config/DruidConfig.class differ diff --git a/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/config/FastJson2JsonRedisSerializer.class b/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/config/FastJson2JsonRedisSerializer.class new file mode 100644 index 0000000..cf90f2e Binary files /dev/null and b/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/config/FastJson2JsonRedisSerializer.class differ diff --git a/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/config/FilterConfig.class b/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/config/FilterConfig.class new file mode 100644 index 0000000..35b7bbf Binary files /dev/null and b/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/config/FilterConfig.class differ diff --git a/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/config/I18nConfig.class b/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/config/I18nConfig.class new file mode 100644 index 0000000..105cbda Binary files /dev/null and b/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/config/I18nConfig.class differ diff --git a/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/config/KaptchaTextCreator.class b/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/config/KaptchaTextCreator.class new file mode 100644 index 0000000..6629428 Binary files /dev/null and b/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/config/KaptchaTextCreator.class differ diff --git a/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/config/MyBatisConfig.class b/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/config/MyBatisConfig.class new file mode 100644 index 0000000..86ad723 Binary files /dev/null and b/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/config/MyBatisConfig.class differ diff --git a/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/config/RedisConfig.class b/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/config/RedisConfig.class new file mode 100644 index 0000000..e5549e4 Binary files /dev/null and b/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/config/RedisConfig.class differ diff --git a/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/config/ResourcesConfig.class b/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/config/ResourcesConfig.class new file mode 100644 index 0000000..d2253f1 Binary files /dev/null and b/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/config/ResourcesConfig.class differ diff --git a/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/config/ServerConfig.class b/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/config/ServerConfig.class new file mode 100644 index 0000000..28a0f0d Binary files /dev/null and b/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/config/ServerConfig.class differ diff --git a/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/config/ThreadPoolConfig$1.class b/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/config/ThreadPoolConfig$1.class new file mode 100644 index 0000000..098ad13 Binary files /dev/null and b/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/config/ThreadPoolConfig$1.class differ diff --git a/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/config/ThreadPoolConfig.class b/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/config/ThreadPoolConfig.class new file mode 100644 index 0000000..a46228a Binary files /dev/null and b/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/config/ThreadPoolConfig.class differ diff --git a/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/config/properties/DruidProperties.class b/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/config/properties/DruidProperties.class new file mode 100644 index 0000000..4a2b596 Binary files /dev/null and b/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/config/properties/DruidProperties.class differ diff --git a/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/config/properties/PermitAllUrlProperties.class b/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/config/properties/PermitAllUrlProperties.class new file mode 100644 index 0000000..e375c99 Binary files /dev/null and b/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/config/properties/PermitAllUrlProperties.class differ diff --git a/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/datasource/DynamicDataSource.class b/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/datasource/DynamicDataSource.class new file mode 100644 index 0000000..9dfb0c2 Binary files /dev/null and b/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/datasource/DynamicDataSource.class differ diff --git a/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/datasource/DynamicDataSourceContextHolder.class b/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/datasource/DynamicDataSourceContextHolder.class new file mode 100644 index 0000000..08b349a Binary files /dev/null and b/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/datasource/DynamicDataSourceContextHolder.class differ diff --git a/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/interceptor/RepeatSubmitInterceptor.class b/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/interceptor/RepeatSubmitInterceptor.class new file mode 100644 index 0000000..88c83bd Binary files /dev/null and b/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/interceptor/RepeatSubmitInterceptor.class differ diff --git a/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/interceptor/impl/SameUrlDataInterceptor.class b/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/interceptor/impl/SameUrlDataInterceptor.class new file mode 100644 index 0000000..091d105 Binary files /dev/null and b/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/interceptor/impl/SameUrlDataInterceptor.class differ diff --git a/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/manager/AsyncManager.class b/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/manager/AsyncManager.class new file mode 100644 index 0000000..b37ad9e Binary files /dev/null and b/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/manager/AsyncManager.class differ diff --git a/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/manager/ShutdownManager.class b/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/manager/ShutdownManager.class new file mode 100644 index 0000000..f573626 Binary files /dev/null and b/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/manager/ShutdownManager.class differ diff --git a/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/manager/factory/AsyncFactory$1.class b/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/manager/factory/AsyncFactory$1.class new file mode 100644 index 0000000..20f0560 Binary files /dev/null and b/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/manager/factory/AsyncFactory$1.class differ diff --git a/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/manager/factory/AsyncFactory$2.class b/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/manager/factory/AsyncFactory$2.class new file mode 100644 index 0000000..aead69f Binary files /dev/null and b/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/manager/factory/AsyncFactory$2.class differ diff --git a/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/manager/factory/AsyncFactory.class b/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/manager/factory/AsyncFactory.class new file mode 100644 index 0000000..3486605 Binary files /dev/null and b/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/manager/factory/AsyncFactory.class differ diff --git a/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/security/context/AuthenticationContextHolder.class b/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/security/context/AuthenticationContextHolder.class new file mode 100644 index 0000000..141c2ed Binary files /dev/null and b/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/security/context/AuthenticationContextHolder.class differ diff --git a/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/security/context/PermissionContextHolder.class b/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/security/context/PermissionContextHolder.class new file mode 100644 index 0000000..1c6f6a5 Binary files /dev/null and b/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/security/context/PermissionContextHolder.class differ diff --git a/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/web/domain/Server.class b/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/web/domain/Server.class new file mode 100644 index 0000000..cb84357 Binary files /dev/null and b/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/web/domain/Server.class differ diff --git a/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/web/domain/server/Cpu.class b/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/web/domain/server/Cpu.class new file mode 100644 index 0000000..4761198 Binary files /dev/null and b/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/web/domain/server/Cpu.class differ diff --git a/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/web/domain/server/Jvm.class b/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/web/domain/server/Jvm.class new file mode 100644 index 0000000..3de7b10 Binary files /dev/null and b/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/web/domain/server/Jvm.class differ diff --git a/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/web/domain/server/Mem.class b/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/web/domain/server/Mem.class new file mode 100644 index 0000000..5345658 Binary files /dev/null and b/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/web/domain/server/Mem.class differ diff --git a/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/web/domain/server/Sys.class b/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/web/domain/server/Sys.class new file mode 100644 index 0000000..696776e Binary files /dev/null and b/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/web/domain/server/Sys.class differ diff --git a/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/web/domain/server/SysFile.class b/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/web/domain/server/SysFile.class new file mode 100644 index 0000000..1998efc Binary files /dev/null and b/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/web/domain/server/SysFile.class differ diff --git a/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/web/exception/GlobalExceptionHandler.class b/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/web/exception/GlobalExceptionHandler.class new file mode 100644 index 0000000..46b7866 Binary files /dev/null and b/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/web/exception/GlobalExceptionHandler.class differ diff --git a/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/web/service/SysPasswordService.class b/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/web/service/SysPasswordService.class new file mode 100644 index 0000000..60d785f Binary files /dev/null and b/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/web/service/SysPasswordService.class differ diff --git a/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/web/service/SysPermissionService.class b/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/web/service/SysPermissionService.class new file mode 100644 index 0000000..8403662 Binary files /dev/null and b/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/web/service/SysPermissionService.class differ diff --git a/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/web/service/SysRegisterService.class b/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/web/service/SysRegisterService.class new file mode 100644 index 0000000..c13868b Binary files /dev/null and b/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/web/service/SysRegisterService.class differ diff --git a/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/web/service/UserDetailsServiceImpl.class b/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/web/service/UserDetailsServiceImpl.class new file mode 100644 index 0000000..3921315 Binary files /dev/null and b/portal/backend/portal-framework/target/classes/org/lingniu/portal/framework/web/service/UserDetailsServiceImpl.class differ diff --git a/portal/backend/portal-framework/target/maven-archiver/pom.properties b/portal/backend/portal-framework/target/maven-archiver/pom.properties new file mode 100644 index 0000000..92e773e --- /dev/null +++ b/portal/backend/portal-framework/target/maven-archiver/pom.properties @@ -0,0 +1,5 @@ +#Generated by Maven +#Tue Feb 10 15:13:56 CST 2026 +groupId=org.lingniu +artifactId=portal-framework +version=1.0.0 diff --git a/portal/backend/portal-framework/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst b/portal/backend/portal-framework/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst new file mode 100644 index 0000000..d4e1a9a --- /dev/null +++ b/portal/backend/portal-framework/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst @@ -0,0 +1,42 @@ +org\lingniu\portal\framework\config\I18nConfig.class +org\lingniu\portal\framework\datasource\DynamicDataSourceContextHolder.class +org\lingniu\portal\framework\web\exception\GlobalExceptionHandler.class +org\lingniu\portal\framework\config\KaptchaTextCreator.class +org\lingniu\portal\framework\aspectj\DataScopeAspect.class +org\lingniu\portal\framework\config\DruidConfig$1.class +org\lingniu\portal\framework\config\RedisConfig.class +org\lingniu\portal\framework\security\context\AuthenticationContextHolder.class +org\lingniu\portal\framework\aspectj\DataSourceAspect.class +org\lingniu\portal\framework\web\domain\server\SysFile.class +org\lingniu\portal\framework\web\domain\server\Mem.class +org\lingniu\portal\framework\config\ServerConfig.class +org\lingniu\portal\framework\config\ThreadPoolConfig$1.class +org\lingniu\portal\framework\config\FastJson2JsonRedisSerializer.class +org\lingniu\portal\framework\security\context\PermissionContextHolder.class +org\lingniu\portal\framework\web\domain\server\Cpu.class +org\lingniu\portal\framework\web\domain\server\Jvm.class +org\lingniu\portal\framework\web\domain\Server.class +org\lingniu\portal\framework\datasource\DynamicDataSource.class +org\lingniu\portal\framework\manager\ShutdownManager.class +org\lingniu\portal\framework\web\service\SysRegisterService.class +org\lingniu\portal\framework\config\DruidConfig.class +org\lingniu\portal\framework\manager\factory\AsyncFactory$2.class +org\lingniu\portal\framework\config\ThreadPoolConfig.class +org\lingniu\portal\framework\config\CaptchaConfig.class +org\lingniu\portal\framework\interceptor\RepeatSubmitInterceptor.class +org\lingniu\portal\framework\web\service\SysPasswordService.class +org\lingniu\portal\framework\config\ResourcesConfig.class +org\lingniu\portal\framework\aspectj\LogAspect.class +org\lingniu\portal\framework\interceptor\impl\SameUrlDataInterceptor.class +org\lingniu\portal\framework\manager\factory\AsyncFactory.class +org\lingniu\portal\framework\web\domain\server\Sys.class +org\lingniu\portal\framework\web\service\UserDetailsServiceImpl.class +org\lingniu\portal\framework\config\properties\PermitAllUrlProperties.class +org\lingniu\portal\framework\config\MyBatisConfig.class +org\lingniu\portal\framework\aspectj\RateLimiterAspect.class +org\lingniu\portal\framework\config\ApplicationConfig.class +org\lingniu\portal\framework\manager\factory\AsyncFactory$1.class +org\lingniu\portal\framework\manager\AsyncManager.class +org\lingniu\portal\framework\web\service\SysPermissionService.class +org\lingniu\portal\framework\config\FilterConfig.class +org\lingniu\portal\framework\config\properties\DruidProperties.class diff --git a/portal/backend/portal-framework/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst b/portal/backend/portal-framework/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst new file mode 100644 index 0000000..d9078d1 --- /dev/null +++ b/portal/backend/portal-framework/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst @@ -0,0 +1,39 @@ +D:\RuoYi-Vue\portal-framework\src\main\java\org\lingniu\portal\framework\aspectj\DataScopeAspect.java +D:\RuoYi-Vue\portal-framework\src\main\java\org\lingniu\portal\framework\aspectj\DataSourceAspect.java +D:\RuoYi-Vue\portal-framework\src\main\java\org\lingniu\portal\framework\aspectj\LogAspect.java +D:\RuoYi-Vue\portal-framework\src\main\java\org\lingniu\portal\framework\aspectj\RateLimiterAspect.java +D:\RuoYi-Vue\portal-framework\src\main\java\org\lingniu\portal\framework\config\ApplicationConfig.java +D:\RuoYi-Vue\portal-framework\src\main\java\org\lingniu\portal\framework\config\CaptchaConfig.java +D:\RuoYi-Vue\portal-framework\src\main\java\org\lingniu\portal\framework\config\DruidConfig.java +D:\RuoYi-Vue\portal-framework\src\main\java\org\lingniu\portal\framework\config\FastJson2JsonRedisSerializer.java +D:\RuoYi-Vue\portal-framework\src\main\java\org\lingniu\portal\framework\config\FilterConfig.java +D:\RuoYi-Vue\portal-framework\src\main\java\org\lingniu\portal\framework\config\I18nConfig.java +D:\RuoYi-Vue\portal-framework\src\main\java\org\lingniu\portal\framework\config\KaptchaTextCreator.java +D:\RuoYi-Vue\portal-framework\src\main\java\org\lingniu\portal\framework\config\MyBatisConfig.java +D:\RuoYi-Vue\portal-framework\src\main\java\org\lingniu\portal\framework\config\properties\DruidProperties.java +D:\RuoYi-Vue\portal-framework\src\main\java\org\lingniu\portal\framework\config\properties\PermitAllUrlProperties.java +D:\RuoYi-Vue\portal-framework\src\main\java\org\lingniu\portal\framework\config\RedisConfig.java +D:\RuoYi-Vue\portal-framework\src\main\java\org\lingniu\portal\framework\config\ResourcesConfig.java +D:\RuoYi-Vue\portal-framework\src\main\java\org\lingniu\portal\framework\config\SecurityConfig.java +D:\RuoYi-Vue\portal-framework\src\main\java\org\lingniu\portal\framework\config\ServerConfig.java +D:\RuoYi-Vue\portal-framework\src\main\java\org\lingniu\portal\framework\config\ThreadPoolConfig.java +D:\RuoYi-Vue\portal-framework\src\main\java\org\lingniu\portal\framework\datasource\DynamicDataSource.java +D:\RuoYi-Vue\portal-framework\src\main\java\org\lingniu\portal\framework\datasource\DynamicDataSourceContextHolder.java +D:\RuoYi-Vue\portal-framework\src\main\java\org\lingniu\portal\framework\interceptor\impl\SameUrlDataInterceptor.java +D:\RuoYi-Vue\portal-framework\src\main\java\org\lingniu\portal\framework\interceptor\RepeatSubmitInterceptor.java +D:\RuoYi-Vue\portal-framework\src\main\java\org\lingniu\portal\framework\manager\AsyncManager.java +D:\RuoYi-Vue\portal-framework\src\main\java\org\lingniu\portal\framework\manager\factory\AsyncFactory.java +D:\RuoYi-Vue\portal-framework\src\main\java\org\lingniu\portal\framework\manager\ShutdownManager.java +D:\RuoYi-Vue\portal-framework\src\main\java\org\lingniu\portal\framework\security\context\AuthenticationContextHolder.java +D:\RuoYi-Vue\portal-framework\src\main\java\org\lingniu\portal\framework\security\context\PermissionContextHolder.java +D:\RuoYi-Vue\portal-framework\src\main\java\org\lingniu\portal\framework\web\domain\Server.java +D:\RuoYi-Vue\portal-framework\src\main\java\org\lingniu\portal\framework\web\domain\server\Cpu.java +D:\RuoYi-Vue\portal-framework\src\main\java\org\lingniu\portal\framework\web\domain\server\Jvm.java +D:\RuoYi-Vue\portal-framework\src\main\java\org\lingniu\portal\framework\web\domain\server\Mem.java +D:\RuoYi-Vue\portal-framework\src\main\java\org\lingniu\portal\framework\web\domain\server\Sys.java +D:\RuoYi-Vue\portal-framework\src\main\java\org\lingniu\portal\framework\web\domain\server\SysFile.java +D:\RuoYi-Vue\portal-framework\src\main\java\org\lingniu\portal\framework\web\exception\GlobalExceptionHandler.java +D:\RuoYi-Vue\portal-framework\src\main\java\org\lingniu\portal\framework\web\service\SysPasswordService.java +D:\RuoYi-Vue\portal-framework\src\main\java\org\lingniu\portal\framework\web\service\SysPermissionService.java +D:\RuoYi-Vue\portal-framework\src\main\java\org\lingniu\portal\framework\web\service\SysRegisterService.java +D:\RuoYi-Vue\portal-framework\src\main\java\org\lingniu\portal\framework\web\service\UserDetailsServiceImpl.java diff --git a/portal/backend/portal-framework/target/portal-framework-1.0.0.jar b/portal/backend/portal-framework/target/portal-framework-1.0.0.jar new file mode 100644 index 0000000..b80c4cc Binary files /dev/null and b/portal/backend/portal-framework/target/portal-framework-1.0.0.jar differ diff --git a/portal/backend/portal-system/pom.xml b/portal/backend/portal-system/pom.xml new file mode 100644 index 0000000..193def1 --- /dev/null +++ b/portal/backend/portal-system/pom.xml @@ -0,0 +1,28 @@ + + + + portal + org.lingniu + 1.0.0 + + 4.0.0 + + portal-system + + + system系统模块 + + + + + + + org.lingniu + portal-common + + + + + \ No newline at end of file diff --git a/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/domain/SysCache.java b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/domain/SysCache.java new file mode 100644 index 0000000..f1c52df --- /dev/null +++ b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/domain/SysCache.java @@ -0,0 +1,81 @@ +package org.lingniu.portal.system.domain; + +import org.lingniu.portal.common.utils.StringUtils; + +/** + * 缓存信息 + * + * @author lingniu + */ +public class SysCache +{ + /** 缓存名称 */ + private String cacheName = ""; + + /** 缓存键名 */ + private String cacheKey = ""; + + /** 缓存内容 */ + private String cacheValue = ""; + + /** 备注 */ + private String remark = ""; + + public SysCache() + { + + } + + public SysCache(String cacheName, String remark) + { + this.cacheName = cacheName; + this.remark = remark; + } + + public SysCache(String cacheName, String cacheKey, String cacheValue) + { + this.cacheName = StringUtils.replace(cacheName, ":", ""); + this.cacheKey = StringUtils.replace(cacheKey, cacheName, ""); + this.cacheValue = cacheValue; + } + + public String getCacheName() + { + return cacheName; + } + + public void setCacheName(String cacheName) + { + this.cacheName = cacheName; + } + + public String getCacheKey() + { + return cacheKey; + } + + public void setCacheKey(String cacheKey) + { + this.cacheKey = cacheKey; + } + + public String getCacheValue() + { + return cacheValue; + } + + public void setCacheValue(String cacheValue) + { + this.cacheValue = cacheValue; + } + + public String getRemark() + { + return remark; + } + + public void setRemark(String remark) + { + this.remark = remark; + } +} diff --git a/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/domain/SysConfig.java b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/domain/SysConfig.java new file mode 100644 index 0000000..efb7941 --- /dev/null +++ b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/domain/SysConfig.java @@ -0,0 +1,111 @@ +package org.lingniu.portal.system.domain; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import org.lingniu.portal.common.annotation.Excel; +import org.lingniu.portal.common.annotation.Excel.ColumnType; +import org.lingniu.portal.common.core.domain.BaseEntity; + +/** + * 参数配置表 sys_config + * + * @author lingniu + */ +public class SysConfig extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 参数主键 */ + @Excel(name = "参数主键", cellType = ColumnType.NUMERIC) + private Long configId; + + /** 参数名称 */ + @Excel(name = "参数名称") + private String configName; + + /** 参数键名 */ + @Excel(name = "参数键名") + private String configKey; + + /** 参数键值 */ + @Excel(name = "参数键值") + private String configValue; + + /** 系统内置(Y是 N否) */ + @Excel(name = "系统内置", readConverterExp = "Y=是,N=否") + private String configType; + + public Long getConfigId() + { + return configId; + } + + public void setConfigId(Long configId) + { + this.configId = configId; + } + + @NotBlank(message = "参数名称不能为空") + @Size(min = 0, max = 100, message = "参数名称不能超过100个字符") + public String getConfigName() + { + return configName; + } + + public void setConfigName(String configName) + { + this.configName = configName; + } + + @NotBlank(message = "参数键名长度不能为空") + @Size(min = 0, max = 100, message = "参数键名长度不能超过100个字符") + public String getConfigKey() + { + return configKey; + } + + public void setConfigKey(String configKey) + { + this.configKey = configKey; + } + + @NotBlank(message = "参数键值不能为空") + @Size(min = 0, max = 500, message = "参数键值长度不能超过500个字符") + public String getConfigValue() + { + return configValue; + } + + public void setConfigValue(String configValue) + { + this.configValue = configValue; + } + + public String getConfigType() + { + return configType; + } + + public void setConfigType(String configType) + { + this.configType = configType; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("configId", getConfigId()) + .append("configName", getConfigName()) + .append("configKey", getConfigKey()) + .append("configValue", getConfigValue()) + .append("configType", getConfigType()) + .append("createBy", getCreateBy()) + .append("createTime", getCreateTime()) + .append("updateBy", getUpdateBy()) + .append("updateTime", getUpdateTime()) + .append("remark", getRemark()) + .toString(); + } +} diff --git a/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/domain/SysLogininfor.java b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/domain/SysLogininfor.java new file mode 100644 index 0000000..5c781a9 --- /dev/null +++ b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/domain/SysLogininfor.java @@ -0,0 +1,144 @@ +package org.lingniu.portal.system.domain; + +import java.util.Date; +import com.fasterxml.jackson.annotation.JsonFormat; +import org.lingniu.portal.common.annotation.Excel; +import org.lingniu.portal.common.annotation.Excel.ColumnType; +import org.lingniu.portal.common.core.domain.BaseEntity; + +/** + * 系统访问记录表 sys_logininfor + * + * @author lingniu + */ +public class SysLogininfor extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** ID */ + @Excel(name = "序号", cellType = ColumnType.NUMERIC) + private Long infoId; + + /** 用户账号 */ + @Excel(name = "用户账号") + private String userName; + + /** 登录状态 0成功 1失败 */ + @Excel(name = "登录状态", readConverterExp = "0=成功,1=失败") + private String status; + + /** 登录IP地址 */ + @Excel(name = "登录地址") + private String ipaddr; + + /** 登录地点 */ + @Excel(name = "登录地点") + private String loginLocation; + + /** 浏览器类型 */ + @Excel(name = "浏览器") + private String browser; + + /** 操作系统 */ + @Excel(name = "操作系统") + private String os; + + /** 提示消息 */ + @Excel(name = "提示消息") + private String msg; + + /** 访问时间 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @Excel(name = "访问时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss") + private Date loginTime; + + public Long getInfoId() + { + return infoId; + } + + public void setInfoId(Long infoId) + { + this.infoId = infoId; + } + + public String getUserName() + { + return userName; + } + + public void setUserName(String userName) + { + this.userName = userName; + } + + public String getStatus() + { + return status; + } + + public void setStatus(String status) + { + this.status = status; + } + + public String getIpaddr() + { + return ipaddr; + } + + public void setIpaddr(String ipaddr) + { + this.ipaddr = ipaddr; + } + + public String getLoginLocation() + { + return loginLocation; + } + + public void setLoginLocation(String loginLocation) + { + this.loginLocation = loginLocation; + } + + public String getBrowser() + { + return browser; + } + + public void setBrowser(String browser) + { + this.browser = browser; + } + + public String getOs() + { + return os; + } + + public void setOs(String os) + { + this.os = os; + } + + public String getMsg() + { + return msg; + } + + public void setMsg(String msg) + { + this.msg = msg; + } + + public Date getLoginTime() + { + return loginTime; + } + + public void setLoginTime(Date loginTime) + { + this.loginTime = loginTime; + } +} diff --git a/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/domain/SysNotice.java b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/domain/SysNotice.java new file mode 100644 index 0000000..5b35315 --- /dev/null +++ b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/domain/SysNotice.java @@ -0,0 +1,102 @@ +package org.lingniu.portal.system.domain; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import org.lingniu.portal.common.core.domain.BaseEntity; +import org.lingniu.portal.common.xss.Xss; + +/** + * 通知公告表 sys_notice + * + * @author lingniu + */ +public class SysNotice extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 公告ID */ + private Long noticeId; + + /** 公告标题 */ + private String noticeTitle; + + /** 公告类型(1通知 2公告) */ + private String noticeType; + + /** 公告内容 */ + private String noticeContent; + + /** 公告状态(0正常 1关闭) */ + private String status; + + public Long getNoticeId() + { + return noticeId; + } + + public void setNoticeId(Long noticeId) + { + this.noticeId = noticeId; + } + + public void setNoticeTitle(String noticeTitle) + { + this.noticeTitle = noticeTitle; + } + + @Xss(message = "公告标题不能包含脚本字符") + @NotBlank(message = "公告标题不能为空") + @Size(min = 0, max = 50, message = "公告标题不能超过50个字符") + public String getNoticeTitle() + { + return noticeTitle; + } + + public void setNoticeType(String noticeType) + { + this.noticeType = noticeType; + } + + public String getNoticeType() + { + return noticeType; + } + + public void setNoticeContent(String noticeContent) + { + this.noticeContent = noticeContent; + } + + public String getNoticeContent() + { + return noticeContent; + } + + public void setStatus(String status) + { + this.status = status; + } + + public String getStatus() + { + return status; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("noticeId", getNoticeId()) + .append("noticeTitle", getNoticeTitle()) + .append("noticeType", getNoticeType()) + .append("noticeContent", getNoticeContent()) + .append("status", getStatus()) + .append("createBy", getCreateBy()) + .append("createTime", getCreateTime()) + .append("updateBy", getUpdateBy()) + .append("updateTime", getUpdateTime()) + .append("remark", getRemark()) + .toString(); + } +} diff --git a/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/domain/SysOperLog.java b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/domain/SysOperLog.java new file mode 100644 index 0000000..5d1f763 --- /dev/null +++ b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/domain/SysOperLog.java @@ -0,0 +1,269 @@ +package org.lingniu.portal.system.domain; + +import java.util.Date; +import com.fasterxml.jackson.annotation.JsonFormat; +import org.lingniu.portal.common.annotation.Excel; +import org.lingniu.portal.common.annotation.Excel.ColumnType; +import org.lingniu.portal.common.core.domain.BaseEntity; + +/** + * 操作日志记录表 oper_log + * + * @author lingniu + */ +public class SysOperLog extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 日志主键 */ + @Excel(name = "操作序号", cellType = ColumnType.NUMERIC) + private Long operId; + + /** 操作模块 */ + @Excel(name = "操作模块") + private String title; + + /** 业务类型(0其它 1新增 2修改 3删除) */ + @Excel(name = "业务类型", readConverterExp = "0=其它,1=新增,2=修改,3=删除,4=授权,5=导出,6=导入,7=强退,8=生成代码,9=清空数据") + private Integer businessType; + + /** 业务类型数组 */ + private Integer[] businessTypes; + + /** 请求方法 */ + @Excel(name = "请求方法") + private String method; + + /** 请求方式 */ + @Excel(name = "请求方式") + private String requestMethod; + + /** 操作类别(0其它 1后台用户 2手机端用户) */ + @Excel(name = "操作类别", readConverterExp = "0=其它,1=后台用户,2=手机端用户") + private Integer operatorType; + + /** 操作人员 */ + @Excel(name = "操作人员") + private String operName; + + /** 部门名称 */ + @Excel(name = "部门名称") + private String deptName; + + /** 请求url */ + @Excel(name = "请求地址") + private String operUrl; + + /** 操作地址 */ + @Excel(name = "操作地址") + private String operIp; + + /** 操作地点 */ + @Excel(name = "操作地点") + private String operLocation; + + /** 请求参数 */ + @Excel(name = "请求参数") + private String operParam; + + /** 返回参数 */ + @Excel(name = "返回参数") + private String jsonResult; + + /** 操作状态(0正常 1异常) */ + @Excel(name = "状态", readConverterExp = "0=正常,1=异常") + private Integer status; + + /** 错误消息 */ + @Excel(name = "错误消息") + private String errorMsg; + + /** 操作时间 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @Excel(name = "操作时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss") + private Date operTime; + + /** 消耗时间 */ + @Excel(name = "消耗时间", suffix = "毫秒") + private Long costTime; + + public Long getOperId() + { + return operId; + } + + public void setOperId(Long operId) + { + this.operId = operId; + } + + public String getTitle() + { + return title; + } + + public void setTitle(String title) + { + this.title = title; + } + + public Integer getBusinessType() + { + return businessType; + } + + public void setBusinessType(Integer businessType) + { + this.businessType = businessType; + } + + public Integer[] getBusinessTypes() + { + return businessTypes; + } + + public void setBusinessTypes(Integer[] businessTypes) + { + this.businessTypes = businessTypes; + } + + public String getMethod() + { + return method; + } + + public void setMethod(String method) + { + this.method = method; + } + + public String getRequestMethod() + { + return requestMethod; + } + + public void setRequestMethod(String requestMethod) + { + this.requestMethod = requestMethod; + } + + public Integer getOperatorType() + { + return operatorType; + } + + public void setOperatorType(Integer operatorType) + { + this.operatorType = operatorType; + } + + public String getOperName() + { + return operName; + } + + public void setOperName(String operName) + { + this.operName = operName; + } + + public String getDeptName() + { + return deptName; + } + + public void setDeptName(String deptName) + { + this.deptName = deptName; + } + + public String getOperUrl() + { + return operUrl; + } + + public void setOperUrl(String operUrl) + { + this.operUrl = operUrl; + } + + public String getOperIp() + { + return operIp; + } + + public void setOperIp(String operIp) + { + this.operIp = operIp; + } + + public String getOperLocation() + { + return operLocation; + } + + public void setOperLocation(String operLocation) + { + this.operLocation = operLocation; + } + + public String getOperParam() + { + return operParam; + } + + public void setOperParam(String operParam) + { + this.operParam = operParam; + } + + public String getJsonResult() + { + return jsonResult; + } + + public void setJsonResult(String jsonResult) + { + this.jsonResult = jsonResult; + } + + public Integer getStatus() + { + return status; + } + + public void setStatus(Integer status) + { + this.status = status; + } + + public String getErrorMsg() + { + return errorMsg; + } + + public void setErrorMsg(String errorMsg) + { + this.errorMsg = errorMsg; + } + + public Date getOperTime() + { + return operTime; + } + + public void setOperTime(Date operTime) + { + this.operTime = operTime; + } + + public Long getCostTime() + { + return costTime; + } + + public void setCostTime(Long costTime) + { + this.costTime = costTime; + } +} diff --git a/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/domain/SysPost.java b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/domain/SysPost.java new file mode 100644 index 0000000..adf0def --- /dev/null +++ b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/domain/SysPost.java @@ -0,0 +1,124 @@ +package org.lingniu.portal.system.domain; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import org.lingniu.portal.common.annotation.Excel; +import org.lingniu.portal.common.annotation.Excel.ColumnType; +import org.lingniu.portal.common.core.domain.BaseEntity; + +/** + * 岗位表 sys_post + * + * @author lingniu + */ +public class SysPost extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 岗位序号 */ + @Excel(name = "岗位序号", cellType = ColumnType.NUMERIC) + private Long postId; + + /** 岗位编码 */ + @Excel(name = "岗位编码") + private String postCode; + + /** 岗位名称 */ + @Excel(name = "岗位名称") + private String postName; + + /** 岗位排序 */ + @Excel(name = "岗位排序") + private Integer postSort; + + /** 状态(0正常 1停用) */ + @Excel(name = "状态", readConverterExp = "0=正常,1=停用") + private String status; + + /** 用户是否存在此岗位标识 默认不存在 */ + private boolean flag = false; + + public Long getPostId() + { + return postId; + } + + public void setPostId(Long postId) + { + this.postId = postId; + } + + @NotBlank(message = "岗位编码不能为空") + @Size(min = 0, max = 64, message = "岗位编码长度不能超过64个字符") + public String getPostCode() + { + return postCode; + } + + public void setPostCode(String postCode) + { + this.postCode = postCode; + } + + @NotBlank(message = "岗位名称不能为空") + @Size(min = 0, max = 50, message = "岗位名称长度不能超过50个字符") + public String getPostName() + { + return postName; + } + + public void setPostName(String postName) + { + this.postName = postName; + } + + @NotNull(message = "显示顺序不能为空") + public Integer getPostSort() + { + return postSort; + } + + public void setPostSort(Integer postSort) + { + this.postSort = postSort; + } + + public String getStatus() + { + return status; + } + + public void setStatus(String status) + { + this.status = status; + } + + public boolean isFlag() + { + return flag; + } + + public void setFlag(boolean flag) + { + this.flag = flag; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("postId", getPostId()) + .append("postCode", getPostCode()) + .append("postName", getPostName()) + .append("postSort", getPostSort()) + .append("status", getStatus()) + .append("createBy", getCreateBy()) + .append("createTime", getCreateTime()) + .append("updateBy", getUpdateBy()) + .append("updateTime", getUpdateTime()) + .append("remark", getRemark()) + .toString(); + } +} diff --git a/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/domain/SysRoleDept.java b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/domain/SysRoleDept.java new file mode 100644 index 0000000..e0e1b5a --- /dev/null +++ b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/domain/SysRoleDept.java @@ -0,0 +1,46 @@ +package org.lingniu.portal.system.domain; + +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + +/** + * 角色和部门关联 sys_role_dept + * + * @author lingniu + */ +public class SysRoleDept +{ + /** 角色ID */ + private Long roleId; + + /** 部门ID */ + private Long deptId; + + public Long getRoleId() + { + return roleId; + } + + public void setRoleId(Long roleId) + { + this.roleId = roleId; + } + + public Long getDeptId() + { + return deptId; + } + + public void setDeptId(Long deptId) + { + this.deptId = deptId; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("roleId", getRoleId()) + .append("deptId", getDeptId()) + .toString(); + } +} diff --git a/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/domain/SysRoleMenu.java b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/domain/SysRoleMenu.java new file mode 100644 index 0000000..f25933c --- /dev/null +++ b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/domain/SysRoleMenu.java @@ -0,0 +1,46 @@ +package org.lingniu.portal.system.domain; + +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + +/** + * 角色和菜单关联 sys_role_menu + * + * @author lingniu + */ +public class SysRoleMenu +{ + /** 角色ID */ + private Long roleId; + + /** 菜单ID */ + private Long menuId; + + public Long getRoleId() + { + return roleId; + } + + public void setRoleId(Long roleId) + { + this.roleId = roleId; + } + + public Long getMenuId() + { + return menuId; + } + + public void setMenuId(Long menuId) + { + this.menuId = menuId; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("roleId", getRoleId()) + .append("menuId", getMenuId()) + .toString(); + } +} diff --git a/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/domain/SysUserOnline.java b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/domain/SysUserOnline.java new file mode 100644 index 0000000..e06ba3d --- /dev/null +++ b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/domain/SysUserOnline.java @@ -0,0 +1,113 @@ +package org.lingniu.portal.system.domain; + +/** + * 当前在线会话 + * + * @author lingniu + */ +public class SysUserOnline +{ + /** 会话编号 */ + private String tokenId; + + /** 部门名称 */ + private String deptName; + + /** 用户名称 */ + private String userName; + + /** 登录IP地址 */ + private String ipaddr; + + /** 登录地址 */ + private String loginLocation; + + /** 浏览器类型 */ + private String browser; + + /** 操作系统 */ + private String os; + + /** 登录时间 */ + private Long loginTime; + + public String getTokenId() + { + return tokenId; + } + + public void setTokenId(String tokenId) + { + this.tokenId = tokenId; + } + + public String getDeptName() + { + return deptName; + } + + public void setDeptName(String deptName) + { + this.deptName = deptName; + } + + public String getUserName() + { + return userName; + } + + public void setUserName(String userName) + { + this.userName = userName; + } + + public String getIpaddr() + { + return ipaddr; + } + + public void setIpaddr(String ipaddr) + { + this.ipaddr = ipaddr; + } + + public String getLoginLocation() + { + return loginLocation; + } + + public void setLoginLocation(String loginLocation) + { + this.loginLocation = loginLocation; + } + + public String getBrowser() + { + return browser; + } + + public void setBrowser(String browser) + { + this.browser = browser; + } + + public String getOs() + { + return os; + } + + public void setOs(String os) + { + this.os = os; + } + + public Long getLoginTime() + { + return loginTime; + } + + public void setLoginTime(Long loginTime) + { + this.loginTime = loginTime; + } +} diff --git a/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/domain/SysUserPost.java b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/domain/SysUserPost.java new file mode 100644 index 0000000..f48fd28 --- /dev/null +++ b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/domain/SysUserPost.java @@ -0,0 +1,46 @@ +package org.lingniu.portal.system.domain; + +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + +/** + * 用户和岗位关联 sys_user_post + * + * @author lingniu + */ +public class SysUserPost +{ + /** 用户ID */ + private Long userId; + + /** 岗位ID */ + private Long postId; + + public Long getUserId() + { + return userId; + } + + public void setUserId(Long userId) + { + this.userId = userId; + } + + public Long getPostId() + { + return postId; + } + + public void setPostId(Long postId) + { + this.postId = postId; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("userId", getUserId()) + .append("postId", getPostId()) + .toString(); + } +} diff --git a/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/domain/SysUserRole.java b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/domain/SysUserRole.java new file mode 100644 index 0000000..741a990 --- /dev/null +++ b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/domain/SysUserRole.java @@ -0,0 +1,46 @@ +package org.lingniu.portal.system.domain; + +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + +/** + * 用户和角色关联 sys_user_role + * + * @author lingniu + */ +public class SysUserRole +{ + /** 用户ID */ + private Long userId; + + /** 角色ID */ + private Long roleId; + + public Long getUserId() + { + return userId; + } + + public void setUserId(Long userId) + { + this.userId = userId; + } + + public Long getRoleId() + { + return roleId; + } + + public void setRoleId(Long roleId) + { + this.roleId = roleId; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("userId", getUserId()) + .append("roleId", getRoleId()) + .toString(); + } +} diff --git a/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/domain/vo/MetaVo.java b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/domain/vo/MetaVo.java new file mode 100644 index 0000000..bba8b20 --- /dev/null +++ b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/domain/vo/MetaVo.java @@ -0,0 +1,106 @@ +package org.lingniu.portal.system.domain.vo; + +import org.lingniu.portal.common.utils.StringUtils; + +/** + * 路由显示信息 + * + * @author lingniu + */ +public class MetaVo +{ + /** + * 设置该路由在侧边栏和面包屑中展示的名字 + */ + private String title; + + /** + * 设置该路由的图标,对应路径src/assets/icons/svg + */ + private String icon; + + /** + * 设置为true,则不会被 缓存 + */ + private boolean noCache; + + /** + * 内链地址(http(s)://开头) + */ + private String link; + + public MetaVo() + { + } + + public MetaVo(String title, String icon) + { + this.title = title; + this.icon = icon; + } + + public MetaVo(String title, String icon, boolean noCache) + { + this.title = title; + this.icon = icon; + this.noCache = noCache; + } + + public MetaVo(String title, String icon, String link) + { + this.title = title; + this.icon = icon; + this.link = link; + } + + public MetaVo(String title, String icon, boolean noCache, String link) + { + this.title = title; + this.icon = icon; + this.noCache = noCache; + if (StringUtils.ishttp(link)) + { + this.link = link; + } + } + + public boolean isNoCache() + { + return noCache; + } + + public void setNoCache(boolean noCache) + { + this.noCache = noCache; + } + + public String getTitle() + { + return title; + } + + public void setTitle(String title) + { + this.title = title; + } + + public String getIcon() + { + return icon; + } + + public void setIcon(String icon) + { + this.icon = icon; + } + + public String getLink() + { + return link; + } + + public void setLink(String link) + { + this.link = link; + } +} diff --git a/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/domain/vo/RouterVo.java b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/domain/vo/RouterVo.java new file mode 100644 index 0000000..f68f0ff --- /dev/null +++ b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/domain/vo/RouterVo.java @@ -0,0 +1,148 @@ +package org.lingniu.portal.system.domain.vo; + +import com.fasterxml.jackson.annotation.JsonInclude; +import java.util.List; + +/** + * 路由配置信息 + * + * @author lingniu + */ +@JsonInclude(JsonInclude.Include.NON_EMPTY) +public class RouterVo +{ + /** + * 路由名字 + */ + private String name; + + /** + * 路由地址 + */ + private String path; + + /** + * 是否隐藏路由,当设置 true 的时候该路由不会再侧边栏出现 + */ + private boolean hidden; + + /** + * 重定向地址,当设置 noRedirect 的时候该路由在面包屑导航中不可被点击 + */ + private String redirect; + + /** + * 组件地址 + */ + private String component; + + /** + * 路由参数:如 {"id": 1, "name": "ry"} + */ + private String query; + + /** + * 当你一个路由下面的 children 声明的路由大于1个时,自动会变成嵌套的模式--如组件页面 + */ + private Boolean alwaysShow; + + /** + * 其他元素 + */ + private MetaVo meta; + + /** + * 子路由 + */ + private List children; + + public String getName() + { + return name; + } + + public void setName(String name) + { + this.name = name; + } + + public String getPath() + { + return path; + } + + public void setPath(String path) + { + this.path = path; + } + + public boolean getHidden() + { + return hidden; + } + + public void setHidden(boolean hidden) + { + this.hidden = hidden; + } + + public String getRedirect() + { + return redirect; + } + + public void setRedirect(String redirect) + { + this.redirect = redirect; + } + + public String getComponent() + { + return component; + } + + public void setComponent(String component) + { + this.component = component; + } + + public String getQuery() + { + return query; + } + + public void setQuery(String query) + { + this.query = query; + } + + public Boolean getAlwaysShow() + { + return alwaysShow; + } + + public void setAlwaysShow(Boolean alwaysShow) + { + this.alwaysShow = alwaysShow; + } + + public MetaVo getMeta() + { + return meta; + } + + public void setMeta(MetaVo meta) + { + this.meta = meta; + } + + public List getChildren() + { + return children; + } + + public void setChildren(List children) + { + this.children = children; + } +} diff --git a/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/mapper/SysConfigMapper.java b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/mapper/SysConfigMapper.java new file mode 100644 index 0000000..fed448c --- /dev/null +++ b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/mapper/SysConfigMapper.java @@ -0,0 +1,76 @@ +package org.lingniu.portal.system.mapper; + +import java.util.List; +import org.lingniu.portal.system.domain.SysConfig; + +/** + * 参数配置 数据层 + * + * @author lingniu + */ +public interface SysConfigMapper +{ + /** + * 查询参数配置信息 + * + * @param config 参数配置信息 + * @return 参数配置信息 + */ + public SysConfig selectConfig(SysConfig config); + + /** + * 通过ID查询配置 + * + * @param configId 参数ID + * @return 参数配置信息 + */ + public SysConfig selectConfigById(Long configId); + + /** + * 查询参数配置列表 + * + * @param config 参数配置信息 + * @return 参数配置集合 + */ + public List selectConfigList(SysConfig config); + + /** + * 根据键名查询参数配置信息 + * + * @param configKey 参数键名 + * @return 参数配置信息 + */ + public SysConfig checkConfigKeyUnique(String configKey); + + /** + * 新增参数配置 + * + * @param config 参数配置信息 + * @return 结果 + */ + public int insertConfig(SysConfig config); + + /** + * 修改参数配置 + * + * @param config 参数配置信息 + * @return 结果 + */ + public int updateConfig(SysConfig config); + + /** + * 删除参数配置 + * + * @param configId 参数ID + * @return 结果 + */ + public int deleteConfigById(Long configId); + + /** + * 批量删除参数信息 + * + * @param configIds 需要删除的参数ID + * @return 结果 + */ + public int deleteConfigByIds(Long[] configIds); +} diff --git a/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/mapper/SysDeptMapper.java b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/mapper/SysDeptMapper.java new file mode 100644 index 0000000..860953d --- /dev/null +++ b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/mapper/SysDeptMapper.java @@ -0,0 +1,118 @@ +package org.lingniu.portal.system.mapper; + +import java.util.List; +import org.apache.ibatis.annotations.Param; +import org.lingniu.portal.common.core.domain.entity.SysDept; + +/** + * 部门管理 数据层 + * + * @author lingniu + */ +public interface SysDeptMapper +{ + /** + * 查询部门管理数据 + * + * @param dept 部门信息 + * @return 部门信息集合 + */ + public List selectDeptList(SysDept dept); + + /** + * 根据角色ID查询部门树信息 + * + * @param roleId 角色ID + * @param deptCheckStrictly 部门树选择项是否关联显示 + * @return 选中部门列表 + */ + public List selectDeptListByRoleId(@Param("roleId") Long roleId, @Param("deptCheckStrictly") boolean deptCheckStrictly); + + /** + * 根据部门ID查询信息 + * + * @param deptId 部门ID + * @return 部门信息 + */ + public SysDept selectDeptById(Long deptId); + + /** + * 根据ID查询所有子部门 + * + * @param deptId 部门ID + * @return 部门列表 + */ + public List selectChildrenDeptById(Long deptId); + + /** + * 根据ID查询所有子部门(正常状态) + * + * @param deptId 部门ID + * @return 子部门数 + */ + public int selectNormalChildrenDeptById(Long deptId); + + /** + * 是否存在子节点 + * + * @param deptId 部门ID + * @return 结果 + */ + public int hasChildByDeptId(Long deptId); + + /** + * 查询部门是否存在用户 + * + * @param deptId 部门ID + * @return 结果 + */ + public int checkDeptExistUser(Long deptId); + + /** + * 校验部门名称是否唯一 + * + * @param deptName 部门名称 + * @param parentId 父部门ID + * @return 结果 + */ + public SysDept checkDeptNameUnique(@Param("deptName") String deptName, @Param("parentId") Long parentId); + + /** + * 新增部门信息 + * + * @param dept 部门信息 + * @return 结果 + */ + public int insertDept(SysDept dept); + + /** + * 修改部门信息 + * + * @param dept 部门信息 + * @return 结果 + */ + public int updateDept(SysDept dept); + + /** + * 修改所在部门正常状态 + * + * @param deptIds 部门ID组 + */ + public void updateDeptStatusNormal(Long[] deptIds); + + /** + * 修改子元素关系 + * + * @param depts 子元素 + * @return 结果 + */ + public int updateDeptChildren(@Param("depts") List depts); + + /** + * 删除部门管理信息 + * + * @param deptId 部门ID + * @return 结果 + */ + public int deleteDeptById(Long deptId); +} diff --git a/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/mapper/SysDictDataMapper.java b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/mapper/SysDictDataMapper.java new file mode 100644 index 0000000..bd9fb77 --- /dev/null +++ b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/mapper/SysDictDataMapper.java @@ -0,0 +1,95 @@ +package org.lingniu.portal.system.mapper; + +import java.util.List; +import org.apache.ibatis.annotations.Param; +import org.lingniu.portal.common.core.domain.entity.SysDictData; + +/** + * 字典表 数据层 + * + * @author lingniu + */ +public interface SysDictDataMapper +{ + /** + * 根据条件分页查询字典数据 + * + * @param dictData 字典数据信息 + * @return 字典数据集合信息 + */ + public List selectDictDataList(SysDictData dictData); + + /** + * 根据字典类型查询字典数据 + * + * @param dictType 字典类型 + * @return 字典数据集合信息 + */ + public List selectDictDataByType(String dictType); + + /** + * 根据字典类型和字典键值查询字典数据信息 + * + * @param dictType 字典类型 + * @param dictValue 字典键值 + * @return 字典标签 + */ + public String selectDictLabel(@Param("dictType") String dictType, @Param("dictValue") String dictValue); + + /** + * 根据字典数据ID查询信息 + * + * @param dictCode 字典数据ID + * @return 字典数据 + */ + public SysDictData selectDictDataById(Long dictCode); + + /** + * 查询字典数据 + * + * @param dictType 字典类型 + * @return 字典数据 + */ + public int countDictDataByType(String dictType); + + /** + * 通过字典ID删除字典数据信息 + * + * @param dictCode 字典数据ID + * @return 结果 + */ + public int deleteDictDataById(Long dictCode); + + /** + * 批量删除字典数据信息 + * + * @param dictCodes 需要删除的字典数据ID + * @return 结果 + */ + public int deleteDictDataByIds(Long[] dictCodes); + + /** + * 新增字典数据信息 + * + * @param dictData 字典数据信息 + * @return 结果 + */ + public int insertDictData(SysDictData dictData); + + /** + * 修改字典数据信息 + * + * @param dictData 字典数据信息 + * @return 结果 + */ + public int updateDictData(SysDictData dictData); + + /** + * 同步修改字典类型 + * + * @param oldDictType 旧字典类型 + * @param newDictType 新旧字典类型 + * @return 结果 + */ + public int updateDictDataType(@Param("oldDictType") String oldDictType, @Param("newDictType") String newDictType); +} diff --git a/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/mapper/SysDictTypeMapper.java b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/mapper/SysDictTypeMapper.java new file mode 100644 index 0000000..c661ad1 --- /dev/null +++ b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/mapper/SysDictTypeMapper.java @@ -0,0 +1,83 @@ +package org.lingniu.portal.system.mapper; + +import java.util.List; +import org.lingniu.portal.common.core.domain.entity.SysDictType; + +/** + * 字典表 数据层 + * + * @author lingniu + */ +public interface SysDictTypeMapper +{ + /** + * 根据条件分页查询字典类型 + * + * @param dictType 字典类型信息 + * @return 字典类型集合信息 + */ + public List selectDictTypeList(SysDictType dictType); + + /** + * 根据所有字典类型 + * + * @return 字典类型集合信息 + */ + public List selectDictTypeAll(); + + /** + * 根据字典类型ID查询信息 + * + * @param dictId 字典类型ID + * @return 字典类型 + */ + public SysDictType selectDictTypeById(Long dictId); + + /** + * 根据字典类型查询信息 + * + * @param dictType 字典类型 + * @return 字典类型 + */ + public SysDictType selectDictTypeByType(String dictType); + + /** + * 通过字典ID删除字典信息 + * + * @param dictId 字典ID + * @return 结果 + */ + public int deleteDictTypeById(Long dictId); + + /** + * 批量删除字典类型信息 + * + * @param dictIds 需要删除的字典ID + * @return 结果 + */ + public int deleteDictTypeByIds(Long[] dictIds); + + /** + * 新增字典类型信息 + * + * @param dictType 字典类型信息 + * @return 结果 + */ + public int insertDictType(SysDictType dictType); + + /** + * 修改字典类型信息 + * + * @param dictType 字典类型信息 + * @return 结果 + */ + public int updateDictType(SysDictType dictType); + + /** + * 校验字典类型称是否唯一 + * + * @param dictType 字典类型 + * @return 结果 + */ + public SysDictType checkDictTypeUnique(String dictType); +} diff --git a/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/mapper/SysLogininforMapper.java b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/mapper/SysLogininforMapper.java new file mode 100644 index 0000000..cc5edc2 --- /dev/null +++ b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/mapper/SysLogininforMapper.java @@ -0,0 +1,42 @@ +package org.lingniu.portal.system.mapper; + +import java.util.List; +import org.lingniu.portal.system.domain.SysLogininfor; + +/** + * 系统访问日志情况信息 数据层 + * + * @author lingniu + */ +public interface SysLogininforMapper +{ + /** + * 新增系统登录日志 + * + * @param logininfor 访问日志对象 + */ + public void insertLogininfor(SysLogininfor logininfor); + + /** + * 查询系统登录日志集合 + * + * @param logininfor 访问日志对象 + * @return 登录记录集合 + */ + public List selectLogininforList(SysLogininfor logininfor); + + /** + * 批量删除系统登录日志 + * + * @param infoIds 需要删除的登录日志ID + * @return 结果 + */ + public int deleteLogininforByIds(Long[] infoIds); + + /** + * 清空系统登录日志 + * + * @return 结果 + */ + public int cleanLogininfor(); +} diff --git a/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/mapper/SysMenuMapper.java b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/mapper/SysMenuMapper.java new file mode 100644 index 0000000..4756528 --- /dev/null +++ b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/mapper/SysMenuMapper.java @@ -0,0 +1,134 @@ +package org.lingniu.portal.system.mapper; + +import java.util.List; +import org.apache.ibatis.annotations.Param; +import org.lingniu.portal.common.core.domain.entity.SysMenu; + +/** + * 菜单表 数据层 + * + * @author lingniu + */ +public interface SysMenuMapper +{ + /** + * 查询系统菜单列表 + * + * @param menu 菜单信息 + * @return 菜单列表 + */ + public List selectMenuList(SysMenu menu); + + /** + * 根据用户所有权限 + * + * @return 权限列表 + */ + public List selectMenuPerms(); + + /** + * 根据用户查询系统菜单列表 + * + * @param menu 菜单信息 + * @return 菜单列表 + */ + public List selectMenuListByUserId(SysMenu menu); + + /** + * 根据角色ID查询权限 + * + * @param roleId 角色ID + * @return 权限列表 + */ + public List selectMenuPermsByRoleId(Long roleId); + + /** + * 根据用户ID查询权限 + * + * @param userId 用户ID + * @return 权限列表 + */ + public List selectMenuPermsByUserId(Long userId); + + /** + * 根据用户ID查询菜单 + * + * @return 菜单列表 + */ + public List selectMenuTreeAll(); + + /** + * 根据用户ID查询菜单 + * + * @param userId 用户ID + * @return 菜单列表 + */ + public List selectMenuTreeByUserId(Long userId); + + /** + * 根据角色ID查询菜单树信息 + * + * @param roleId 角色ID + * @param menuCheckStrictly 菜单树选择项是否关联显示 + * @return 选中菜单列表 + */ + public List selectMenuListByRoleId(@Param("roleId") Long roleId, @Param("menuCheckStrictly") boolean menuCheckStrictly); + + /** + * 根据菜单ID查询信息 + * + * @param menuId 菜单ID + * @return 菜单信息 + */ + public SysMenu selectMenuById(Long menuId); + + /** + * 是否存在菜单子节点 + * + * @param menuId 菜单ID + * @return 结果 + */ + public int hasChildByMenuId(Long menuId); + + /** + * 新增菜单信息 + * + * @param menu 菜单信息 + * @return 结果 + */ + public int insertMenu(SysMenu menu); + + /** + * 修改菜单信息 + * + * @param menu 菜单信息 + * @return 结果 + */ + public int updateMenu(SysMenu menu); + + /** + * 删除菜单管理信息 + * + * @param menuId 菜单ID + * @return 结果 + */ + public int deleteMenuById(Long menuId); + + /** + * 校验菜单名称是否唯一 + * + * @param menuName 菜单名称 + * @param parentId 父菜单ID + * @return 结果 + */ + public SysMenu checkMenuNameUnique(@Param("menuName") String menuName, @Param("parentId") Long parentId); + + /** + * 根据路由路径或名称查询菜单信息(用于唯一性校验) + * + * @param path 路由地址 + * @param routeName 路由名称 + * @return 匹配的菜单列表 + */ + public List selectMenusByPathOrRouteName(@Param("path") String path, @Param("routeName") String routeName); +} diff --git a/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/mapper/SysNoticeMapper.java b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/mapper/SysNoticeMapper.java new file mode 100644 index 0000000..fd3e1bc --- /dev/null +++ b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/mapper/SysNoticeMapper.java @@ -0,0 +1,60 @@ +package org.lingniu.portal.system.mapper; + +import java.util.List; +import org.lingniu.portal.system.domain.SysNotice; + +/** + * 通知公告表 数据层 + * + * @author lingniu + */ +public interface SysNoticeMapper +{ + /** + * 查询公告信息 + * + * @param noticeId 公告ID + * @return 公告信息 + */ + public SysNotice selectNoticeById(Long noticeId); + + /** + * 查询公告列表 + * + * @param notice 公告信息 + * @return 公告集合 + */ + public List selectNoticeList(SysNotice notice); + + /** + * 新增公告 + * + * @param notice 公告信息 + * @return 结果 + */ + public int insertNotice(SysNotice notice); + + /** + * 修改公告 + * + * @param notice 公告信息 + * @return 结果 + */ + public int updateNotice(SysNotice notice); + + /** + * 批量删除公告 + * + * @param noticeId 公告ID + * @return 结果 + */ + public int deleteNoticeById(Long noticeId); + + /** + * 批量删除公告信息 + * + * @param noticeIds 需要删除的公告ID + * @return 结果 + */ + public int deleteNoticeByIds(Long[] noticeIds); +} diff --git a/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/mapper/SysOperLogMapper.java b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/mapper/SysOperLogMapper.java new file mode 100644 index 0000000..1a0c588 --- /dev/null +++ b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/mapper/SysOperLogMapper.java @@ -0,0 +1,48 @@ +package org.lingniu.portal.system.mapper; + +import java.util.List; +import org.lingniu.portal.system.domain.SysOperLog; + +/** + * 操作日志 数据层 + * + * @author lingniu + */ +public interface SysOperLogMapper +{ + /** + * 新增操作日志 + * + * @param operLog 操作日志对象 + */ + public void insertOperlog(SysOperLog operLog); + + /** + * 查询系统操作日志集合 + * + * @param operLog 操作日志对象 + * @return 操作日志集合 + */ + public List selectOperLogList(SysOperLog operLog); + + /** + * 批量删除系统操作日志 + * + * @param operIds 需要删除的操作日志ID + * @return 结果 + */ + public int deleteOperLogByIds(Long[] operIds); + + /** + * 查询操作日志详细 + * + * @param operId 操作ID + * @return 操作日志对象 + */ + public SysOperLog selectOperLogById(Long operId); + + /** + * 清空操作日志 + */ + public void cleanOperLog(); +} diff --git a/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/mapper/SysPostMapper.java b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/mapper/SysPostMapper.java new file mode 100644 index 0000000..ba75bb4 --- /dev/null +++ b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/mapper/SysPostMapper.java @@ -0,0 +1,99 @@ +package org.lingniu.portal.system.mapper; + +import java.util.List; +import org.lingniu.portal.system.domain.SysPost; + +/** + * 岗位信息 数据层 + * + * @author lingniu + */ +public interface SysPostMapper +{ + /** + * 查询岗位数据集合 + * + * @param post 岗位信息 + * @return 岗位数据集合 + */ + public List selectPostList(SysPost post); + + /** + * 查询所有岗位 + * + * @return 岗位列表 + */ + public List selectPostAll(); + + /** + * 通过岗位ID查询岗位信息 + * + * @param postId 岗位ID + * @return 角色对象信息 + */ + public SysPost selectPostById(Long postId); + + /** + * 根据用户ID获取岗位选择框列表 + * + * @param userId 用户ID + * @return 选中岗位ID列表 + */ + public List selectPostListByUserId(Long userId); + + /** + * 查询用户所属岗位组 + * + * @param userName 用户名 + * @return 结果 + */ + public List selectPostsByUserName(String userName); + + /** + * 删除岗位信息 + * + * @param postId 岗位ID + * @return 结果 + */ + public int deletePostById(Long postId); + + /** + * 批量删除岗位信息 + * + * @param postIds 需要删除的岗位ID + * @return 结果 + */ + public int deletePostByIds(Long[] postIds); + + /** + * 修改岗位信息 + * + * @param post 岗位信息 + * @return 结果 + */ + public int updatePost(SysPost post); + + /** + * 新增岗位信息 + * + * @param post 岗位信息 + * @return 结果 + */ + public int insertPost(SysPost post); + + /** + * 校验岗位名称 + * + * @param postName 岗位名称 + * @return 结果 + */ + public SysPost checkPostNameUnique(String postName); + + /** + * 校验岗位编码 + * + * @param postCode 岗位编码 + * @return 结果 + */ + public SysPost checkPostCodeUnique(String postCode); +} diff --git a/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/mapper/SysRoleDeptMapper.java b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/mapper/SysRoleDeptMapper.java new file mode 100644 index 0000000..656af9b --- /dev/null +++ b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/mapper/SysRoleDeptMapper.java @@ -0,0 +1,44 @@ +package org.lingniu.portal.system.mapper; + +import java.util.List; +import org.lingniu.portal.system.domain.SysRoleDept; + +/** + * 角色与部门关联表 数据层 + * + * @author lingniu + */ +public interface SysRoleDeptMapper +{ + /** + * 通过角色ID删除角色和部门关联 + * + * @param roleId 角色ID + * @return 结果 + */ + public int deleteRoleDeptByRoleId(Long roleId); + + /** + * 批量删除角色部门关联信息 + * + * @param ids 需要删除的数据ID + * @return 结果 + */ + public int deleteRoleDept(Long[] ids); + + /** + * 查询部门使用数量 + * + * @param deptId 部门ID + * @return 结果 + */ + public int selectCountRoleDeptByDeptId(Long deptId); + + /** + * 批量新增角色部门信息 + * + * @param roleDeptList 角色部门列表 + * @return 结果 + */ + public int batchRoleDept(List roleDeptList); +} diff --git a/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/mapper/SysRoleMapper.java b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/mapper/SysRoleMapper.java new file mode 100644 index 0000000..bc7e932 --- /dev/null +++ b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/mapper/SysRoleMapper.java @@ -0,0 +1,107 @@ +package org.lingniu.portal.system.mapper; + +import java.util.List; +import org.lingniu.portal.common.core.domain.entity.SysRole; + +/** + * 角色表 数据层 + * + * @author lingniu + */ +public interface SysRoleMapper +{ + /** + * 根据条件分页查询角色数据 + * + * @param role 角色信息 + * @return 角色数据集合信息 + */ + public List selectRoleList(SysRole role); + + /** + * 根据用户ID查询角色 + * + * @param userId 用户ID + * @return 角色列表 + */ + public List selectRolePermissionByUserId(Long userId); + + /** + * 查询所有角色 + * + * @return 角色列表 + */ + public List selectRoleAll(); + + /** + * 根据用户ID获取角色选择框列表 + * + * @param userId 用户ID + * @return 选中角色ID列表 + */ + public List selectRoleListByUserId(Long userId); + + /** + * 通过角色ID查询角色 + * + * @param roleId 角色ID + * @return 角色对象信息 + */ + public SysRole selectRoleById(Long roleId); + + /** + * 根据用户ID查询角色 + * + * @param userName 用户名 + * @return 角色列表 + */ + public List selectRolesByUserName(String userName); + + /** + * 校验角色名称是否唯一 + * + * @param roleName 角色名称 + * @return 角色信息 + */ + public SysRole checkRoleNameUnique(String roleName); + + /** + * 校验角色权限是否唯一 + * + * @param roleKey 角色权限 + * @return 角色信息 + */ + public SysRole checkRoleKeyUnique(String roleKey); + + /** + * 修改角色信息 + * + * @param role 角色信息 + * @return 结果 + */ + public int updateRole(SysRole role); + + /** + * 新增角色信息 + * + * @param role 角色信息 + * @return 结果 + */ + public int insertRole(SysRole role); + + /** + * 通过角色ID删除角色 + * + * @param roleId 角色ID + * @return 结果 + */ + public int deleteRoleById(Long roleId); + + /** + * 批量删除角色信息 + * + * @param roleIds 需要删除的角色ID + * @return 结果 + */ + public int deleteRoleByIds(Long[] roleIds); +} diff --git a/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/mapper/SysRoleMenuMapper.java b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/mapper/SysRoleMenuMapper.java new file mode 100644 index 0000000..3008e53 --- /dev/null +++ b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/mapper/SysRoleMenuMapper.java @@ -0,0 +1,44 @@ +package org.lingniu.portal.system.mapper; + +import java.util.List; +import org.lingniu.portal.system.domain.SysRoleMenu; + +/** + * 角色与菜单关联表 数据层 + * + * @author lingniu + */ +public interface SysRoleMenuMapper +{ + /** + * 查询菜单使用数量 + * + * @param menuId 菜单ID + * @return 结果 + */ + public int checkMenuExistRole(Long menuId); + + /** + * 通过角色ID删除角色和菜单关联 + * + * @param roleId 角色ID + * @return 结果 + */ + public int deleteRoleMenuByRoleId(Long roleId); + + /** + * 批量删除角色菜单关联信息 + * + * @param ids 需要删除的数据ID + * @return 结果 + */ + public int deleteRoleMenu(Long[] ids); + + /** + * 批量新增角色菜单信息 + * + * @param roleMenuList 角色菜单列表 + * @return 结果 + */ + public int batchRoleMenu(List roleMenuList); +} diff --git a/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/mapper/SysUserMapper.java b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/mapper/SysUserMapper.java new file mode 100644 index 0000000..a1e436c --- /dev/null +++ b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/mapper/SysUserMapper.java @@ -0,0 +1,147 @@ +package org.lingniu.portal.system.mapper; + +import java.util.Date; +import java.util.List; +import org.apache.ibatis.annotations.Param; +import org.lingniu.portal.common.core.domain.entity.SysUser; + +/** + * 用户表 数据层 + * + * @author lingniu + */ +public interface SysUserMapper +{ + /** + * 根据条件分页查询用户列表 + * + * @param sysUser 用户信息 + * @return 用户信息集合信息 + */ + public List selectUserList(SysUser sysUser); + + /** + * 根据条件分页查询已配用户角色列表 + * + * @param user 用户信息 + * @return 用户信息集合信息 + */ + public List selectAllocatedList(SysUser user); + + /** + * 根据条件分页查询未分配用户角色列表 + * + * @param user 用户信息 + * @return 用户信息集合信息 + */ + public List selectUnallocatedList(SysUser user); + + /** + * 通过用户名查询用户 + * + * @param userName 用户名 + * @return 用户对象信息 + */ + public SysUser selectUserByUserName(String userName); + + /** + * 通过用户ID查询用户 + * + * @param userId 用户ID + * @return 用户对象信息 + */ + public SysUser selectUserById(Long userId); + + /** + * 新增用户信息 + * + * @param user 用户信息 + * @return 结果 + */ + public int insertUser(SysUser user); + + /** + * 修改用户信息 + * + * @param user 用户信息 + * @return 结果 + */ + public int updateUser(SysUser user); + + /** + * 修改用户头像 + * + * @param userId 用户ID + * @param avatar 头像地址 + * @return 结果 + */ + public int updateUserAvatar(@Param("userId") Long userId, @Param("avatar") String avatar); + + /** + * 修改用户状态 + * + * @param userId 用户ID + * @param status 状态 + * @return 结果 + */ + public int updateUserStatus(@Param("userId") Long userId, @Param("status") String status); + + /** + * 更新用户登录信息(IP和登录时间) + * + * @param userId 用户ID + * @param loginIp 登录IP地址 + * @param loginDate 登录时间 + * @return 结果 + */ + public int updateLoginInfo(@Param("userId") Long userId, @Param("loginIp") String loginIp, @Param("loginDate") Date loginDate); + + /** + * 重置用户密码 + * + * @param userId 用户ID + * @param password 密码 + * @return 结果 + */ + public int resetUserPwd(@Param("userId") Long userId, @Param("password") String password); + + /** + * 通过用户ID删除用户 + * + * @param userId 用户ID + * @return 结果 + */ + public int deleteUserById(Long userId); + + /** + * 批量删除用户信息 + * + * @param userIds 需要删除的用户ID + * @return 结果 + */ + public int deleteUserByIds(Long[] userIds); + + /** + * 校验用户名称是否唯一 + * + * @param userName 用户名称 + * @return 结果 + */ + public SysUser checkUserNameUnique(String userName); + + /** + * 校验手机号码是否唯一 + * + * @param phonenumber 手机号码 + * @return 结果 + */ + public SysUser checkPhoneUnique(String phonenumber); + + /** + * 校验email是否唯一 + * + * @param email 用户邮箱 + * @return 结果 + */ + public SysUser checkEmailUnique(String email); +} diff --git a/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/mapper/SysUserPostMapper.java b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/mapper/SysUserPostMapper.java new file mode 100644 index 0000000..590f806 --- /dev/null +++ b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/mapper/SysUserPostMapper.java @@ -0,0 +1,44 @@ +package org.lingniu.portal.system.mapper; + +import java.util.List; +import org.lingniu.portal.system.domain.SysUserPost; + +/** + * 用户与岗位关联表 数据层 + * + * @author lingniu + */ +public interface SysUserPostMapper +{ + /** + * 通过用户ID删除用户和岗位关联 + * + * @param userId 用户ID + * @return 结果 + */ + public int deleteUserPostByUserId(Long userId); + + /** + * 通过岗位ID查询岗位使用数量 + * + * @param postId 岗位ID + * @return 结果 + */ + public int countUserPostById(Long postId); + + /** + * 批量删除用户和岗位关联 + * + * @param ids 需要删除的数据ID + * @return 结果 + */ + public int deleteUserPost(Long[] ids); + + /** + * 批量新增用户岗位信息 + * + * @param userPostList 用户岗位列表 + * @return 结果 + */ + public int batchUserPost(List userPostList); +} diff --git a/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/mapper/SysUserRoleMapper.java b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/mapper/SysUserRoleMapper.java new file mode 100644 index 0000000..172d4e8 --- /dev/null +++ b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/mapper/SysUserRoleMapper.java @@ -0,0 +1,62 @@ +package org.lingniu.portal.system.mapper; + +import java.util.List; +import org.apache.ibatis.annotations.Param; +import org.lingniu.portal.system.domain.SysUserRole; + +/** + * 用户与角色关联表 数据层 + * + * @author lingniu + */ +public interface SysUserRoleMapper +{ + /** + * 通过用户ID删除用户和角色关联 + * + * @param userId 用户ID + * @return 结果 + */ + public int deleteUserRoleByUserId(Long userId); + + /** + * 批量删除用户和角色关联 + * + * @param ids 需要删除的数据ID + * @return 结果 + */ + public int deleteUserRole(Long[] ids); + + /** + * 通过角色ID查询角色使用数量 + * + * @param roleId 角色ID + * @return 结果 + */ + public int countUserRoleByRoleId(Long roleId); + + /** + * 批量新增用户角色信息 + * + * @param userRoleList 用户角色列表 + * @return 结果 + */ + public int batchUserRole(List userRoleList); + + /** + * 删除用户和角色关联信息 + * + * @param userRole 用户和角色关联信息 + * @return 结果 + */ + public int deleteUserRoleInfo(SysUserRole userRole); + + /** + * 批量取消授权用户角色 + * + * @param roleId 角色ID + * @param userIds 需要删除的用户数据ID + * @return 结果 + */ + public int deleteUserRoleInfos(@Param("roleId") Long roleId, @Param("userIds") Long[] userIds); +} diff --git a/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/service/ISysConfigService.java b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/service/ISysConfigService.java new file mode 100644 index 0000000..68becb4 --- /dev/null +++ b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/service/ISysConfigService.java @@ -0,0 +1,89 @@ +package org.lingniu.portal.system.service; + +import java.util.List; +import org.lingniu.portal.system.domain.SysConfig; + +/** + * 参数配置 服务层 + * + * @author lingniu + */ +public interface ISysConfigService +{ + /** + * 查询参数配置信息 + * + * @param configId 参数配置ID + * @return 参数配置信息 + */ + public SysConfig selectConfigById(Long configId); + + /** + * 根据键名查询参数配置信息 + * + * @param configKey 参数键名 + * @return 参数键值 + */ + public String selectConfigByKey(String configKey); + + /** + * 获取验证码开关 + * + * @return true开启,false关闭 + */ + public boolean selectCaptchaEnabled(); + + /** + * 查询参数配置列表 + * + * @param config 参数配置信息 + * @return 参数配置集合 + */ + public List selectConfigList(SysConfig config); + + /** + * 新增参数配置 + * + * @param config 参数配置信息 + * @return 结果 + */ + public int insertConfig(SysConfig config); + + /** + * 修改参数配置 + * + * @param config 参数配置信息 + * @return 结果 + */ + public int updateConfig(SysConfig config); + + /** + * 批量删除参数信息 + * + * @param configIds 需要删除的参数ID + */ + public void deleteConfigByIds(Long[] configIds); + + /** + * 加载参数缓存数据 + */ + public void loadingConfigCache(); + + /** + * 清空参数缓存数据 + */ + public void clearConfigCache(); + + /** + * 重置参数缓存数据 + */ + public void resetConfigCache(); + + /** + * 校验参数键名是否唯一 + * + * @param config 参数信息 + * @return 结果 + */ + public boolean checkConfigKeyUnique(SysConfig config); +} diff --git a/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/service/ISysDeptService.java b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/service/ISysDeptService.java new file mode 100644 index 0000000..a2e5386 --- /dev/null +++ b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/service/ISysDeptService.java @@ -0,0 +1,124 @@ +package org.lingniu.portal.system.service; + +import java.util.List; +import org.lingniu.portal.common.core.domain.TreeSelect; +import org.lingniu.portal.common.core.domain.entity.SysDept; + +/** + * 部门管理 服务层 + * + * @author lingniu + */ +public interface ISysDeptService +{ + /** + * 查询部门管理数据 + * + * @param dept 部门信息 + * @return 部门信息集合 + */ + public List selectDeptList(SysDept dept); + + /** + * 查询部门树结构信息 + * + * @param dept 部门信息 + * @return 部门树信息集合 + */ + public List selectDeptTreeList(SysDept dept); + + /** + * 构建前端所需要树结构 + * + * @param depts 部门列表 + * @return 树结构列表 + */ + public List buildDeptTree(List depts); + + /** + * 构建前端所需要下拉树结构 + * + * @param depts 部门列表 + * @return 下拉树结构列表 + */ + public List buildDeptTreeSelect(List depts); + + /** + * 根据角色ID查询部门树信息 + * + * @param roleId 角色ID + * @return 选中部门列表 + */ + public List selectDeptListByRoleId(Long roleId); + + /** + * 根据部门ID查询信息 + * + * @param deptId 部门ID + * @return 部门信息 + */ + public SysDept selectDeptById(Long deptId); + + /** + * 根据ID查询所有子部门(正常状态) + * + * @param deptId 部门ID + * @return 子部门数 + */ + public int selectNormalChildrenDeptById(Long deptId); + + /** + * 是否存在部门子节点 + * + * @param deptId 部门ID + * @return 结果 + */ + public boolean hasChildByDeptId(Long deptId); + + /** + * 查询部门是否存在用户 + * + * @param deptId 部门ID + * @return 结果 true 存在 false 不存在 + */ + public boolean checkDeptExistUser(Long deptId); + + /** + * 校验部门名称是否唯一 + * + * @param dept 部门信息 + * @return 结果 + */ + public boolean checkDeptNameUnique(SysDept dept); + + /** + * 校验部门是否有数据权限 + * + * @param deptId 部门id + */ + public void checkDeptDataScope(Long deptId); + + /** + * 新增保存部门信息 + * + * @param dept 部门信息 + * @return 结果 + */ + public int insertDept(SysDept dept); + + /** + * 修改保存部门信息 + * + * @param dept 部门信息 + * @return 结果 + */ + public int updateDept(SysDept dept); + + /** + * 删除部门管理信息 + * + * @param deptId 部门ID + * @return 结果 + */ + public int deleteDeptById(Long deptId); +} diff --git a/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/service/ISysDictDataService.java b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/service/ISysDictDataService.java new file mode 100644 index 0000000..dfe9b0f --- /dev/null +++ b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/service/ISysDictDataService.java @@ -0,0 +1,60 @@ +package org.lingniu.portal.system.service; + +import java.util.List; +import org.lingniu.portal.common.core.domain.entity.SysDictData; + +/** + * 字典 业务层 + * + * @author lingniu + */ +public interface ISysDictDataService +{ + /** + * 根据条件分页查询字典数据 + * + * @param dictData 字典数据信息 + * @return 字典数据集合信息 + */ + public List selectDictDataList(SysDictData dictData); + + /** + * 根据字典类型和字典键值查询字典数据信息 + * + * @param dictType 字典类型 + * @param dictValue 字典键值 + * @return 字典标签 + */ + public String selectDictLabel(String dictType, String dictValue); + + /** + * 根据字典数据ID查询信息 + * + * @param dictCode 字典数据ID + * @return 字典数据 + */ + public SysDictData selectDictDataById(Long dictCode); + + /** + * 批量删除字典数据信息 + * + * @param dictCodes 需要删除的字典数据ID + */ + public void deleteDictDataByIds(Long[] dictCodes); + + /** + * 新增保存字典数据信息 + * + * @param dictData 字典数据信息 + * @return 结果 + */ + public int insertDictData(SysDictData dictData); + + /** + * 修改保存字典数据信息 + * + * @param dictData 字典数据信息 + * @return 结果 + */ + public int updateDictData(SysDictData dictData); +} diff --git a/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/service/ISysDictTypeService.java b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/service/ISysDictTypeService.java new file mode 100644 index 0000000..3d87115 --- /dev/null +++ b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/service/ISysDictTypeService.java @@ -0,0 +1,98 @@ +package org.lingniu.portal.system.service; + +import java.util.List; +import org.lingniu.portal.common.core.domain.entity.SysDictData; +import org.lingniu.portal.common.core.domain.entity.SysDictType; + +/** + * 字典 业务层 + * + * @author lingniu + */ +public interface ISysDictTypeService +{ + /** + * 根据条件分页查询字典类型 + * + * @param dictType 字典类型信息 + * @return 字典类型集合信息 + */ + public List selectDictTypeList(SysDictType dictType); + + /** + * 根据所有字典类型 + * + * @return 字典类型集合信息 + */ + public List selectDictTypeAll(); + + /** + * 根据字典类型查询字典数据 + * + * @param dictType 字典类型 + * @return 字典数据集合信息 + */ + public List selectDictDataByType(String dictType); + + /** + * 根据字典类型ID查询信息 + * + * @param dictId 字典类型ID + * @return 字典类型 + */ + public SysDictType selectDictTypeById(Long dictId); + + /** + * 根据字典类型查询信息 + * + * @param dictType 字典类型 + * @return 字典类型 + */ + public SysDictType selectDictTypeByType(String dictType); + + /** + * 批量删除字典信息 + * + * @param dictIds 需要删除的字典ID + */ + public void deleteDictTypeByIds(Long[] dictIds); + + /** + * 加载字典缓存数据 + */ + public void loadingDictCache(); + + /** + * 清空字典缓存数据 + */ + public void clearDictCache(); + + /** + * 重置字典缓存数据 + */ + public void resetDictCache(); + + /** + * 新增保存字典类型信息 + * + * @param dictType 字典类型信息 + * @return 结果 + */ + public int insertDictType(SysDictType dictType); + + /** + * 修改保存字典类型信息 + * + * @param dictType 字典类型信息 + * @return 结果 + */ + public int updateDictType(SysDictType dictType); + + /** + * 校验字典类型称是否唯一 + * + * @param dictType 字典类型 + * @return 结果 + */ + public boolean checkDictTypeUnique(SysDictType dictType); +} diff --git a/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/service/ISysLogininforService.java b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/service/ISysLogininforService.java new file mode 100644 index 0000000..c7da876 --- /dev/null +++ b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/service/ISysLogininforService.java @@ -0,0 +1,40 @@ +package org.lingniu.portal.system.service; + +import java.util.List; +import org.lingniu.portal.system.domain.SysLogininfor; + +/** + * 系统访问日志情况信息 服务层 + * + * @author lingniu + */ +public interface ISysLogininforService +{ + /** + * 新增系统登录日志 + * + * @param logininfor 访问日志对象 + */ + public void insertLogininfor(SysLogininfor logininfor); + + /** + * 查询系统登录日志集合 + * + * @param logininfor 访问日志对象 + * @return 登录记录集合 + */ + public List selectLogininforList(SysLogininfor logininfor); + + /** + * 批量删除系统登录日志 + * + * @param infoIds 需要删除的登录日志ID + * @return 结果 + */ + public int deleteLogininforByIds(Long[] infoIds); + + /** + * 清空系统登录日志 + */ + public void cleanLogininfor(); +} diff --git a/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/service/ISysMenuService.java b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/service/ISysMenuService.java new file mode 100644 index 0000000..9d01435 --- /dev/null +++ b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/service/ISysMenuService.java @@ -0,0 +1,152 @@ +package org.lingniu.portal.system.service; + +import java.util.List; +import java.util.Set; +import org.lingniu.portal.common.core.domain.TreeSelect; +import org.lingniu.portal.common.core.domain.entity.SysMenu; +import org.lingniu.portal.system.domain.vo.RouterVo; + +/** + * 菜单 业务层 + * + * @author lingniu + */ +public interface ISysMenuService +{ + /** + * 根据用户查询系统菜单列表 + * + * @param userId 用户ID + * @return 菜单列表 + */ + public List selectMenuList(Long userId); + + /** + * 根据用户查询系统菜单列表 + * + * @param menu 菜单信息 + * @param userId 用户ID + * @return 菜单列表 + */ + public List selectMenuList(SysMenu menu, Long userId); + + /** + * 根据用户ID查询权限 + * + * @param userId 用户ID + * @return 权限列表 + */ + public Set selectMenuPermsByUserId(Long userId); + + /** + * 根据角色ID查询权限 + * + * @param roleId 角色ID + * @return 权限列表 + */ + public Set selectMenuPermsByRoleId(Long roleId); + + /** + * 根据用户ID查询菜单树信息 + * + * @param userId 用户ID + * @return 菜单列表 + */ + public List selectMenuTreeByUserId(Long userId); + + /** + * 根据角色ID查询菜单树信息 + * + * @param roleId 角色ID + * @return 选中菜单列表 + */ + public List selectMenuListByRoleId(Long roleId); + + /** + * 构建前端路由所需要的菜单 + * + * @param menus 菜单列表 + * @return 路由列表 + */ + public List buildMenus(List menus); + + /** + * 构建前端所需要树结构 + * + * @param menus 菜单列表 + * @return 树结构列表 + */ + public List buildMenuTree(List menus); + + /** + * 构建前端所需要下拉树结构 + * + * @param menus 菜单列表 + * @return 下拉树结构列表 + */ + public List buildMenuTreeSelect(List menus); + + /** + * 根据菜单ID查询信息 + * + * @param menuId 菜单ID + * @return 菜单信息 + */ + public SysMenu selectMenuById(Long menuId); + + /** + * 是否存在菜单子节点 + * + * @param menuId 菜单ID + * @return 结果 true 存在 false 不存在 + */ + public boolean hasChildByMenuId(Long menuId); + + /** + * 查询菜单是否存在角色 + * + * @param menuId 菜单ID + * @return 结果 true 存在 false 不存在 + */ + public boolean checkMenuExistRole(Long menuId); + + /** + * 新增保存菜单信息 + * + * @param menu 菜单信息 + * @return 结果 + */ + public int insertMenu(SysMenu menu); + + /** + * 修改保存菜单信息 + * + * @param menu 菜单信息 + * @return 结果 + */ + public int updateMenu(SysMenu menu); + + /** + * 删除菜单管理信息 + * + * @param menuId 菜单ID + * @return 结果 + */ + public int deleteMenuById(Long menuId); + + /** + * 校验菜单名称是否唯一 + * + * @param menu 菜单信息 + * @return 结果 + */ + public boolean checkMenuNameUnique(SysMenu menu); + + /** + * 校验路由组合是否唯一 + * + * @param menu 菜单信息 + * @return 结果 + */ + public boolean checkRouteConfigUnique(SysMenu menu); +} diff --git a/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/service/ISysNoticeService.java b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/service/ISysNoticeService.java new file mode 100644 index 0000000..afcd18c --- /dev/null +++ b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/service/ISysNoticeService.java @@ -0,0 +1,60 @@ +package org.lingniu.portal.system.service; + +import java.util.List; +import org.lingniu.portal.system.domain.SysNotice; + +/** + * 公告 服务层 + * + * @author lingniu + */ +public interface ISysNoticeService +{ + /** + * 查询公告信息 + * + * @param noticeId 公告ID + * @return 公告信息 + */ + public SysNotice selectNoticeById(Long noticeId); + + /** + * 查询公告列表 + * + * @param notice 公告信息 + * @return 公告集合 + */ + public List selectNoticeList(SysNotice notice); + + /** + * 新增公告 + * + * @param notice 公告信息 + * @return 结果 + */ + public int insertNotice(SysNotice notice); + + /** + * 修改公告 + * + * @param notice 公告信息 + * @return 结果 + */ + public int updateNotice(SysNotice notice); + + /** + * 删除公告信息 + * + * @param noticeId 公告ID + * @return 结果 + */ + public int deleteNoticeById(Long noticeId); + + /** + * 批量删除公告信息 + * + * @param noticeIds 需要删除的公告ID + * @return 结果 + */ + public int deleteNoticeByIds(Long[] noticeIds); +} diff --git a/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/service/ISysOperLogService.java b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/service/ISysOperLogService.java new file mode 100644 index 0000000..f03625c --- /dev/null +++ b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/service/ISysOperLogService.java @@ -0,0 +1,48 @@ +package org.lingniu.portal.system.service; + +import java.util.List; +import org.lingniu.portal.system.domain.SysOperLog; + +/** + * 操作日志 服务层 + * + * @author lingniu + */ +public interface ISysOperLogService +{ + /** + * 新增操作日志 + * + * @param operLog 操作日志对象 + */ + public void insertOperlog(SysOperLog operLog); + + /** + * 查询系统操作日志集合 + * + * @param operLog 操作日志对象 + * @return 操作日志集合 + */ + public List selectOperLogList(SysOperLog operLog); + + /** + * 批量删除系统操作日志 + * + * @param operIds 需要删除的操作日志ID + * @return 结果 + */ + public int deleteOperLogByIds(Long[] operIds); + + /** + * 查询操作日志详细 + * + * @param operId 操作ID + * @return 操作日志对象 + */ + public SysOperLog selectOperLogById(Long operId); + + /** + * 清空操作日志 + */ + public void cleanOperLog(); +} diff --git a/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/service/ISysPostService.java b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/service/ISysPostService.java new file mode 100644 index 0000000..1610a6d --- /dev/null +++ b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/service/ISysPostService.java @@ -0,0 +1,99 @@ +package org.lingniu.portal.system.service; + +import java.util.List; +import org.lingniu.portal.system.domain.SysPost; + +/** + * 岗位信息 服务层 + * + * @author lingniu + */ +public interface ISysPostService +{ + /** + * 查询岗位信息集合 + * + * @param post 岗位信息 + * @return 岗位列表 + */ + public List selectPostList(SysPost post); + + /** + * 查询所有岗位 + * + * @return 岗位列表 + */ + public List selectPostAll(); + + /** + * 通过岗位ID查询岗位信息 + * + * @param postId 岗位ID + * @return 角色对象信息 + */ + public SysPost selectPostById(Long postId); + + /** + * 根据用户ID获取岗位选择框列表 + * + * @param userId 用户ID + * @return 选中岗位ID列表 + */ + public List selectPostListByUserId(Long userId); + + /** + * 校验岗位名称 + * + * @param post 岗位信息 + * @return 结果 + */ + public boolean checkPostNameUnique(SysPost post); + + /** + * 校验岗位编码 + * + * @param post 岗位信息 + * @return 结果 + */ + public boolean checkPostCodeUnique(SysPost post); + + /** + * 通过岗位ID查询岗位使用数量 + * + * @param postId 岗位ID + * @return 结果 + */ + public int countUserPostById(Long postId); + + /** + * 删除岗位信息 + * + * @param postId 岗位ID + * @return 结果 + */ + public int deletePostById(Long postId); + + /** + * 批量删除岗位信息 + * + * @param postIds 需要删除的岗位ID + * @return 结果 + */ + public int deletePostByIds(Long[] postIds); + + /** + * 新增保存岗位信息 + * + * @param post 岗位信息 + * @return 结果 + */ + public int insertPost(SysPost post); + + /** + * 修改保存岗位信息 + * + * @param post 岗位信息 + * @return 结果 + */ + public int updatePost(SysPost post); +} diff --git a/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/service/ISysRoleService.java b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/service/ISysRoleService.java new file mode 100644 index 0000000..0b57dd0 --- /dev/null +++ b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/service/ISysRoleService.java @@ -0,0 +1,173 @@ +package org.lingniu.portal.system.service; + +import java.util.List; +import java.util.Set; +import org.lingniu.portal.common.core.domain.entity.SysRole; +import org.lingniu.portal.system.domain.SysUserRole; + +/** + * 角色业务层 + * + * @author lingniu + */ +public interface ISysRoleService +{ + /** + * 根据条件分页查询角色数据 + * + * @param role 角色信息 + * @return 角色数据集合信息 + */ + public List selectRoleList(SysRole role); + + /** + * 根据用户ID查询角色列表 + * + * @param userId 用户ID + * @return 角色列表 + */ + public List selectRolesByUserId(Long userId); + + /** + * 根据用户ID查询角色权限 + * + * @param userId 用户ID + * @return 权限列表 + */ + public Set selectRolePermissionByUserId(Long userId); + + /** + * 查询所有角色 + * + * @return 角色列表 + */ + public List selectRoleAll(); + + /** + * 根据用户ID获取角色选择框列表 + * + * @param userId 用户ID + * @return 选中角色ID列表 + */ + public List selectRoleListByUserId(Long userId); + + /** + * 通过角色ID查询角色 + * + * @param roleId 角色ID + * @return 角色对象信息 + */ + public SysRole selectRoleById(Long roleId); + + /** + * 校验角色名称是否唯一 + * + * @param role 角色信息 + * @return 结果 + */ + public boolean checkRoleNameUnique(SysRole role); + + /** + * 校验角色权限是否唯一 + * + * @param role 角色信息 + * @return 结果 + */ + public boolean checkRoleKeyUnique(SysRole role); + + /** + * 校验角色是否允许操作 + * + * @param role 角色信息 + */ + public void checkRoleAllowed(SysRole role); + + /** + * 校验角色是否有数据权限 + * + * @param roleIds 角色id + */ + public void checkRoleDataScope(Long... roleIds); + + /** + * 通过角色ID查询角色使用数量 + * + * @param roleId 角色ID + * @return 结果 + */ + public int countUserRoleByRoleId(Long roleId); + + /** + * 新增保存角色信息 + * + * @param role 角色信息 + * @return 结果 + */ + public int insertRole(SysRole role); + + /** + * 修改保存角色信息 + * + * @param role 角色信息 + * @return 结果 + */ + public int updateRole(SysRole role); + + /** + * 修改角色状态 + * + * @param role 角色信息 + * @return 结果 + */ + public int updateRoleStatus(SysRole role); + + /** + * 修改数据权限信息 + * + * @param role 角色信息 + * @return 结果 + */ + public int authDataScope(SysRole role); + + /** + * 通过角色ID删除角色 + * + * @param roleId 角色ID + * @return 结果 + */ + public int deleteRoleById(Long roleId); + + /** + * 批量删除角色信息 + * + * @param roleIds 需要删除的角色ID + * @return 结果 + */ + public int deleteRoleByIds(Long[] roleIds); + + /** + * 取消授权用户角色 + * + * @param userRole 用户和角色关联信息 + * @return 结果 + */ + public int deleteAuthUser(SysUserRole userRole); + + /** + * 批量取消授权用户角色 + * + * @param roleId 角色ID + * @param userIds 需要取消授权的用户数据ID + * @return 结果 + */ + public int deleteAuthUsers(Long roleId, Long[] userIds); + + /** + * 批量选择授权用户角色 + * + * @param roleId 角色ID + * @param userIds 需要删除的用户数据ID + * @return 结果 + */ + public int insertAuthUsers(Long roleId, Long[] userIds); +} diff --git a/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/service/ISysUserOnlineService.java b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/service/ISysUserOnlineService.java new file mode 100644 index 0000000..fc01513 --- /dev/null +++ b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/service/ISysUserOnlineService.java @@ -0,0 +1,48 @@ +package org.lingniu.portal.system.service; + +import org.lingniu.portal.common.core.domain.model.LoginUser; +import org.lingniu.portal.system.domain.SysUserOnline; + +/** + * 在线用户 服务层 + * + * @author lingniu + */ +public interface ISysUserOnlineService +{ + /** + * 通过登录地址查询信息 + * + * @param ipaddr 登录地址 + * @param user 用户信息 + * @return 在线用户信息 + */ + public SysUserOnline selectOnlineByIpaddr(String ipaddr, LoginUser user); + + /** + * 通过用户名称查询信息 + * + * @param userName 用户名称 + * @param user 用户信息 + * @return 在线用户信息 + */ + public SysUserOnline selectOnlineByUserName(String userName, LoginUser user); + + /** + * 通过登录地址/用户名称查询信息 + * + * @param ipaddr 登录地址 + * @param userName 用户名称 + * @param user 用户信息 + * @return 在线用户信息 + */ + public SysUserOnline selectOnlineByInfo(String ipaddr, String userName, LoginUser user); + + /** + * 设置在线用户信息 + * + * @param user 用户信息 + * @return 在线用户 + */ + public SysUserOnline loginUserToUserOnline(LoginUser user); +} diff --git a/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/service/ISysUserService.java b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/service/ISysUserService.java new file mode 100644 index 0000000..b99b8b0 --- /dev/null +++ b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/service/ISysUserService.java @@ -0,0 +1,217 @@ +package org.lingniu.portal.system.service; + +import java.util.Date; +import java.util.List; +import org.lingniu.portal.common.core.domain.entity.SysUser; + +/** + * 用户 业务层 + * + * @author lingniu + */ +public interface ISysUserService +{ + /** + * 根据条件分页查询用户列表 + * + * @param user 用户信息 + * @return 用户信息集合信息 + */ + public List selectUserList(SysUser user); + + /** + * 根据条件分页查询已分配用户角色列表 + * + * @param user 用户信息 + * @return 用户信息集合信息 + */ + public List selectAllocatedList(SysUser user); + + /** + * 根据条件分页查询未分配用户角色列表 + * + * @param user 用户信息 + * @return 用户信息集合信息 + */ + public List selectUnallocatedList(SysUser user); + + /** + * 通过用户名查询用户 + * + * @param userName 用户名 + * @return 用户对象信息 + */ + public SysUser selectUserByUserName(String userName); + + /** + * 通过用户ID查询用户 + * + * @param userId 用户ID + * @return 用户对象信息 + */ + public SysUser selectUserById(Long userId); + + /** + * 根据用户ID查询用户所属角色组 + * + * @param userName 用户名 + * @return 结果 + */ + public String selectUserRoleGroup(String userName); + + /** + * 根据用户ID查询用户所属岗位组 + * + * @param userName 用户名 + * @return 结果 + */ + public String selectUserPostGroup(String userName); + + /** + * 校验用户名称是否唯一 + * + * @param user 用户信息 + * @return 结果 + */ + public boolean checkUserNameUnique(SysUser user); + + /** + * 校验手机号码是否唯一 + * + * @param user 用户信息 + * @return 结果 + */ + public boolean checkPhoneUnique(SysUser user); + + /** + * 校验email是否唯一 + * + * @param user 用户信息 + * @return 结果 + */ + public boolean checkEmailUnique(SysUser user); + + /** + * 校验用户是否允许操作 + * + * @param user 用户信息 + */ + public void checkUserAllowed(SysUser user); + + /** + * 校验用户是否有数据权限 + * + * @param userId 用户id + */ + public void checkUserDataScope(Long userId); + + /** + * 新增用户信息 + * + * @param user 用户信息 + * @return 结果 + */ + public int insertUser(SysUser user); + + /** + * 注册用户信息 + * + * @param user 用户信息 + * @return 结果 + */ + public boolean registerUser(SysUser user); + + /** + * 修改用户信息 + * + * @param user 用户信息 + * @return 结果 + */ + public int updateUser(SysUser user); + + /** + * 用户授权角色 + * + * @param userId 用户ID + * @param roleIds 角色组 + */ + public void insertUserAuth(Long userId, Long[] roleIds); + + /** + * 修改用户状态 + * + * @param user 用户信息 + * @return 结果 + */ + public int updateUserStatus(SysUser user); + + /** + * 修改用户基本信息 + * + * @param user 用户信息 + * @return 结果 + */ + public int updateUserProfile(SysUser user); + + /** + * 修改用户头像 + * + * @param userId 用户ID + * @param avatar 头像地址 + * @return 结果 + */ + public boolean updateUserAvatar(Long userId, String avatar); + + /** + * 更新用户登录信息(IP和登录时间) + * + * @param userId 用户ID + * @param loginIp 登录IP地址 + * @param loginDate 登录时间 + * @return 结果 + */ + public void updateLoginInfo(Long userId, String loginIp, Date loginDate); + + /** + * 重置用户密码 + * + * @param user 用户信息 + * @return 结果 + */ + public int resetPwd(SysUser user); + + /** + * 重置用户密码 + * + * @param userId 用户ID + * @param password 密码 + * @return 结果 + */ + public int resetUserPwd(Long userId, String password); + + /** + * 通过用户ID删除用户 + * + * @param userId 用户ID + * @return 结果 + */ + public int deleteUserById(Long userId); + + /** + * 批量删除用户信息 + * + * @param userIds 需要删除的用户ID + * @return 结果 + */ + public int deleteUserByIds(Long[] userIds); + + /** + * 导入用户数据 + * + * @param userList 用户数据列表 + * @param isUpdateSupport 是否更新支持,如果已存在,则进行更新数据 + * @param operName 操作用户 + * @return 结果 + */ + public String importUser(List userList, Boolean isUpdateSupport, String operName); +} diff --git a/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/service/impl/SysConfigServiceImpl.java b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/service/impl/SysConfigServiceImpl.java new file mode 100644 index 0000000..6fee006 --- /dev/null +++ b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/service/impl/SysConfigServiceImpl.java @@ -0,0 +1,232 @@ +package org.lingniu.portal.system.service.impl; + +import java.util.Collection; +import java.util.List; +import jakarta.annotation.PostConstruct; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.lingniu.portal.common.annotation.DataSource; +import org.lingniu.portal.common.constant.CacheConstants; +import org.lingniu.portal.common.constant.UserConstants; +import org.lingniu.portal.common.core.redis.RedisCache; +import org.lingniu.portal.common.core.text.Convert; +import org.lingniu.portal.common.enums.DataSourceType; +import org.lingniu.portal.common.exception.ServiceException; +import org.lingniu.portal.common.utils.StringUtils; +import org.lingniu.portal.system.domain.SysConfig; +import org.lingniu.portal.system.mapper.SysConfigMapper; +import org.lingniu.portal.system.service.ISysConfigService; + +/** + * 参数配置 服务层实现 + * + * @author lingniu + */ +@Service +public class SysConfigServiceImpl implements ISysConfigService +{ + @Autowired + private SysConfigMapper configMapper; + + @Autowired + private RedisCache redisCache; + + /** + * 项目启动时,初始化参数到缓存 + */ + @PostConstruct + public void init() + { + loadingConfigCache(); + } + + /** + * 查询参数配置信息 + * + * @param configId 参数配置ID + * @return 参数配置信息 + */ + @Override + @DataSource(DataSourceType.MASTER) + public SysConfig selectConfigById(Long configId) + { + SysConfig config = new SysConfig(); + config.setConfigId(configId); + return configMapper.selectConfig(config); + } + + /** + * 根据键名查询参数配置信息 + * + * @param configKey 参数key + * @return 参数键值 + */ + @Override + public String selectConfigByKey(String configKey) + { + String configValue = Convert.toStr(redisCache.getCacheObject(getCacheKey(configKey))); + if (StringUtils.isNotEmpty(configValue)) + { + return configValue; + } + SysConfig config = new SysConfig(); + config.setConfigKey(configKey); + SysConfig retConfig = configMapper.selectConfig(config); + if (StringUtils.isNotNull(retConfig)) + { + redisCache.setCacheObject(getCacheKey(configKey), retConfig.getConfigValue()); + return retConfig.getConfigValue(); + } + return StringUtils.EMPTY; + } + + /** + * 获取验证码开关 + * + * @return true开启,false关闭 + */ + @Override + public boolean selectCaptchaEnabled() + { + String captchaEnabled = selectConfigByKey("sys.account.captchaEnabled"); + if (StringUtils.isEmpty(captchaEnabled)) + { + return true; + } + return Convert.toBool(captchaEnabled); + } + + /** + * 查询参数配置列表 + * + * @param config 参数配置信息 + * @return 参数配置集合 + */ + @Override + public List selectConfigList(SysConfig config) + { + return configMapper.selectConfigList(config); + } + + /** + * 新增参数配置 + * + * @param config 参数配置信息 + * @return 结果 + */ + @Override + public int insertConfig(SysConfig config) + { + int row = configMapper.insertConfig(config); + if (row > 0) + { + redisCache.setCacheObject(getCacheKey(config.getConfigKey()), config.getConfigValue()); + } + return row; + } + + /** + * 修改参数配置 + * + * @param config 参数配置信息 + * @return 结果 + */ + @Override + public int updateConfig(SysConfig config) + { + SysConfig temp = configMapper.selectConfigById(config.getConfigId()); + if (!StringUtils.equals(temp.getConfigKey(), config.getConfigKey())) + { + redisCache.deleteObject(getCacheKey(temp.getConfigKey())); + } + + int row = configMapper.updateConfig(config); + if (row > 0) + { + redisCache.setCacheObject(getCacheKey(config.getConfigKey()), config.getConfigValue()); + } + return row; + } + + /** + * 批量删除参数信息 + * + * @param configIds 需要删除的参数ID + */ + @Override + public void deleteConfigByIds(Long[] configIds) + { + for (Long configId : configIds) + { + SysConfig config = selectConfigById(configId); + if (StringUtils.equals(UserConstants.YES, config.getConfigType())) + { + throw new ServiceException(String.format("内置参数【%1$s】不能删除 ", config.getConfigKey())); + } + configMapper.deleteConfigById(configId); + redisCache.deleteObject(getCacheKey(config.getConfigKey())); + } + } + + /** + * 加载参数缓存数据 + */ + @Override + public void loadingConfigCache() + { + List configsList = configMapper.selectConfigList(new SysConfig()); + for (SysConfig config : configsList) + { + redisCache.setCacheObject(getCacheKey(config.getConfigKey()), config.getConfigValue()); + } + } + + /** + * 清空参数缓存数据 + */ + @Override + public void clearConfigCache() + { + Collection keys = redisCache.keys(CacheConstants.SYS_CONFIG_KEY + "*"); + redisCache.deleteObject(keys); + } + + /** + * 重置参数缓存数据 + */ + @Override + public void resetConfigCache() + { + clearConfigCache(); + loadingConfigCache(); + } + + /** + * 校验参数键名是否唯一 + * + * @param config 参数配置信息 + * @return 结果 + */ + @Override + public boolean checkConfigKeyUnique(SysConfig config) + { + Long configId = StringUtils.isNull(config.getConfigId()) ? -1L : config.getConfigId(); + SysConfig info = configMapper.checkConfigKeyUnique(config.getConfigKey()); + if (StringUtils.isNotNull(info) && info.getConfigId().longValue() != configId.longValue()) + { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 设置cache key + * + * @param configKey 参数键 + * @return 缓存键key + */ + private String getCacheKey(String configKey) + { + return CacheConstants.SYS_CONFIG_KEY + configKey; + } +} diff --git a/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/service/impl/SysDeptServiceImpl.java b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/service/impl/SysDeptServiceImpl.java new file mode 100644 index 0000000..f899ce4 --- /dev/null +++ b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/service/impl/SysDeptServiceImpl.java @@ -0,0 +1,337 @@ +package org.lingniu.portal.system.service.impl; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.stream.Collectors; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.lingniu.portal.common.annotation.DataScope; +import org.lingniu.portal.common.constant.UserConstants; +import org.lingniu.portal.common.core.domain.TreeSelect; +import org.lingniu.portal.common.core.domain.entity.SysDept; +import org.lingniu.portal.common.core.domain.entity.SysRole; +import org.lingniu.portal.common.core.text.Convert; +import org.lingniu.portal.common.exception.ServiceException; +import org.lingniu.portal.common.utils.SecurityUtils; +import org.lingniu.portal.common.utils.StringUtils; +import org.lingniu.portal.common.utils.spring.SpringUtils; +import org.lingniu.portal.system.mapper.SysDeptMapper; +import org.lingniu.portal.system.mapper.SysRoleMapper; +import org.lingniu.portal.system.service.ISysDeptService; + +/** + * 部门管理 服务实现 + * + * @author lingniu + */ +@Service +public class SysDeptServiceImpl implements ISysDeptService +{ + @Autowired + private SysDeptMapper deptMapper; + + @Autowired + private SysRoleMapper roleMapper; + + /** + * 查询部门管理数据 + * + * @param dept 部门信息 + * @return 部门信息集合 + */ + @Override + @DataScope(deptAlias = "d") + public List selectDeptList(SysDept dept) + { + return deptMapper.selectDeptList(dept); + } + + /** + * 查询部门树结构信息 + * + * @param dept 部门信息 + * @return 部门树信息集合 + */ + @Override + public List selectDeptTreeList(SysDept dept) + { + List depts = SpringUtils.getAopProxy(this).selectDeptList(dept); + return buildDeptTreeSelect(depts); + } + + /** + * 构建前端所需要树结构 + * + * @param depts 部门列表 + * @return 树结构列表 + */ + @Override + public List buildDeptTree(List depts) + { + List returnList = new ArrayList(); + List tempList = depts.stream().map(SysDept::getDeptId).collect(Collectors.toList()); + for (SysDept dept : depts) + { + // 如果是顶级节点, 遍历该父节点的所有子节点 + if (!tempList.contains(dept.getParentId())) + { + recursionFn(depts, dept); + returnList.add(dept); + } + } + if (returnList.isEmpty()) + { + returnList = depts; + } + return returnList; + } + + /** + * 构建前端所需要下拉树结构 + * + * @param depts 部门列表 + * @return 下拉树结构列表 + */ + @Override + public List buildDeptTreeSelect(List depts) + { + List deptTrees = buildDeptTree(depts); + return deptTrees.stream().map(TreeSelect::new).collect(Collectors.toList()); + } + + /** + * 根据角色ID查询部门树信息 + * + * @param roleId 角色ID + * @return 选中部门列表 + */ + @Override + public List selectDeptListByRoleId(Long roleId) + { + SysRole role = roleMapper.selectRoleById(roleId); + return deptMapper.selectDeptListByRoleId(roleId, role.isDeptCheckStrictly()); + } + + /** + * 根据部门ID查询信息 + * + * @param deptId 部门ID + * @return 部门信息 + */ + @Override + public SysDept selectDeptById(Long deptId) + { + return deptMapper.selectDeptById(deptId); + } + + /** + * 根据ID查询所有子部门(正常状态) + * + * @param deptId 部门ID + * @return 子部门数 + */ + @Override + public int selectNormalChildrenDeptById(Long deptId) + { + return deptMapper.selectNormalChildrenDeptById(deptId); + } + + /** + * 是否存在子节点 + * + * @param deptId 部门ID + * @return 结果 + */ + @Override + public boolean hasChildByDeptId(Long deptId) + { + int result = deptMapper.hasChildByDeptId(deptId); + return result > 0; + } + + /** + * 查询部门是否存在用户 + * + * @param deptId 部门ID + * @return 结果 true 存在 false 不存在 + */ + @Override + public boolean checkDeptExistUser(Long deptId) + { + int result = deptMapper.checkDeptExistUser(deptId); + return result > 0; + } + + /** + * 校验部门名称是否唯一 + * + * @param dept 部门信息 + * @return 结果 + */ + @Override + public boolean checkDeptNameUnique(SysDept dept) + { + Long deptId = StringUtils.isNull(dept.getDeptId()) ? -1L : dept.getDeptId(); + SysDept info = deptMapper.checkDeptNameUnique(dept.getDeptName(), dept.getParentId()); + if (StringUtils.isNotNull(info) && info.getDeptId().longValue() != deptId.longValue()) + { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 校验部门是否有数据权限 + * + * @param deptId 部门id + */ + @Override + public void checkDeptDataScope(Long deptId) + { + if (!SecurityUtils.isAdmin() && StringUtils.isNotNull(deptId)) + { + SysDept dept = new SysDept(); + dept.setDeptId(deptId); + List depts = SpringUtils.getAopProxy(this).selectDeptList(dept); + if (StringUtils.isEmpty(depts)) + { + throw new ServiceException("没有权限访问部门数据!"); + } + } + } + + /** + * 新增保存部门信息 + * + * @param dept 部门信息 + * @return 结果 + */ + @Override + public int insertDept(SysDept dept) + { + SysDept info = deptMapper.selectDeptById(dept.getParentId()); + // 如果父节点不为正常状态,则不允许新增子节点 + if (!UserConstants.DEPT_NORMAL.equals(info.getStatus())) + { + throw new ServiceException("部门停用,不允许新增"); + } + dept.setAncestors(info.getAncestors() + "," + dept.getParentId()); + return deptMapper.insertDept(dept); + } + + /** + * 修改保存部门信息 + * + * @param dept 部门信息 + * @return 结果 + */ + @Override + public int updateDept(SysDept dept) + { + SysDept newParentDept = deptMapper.selectDeptById(dept.getParentId()); + SysDept oldDept = deptMapper.selectDeptById(dept.getDeptId()); + if (StringUtils.isNotNull(newParentDept) && StringUtils.isNotNull(oldDept)) + { + String newAncestors = newParentDept.getAncestors() + "," + newParentDept.getDeptId(); + String oldAncestors = oldDept.getAncestors(); + dept.setAncestors(newAncestors); + updateDeptChildren(dept.getDeptId(), newAncestors, oldAncestors); + } + int result = deptMapper.updateDept(dept); + if (UserConstants.DEPT_NORMAL.equals(dept.getStatus()) && StringUtils.isNotEmpty(dept.getAncestors()) + && !StringUtils.equals("0", dept.getAncestors())) + { + // 如果该部门是启用状态,则启用该部门的所有上级部门 + updateParentDeptStatusNormal(dept); + } + return result; + } + + /** + * 修改该部门的父级部门状态 + * + * @param dept 当前部门 + */ + private void updateParentDeptStatusNormal(SysDept dept) + { + String ancestors = dept.getAncestors(); + Long[] deptIds = Convert.toLongArray(ancestors); + deptMapper.updateDeptStatusNormal(deptIds); + } + + /** + * 修改子元素关系 + * + * @param deptId 被修改的部门ID + * @param newAncestors 新的父ID集合 + * @param oldAncestors 旧的父ID集合 + */ + public void updateDeptChildren(Long deptId, String newAncestors, String oldAncestors) + { + List children = deptMapper.selectChildrenDeptById(deptId); + for (SysDept child : children) + { + child.setAncestors(child.getAncestors().replaceFirst(oldAncestors, newAncestors)); + } + if (children.size() > 0) + { + deptMapper.updateDeptChildren(children); + } + } + + /** + * 删除部门管理信息 + * + * @param deptId 部门ID + * @return 结果 + */ + @Override + public int deleteDeptById(Long deptId) + { + return deptMapper.deleteDeptById(deptId); + } + + /** + * 递归列表 + */ + private void recursionFn(List list, SysDept t) + { + // 得到子节点列表 + List childList = getChildList(list, t); + t.setChildren(childList); + for (SysDept tChild : childList) + { + if (hasChild(list, tChild)) + { + recursionFn(list, tChild); + } + } + } + + /** + * 得到子节点列表 + */ + private List getChildList(List list, SysDept t) + { + List tlist = new ArrayList(); + Iterator it = list.iterator(); + while (it.hasNext()) + { + SysDept n = (SysDept) it.next(); + if (StringUtils.isNotNull(n.getParentId()) && n.getParentId().longValue() == t.getDeptId().longValue()) + { + tlist.add(n); + } + } + return tlist; + } + + /** + * 判断是否有子节点 + */ + private boolean hasChild(List list, SysDept t) + { + return getChildList(list, t).size() > 0; + } +} diff --git a/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/service/impl/SysDictDataServiceImpl.java b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/service/impl/SysDictDataServiceImpl.java new file mode 100644 index 0000000..df02725 --- /dev/null +++ b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/service/impl/SysDictDataServiceImpl.java @@ -0,0 +1,111 @@ +package org.lingniu.portal.system.service.impl; + +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.lingniu.portal.common.core.domain.entity.SysDictData; +import org.lingniu.portal.common.utils.DictUtils; +import org.lingniu.portal.system.mapper.SysDictDataMapper; +import org.lingniu.portal.system.service.ISysDictDataService; + +/** + * 字典 业务层处理 + * + * @author lingniu + */ +@Service +public class SysDictDataServiceImpl implements ISysDictDataService +{ + @Autowired + private SysDictDataMapper dictDataMapper; + + /** + * 根据条件分页查询字典数据 + * + * @param dictData 字典数据信息 + * @return 字典数据集合信息 + */ + @Override + public List selectDictDataList(SysDictData dictData) + { + return dictDataMapper.selectDictDataList(dictData); + } + + /** + * 根据字典类型和字典键值查询字典数据信息 + * + * @param dictType 字典类型 + * @param dictValue 字典键值 + * @return 字典标签 + */ + @Override + public String selectDictLabel(String dictType, String dictValue) + { + return dictDataMapper.selectDictLabel(dictType, dictValue); + } + + /** + * 根据字典数据ID查询信息 + * + * @param dictCode 字典数据ID + * @return 字典数据 + */ + @Override + public SysDictData selectDictDataById(Long dictCode) + { + return dictDataMapper.selectDictDataById(dictCode); + } + + /** + * 批量删除字典数据信息 + * + * @param dictCodes 需要删除的字典数据ID + */ + @Override + public void deleteDictDataByIds(Long[] dictCodes) + { + for (Long dictCode : dictCodes) + { + SysDictData data = selectDictDataById(dictCode); + dictDataMapper.deleteDictDataById(dictCode); + List dictDatas = dictDataMapper.selectDictDataByType(data.getDictType()); + DictUtils.setDictCache(data.getDictType(), dictDatas); + } + } + + /** + * 新增保存字典数据信息 + * + * @param data 字典数据信息 + * @return 结果 + */ + @Override + public int insertDictData(SysDictData data) + { + int row = dictDataMapper.insertDictData(data); + if (row > 0) + { + List dictDatas = dictDataMapper.selectDictDataByType(data.getDictType()); + DictUtils.setDictCache(data.getDictType(), dictDatas); + } + return row; + } + + /** + * 修改保存字典数据信息 + * + * @param data 字典数据信息 + * @return 结果 + */ + @Override + public int updateDictData(SysDictData data) + { + int row = dictDataMapper.updateDictData(data); + if (row > 0) + { + List dictDatas = dictDataMapper.selectDictDataByType(data.getDictType()); + DictUtils.setDictCache(data.getDictType(), dictDatas); + } + return row; + } +} diff --git a/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/service/impl/SysDictTypeServiceImpl.java b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/service/impl/SysDictTypeServiceImpl.java new file mode 100644 index 0000000..91bf6c5 --- /dev/null +++ b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/service/impl/SysDictTypeServiceImpl.java @@ -0,0 +1,223 @@ +package org.lingniu.portal.system.service.impl; + +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import jakarta.annotation.PostConstruct; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.lingniu.portal.common.constant.UserConstants; +import org.lingniu.portal.common.core.domain.entity.SysDictData; +import org.lingniu.portal.common.core.domain.entity.SysDictType; +import org.lingniu.portal.common.exception.ServiceException; +import org.lingniu.portal.common.utils.DictUtils; +import org.lingniu.portal.common.utils.StringUtils; +import org.lingniu.portal.system.mapper.SysDictDataMapper; +import org.lingniu.portal.system.mapper.SysDictTypeMapper; +import org.lingniu.portal.system.service.ISysDictTypeService; + +/** + * 字典 业务层处理 + * + * @author lingniu + */ +@Service +public class SysDictTypeServiceImpl implements ISysDictTypeService +{ + @Autowired + private SysDictTypeMapper dictTypeMapper; + + @Autowired + private SysDictDataMapper dictDataMapper; + + /** + * 项目启动时,初始化字典到缓存 + */ + @PostConstruct + public void init() + { + loadingDictCache(); + } + + /** + * 根据条件分页查询字典类型 + * + * @param dictType 字典类型信息 + * @return 字典类型集合信息 + */ + @Override + public List selectDictTypeList(SysDictType dictType) + { + return dictTypeMapper.selectDictTypeList(dictType); + } + + /** + * 根据所有字典类型 + * + * @return 字典类型集合信息 + */ + @Override + public List selectDictTypeAll() + { + return dictTypeMapper.selectDictTypeAll(); + } + + /** + * 根据字典类型查询字典数据 + * + * @param dictType 字典类型 + * @return 字典数据集合信息 + */ + @Override + public List selectDictDataByType(String dictType) + { + List dictDatas = DictUtils.getDictCache(dictType); + if (StringUtils.isNotEmpty(dictDatas)) + { + return dictDatas; + } + dictDatas = dictDataMapper.selectDictDataByType(dictType); + if (StringUtils.isNotEmpty(dictDatas)) + { + DictUtils.setDictCache(dictType, dictDatas); + return dictDatas; + } + return null; + } + + /** + * 根据字典类型ID查询信息 + * + * @param dictId 字典类型ID + * @return 字典类型 + */ + @Override + public SysDictType selectDictTypeById(Long dictId) + { + return dictTypeMapper.selectDictTypeById(dictId); + } + + /** + * 根据字典类型查询信息 + * + * @param dictType 字典类型 + * @return 字典类型 + */ + @Override + public SysDictType selectDictTypeByType(String dictType) + { + return dictTypeMapper.selectDictTypeByType(dictType); + } + + /** + * 批量删除字典类型信息 + * + * @param dictIds 需要删除的字典ID + */ + @Override + public void deleteDictTypeByIds(Long[] dictIds) + { + for (Long dictId : dictIds) + { + SysDictType dictType = selectDictTypeById(dictId); + if (dictDataMapper.countDictDataByType(dictType.getDictType()) > 0) + { + throw new ServiceException(String.format("%1$s已分配,不能删除", dictType.getDictName())); + } + dictTypeMapper.deleteDictTypeById(dictId); + DictUtils.removeDictCache(dictType.getDictType()); + } + } + + /** + * 加载字典缓存数据 + */ + @Override + public void loadingDictCache() + { + SysDictData dictData = new SysDictData(); + dictData.setStatus("0"); + Map> dictDataMap = dictDataMapper.selectDictDataList(dictData).stream().collect(Collectors.groupingBy(SysDictData::getDictType)); + for (Map.Entry> entry : dictDataMap.entrySet()) + { + DictUtils.setDictCache(entry.getKey(), entry.getValue().stream().sorted(Comparator.comparing(SysDictData::getDictSort)).collect(Collectors.toList())); + } + } + + /** + * 清空字典缓存数据 + */ + @Override + public void clearDictCache() + { + DictUtils.clearDictCache(); + } + + /** + * 重置字典缓存数据 + */ + @Override + public void resetDictCache() + { + clearDictCache(); + loadingDictCache(); + } + + /** + * 新增保存字典类型信息 + * + * @param dict 字典类型信息 + * @return 结果 + */ + @Override + public int insertDictType(SysDictType dict) + { + int row = dictTypeMapper.insertDictType(dict); + if (row > 0) + { + DictUtils.setDictCache(dict.getDictType(), null); + } + return row; + } + + /** + * 修改保存字典类型信息 + * + * @param dict 字典类型信息 + * @return 结果 + */ + @Override + @Transactional + public int updateDictType(SysDictType dict) + { + SysDictType oldDict = dictTypeMapper.selectDictTypeById(dict.getDictId()); + dictDataMapper.updateDictDataType(oldDict.getDictType(), dict.getDictType()); + int row = dictTypeMapper.updateDictType(dict); + if (row > 0) + { + List dictDatas = dictDataMapper.selectDictDataByType(dict.getDictType()); + DictUtils.setDictCache(dict.getDictType(), dictDatas); + } + return row; + } + + /** + * 校验字典类型称是否唯一 + * + * @param dict 字典类型 + * @return 结果 + */ + @Override + public boolean checkDictTypeUnique(SysDictType dict) + { + Long dictId = StringUtils.isNull(dict.getDictId()) ? -1L : dict.getDictId(); + SysDictType dictType = dictTypeMapper.checkDictTypeUnique(dict.getDictType()); + if (StringUtils.isNotNull(dictType) && dictType.getDictId().longValue() != dictId.longValue()) + { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } +} diff --git a/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/service/impl/SysLogininforServiceImpl.java b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/service/impl/SysLogininforServiceImpl.java new file mode 100644 index 0000000..72b8a4d --- /dev/null +++ b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/service/impl/SysLogininforServiceImpl.java @@ -0,0 +1,65 @@ +package org.lingniu.portal.system.service.impl; + +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.lingniu.portal.system.domain.SysLogininfor; +import org.lingniu.portal.system.mapper.SysLogininforMapper; +import org.lingniu.portal.system.service.ISysLogininforService; + +/** + * 系统访问日志情况信息 服务层处理 + * + * @author lingniu + */ +@Service +public class SysLogininforServiceImpl implements ISysLogininforService +{ + + @Autowired + private SysLogininforMapper logininforMapper; + + /** + * 新增系统登录日志 + * + * @param logininfor 访问日志对象 + */ + @Override + public void insertLogininfor(SysLogininfor logininfor) + { + logininforMapper.insertLogininfor(logininfor); + } + + /** + * 查询系统登录日志集合 + * + * @param logininfor 访问日志对象 + * @return 登录记录集合 + */ + @Override + public List selectLogininforList(SysLogininfor logininfor) + { + return logininforMapper.selectLogininforList(logininfor); + } + + /** + * 批量删除系统登录日志 + * + * @param infoIds 需要删除的登录日志ID + * @return 结果 + */ + @Override + public int deleteLogininforByIds(Long[] infoIds) + { + return logininforMapper.deleteLogininforByIds(infoIds); + } + + /** + * 清空系统登录日志 + */ + @Override + public void cleanLogininfor() + { + logininforMapper.cleanLogininfor(); + } +} diff --git a/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/service/impl/SysMenuServiceImpl.java b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/service/impl/SysMenuServiceImpl.java new file mode 100644 index 0000000..6fc5757 --- /dev/null +++ b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/service/impl/SysMenuServiceImpl.java @@ -0,0 +1,589 @@ +package org.lingniu.portal.system.service.impl; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.lingniu.portal.common.constant.Constants; +import org.lingniu.portal.common.constant.UserConstants; +import org.lingniu.portal.common.core.domain.TreeSelect; +import org.lingniu.portal.common.core.domain.entity.SysMenu; +import org.lingniu.portal.common.core.domain.entity.SysRole; +import org.lingniu.portal.common.utils.SecurityUtils; +import org.lingniu.portal.common.utils.StringUtils; +import org.lingniu.portal.system.domain.vo.MetaVo; +import org.lingniu.portal.system.domain.vo.RouterVo; +import org.lingniu.portal.system.mapper.SysMenuMapper; +import org.lingniu.portal.system.mapper.SysRoleMapper; +import org.lingniu.portal.system.mapper.SysRoleMenuMapper; +import org.lingniu.portal.system.service.ISysMenuService; + +/** + * 菜单 业务层处理 + * + * @author lingniu + */ +@Service +public class SysMenuServiceImpl implements ISysMenuService +{ + private static final Logger log = LoggerFactory.getLogger(SysMenuServiceImpl.class); + + public static final String PREMISSION_STRING = "perms[\"{0}\"]"; + + public static final Long MENU_ROOT_ID = 0L; + + @Autowired + private SysMenuMapper menuMapper; + + @Autowired + private SysRoleMapper roleMapper; + + @Autowired + private SysRoleMenuMapper roleMenuMapper; + + /** + * 根据用户查询系统菜单列表 + * + * @param userId 用户ID + * @return 菜单列表 + */ + @Override + public List selectMenuList(Long userId) + { + return selectMenuList(new SysMenu(), userId); + } + + /** + * 查询系统菜单列表 + * + * @param menu 菜单信息 + * @return 菜单列表 + */ + @Override + public List selectMenuList(SysMenu menu, Long userId) + { + List menuList = null; + // 管理员显示所有菜单信息 + if (SecurityUtils.isAdmin(userId)) + { + menuList = menuMapper.selectMenuList(menu); + } + else + { + menu.getParams().put("userId", userId); + menuList = menuMapper.selectMenuListByUserId(menu); + } + return menuList; + } + + /** + * 根据用户ID查询权限 + * + * @param userId 用户ID + * @return 权限列表 + */ + @Override + public Set selectMenuPermsByUserId(Long userId) + { + List perms = menuMapper.selectMenuPermsByUserId(userId); + Set permsSet = new HashSet<>(); + for (String perm : perms) + { + if (StringUtils.isNotEmpty(perm)) + { + permsSet.addAll(Arrays.asList(perm.trim().split(","))); + } + } + return permsSet; + } + + /** + * 根据角色ID查询权限 + * + * @param roleId 角色ID + * @return 权限列表 + */ + @Override + public Set selectMenuPermsByRoleId(Long roleId) + { + List perms = menuMapper.selectMenuPermsByRoleId(roleId); + Set permsSet = new HashSet<>(); + for (String perm : perms) + { + if (StringUtils.isNotEmpty(perm)) + { + permsSet.addAll(Arrays.asList(perm.trim().split(","))); + } + } + return permsSet; + } + + /** + * 根据用户ID查询菜单 + * + * @param userId 用户名称 + * @return 菜单列表 + */ + @Override + public List selectMenuTreeByUserId(Long userId) + { + List menus = null; + if (SecurityUtils.isAdmin(userId)) + { + menus = menuMapper.selectMenuTreeAll(); + } + else + { + menus = menuMapper.selectMenuTreeByUserId(userId); + } + return getChildPerms(menus, MENU_ROOT_ID); + } + + /** + * 根据角色ID查询菜单树信息 + * + * @param roleId 角色ID + * @return 选中菜单列表 + */ + @Override + public List selectMenuListByRoleId(Long roleId) + { + SysRole role = roleMapper.selectRoleById(roleId); + return menuMapper.selectMenuListByRoleId(roleId, role.isMenuCheckStrictly()); + } + + /** + * 构建前端路由所需要的菜单 + * + * @param menus 菜单列表 + * @return 路由列表 + */ + @Override + public List buildMenus(List menus) + { + List routers = new LinkedList(); + for (SysMenu menu : menus) + { + RouterVo router = new RouterVo(); + router.setHidden("1".equals(menu.getVisible())); + router.setName(getRouteName(menu)); + router.setPath(getRouterPath(menu)); + router.setComponent(getComponent(menu)); + router.setQuery(menu.getQuery()); + router.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), StringUtils.equals("1", menu.getIsCache()), menu.getPath())); + List cMenus = menu.getChildren(); + if (StringUtils.isNotEmpty(cMenus) && UserConstants.TYPE_DIR.equals(menu.getMenuType())) + { + router.setAlwaysShow(true); + router.setRedirect("noRedirect"); + router.setChildren(buildMenus(cMenus)); + } + else if (isMenuFrame(menu)) + { + router.setMeta(null); + List childrenList = new ArrayList(); + RouterVo children = new RouterVo(); + children.setPath(menu.getPath()); + children.setComponent(menu.getComponent()); + children.setName(getRouteName(menu.getRouteName(), menu.getPath())); + children.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), StringUtils.equals("1", menu.getIsCache()), menu.getPath())); + children.setQuery(menu.getQuery()); + childrenList.add(children); + router.setChildren(childrenList); + } + else if (menu.getParentId().intValue() == MENU_ROOT_ID && isInnerLink(menu)) + { + router.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon())); + router.setPath("/"); + List childrenList = new ArrayList(); + RouterVo children = new RouterVo(); + String routerPath = innerLinkReplaceEach(menu.getPath()); + children.setPath(routerPath); + children.setComponent(UserConstants.INNER_LINK); + children.setName(getRouteName(menu.getRouteName(), routerPath)); + children.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), menu.getPath())); + childrenList.add(children); + router.setChildren(childrenList); + } + routers.add(router); + } + return routers; + } + + /** + * 构建前端所需要树结构 + * + * @param menus 菜单列表 + * @return 树结构列表 + */ + @Override + public List buildMenuTree(List menus) + { + List returnList = new ArrayList(); + List tempList = menus.stream().map(SysMenu::getMenuId).collect(Collectors.toList()); + for (Iterator iterator = menus.iterator(); iterator.hasNext();) + { + SysMenu menu = (SysMenu) iterator.next(); + // 如果是顶级节点, 遍历该父节点的所有子节点 + if (!tempList.contains(menu.getParentId())) + { + recursionFn(menus, menu); + returnList.add(menu); + } + } + if (returnList.isEmpty()) + { + returnList = menus; + } + return returnList; + } + + /** + * 构建前端所需要下拉树结构 + * + * @param menus 菜单列表 + * @return 下拉树结构列表 + */ + @Override + public List buildMenuTreeSelect(List menus) + { + List menuTrees = buildMenuTree(menus); + return menuTrees.stream().map(TreeSelect::new).collect(Collectors.toList()); + } + + /** + * 根据菜单ID查询信息 + * + * @param menuId 菜单ID + * @return 菜单信息 + */ + @Override + public SysMenu selectMenuById(Long menuId) + { + return menuMapper.selectMenuById(menuId); + } + + /** + * 是否存在菜单子节点 + * + * @param menuId 菜单ID + * @return 结果 + */ + @Override + public boolean hasChildByMenuId(Long menuId) + { + int result = menuMapper.hasChildByMenuId(menuId); + return result > 0; + } + + /** + * 查询菜单使用数量 + * + * @param menuId 菜单ID + * @return 结果 + */ + @Override + public boolean checkMenuExistRole(Long menuId) + { + int result = roleMenuMapper.checkMenuExistRole(menuId); + return result > 0; + } + + /** + * 新增保存菜单信息 + * + * @param menu 菜单信息 + * @return 结果 + */ + @Override + public int insertMenu(SysMenu menu) + { + return menuMapper.insertMenu(menu); + } + + /** + * 修改保存菜单信息 + * + * @param menu 菜单信息 + * @return 结果 + */ + @Override + public int updateMenu(SysMenu menu) + { + return menuMapper.updateMenu(menu); + } + + /** + * 删除菜单管理信息 + * + * @param menuId 菜单ID + * @return 结果 + */ + @Override + public int deleteMenuById(Long menuId) + { + return menuMapper.deleteMenuById(menuId); + } + + /** + * 校验菜单名称是否唯一 + * + * @param menu 菜单信息 + * @return 结果 + */ + @Override + public boolean checkMenuNameUnique(SysMenu menu) + { + Long menuId = StringUtils.isNull(menu.getMenuId()) ? -1L : menu.getMenuId(); + SysMenu info = menuMapper.checkMenuNameUnique(menu.getMenuName(), menu.getParentId()); + if (StringUtils.isNotNull(info) && info.getMenuId().longValue() != menuId.longValue()) + { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 校验路由名称是否唯一 + * + * @param menu 菜单信息 + * @return 结果 + */ + @Override + public boolean checkRouteConfigUnique(SysMenu menu) + { + Long menuId = StringUtils.isNull(menu.getMenuId()) ? -1L : menu.getMenuId(); + Long parentId = menu.getParentId(); + String path = menu.getPath(); + String routeName = StringUtils.isEmpty(menu.getRouteName()) ? path : menu.getRouteName(); + List sysMenuList = menuMapper.selectMenusByPathOrRouteName(path, routeName); + for (SysMenu sysMenu : sysMenuList) + { + if (sysMenu.getMenuId().longValue() != menuId.longValue()) + { + Long dbParentId = sysMenu.getParentId(); + String dbPath = sysMenu.getPath(); + String dbRouteName = StringUtils.isEmpty(sysMenu.getRouteName()) ? dbPath : sysMenu.getRouteName(); + if (StringUtils.equalsAnyIgnoreCase(path, dbPath) && parentId.longValue() == dbParentId.longValue()) + { + log.warn("[同级路由冲突] 同级下已存在相同路由路径 '{}',冲突菜单:{}", dbPath, sysMenu.getMenuName()); + return UserConstants.NOT_UNIQUE; + } + else if (StringUtils.equalsAnyIgnoreCase(path, dbPath) && parentId.longValue() == MENU_ROOT_ID) + { + log.warn("[根目录路由冲突] 根目录下路由 '{}' 必须唯一,已被菜单 '{}' 占用", path, sysMenu.getMenuName()); + return UserConstants.NOT_UNIQUE; + } + else if (StringUtils.equalsAnyIgnoreCase(routeName, dbRouteName)) + { + log.warn("[路由名称冲突] 路由名称 '{}' 需全局唯一,已被菜单 '{}' 使用", routeName, sysMenu.getMenuName()); + return UserConstants.NOT_UNIQUE; + } + } + } + return UserConstants.UNIQUE; + } + + /** + * 获取路由名称 + * + * @param menu 菜单信息 + * @return 路由名称 + */ + public String getRouteName(SysMenu menu) + { + // 非外链并且是一级目录(类型为目录) + if (isMenuFrame(menu)) + { + return StringUtils.EMPTY; + } + return getRouteName(menu.getRouteName(), menu.getPath()); + } + + /** + * 获取路由名称,如没有配置路由名称则取路由地址 + * + * @param name 路由名称 + * @param path 路由地址 + * @return 路由名称(驼峰格式) + */ + public String getRouteName(String name, String path) + { + String routerName = StringUtils.isNotEmpty(name) ? name : path; + return StringUtils.capitalize(routerName); + } + + /** + * 获取路由地址 + * + * @param menu 菜单信息 + * @return 路由地址 + */ + public String getRouterPath(SysMenu menu) + { + String routerPath = menu.getPath(); + // 内链打开外网方式 + if (menu.getParentId().intValue() != MENU_ROOT_ID && isInnerLink(menu)) + { + routerPath = innerLinkReplaceEach(routerPath); + } + // 非外链并且是一级目录(类型为目录) + if (MENU_ROOT_ID == menu.getParentId().intValue() && UserConstants.TYPE_DIR.equals(menu.getMenuType()) + && UserConstants.NO_FRAME.equals(menu.getIsFrame())) + { + routerPath = "/" + menu.getPath(); + } + // 非外链并且是一级目录(类型为菜单) + else if (isMenuFrame(menu)) + { + routerPath = "/"; + } + return routerPath; + } + + /** + * 获取组件信息 + * + * @param menu 菜单信息 + * @return 组件信息 + */ + public String getComponent(SysMenu menu) + { + String component = UserConstants.LAYOUT; + if (StringUtils.isNotEmpty(menu.getComponent()) && !isMenuFrame(menu)) + { + component = menu.getComponent(); + } + else if (StringUtils.isEmpty(menu.getComponent()) && menu.getParentId().intValue() != MENU_ROOT_ID && isInnerLink(menu)) + { + component = UserConstants.INNER_LINK; + } + else if (StringUtils.isEmpty(menu.getComponent()) && isParentView(menu)) + { + component = UserConstants.PARENT_VIEW; + } + return component; + } + + /** + * 是否为菜单内部跳转 + * + * @param menu 菜单信息 + * @return 结果 + */ + public boolean isMenuFrame(SysMenu menu) + { + return menu.getParentId().intValue() == MENU_ROOT_ID && UserConstants.TYPE_MENU.equals(menu.getMenuType()) + && menu.getIsFrame().equals(UserConstants.NO_FRAME); + } + + /** + * 是否为parent_view组件 + * + * @param menu 菜单信息 + * @return 结果 + */ + public boolean isParentView(SysMenu menu) + { + return menu.getParentId().intValue() != MENU_ROOT_ID && UserConstants.TYPE_DIR.equals(menu.getMenuType()); + } + + /** + * 是否为内链组件 + * + * @param menu 菜单信息 + * @return 结果 + */ + public boolean isInnerLink(SysMenu menu) + { + return menu.getIsFrame().equals(UserConstants.NO_FRAME) && StringUtils.ishttp(menu.getPath()); + } + + /** + * 根据父节点的ID获取所有子节点 + * + * @param list 分类表 + * @param parentId 传入的父节点ID + * @return String + */ + public List getChildPerms(List list, long parentId) + { + List returnList = new ArrayList(); + for (Iterator iterator = list.iterator(); iterator.hasNext();) + { + SysMenu t = (SysMenu) iterator.next(); + // 一、根据传入的某个父节点ID,遍历该父节点的所有子节点 + if (t.getParentId() == parentId) + { + recursionFn(list, t); + returnList.add(t); + } + } + return returnList; + } + + /** + * 递归列表 + * + * @param list 分类表 + * @param t 子节点 + */ + private void recursionFn(List list, SysMenu t) + { + // 得到子节点列表 + List childList = getChildList(list, t); + t.setChildren(childList); + for (SysMenu tChild : childList) + { + if (hasChild(list, tChild)) + { + recursionFn(list, tChild); + } + } + } + + /** + * 得到子节点列表 + */ + private List getChildList(List list, SysMenu t) + { + List tlist = new ArrayList(); + Iterator it = list.iterator(); + while (it.hasNext()) + { + SysMenu n = (SysMenu) it.next(); + if (n.getParentId().longValue() == t.getMenuId().longValue()) + { + tlist.add(n); + } + } + return tlist; + } + + /** + * 判断是否有子节点 + */ + private boolean hasChild(List list, SysMenu t) + { + return getChildList(list, t).size() > 0; + } + + /** + * 内链域名特殊字符替换 + * + * @return 替换后的内链域名 + */ + public String innerLinkReplaceEach(String path) + { + return StringUtils.replaceEach(path, new String[] { Constants.HTTP, Constants.HTTPS, Constants.WWW, ".", ":" }, + new String[] { "", "", "", "/", "/" }); + } +} diff --git a/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/service/impl/SysNoticeServiceImpl.java b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/service/impl/SysNoticeServiceImpl.java new file mode 100644 index 0000000..69fe573 --- /dev/null +++ b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/service/impl/SysNoticeServiceImpl.java @@ -0,0 +1,92 @@ +package org.lingniu.portal.system.service.impl; + +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.lingniu.portal.system.domain.SysNotice; +import org.lingniu.portal.system.mapper.SysNoticeMapper; +import org.lingniu.portal.system.service.ISysNoticeService; + +/** + * 公告 服务层实现 + * + * @author lingniu + */ +@Service +public class SysNoticeServiceImpl implements ISysNoticeService +{ + @Autowired + private SysNoticeMapper noticeMapper; + + /** + * 查询公告信息 + * + * @param noticeId 公告ID + * @return 公告信息 + */ + @Override + public SysNotice selectNoticeById(Long noticeId) + { + return noticeMapper.selectNoticeById(noticeId); + } + + /** + * 查询公告列表 + * + * @param notice 公告信息 + * @return 公告集合 + */ + @Override + public List selectNoticeList(SysNotice notice) + { + return noticeMapper.selectNoticeList(notice); + } + + /** + * 新增公告 + * + * @param notice 公告信息 + * @return 结果 + */ + @Override + public int insertNotice(SysNotice notice) + { + return noticeMapper.insertNotice(notice); + } + + /** + * 修改公告 + * + * @param notice 公告信息 + * @return 结果 + */ + @Override + public int updateNotice(SysNotice notice) + { + return noticeMapper.updateNotice(notice); + } + + /** + * 删除公告对象 + * + * @param noticeId 公告ID + * @return 结果 + */ + @Override + public int deleteNoticeById(Long noticeId) + { + return noticeMapper.deleteNoticeById(noticeId); + } + + /** + * 批量删除公告信息 + * + * @param noticeIds 需要删除的公告ID + * @return 结果 + */ + @Override + public int deleteNoticeByIds(Long[] noticeIds) + { + return noticeMapper.deleteNoticeByIds(noticeIds); + } +} diff --git a/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/service/impl/SysOperLogServiceImpl.java b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/service/impl/SysOperLogServiceImpl.java new file mode 100644 index 0000000..a8676fa --- /dev/null +++ b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/service/impl/SysOperLogServiceImpl.java @@ -0,0 +1,76 @@ +package org.lingniu.portal.system.service.impl; + +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.lingniu.portal.system.domain.SysOperLog; +import org.lingniu.portal.system.mapper.SysOperLogMapper; +import org.lingniu.portal.system.service.ISysOperLogService; + +/** + * 操作日志 服务层处理 + * + * @author lingniu + */ +@Service +public class SysOperLogServiceImpl implements ISysOperLogService +{ + @Autowired + private SysOperLogMapper operLogMapper; + + /** + * 新增操作日志 + * + * @param operLog 操作日志对象 + */ + @Override + public void insertOperlog(SysOperLog operLog) + { + operLogMapper.insertOperlog(operLog); + } + + /** + * 查询系统操作日志集合 + * + * @param operLog 操作日志对象 + * @return 操作日志集合 + */ + @Override + public List selectOperLogList(SysOperLog operLog) + { + return operLogMapper.selectOperLogList(operLog); + } + + /** + * 批量删除系统操作日志 + * + * @param operIds 需要删除的操作日志ID + * @return 结果 + */ + @Override + public int deleteOperLogByIds(Long[] operIds) + { + return operLogMapper.deleteOperLogByIds(operIds); + } + + /** + * 查询操作日志详细 + * + * @param operId 操作ID + * @return 操作日志对象 + */ + @Override + public SysOperLog selectOperLogById(Long operId) + { + return operLogMapper.selectOperLogById(operId); + } + + /** + * 清空操作日志 + */ + @Override + public void cleanOperLog() + { + operLogMapper.cleanOperLog(); + } +} diff --git a/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/service/impl/SysPostServiceImpl.java b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/service/impl/SysPostServiceImpl.java new file mode 100644 index 0000000..ab13c7c --- /dev/null +++ b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/service/impl/SysPostServiceImpl.java @@ -0,0 +1,178 @@ +package org.lingniu.portal.system.service.impl; + +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.lingniu.portal.common.constant.UserConstants; +import org.lingniu.portal.common.exception.ServiceException; +import org.lingniu.portal.common.utils.StringUtils; +import org.lingniu.portal.system.domain.SysPost; +import org.lingniu.portal.system.mapper.SysPostMapper; +import org.lingniu.portal.system.mapper.SysUserPostMapper; +import org.lingniu.portal.system.service.ISysPostService; + +/** + * 岗位信息 服务层处理 + * + * @author lingniu + */ +@Service +public class SysPostServiceImpl implements ISysPostService +{ + @Autowired + private SysPostMapper postMapper; + + @Autowired + private SysUserPostMapper userPostMapper; + + /** + * 查询岗位信息集合 + * + * @param post 岗位信息 + * @return 岗位信息集合 + */ + @Override + public List selectPostList(SysPost post) + { + return postMapper.selectPostList(post); + } + + /** + * 查询所有岗位 + * + * @return 岗位列表 + */ + @Override + public List selectPostAll() + { + return postMapper.selectPostAll(); + } + + /** + * 通过岗位ID查询岗位信息 + * + * @param postId 岗位ID + * @return 角色对象信息 + */ + @Override + public SysPost selectPostById(Long postId) + { + return postMapper.selectPostById(postId); + } + + /** + * 根据用户ID获取岗位选择框列表 + * + * @param userId 用户ID + * @return 选中岗位ID列表 + */ + @Override + public List selectPostListByUserId(Long userId) + { + return postMapper.selectPostListByUserId(userId); + } + + /** + * 校验岗位名称是否唯一 + * + * @param post 岗位信息 + * @return 结果 + */ + @Override + public boolean checkPostNameUnique(SysPost post) + { + Long postId = StringUtils.isNull(post.getPostId()) ? -1L : post.getPostId(); + SysPost info = postMapper.checkPostNameUnique(post.getPostName()); + if (StringUtils.isNotNull(info) && info.getPostId().longValue() != postId.longValue()) + { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 校验岗位编码是否唯一 + * + * @param post 岗位信息 + * @return 结果 + */ + @Override + public boolean checkPostCodeUnique(SysPost post) + { + Long postId = StringUtils.isNull(post.getPostId()) ? -1L : post.getPostId(); + SysPost info = postMapper.checkPostCodeUnique(post.getPostCode()); + if (StringUtils.isNotNull(info) && info.getPostId().longValue() != postId.longValue()) + { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 通过岗位ID查询岗位使用数量 + * + * @param postId 岗位ID + * @return 结果 + */ + @Override + public int countUserPostById(Long postId) + { + return userPostMapper.countUserPostById(postId); + } + + /** + * 删除岗位信息 + * + * @param postId 岗位ID + * @return 结果 + */ + @Override + public int deletePostById(Long postId) + { + return postMapper.deletePostById(postId); + } + + /** + * 批量删除岗位信息 + * + * @param postIds 需要删除的岗位ID + * @return 结果 + */ + @Override + public int deletePostByIds(Long[] postIds) + { + for (Long postId : postIds) + { + SysPost post = selectPostById(postId); + if (countUserPostById(postId) > 0) + { + throw new ServiceException(String.format("%1$s已分配,不能删除", post.getPostName())); + } + } + return postMapper.deletePostByIds(postIds); + } + + /** + * 新增保存岗位信息 + * + * @param post 岗位信息 + * @return 结果 + */ + @Override + public int insertPost(SysPost post) + { + return postMapper.insertPost(post); + } + + /** + * 修改保存岗位信息 + * + * @param post 岗位信息 + * @return 结果 + */ + @Override + public int updatePost(SysPost post) + { + return postMapper.updatePost(post); + } +} diff --git a/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/service/impl/SysRoleServiceImpl.java b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/service/impl/SysRoleServiceImpl.java new file mode 100644 index 0000000..360e7e3 --- /dev/null +++ b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/service/impl/SysRoleServiceImpl.java @@ -0,0 +1,426 @@ +package org.lingniu.portal.system.service.impl; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.lingniu.portal.common.annotation.DataScope; +import org.lingniu.portal.common.constant.UserConstants; +import org.lingniu.portal.common.core.domain.entity.SysRole; +import org.lingniu.portal.common.exception.ServiceException; +import org.lingniu.portal.common.utils.SecurityUtils; +import org.lingniu.portal.common.utils.StringUtils; +import org.lingniu.portal.common.utils.spring.SpringUtils; +import org.lingniu.portal.system.domain.SysRoleDept; +import org.lingniu.portal.system.domain.SysRoleMenu; +import org.lingniu.portal.system.domain.SysUserRole; +import org.lingniu.portal.system.mapper.SysRoleDeptMapper; +import org.lingniu.portal.system.mapper.SysRoleMapper; +import org.lingniu.portal.system.mapper.SysRoleMenuMapper; +import org.lingniu.portal.system.mapper.SysUserRoleMapper; +import org.lingniu.portal.system.service.ISysRoleService; + +/** + * 角色 业务层处理 + * + * @author lingniu + */ +@Service +public class SysRoleServiceImpl implements ISysRoleService +{ + @Autowired + private SysRoleMapper roleMapper; + + @Autowired + private SysRoleMenuMapper roleMenuMapper; + + @Autowired + private SysUserRoleMapper userRoleMapper; + + @Autowired + private SysRoleDeptMapper roleDeptMapper; + + /** + * 根据条件分页查询角色数据 + * + * @param role 角色信息 + * @return 角色数据集合信息 + */ + @Override + @DataScope(deptAlias = "d") + public List selectRoleList(SysRole role) + { + return roleMapper.selectRoleList(role); + } + + /** + * 根据用户ID查询角色 + * + * @param userId 用户ID + * @return 角色列表 + */ + @Override + public List selectRolesByUserId(Long userId) + { + List userRoles = roleMapper.selectRolePermissionByUserId(userId); + List roles = selectRoleAll(); + for (SysRole role : roles) + { + for (SysRole userRole : userRoles) + { + if (role.getRoleId().longValue() == userRole.getRoleId().longValue()) + { + role.setFlag(true); + break; + } + } + } + return roles; + } + + /** + * 根据用户ID查询权限 + * + * @param userId 用户ID + * @return 权限列表 + */ + @Override + public Set selectRolePermissionByUserId(Long userId) + { + List perms = roleMapper.selectRolePermissionByUserId(userId); + Set permsSet = new HashSet<>(); + for (SysRole perm : perms) + { + if (StringUtils.isNotNull(perm)) + { + permsSet.addAll(Arrays.asList(perm.getRoleKey().trim().split(","))); + } + } + return permsSet; + } + + /** + * 查询所有角色 + * + * @return 角色列表 + */ + @Override + public List selectRoleAll() + { + return SpringUtils.getAopProxy(this).selectRoleList(new SysRole()); + } + + /** + * 根据用户ID获取角色选择框列表 + * + * @param userId 用户ID + * @return 选中角色ID列表 + */ + @Override + public List selectRoleListByUserId(Long userId) + { + return roleMapper.selectRoleListByUserId(userId); + } + + /** + * 通过角色ID查询角色 + * + * @param roleId 角色ID + * @return 角色对象信息 + */ + @Override + public SysRole selectRoleById(Long roleId) + { + return roleMapper.selectRoleById(roleId); + } + + /** + * 校验角色名称是否唯一 + * + * @param role 角色信息 + * @return 结果 + */ + @Override + public boolean checkRoleNameUnique(SysRole role) + { + Long roleId = StringUtils.isNull(role.getRoleId()) ? -1L : role.getRoleId(); + SysRole info = roleMapper.checkRoleNameUnique(role.getRoleName()); + if (StringUtils.isNotNull(info) && info.getRoleId().longValue() != roleId.longValue()) + { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 校验角色权限是否唯一 + * + * @param role 角色信息 + * @return 结果 + */ + @Override + public boolean checkRoleKeyUnique(SysRole role) + { + Long roleId = StringUtils.isNull(role.getRoleId()) ? -1L : role.getRoleId(); + SysRole info = roleMapper.checkRoleKeyUnique(role.getRoleKey()); + if (StringUtils.isNotNull(info) && info.getRoleId().longValue() != roleId.longValue()) + { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 校验角色是否允许操作 + * + * @param role 角色信息 + */ + @Override + public void checkRoleAllowed(SysRole role) + { + if (StringUtils.isNotNull(role.getRoleId()) && role.isAdmin()) + { + throw new ServiceException("不允许操作超级管理员角色"); + } + } + + /** + * 校验角色是否有数据权限 + * + * @param roleIds 角色id + */ + @Override + public void checkRoleDataScope(Long... roleIds) + { + if (!SecurityUtils.isAdmin()) + { + for (Long roleId : roleIds) + { + SysRole role = new SysRole(); + role.setRoleId(roleId); + List roles = SpringUtils.getAopProxy(this).selectRoleList(role); + if (StringUtils.isEmpty(roles)) + { + throw new ServiceException("没有权限访问角色数据!"); + } + } + } + } + + /** + * 通过角色ID查询角色使用数量 + * + * @param roleId 角色ID + * @return 结果 + */ + @Override + public int countUserRoleByRoleId(Long roleId) + { + return userRoleMapper.countUserRoleByRoleId(roleId); + } + + /** + * 新增保存角色信息 + * + * @param role 角色信息 + * @return 结果 + */ + @Override + @Transactional + public int insertRole(SysRole role) + { + // 新增角色信息 + roleMapper.insertRole(role); + return insertRoleMenu(role); + } + + /** + * 修改保存角色信息 + * + * @param role 角色信息 + * @return 结果 + */ + @Override + @Transactional + public int updateRole(SysRole role) + { + // 修改角色信息 + roleMapper.updateRole(role); + // 删除角色与菜单关联 + roleMenuMapper.deleteRoleMenuByRoleId(role.getRoleId()); + return insertRoleMenu(role); + } + + /** + * 修改角色状态 + * + * @param role 角色信息 + * @return 结果 + */ + @Override + public int updateRoleStatus(SysRole role) + { + return roleMapper.updateRole(role); + } + + /** + * 修改数据权限信息 + * + * @param role 角色信息 + * @return 结果 + */ + @Override + @Transactional + public int authDataScope(SysRole role) + { + // 修改角色信息 + roleMapper.updateRole(role); + // 删除角色与部门关联 + roleDeptMapper.deleteRoleDeptByRoleId(role.getRoleId()); + // 新增角色和部门信息(数据权限) + return insertRoleDept(role); + } + + /** + * 新增角色菜单信息 + * + * @param role 角色对象 + */ + public int insertRoleMenu(SysRole role) + { + int rows = 1; + // 新增用户与角色管理 + List list = new ArrayList(); + for (Long menuId : role.getMenuIds()) + { + SysRoleMenu rm = new SysRoleMenu(); + rm.setRoleId(role.getRoleId()); + rm.setMenuId(menuId); + list.add(rm); + } + if (list.size() > 0) + { + rows = roleMenuMapper.batchRoleMenu(list); + } + return rows; + } + + /** + * 新增角色部门信息(数据权限) + * + * @param role 角色对象 + */ + public int insertRoleDept(SysRole role) + { + int rows = 1; + // 新增角色与部门(数据权限)管理 + List list = new ArrayList(); + for (Long deptId : role.getDeptIds()) + { + SysRoleDept rd = new SysRoleDept(); + rd.setRoleId(role.getRoleId()); + rd.setDeptId(deptId); + list.add(rd); + } + if (list.size() > 0) + { + rows = roleDeptMapper.batchRoleDept(list); + } + return rows; + } + + /** + * 通过角色ID删除角色 + * + * @param roleId 角色ID + * @return 结果 + */ + @Override + @Transactional + public int deleteRoleById(Long roleId) + { + // 删除角色与菜单关联 + roleMenuMapper.deleteRoleMenuByRoleId(roleId); + // 删除角色与部门关联 + roleDeptMapper.deleteRoleDeptByRoleId(roleId); + return roleMapper.deleteRoleById(roleId); + } + + /** + * 批量删除角色信息 + * + * @param roleIds 需要删除的角色ID + * @return 结果 + */ + @Override + @Transactional + public int deleteRoleByIds(Long[] roleIds) + { + for (Long roleId : roleIds) + { + checkRoleAllowed(new SysRole(roleId)); + checkRoleDataScope(roleId); + SysRole role = selectRoleById(roleId); + if (countUserRoleByRoleId(roleId) > 0) + { + throw new ServiceException(String.format("%1$s已分配,不能删除", role.getRoleName())); + } + } + // 删除角色与菜单关联 + roleMenuMapper.deleteRoleMenu(roleIds); + // 删除角色与部门关联 + roleDeptMapper.deleteRoleDept(roleIds); + return roleMapper.deleteRoleByIds(roleIds); + } + + /** + * 取消授权用户角色 + * + * @param userRole 用户和角色关联信息 + * @return 结果 + */ + @Override + public int deleteAuthUser(SysUserRole userRole) + { + return userRoleMapper.deleteUserRoleInfo(userRole); + } + + /** + * 批量取消授权用户角色 + * + * @param roleId 角色ID + * @param userIds 需要取消授权的用户数据ID + * @return 结果 + */ + @Override + public int deleteAuthUsers(Long roleId, Long[] userIds) + { + return userRoleMapper.deleteUserRoleInfos(roleId, userIds); + } + + /** + * 批量选择授权用户角色 + * + * @param roleId 角色ID + * @param userIds 需要授权的用户数据ID + * @return 结果 + */ + @Override + public int insertAuthUsers(Long roleId, Long[] userIds) + { + // 新增用户与角色管理 + List list = new ArrayList(); + for (Long userId : userIds) + { + SysUserRole ur = new SysUserRole(); + ur.setUserId(userId); + ur.setRoleId(roleId); + list.add(ur); + } + return userRoleMapper.batchUserRole(list); + } +} diff --git a/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/service/impl/SysUserOnlineServiceImpl.java b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/service/impl/SysUserOnlineServiceImpl.java new file mode 100644 index 0000000..b6fb7ad --- /dev/null +++ b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/service/impl/SysUserOnlineServiceImpl.java @@ -0,0 +1,96 @@ +package org.lingniu.portal.system.service.impl; + +import org.springframework.stereotype.Service; +import org.lingniu.portal.common.core.domain.model.LoginUser; +import org.lingniu.portal.common.utils.StringUtils; +import org.lingniu.portal.system.domain.SysUserOnline; +import org.lingniu.portal.system.service.ISysUserOnlineService; + +/** + * 在线用户 服务层处理 + * + * @author lingniu + */ +@Service +public class SysUserOnlineServiceImpl implements ISysUserOnlineService +{ + /** + * 通过登录地址查询信息 + * + * @param ipaddr 登录地址 + * @param user 用户信息 + * @return 在线用户信息 + */ + @Override + public SysUserOnline selectOnlineByIpaddr(String ipaddr, LoginUser user) + { + if (StringUtils.equals(ipaddr, user.getIpaddr())) + { + return loginUserToUserOnline(user); + } + return null; + } + + /** + * 通过用户名称查询信息 + * + * @param userName 用户名称 + * @param user 用户信息 + * @return 在线用户信息 + */ + @Override + public SysUserOnline selectOnlineByUserName(String userName, LoginUser user) + { + if (StringUtils.equals(userName, user.getUsername())) + { + return loginUserToUserOnline(user); + } + return null; + } + + /** + * 通过登录地址/用户名称查询信息 + * + * @param ipaddr 登录地址 + * @param userName 用户名称 + * @param user 用户信息 + * @return 在线用户信息 + */ + @Override + public SysUserOnline selectOnlineByInfo(String ipaddr, String userName, LoginUser user) + { + if (StringUtils.equals(ipaddr, user.getIpaddr()) && StringUtils.equals(userName, user.getUsername())) + { + return loginUserToUserOnline(user); + } + return null; + } + + /** + * 设置在线用户信息 + * + * @param user 用户信息 + * @return 在线用户 + */ + @Override + public SysUserOnline loginUserToUserOnline(LoginUser user) + { + if (StringUtils.isNull(user) || StringUtils.isNull(user.getUser())) + { + return null; + } + SysUserOnline sysUserOnline = new SysUserOnline(); + sysUserOnline.setTokenId(user.getToken()); + sysUserOnline.setUserName(user.getUsername()); + sysUserOnline.setIpaddr(user.getIpaddr()); + sysUserOnline.setLoginLocation(user.getLoginLocation()); + sysUserOnline.setBrowser(user.getBrowser()); + sysUserOnline.setOs(user.getOs()); + sysUserOnline.setLoginTime(user.getLoginTime()); + if (StringUtils.isNotNull(user.getUser().getDept())) + { + sysUserOnline.setDeptName(user.getUser().getDept().getDeptName()); + } + return sysUserOnline; + } +} diff --git a/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/service/impl/SysUserServiceImpl.java b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/service/impl/SysUserServiceImpl.java new file mode 100644 index 0000000..c903725 --- /dev/null +++ b/portal/backend/portal-system/src/main/java/org/lingniu/portal/system/service/impl/SysUserServiceImpl.java @@ -0,0 +1,565 @@ +package org.lingniu.portal.system.service.impl; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.stream.Collectors; +import jakarta.validation.Validator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.CollectionUtils; +import org.lingniu.portal.common.annotation.DataScope; +import org.lingniu.portal.common.constant.UserConstants; +import org.lingniu.portal.common.core.domain.entity.SysRole; +import org.lingniu.portal.common.core.domain.entity.SysUser; +import org.lingniu.portal.common.exception.ServiceException; +import org.lingniu.portal.common.utils.SecurityUtils; +import org.lingniu.portal.common.utils.StringUtils; +import org.lingniu.portal.common.utils.bean.BeanValidators; +import org.lingniu.portal.common.utils.spring.SpringUtils; +import org.lingniu.portal.system.domain.SysPost; +import org.lingniu.portal.system.domain.SysUserPost; +import org.lingniu.portal.system.domain.SysUserRole; +import org.lingniu.portal.system.mapper.SysPostMapper; +import org.lingniu.portal.system.mapper.SysRoleMapper; +import org.lingniu.portal.system.mapper.SysUserMapper; +import org.lingniu.portal.system.mapper.SysUserPostMapper; +import org.lingniu.portal.system.mapper.SysUserRoleMapper; +import org.lingniu.portal.system.service.ISysConfigService; +import org.lingniu.portal.system.service.ISysDeptService; +import org.lingniu.portal.system.service.ISysUserService; + +/** + * 用户 业务层处理 + * + * @author lingniu + */ +@Service +public class SysUserServiceImpl implements ISysUserService +{ + private static final Logger log = LoggerFactory.getLogger(SysUserServiceImpl.class); + + @Autowired + private SysUserMapper userMapper; + + @Autowired + private SysRoleMapper roleMapper; + + @Autowired + private SysPostMapper postMapper; + + @Autowired + private SysUserRoleMapper userRoleMapper; + + @Autowired + private SysUserPostMapper userPostMapper; + + @Autowired + private ISysConfigService configService; + + @Autowired + private ISysDeptService deptService; + + @Autowired + protected Validator validator; + + /** + * 根据条件分页查询用户列表 + * + * @param user 用户信息 + * @return 用户信息集合信息 + */ + @Override + @DataScope(deptAlias = "d", userAlias = "u") + public List selectUserList(SysUser user) + { + return userMapper.selectUserList(user); + } + + /** + * 根据条件分页查询已分配用户角色列表 + * + * @param user 用户信息 + * @return 用户信息集合信息 + */ + @Override + @DataScope(deptAlias = "d", userAlias = "u") + public List selectAllocatedList(SysUser user) + { + return userMapper.selectAllocatedList(user); + } + + /** + * 根据条件分页查询未分配用户角色列表 + * + * @param user 用户信息 + * @return 用户信息集合信息 + */ + @Override + @DataScope(deptAlias = "d", userAlias = "u") + public List selectUnallocatedList(SysUser user) + { + return userMapper.selectUnallocatedList(user); + } + + /** + * 通过用户名查询用户 + * + * @param userName 用户名 + * @return 用户对象信息 + */ + @Override + public SysUser selectUserByUserName(String userName) + { + return userMapper.selectUserByUserName(userName); + } + + /** + * 通过用户ID查询用户 + * + * @param userId 用户ID + * @return 用户对象信息 + */ + @Override + public SysUser selectUserById(Long userId) + { + return userMapper.selectUserById(userId); + } + + /** + * 查询用户所属角色组 + * + * @param userName 用户名 + * @return 结果 + */ + @Override + public String selectUserRoleGroup(String userName) + { + List list = roleMapper.selectRolesByUserName(userName); + if (CollectionUtils.isEmpty(list)) + { + return StringUtils.EMPTY; + } + return list.stream().map(SysRole::getRoleName).collect(Collectors.joining(",")); + } + + /** + * 查询用户所属岗位组 + * + * @param userName 用户名 + * @return 结果 + */ + @Override + public String selectUserPostGroup(String userName) + { + List list = postMapper.selectPostsByUserName(userName); + if (CollectionUtils.isEmpty(list)) + { + return StringUtils.EMPTY; + } + return list.stream().map(SysPost::getPostName).collect(Collectors.joining(",")); + } + + /** + * 校验用户名称是否唯一 + * + * @param user 用户信息 + * @return 结果 + */ + @Override + public boolean checkUserNameUnique(SysUser user) + { + Long userId = StringUtils.isNull(user.getUserId()) ? -1L : user.getUserId(); + SysUser info = userMapper.checkUserNameUnique(user.getUserName()); + if (StringUtils.isNotNull(info) && info.getUserId().longValue() != userId.longValue()) + { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 校验手机号码是否唯一 + * + * @param user 用户信息 + * @return + */ + @Override + public boolean checkPhoneUnique(SysUser user) + { + Long userId = StringUtils.isNull(user.getUserId()) ? -1L : user.getUserId(); + SysUser info = userMapper.checkPhoneUnique(user.getPhonenumber()); + if (StringUtils.isNotNull(info) && info.getUserId().longValue() != userId.longValue()) + { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 校验email是否唯一 + * + * @param user 用户信息 + * @return + */ + @Override + public boolean checkEmailUnique(SysUser user) + { + Long userId = StringUtils.isNull(user.getUserId()) ? -1L : user.getUserId(); + SysUser info = userMapper.checkEmailUnique(user.getEmail()); + if (StringUtils.isNotNull(info) && info.getUserId().longValue() != userId.longValue()) + { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 校验用户是否允许操作 + * + * @param user 用户信息 + */ + @Override + public void checkUserAllowed(SysUser user) + { + if (StringUtils.isNotNull(user.getUserId()) && user.isAdmin()) + { + throw new ServiceException("不允许操作超级管理员用户"); + } + } + + /** + * 校验用户是否有数据权限 + * + * @param userId 用户id + */ + @Override + public void checkUserDataScope(Long userId) + { + if (!SecurityUtils.isAdmin()) + { + SysUser user = new SysUser(); + user.setUserId(userId); + List users = SpringUtils.getAopProxy(this).selectUserList(user); + if (StringUtils.isEmpty(users)) + { + throw new ServiceException("没有权限访问用户数据!"); + } + } + } + + /** + * 新增保存用户信息 + * + * @param user 用户信息 + * @return 结果 + */ + @Override + @Transactional + public int insertUser(SysUser user) + { + // 新增用户信息 + int rows = userMapper.insertUser(user); + // 新增用户岗位关联 + insertUserPost(user); + // 新增用户与角色管理 + insertUserRole(user); + return rows; + } + + /** + * 注册用户信息 + * + * @param user 用户信息 + * @return 结果 + */ + @Override + public boolean registerUser(SysUser user) + { + return userMapper.insertUser(user) > 0; + } + + /** + * 修改保存用户信息 + * + * @param user 用户信息 + * @return 结果 + */ + @Override + @Transactional + public int updateUser(SysUser user) + { + Long userId = user.getUserId(); + // 删除用户与角色关联 + userRoleMapper.deleteUserRoleByUserId(userId); + // 新增用户与角色管理 + insertUserRole(user); + // 删除用户与岗位关联 + userPostMapper.deleteUserPostByUserId(userId); + // 新增用户与岗位管理 + insertUserPost(user); + return userMapper.updateUser(user); + } + + /** + * 用户授权角色 + * + * @param userId 用户ID + * @param roleIds 角色组 + */ + @Override + @Transactional + public void insertUserAuth(Long userId, Long[] roleIds) + { + userRoleMapper.deleteUserRoleByUserId(userId); + insertUserRole(userId, roleIds); + } + + /** + * 修改用户状态 + * + * @param user 用户信息 + * @return 结果 + */ + @Override + public int updateUserStatus(SysUser user) + { + return userMapper.updateUserStatus(user.getUserId(), user.getStatus()); + } + + /** + * 修改用户基本信息 + * + * @param user 用户信息 + * @return 结果 + */ + @Override + public int updateUserProfile(SysUser user) + { + return userMapper.updateUser(user); + } + + /** + * 修改用户头像 + * + * @param userId 用户ID + * @param avatar 头像地址 + * @return 结果 + */ + @Override + public boolean updateUserAvatar(Long userId, String avatar) + { + return userMapper.updateUserAvatar(userId, avatar) > 0; + } + + /** + * 更新用户登录信息(IP和登录时间) + * + * @param userId 用户ID + * @param loginIp 登录IP地址 + * @param loginDate 登录时间 + * @return 结果 + */ + public void updateLoginInfo(Long userId, String loginIp, Date loginDate) + { + userMapper.updateLoginInfo(userId, loginIp, loginDate); + } + + /** + * 重置用户密码 + * + * @param user 用户信息 + * @return 结果 + */ + @Override + public int resetPwd(SysUser user) + { + return userMapper.resetUserPwd(user.getUserId(), user.getPassword()); + } + + /** + * 重置用户密码 + * + * @param userId 用户ID + * @param password 密码 + * @return 结果 + */ + @Override + public int resetUserPwd(Long userId, String password) + { + return userMapper.resetUserPwd(userId, password); + } + + /** + * 新增用户角色信息 + * + * @param user 用户对象 + */ + public void insertUserRole(SysUser user) + { + this.insertUserRole(user.getUserId(), user.getRoleIds()); + } + + /** + * 新增用户岗位信息 + * + * @param user 用户对象 + */ + public void insertUserPost(SysUser user) + { + Long[] posts = user.getPostIds(); + if (StringUtils.isNotEmpty(posts)) + { + // 新增用户与岗位管理 + List list = new ArrayList(posts.length); + for (Long postId : posts) + { + SysUserPost up = new SysUserPost(); + up.setUserId(user.getUserId()); + up.setPostId(postId); + list.add(up); + } + userPostMapper.batchUserPost(list); + } + } + + /** + * 新增用户角色信息 + * + * @param userId 用户ID + * @param roleIds 角色组 + */ + public void insertUserRole(Long userId, Long[] roleIds) + { + if (StringUtils.isNotEmpty(roleIds)) + { + // 新增用户与角色管理 + List list = new ArrayList(roleIds.length); + for (Long roleId : roleIds) + { + SysUserRole ur = new SysUserRole(); + ur.setUserId(userId); + ur.setRoleId(roleId); + list.add(ur); + } + userRoleMapper.batchUserRole(list); + } + } + + /** + * 通过用户ID删除用户 + * + * @param userId 用户ID + * @return 结果 + */ + @Override + @Transactional + public int deleteUserById(Long userId) + { + // 删除用户与角色关联 + userRoleMapper.deleteUserRoleByUserId(userId); + // 删除用户与岗位表 + userPostMapper.deleteUserPostByUserId(userId); + return userMapper.deleteUserById(userId); + } + + /** + * 批量删除用户信息 + * + * @param userIds 需要删除的用户ID + * @return 结果 + */ + @Override + @Transactional + public int deleteUserByIds(Long[] userIds) + { + for (Long userId : userIds) + { + checkUserAllowed(new SysUser(userId)); + checkUserDataScope(userId); + } + // 删除用户与角色关联 + userRoleMapper.deleteUserRole(userIds); + // 删除用户与岗位关联 + userPostMapper.deleteUserPost(userIds); + return userMapper.deleteUserByIds(userIds); + } + + /** + * 导入用户数据 + * + * @param userList 用户数据列表 + * @param isUpdateSupport 是否更新支持,如果已存在,则进行更新数据 + * @param operName 操作用户 + * @return 结果 + */ + @Override + public String importUser(List userList, Boolean isUpdateSupport, String operName) + { + if (StringUtils.isNull(userList) || userList.size() == 0) + { + throw new ServiceException("导入用户数据不能为空!"); + } + int successNum = 0; + int failureNum = 0; + StringBuilder successMsg = new StringBuilder(); + StringBuilder failureMsg = new StringBuilder(); + for (SysUser user : userList) + { + try + { + // 验证是否存在这个用户 + SysUser u = userMapper.selectUserByUserName(user.getUserName()); + if (StringUtils.isNull(u)) + { + BeanValidators.validateWithException(validator, user); + deptService.checkDeptDataScope(user.getDeptId()); + String password = configService.selectConfigByKey("sys.user.initPassword"); + user.setPassword(SecurityUtils.encryptPassword(password)); + user.setCreateBy(operName); + userMapper.insertUser(user); + successNum++; + successMsg.append("
" + successNum + "、账号 " + user.getUserName() + " 导入成功"); + } + else if (isUpdateSupport) + { + BeanValidators.validateWithException(validator, user); + checkUserAllowed(u); + checkUserDataScope(u.getUserId()); + deptService.checkDeptDataScope(user.getDeptId()); + user.setUserId(u.getUserId()); + user.setDeptId(u.getDeptId()); + user.setUpdateBy(operName); + userMapper.updateUser(user); + successNum++; + successMsg.append("
" + successNum + "、账号 " + user.getUserName() + " 更新成功"); + } + else + { + failureNum++; + failureMsg.append("
" + failureNum + "、账号 " + user.getUserName() + " 已存在"); + } + } + catch (Exception e) + { + failureNum++; + String msg = "
" + failureNum + "、账号 " + user.getUserName() + " 导入失败:"; + failureMsg.append(msg + e.getMessage()); + log.error(msg, e); + } + } + if (failureNum > 0) + { + failureMsg.insert(0, "很抱歉,导入失败!共 " + failureNum + " 条数据格式不正确,错误如下:"); + throw new ServiceException(failureMsg.toString()); + } + else + { + successMsg.insert(0, "恭喜您,数据已全部导入成功!共 " + successNum + " 条,数据如下:"); + } + return successMsg.toString(); + } +} diff --git a/portal/backend/portal-system/src/main/resources/mapper/system/SysConfigMapper.xml b/portal/backend/portal-system/src/main/resources/mapper/system/SysConfigMapper.xml new file mode 100644 index 0000000..d8b2154 --- /dev/null +++ b/portal/backend/portal-system/src/main/resources/mapper/system/SysConfigMapper.xml @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + select config_id, config_name, config_key, config_value, config_type, create_by, create_time, update_by, update_time, remark + from sys_config + + + + + + + and config_id = #{configId} + + + and config_key = #{configKey} + + + + + + + + + + + + + + insert into sys_config ( + config_name, + config_key, + config_value, + config_type, + create_by, + remark, + create_time + )values( + #{configName}, + #{configKey}, + #{configValue}, + #{configType}, + #{createBy}, + #{remark}, + sysdate() + ) + + + + update sys_config + + config_name = #{configName}, + config_key = #{configKey}, + config_value = #{configValue}, + config_type = #{configType}, + update_by = #{updateBy}, + remark = #{remark}, + update_time = sysdate() + + where config_id = #{configId} + + + + delete from sys_config where config_id = #{configId} + + + + delete from sys_config where config_id in + + #{configId} + + + + \ No newline at end of file diff --git a/portal/backend/portal-system/src/main/resources/mapper/system/SysDeptMapper.xml b/portal/backend/portal-system/src/main/resources/mapper/system/SysDeptMapper.xml new file mode 100644 index 0000000..158f99c --- /dev/null +++ b/portal/backend/portal-system/src/main/resources/mapper/system/SysDeptMapper.xml @@ -0,0 +1,159 @@ + + + + + + + + + + + + + + + + + + + + + + + + select d.dept_id, d.parent_id, d.ancestors, d.dept_name, d.order_num, d.leader, d.phone, d.email, d.status, d.del_flag, d.create_by, d.create_time + from sys_dept d + + + + + + + + + + + + + + + + + + + + insert into sys_dept( + dept_id, + parent_id, + dept_name, + ancestors, + order_num, + leader, + phone, + email, + status, + create_by, + create_time + )values( + #{deptId}, + #{parentId}, + #{deptName}, + #{ancestors}, + #{orderNum}, + #{leader}, + #{phone}, + #{email}, + #{status}, + #{createBy}, + sysdate() + ) + + + + update sys_dept + + parent_id = #{parentId}, + dept_name = #{deptName}, + ancestors = #{ancestors}, + order_num = #{orderNum}, + leader = #{leader}, + phone = #{phone}, + email = #{email}, + status = #{status}, + update_by = #{updateBy}, + update_time = sysdate() + + where dept_id = #{deptId} + + + + update sys_dept set ancestors = + + when #{item.deptId} then #{item.ancestors} + + where dept_id in + + #{item.deptId} + + + + + update sys_dept set status = '0' where dept_id in + + #{deptId} + + + + + update sys_dept set del_flag = '2' where dept_id = #{deptId} + + + \ No newline at end of file diff --git a/portal/backend/portal-system/src/main/resources/mapper/system/SysDictDataMapper.xml b/portal/backend/portal-system/src/main/resources/mapper/system/SysDictDataMapper.xml new file mode 100644 index 0000000..fc85249 --- /dev/null +++ b/portal/backend/portal-system/src/main/resources/mapper/system/SysDictDataMapper.xml @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + select dict_code, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, create_by, create_time, remark + from sys_dict_data + + + + + + + + + + + + + + delete from sys_dict_data where dict_code = #{dictCode} + + + + delete from sys_dict_data where dict_code in + + #{dictCode} + + + + + update sys_dict_data + + dict_sort = #{dictSort}, + dict_label = #{dictLabel}, + dict_value = #{dictValue}, + dict_type = #{dictType}, + css_class = #{cssClass}, + list_class = #{listClass}, + is_default = #{isDefault}, + status = #{status}, + remark = #{remark}, + update_by = #{updateBy}, + update_time = sysdate() + + where dict_code = #{dictCode} + + + + update sys_dict_data set dict_type = #{newDictType} where dict_type = #{oldDictType} + + + + insert into sys_dict_data( + dict_sort, + dict_label, + dict_value, + dict_type, + css_class, + list_class, + is_default, + status, + remark, + create_by, + create_time + )values( + #{dictSort}, + #{dictLabel}, + #{dictValue}, + #{dictType}, + #{cssClass}, + #{listClass}, + #{isDefault}, + #{status}, + #{remark}, + #{createBy}, + sysdate() + ) + + + \ No newline at end of file diff --git a/portal/backend/portal-system/src/main/resources/mapper/system/SysDictTypeMapper.xml b/portal/backend/portal-system/src/main/resources/mapper/system/SysDictTypeMapper.xml new file mode 100644 index 0000000..ea83654 --- /dev/null +++ b/portal/backend/portal-system/src/main/resources/mapper/system/SysDictTypeMapper.xml @@ -0,0 +1,105 @@ + + + + + + + + + + + + + + + + + select dict_id, dict_name, dict_type, status, create_by, create_time, remark + from sys_dict_type + + + + + + + + + + + + + + delete from sys_dict_type where dict_id = #{dictId} + + + + delete from sys_dict_type where dict_id in + + #{dictId} + + + + + update sys_dict_type + + dict_name = #{dictName}, + dict_type = #{dictType}, + status = #{status}, + remark = #{remark}, + update_by = #{updateBy}, + update_time = sysdate() + + where dict_id = #{dictId} + + + + insert into sys_dict_type( + dict_name, + dict_type, + status, + remark, + create_by, + create_time + )values( + #{dictName}, + #{dictType}, + #{status}, + #{remark}, + #{createBy}, + sysdate() + ) + + + \ No newline at end of file diff --git a/portal/backend/portal-system/src/main/resources/mapper/system/SysLogininforMapper.xml b/portal/backend/portal-system/src/main/resources/mapper/system/SysLogininforMapper.xml new file mode 100644 index 0000000..20c1b46 --- /dev/null +++ b/portal/backend/portal-system/src/main/resources/mapper/system/SysLogininforMapper.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + insert into sys_logininfor (user_name, status, ipaddr, login_location, browser, os, msg, login_time) + values (#{userName}, #{status}, #{ipaddr}, #{loginLocation}, #{browser}, #{os}, #{msg}, sysdate()) + + + + + + delete from sys_logininfor where info_id in + + #{infoId} + + + + + truncate table sys_logininfor + + + \ No newline at end of file diff --git a/portal/backend/portal-system/src/main/resources/mapper/system/SysMenuMapper.xml b/portal/backend/portal-system/src/main/resources/mapper/system/SysMenuMapper.xml new file mode 100644 index 0000000..d276081 --- /dev/null +++ b/portal/backend/portal-system/src/main/resources/mapper/system/SysMenuMapper.xml @@ -0,0 +1,211 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + select menu_id, menu_name, parent_id, order_num, path, component, `query`, route_name, is_frame, is_cache, menu_type, visible, status, ifnull(perms,'') as perms, icon, create_time + from sys_menu + + + + + + + + + + + + + + + + + + + + + + + + + + + + update sys_menu + + menu_name = #{menuName}, + parent_id = #{parentId}, + order_num = #{orderNum}, + path = #{path}, + component = #{component}, + `query` = #{query}, + route_name = #{routeName}, + is_frame = #{isFrame}, + is_cache = #{isCache}, + menu_type = #{menuType}, + visible = #{visible}, + status = #{status}, + perms = #{perms}, + icon = #{icon}, + remark = #{remark}, + update_by = #{updateBy}, + update_time = sysdate() + + where menu_id = #{menuId} + + + + insert into sys_menu( + menu_id, + parent_id, + menu_name, + order_num, + path, + component, + `query`, + route_name, + is_frame, + is_cache, + menu_type, + visible, + status, + perms, + icon, + remark, + create_by, + create_time + )values( + #{menuId}, + #{parentId}, + #{menuName}, + #{orderNum}, + #{path}, + #{component}, + #{query}, + #{routeName}, + #{isFrame}, + #{isCache}, + #{menuType}, + #{visible}, + #{status}, + #{perms}, + #{icon}, + #{remark}, + #{createBy}, + sysdate() + ) + + + + delete from sys_menu where menu_id = #{menuId} + + + \ No newline at end of file diff --git a/portal/backend/portal-system/src/main/resources/mapper/system/SysNoticeMapper.xml b/portal/backend/portal-system/src/main/resources/mapper/system/SysNoticeMapper.xml new file mode 100644 index 0000000..c8db952 --- /dev/null +++ b/portal/backend/portal-system/src/main/resources/mapper/system/SysNoticeMapper.xml @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + + + select notice_id, notice_title, notice_type, cast(notice_content as char) as notice_content, status, create_by, create_time, update_by, update_time, remark + from sys_notice + + + + + + + + insert into sys_notice ( + notice_title, + notice_type, + notice_content, + status, + remark, + create_by, + create_time + )values( + #{noticeTitle}, + #{noticeType}, + #{noticeContent}, + #{status}, + #{remark}, + #{createBy}, + sysdate() + ) + + + + update sys_notice + + notice_title = #{noticeTitle}, + notice_type = #{noticeType}, + notice_content = #{noticeContent}, + status = #{status}, + update_by = #{updateBy}, + update_time = sysdate() + + where notice_id = #{noticeId} + + + + delete from sys_notice where notice_id = #{noticeId} + + + + delete from sys_notice where notice_id in + + #{noticeId} + + + + \ No newline at end of file diff --git a/portal/backend/portal-system/src/main/resources/mapper/system/SysOperLogMapper.xml b/portal/backend/portal-system/src/main/resources/mapper/system/SysOperLogMapper.xml new file mode 100644 index 0000000..9b28a6b --- /dev/null +++ b/portal/backend/portal-system/src/main/resources/mapper/system/SysOperLogMapper.xml @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + select oper_id, title, business_type, method, request_method, operator_type, oper_name, dept_name, oper_url, oper_ip, oper_location, oper_param, json_result, status, error_msg, oper_time, cost_time + from sys_oper_log + + + + insert into sys_oper_log(title, business_type, method, request_method, operator_type, oper_name, dept_name, oper_url, oper_ip, oper_location, oper_param, json_result, status, error_msg, cost_time, oper_time) + values (#{title}, #{businessType}, #{method}, #{requestMethod}, #{operatorType}, #{operName}, #{deptName}, #{operUrl}, #{operIp}, #{operLocation}, #{operParam}, #{jsonResult}, #{status}, #{errorMsg}, #{costTime}, sysdate()) + + + + + + delete from sys_oper_log where oper_id in + + #{operId} + + + + + + + truncate table sys_oper_log + + + \ No newline at end of file diff --git a/portal/backend/portal-system/src/main/resources/mapper/system/SysPostMapper.xml b/portal/backend/portal-system/src/main/resources/mapper/system/SysPostMapper.xml new file mode 100644 index 0000000..1bb7b18 --- /dev/null +++ b/portal/backend/portal-system/src/main/resources/mapper/system/SysPostMapper.xml @@ -0,0 +1,122 @@ + + + + + + + + + + + + + + + + + + + select post_id, post_code, post_name, post_sort, status, create_by, create_time, remark + from sys_post + + + + + + + + + + + + + + + + + + update sys_post + + post_code = #{postCode}, + post_name = #{postName}, + post_sort = #{postSort}, + status = #{status}, + remark = #{remark}, + update_by = #{updateBy}, + update_time = sysdate() + + where post_id = #{postId} + + + + insert into sys_post( + post_id, + post_code, + post_name, + post_sort, + status, + remark, + create_by, + create_time + )values( + #{postId}, + #{postCode}, + #{postName}, + #{postSort}, + #{status}, + #{remark}, + #{createBy}, + sysdate() + ) + + + + delete from sys_post where post_id = #{postId} + + + + delete from sys_post where post_id in + + #{postId} + + + + \ No newline at end of file diff --git a/portal/backend/portal-system/src/main/resources/mapper/system/SysRoleDeptMapper.xml b/portal/backend/portal-system/src/main/resources/mapper/system/SysRoleDeptMapper.xml new file mode 100644 index 0000000..34b89e4 --- /dev/null +++ b/portal/backend/portal-system/src/main/resources/mapper/system/SysRoleDeptMapper.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + delete from sys_role_dept where role_id=#{roleId} + + + + + + delete from sys_role_dept where role_id in + + #{roleId} + + + + + insert into sys_role_dept(role_id, dept_id) values + + (#{item.roleId},#{item.deptId}) + + + + \ No newline at end of file diff --git a/portal/backend/portal-system/src/main/resources/mapper/system/SysRoleMapper.xml b/portal/backend/portal-system/src/main/resources/mapper/system/SysRoleMapper.xml new file mode 100644 index 0000000..a87dede --- /dev/null +++ b/portal/backend/portal-system/src/main/resources/mapper/system/SysRoleMapper.xml @@ -0,0 +1,152 @@ + + + + + + + + + + + + + + + + + + + + + + + select distinct r.role_id, r.role_name, r.role_key, r.role_sort, r.data_scope, r.menu_check_strictly, r.dept_check_strictly, + r.status, r.del_flag, r.create_time, r.remark + from sys_role r + left join sys_user_role ur on ur.role_id = r.role_id + left join sys_user u on u.user_id = ur.user_id + left join sys_dept d on u.dept_id = d.dept_id + + + + + + + + + + + + + + + + + + + + insert into sys_role( + role_id, + role_name, + role_key, + role_sort, + data_scope, + menu_check_strictly, + dept_check_strictly, + status, + remark, + create_by, + create_time + )values( + #{roleId}, + #{roleName}, + #{roleKey}, + #{roleSort}, + #{dataScope}, + #{menuCheckStrictly}, + #{deptCheckStrictly}, + #{status}, + #{remark}, + #{createBy}, + sysdate() + ) + + + + update sys_role + + role_name = #{roleName}, + role_key = #{roleKey}, + role_sort = #{roleSort}, + data_scope = #{dataScope}, + menu_check_strictly = #{menuCheckStrictly}, + dept_check_strictly = #{deptCheckStrictly}, + status = #{status}, + remark = #{remark}, + update_by = #{updateBy}, + update_time = sysdate() + + where role_id = #{roleId} + + + + update sys_role set del_flag = '2' where role_id = #{roleId} + + + + update sys_role set del_flag = '2' where role_id in + + #{roleId} + + + + \ No newline at end of file diff --git a/portal/backend/portal-system/src/main/resources/mapper/system/SysRoleMenuMapper.xml b/portal/backend/portal-system/src/main/resources/mapper/system/SysRoleMenuMapper.xml new file mode 100644 index 0000000..0204ab9 --- /dev/null +++ b/portal/backend/portal-system/src/main/resources/mapper/system/SysRoleMenuMapper.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + delete from sys_role_menu where role_id=#{roleId} + + + + delete from sys_role_menu where role_id in + + #{roleId} + + + + + insert into sys_role_menu(role_id, menu_id) values + + (#{item.roleId},#{item.menuId}) + + + + \ No newline at end of file diff --git a/portal/backend/portal-system/src/main/resources/mapper/system/SysUserMapper.xml b/portal/backend/portal-system/src/main/resources/mapper/system/SysUserMapper.xml new file mode 100644 index 0000000..cd19bcb --- /dev/null +++ b/portal/backend/portal-system/src/main/resources/mapper/system/SysUserMapper.xml @@ -0,0 +1,227 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + select u.user_id, u.dept_id, u.user_name, u.nick_name, u.email, u.avatar, u.phonenumber, u.password, u.sex, u.status, u.del_flag, u.login_ip, u.login_date, u.pwd_update_date, u.create_by, u.create_time, u.remark, + d.dept_id, d.parent_id, d.ancestors, d.dept_name, d.order_num, d.leader, d.status as dept_status, + r.role_id, r.role_name, r.role_key, r.role_sort, r.data_scope, r.status as role_status + from sys_user u + left join sys_dept d on u.dept_id = d.dept_id + left join sys_user_role ur on u.user_id = ur.user_id + left join sys_role r on r.role_id = ur.role_id + + + + + + + + + + + + + + + + + + + + insert into sys_user( + user_id, + dept_id, + user_name, + nick_name, + email, + avatar, + phonenumber, + sex, + password, + status, + pwd_update_date, + create_by, + remark, + create_time + )values( + #{userId}, + #{deptId}, + #{userName}, + #{nickName}, + #{email}, + #{avatar}, + #{phonenumber}, + #{sex}, + #{password}, + #{status}, + #{pwdUpdateDate}, + #{createBy}, + #{remark}, + sysdate() + ) + + + + update sys_user + + dept_id = #{deptId}, + nick_name = #{nickName}, + email = #{email}, + phonenumber = #{phonenumber}, + sex = #{sex}, + avatar = #{avatar}, + password = #{password}, + status = #{status}, + login_ip = #{loginIp}, + login_date = #{loginDate}, + update_by = #{updateBy}, + remark = #{remark}, + update_time = sysdate() + + where user_id = #{userId} + + + + update sys_user set status = #{status}, update_time = sysdate() where user_id = #{userId} + + + + update sys_user set avatar = #{avatar}, update_time = sysdate() where user_id = #{userId} + + + + update sys_user set login_ip = #{loginIp}, login_date = #{loginDate} where user_id = #{userId} + + + + update sys_user set pwd_update_date = sysdate(), password = #{password}, update_time = sysdate() where user_id = #{userId} + + + + update sys_user set del_flag = '2' where user_id = #{userId} + + + + update sys_user set del_flag = '2' where user_id in + + #{userId} + + + + \ No newline at end of file diff --git a/portal/backend/portal-system/src/main/resources/mapper/system/SysUserPostMapper.xml b/portal/backend/portal-system/src/main/resources/mapper/system/SysUserPostMapper.xml new file mode 100644 index 0000000..875aed2 --- /dev/null +++ b/portal/backend/portal-system/src/main/resources/mapper/system/SysUserPostMapper.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + delete from sys_user_post where user_id=#{userId} + + + + + + delete from sys_user_post where user_id in + + #{userId} + + + + + insert into sys_user_post(user_id, post_id) values + + (#{item.userId},#{item.postId}) + + + + \ No newline at end of file diff --git a/portal/backend/portal-system/src/main/resources/mapper/system/SysUserRoleMapper.xml b/portal/backend/portal-system/src/main/resources/mapper/system/SysUserRoleMapper.xml new file mode 100644 index 0000000..a9c0f2a --- /dev/null +++ b/portal/backend/portal-system/src/main/resources/mapper/system/SysUserRoleMapper.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + delete from sys_user_role where user_id=#{userId} + + + + + + delete from sys_user_role where user_id in + + #{userId} + + + + + insert into sys_user_role(user_id, role_id) values + + (#{item.userId},#{item.roleId}) + + + + + delete from sys_user_role where user_id=#{userId} and role_id=#{roleId} + + + + delete from sys_user_role where role_id=#{roleId} and user_id in + + #{userId} + + + \ No newline at end of file diff --git a/portal/backend/portal-system/target/classes/mapper/system/SysConfigMapper.xml b/portal/backend/portal-system/target/classes/mapper/system/SysConfigMapper.xml new file mode 100644 index 0000000..d8b2154 --- /dev/null +++ b/portal/backend/portal-system/target/classes/mapper/system/SysConfigMapper.xml @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + select config_id, config_name, config_key, config_value, config_type, create_by, create_time, update_by, update_time, remark + from sys_config + + + + + + + and config_id = #{configId} + + + and config_key = #{configKey} + + + + + + + + + + + + + + insert into sys_config ( + config_name, + config_key, + config_value, + config_type, + create_by, + remark, + create_time + )values( + #{configName}, + #{configKey}, + #{configValue}, + #{configType}, + #{createBy}, + #{remark}, + sysdate() + ) + + + + update sys_config + + config_name = #{configName}, + config_key = #{configKey}, + config_value = #{configValue}, + config_type = #{configType}, + update_by = #{updateBy}, + remark = #{remark}, + update_time = sysdate() + + where config_id = #{configId} + + + + delete from sys_config where config_id = #{configId} + + + + delete from sys_config where config_id in + + #{configId} + + + + \ No newline at end of file diff --git a/portal/backend/portal-system/target/classes/mapper/system/SysDeptMapper.xml b/portal/backend/portal-system/target/classes/mapper/system/SysDeptMapper.xml new file mode 100644 index 0000000..158f99c --- /dev/null +++ b/portal/backend/portal-system/target/classes/mapper/system/SysDeptMapper.xml @@ -0,0 +1,159 @@ + + + + + + + + + + + + + + + + + + + + + + + + select d.dept_id, d.parent_id, d.ancestors, d.dept_name, d.order_num, d.leader, d.phone, d.email, d.status, d.del_flag, d.create_by, d.create_time + from sys_dept d + + + + + + + + + + + + + + + + + + + + insert into sys_dept( + dept_id, + parent_id, + dept_name, + ancestors, + order_num, + leader, + phone, + email, + status, + create_by, + create_time + )values( + #{deptId}, + #{parentId}, + #{deptName}, + #{ancestors}, + #{orderNum}, + #{leader}, + #{phone}, + #{email}, + #{status}, + #{createBy}, + sysdate() + ) + + + + update sys_dept + + parent_id = #{parentId}, + dept_name = #{deptName}, + ancestors = #{ancestors}, + order_num = #{orderNum}, + leader = #{leader}, + phone = #{phone}, + email = #{email}, + status = #{status}, + update_by = #{updateBy}, + update_time = sysdate() + + where dept_id = #{deptId} + + + + update sys_dept set ancestors = + + when #{item.deptId} then #{item.ancestors} + + where dept_id in + + #{item.deptId} + + + + + update sys_dept set status = '0' where dept_id in + + #{deptId} + + + + + update sys_dept set del_flag = '2' where dept_id = #{deptId} + + + \ No newline at end of file diff --git a/portal/backend/portal-system/target/classes/mapper/system/SysDictDataMapper.xml b/portal/backend/portal-system/target/classes/mapper/system/SysDictDataMapper.xml new file mode 100644 index 0000000..fc85249 --- /dev/null +++ b/portal/backend/portal-system/target/classes/mapper/system/SysDictDataMapper.xml @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + select dict_code, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, create_by, create_time, remark + from sys_dict_data + + + + + + + + + + + + + + delete from sys_dict_data where dict_code = #{dictCode} + + + + delete from sys_dict_data where dict_code in + + #{dictCode} + + + + + update sys_dict_data + + dict_sort = #{dictSort}, + dict_label = #{dictLabel}, + dict_value = #{dictValue}, + dict_type = #{dictType}, + css_class = #{cssClass}, + list_class = #{listClass}, + is_default = #{isDefault}, + status = #{status}, + remark = #{remark}, + update_by = #{updateBy}, + update_time = sysdate() + + where dict_code = #{dictCode} + + + + update sys_dict_data set dict_type = #{newDictType} where dict_type = #{oldDictType} + + + + insert into sys_dict_data( + dict_sort, + dict_label, + dict_value, + dict_type, + css_class, + list_class, + is_default, + status, + remark, + create_by, + create_time + )values( + #{dictSort}, + #{dictLabel}, + #{dictValue}, + #{dictType}, + #{cssClass}, + #{listClass}, + #{isDefault}, + #{status}, + #{remark}, + #{createBy}, + sysdate() + ) + + + \ No newline at end of file diff --git a/portal/backend/portal-system/target/classes/mapper/system/SysDictTypeMapper.xml b/portal/backend/portal-system/target/classes/mapper/system/SysDictTypeMapper.xml new file mode 100644 index 0000000..ea83654 --- /dev/null +++ b/portal/backend/portal-system/target/classes/mapper/system/SysDictTypeMapper.xml @@ -0,0 +1,105 @@ + + + + + + + + + + + + + + + + + select dict_id, dict_name, dict_type, status, create_by, create_time, remark + from sys_dict_type + + + + + + + + + + + + + + delete from sys_dict_type where dict_id = #{dictId} + + + + delete from sys_dict_type where dict_id in + + #{dictId} + + + + + update sys_dict_type + + dict_name = #{dictName}, + dict_type = #{dictType}, + status = #{status}, + remark = #{remark}, + update_by = #{updateBy}, + update_time = sysdate() + + where dict_id = #{dictId} + + + + insert into sys_dict_type( + dict_name, + dict_type, + status, + remark, + create_by, + create_time + )values( + #{dictName}, + #{dictType}, + #{status}, + #{remark}, + #{createBy}, + sysdate() + ) + + + \ No newline at end of file diff --git a/portal/backend/portal-system/target/classes/mapper/system/SysLogininforMapper.xml b/portal/backend/portal-system/target/classes/mapper/system/SysLogininforMapper.xml new file mode 100644 index 0000000..20c1b46 --- /dev/null +++ b/portal/backend/portal-system/target/classes/mapper/system/SysLogininforMapper.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + insert into sys_logininfor (user_name, status, ipaddr, login_location, browser, os, msg, login_time) + values (#{userName}, #{status}, #{ipaddr}, #{loginLocation}, #{browser}, #{os}, #{msg}, sysdate()) + + + + + + delete from sys_logininfor where info_id in + + #{infoId} + + + + + truncate table sys_logininfor + + + \ No newline at end of file diff --git a/portal/backend/portal-system/target/classes/mapper/system/SysMenuMapper.xml b/portal/backend/portal-system/target/classes/mapper/system/SysMenuMapper.xml new file mode 100644 index 0000000..d276081 --- /dev/null +++ b/portal/backend/portal-system/target/classes/mapper/system/SysMenuMapper.xml @@ -0,0 +1,211 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + select menu_id, menu_name, parent_id, order_num, path, component, `query`, route_name, is_frame, is_cache, menu_type, visible, status, ifnull(perms,'') as perms, icon, create_time + from sys_menu + + + + + + + + + + + + + + + + + + + + + + + + + + + + update sys_menu + + menu_name = #{menuName}, + parent_id = #{parentId}, + order_num = #{orderNum}, + path = #{path}, + component = #{component}, + `query` = #{query}, + route_name = #{routeName}, + is_frame = #{isFrame}, + is_cache = #{isCache}, + menu_type = #{menuType}, + visible = #{visible}, + status = #{status}, + perms = #{perms}, + icon = #{icon}, + remark = #{remark}, + update_by = #{updateBy}, + update_time = sysdate() + + where menu_id = #{menuId} + + + + insert into sys_menu( + menu_id, + parent_id, + menu_name, + order_num, + path, + component, + `query`, + route_name, + is_frame, + is_cache, + menu_type, + visible, + status, + perms, + icon, + remark, + create_by, + create_time + )values( + #{menuId}, + #{parentId}, + #{menuName}, + #{orderNum}, + #{path}, + #{component}, + #{query}, + #{routeName}, + #{isFrame}, + #{isCache}, + #{menuType}, + #{visible}, + #{status}, + #{perms}, + #{icon}, + #{remark}, + #{createBy}, + sysdate() + ) + + + + delete from sys_menu where menu_id = #{menuId} + + + \ No newline at end of file diff --git a/portal/backend/portal-system/target/classes/mapper/system/SysNoticeMapper.xml b/portal/backend/portal-system/target/classes/mapper/system/SysNoticeMapper.xml new file mode 100644 index 0000000..c8db952 --- /dev/null +++ b/portal/backend/portal-system/target/classes/mapper/system/SysNoticeMapper.xml @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + + + select notice_id, notice_title, notice_type, cast(notice_content as char) as notice_content, status, create_by, create_time, update_by, update_time, remark + from sys_notice + + + + + + + + insert into sys_notice ( + notice_title, + notice_type, + notice_content, + status, + remark, + create_by, + create_time + )values( + #{noticeTitle}, + #{noticeType}, + #{noticeContent}, + #{status}, + #{remark}, + #{createBy}, + sysdate() + ) + + + + update sys_notice + + notice_title = #{noticeTitle}, + notice_type = #{noticeType}, + notice_content = #{noticeContent}, + status = #{status}, + update_by = #{updateBy}, + update_time = sysdate() + + where notice_id = #{noticeId} + + + + delete from sys_notice where notice_id = #{noticeId} + + + + delete from sys_notice where notice_id in + + #{noticeId} + + + + \ No newline at end of file diff --git a/portal/backend/portal-system/target/classes/mapper/system/SysOperLogMapper.xml b/portal/backend/portal-system/target/classes/mapper/system/SysOperLogMapper.xml new file mode 100644 index 0000000..9b28a6b --- /dev/null +++ b/portal/backend/portal-system/target/classes/mapper/system/SysOperLogMapper.xml @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + select oper_id, title, business_type, method, request_method, operator_type, oper_name, dept_name, oper_url, oper_ip, oper_location, oper_param, json_result, status, error_msg, oper_time, cost_time + from sys_oper_log + + + + insert into sys_oper_log(title, business_type, method, request_method, operator_type, oper_name, dept_name, oper_url, oper_ip, oper_location, oper_param, json_result, status, error_msg, cost_time, oper_time) + values (#{title}, #{businessType}, #{method}, #{requestMethod}, #{operatorType}, #{operName}, #{deptName}, #{operUrl}, #{operIp}, #{operLocation}, #{operParam}, #{jsonResult}, #{status}, #{errorMsg}, #{costTime}, sysdate()) + + + + + + delete from sys_oper_log where oper_id in + + #{operId} + + + + + + + truncate table sys_oper_log + + + \ No newline at end of file diff --git a/portal/backend/portal-system/target/classes/mapper/system/SysPostMapper.xml b/portal/backend/portal-system/target/classes/mapper/system/SysPostMapper.xml new file mode 100644 index 0000000..1bb7b18 --- /dev/null +++ b/portal/backend/portal-system/target/classes/mapper/system/SysPostMapper.xml @@ -0,0 +1,122 @@ + + + + + + + + + + + + + + + + + + + select post_id, post_code, post_name, post_sort, status, create_by, create_time, remark + from sys_post + + + + + + + + + + + + + + + + + + update sys_post + + post_code = #{postCode}, + post_name = #{postName}, + post_sort = #{postSort}, + status = #{status}, + remark = #{remark}, + update_by = #{updateBy}, + update_time = sysdate() + + where post_id = #{postId} + + + + insert into sys_post( + post_id, + post_code, + post_name, + post_sort, + status, + remark, + create_by, + create_time + )values( + #{postId}, + #{postCode}, + #{postName}, + #{postSort}, + #{status}, + #{remark}, + #{createBy}, + sysdate() + ) + + + + delete from sys_post where post_id = #{postId} + + + + delete from sys_post where post_id in + + #{postId} + + + + \ No newline at end of file diff --git a/portal/backend/portal-system/target/classes/mapper/system/SysRoleDeptMapper.xml b/portal/backend/portal-system/target/classes/mapper/system/SysRoleDeptMapper.xml new file mode 100644 index 0000000..34b89e4 --- /dev/null +++ b/portal/backend/portal-system/target/classes/mapper/system/SysRoleDeptMapper.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + delete from sys_role_dept where role_id=#{roleId} + + + + + + delete from sys_role_dept where role_id in + + #{roleId} + + + + + insert into sys_role_dept(role_id, dept_id) values + + (#{item.roleId},#{item.deptId}) + + + + \ No newline at end of file diff --git a/portal/backend/portal-system/target/classes/mapper/system/SysRoleMapper.xml b/portal/backend/portal-system/target/classes/mapper/system/SysRoleMapper.xml new file mode 100644 index 0000000..a87dede --- /dev/null +++ b/portal/backend/portal-system/target/classes/mapper/system/SysRoleMapper.xml @@ -0,0 +1,152 @@ + + + + + + + + + + + + + + + + + + + + + + + select distinct r.role_id, r.role_name, r.role_key, r.role_sort, r.data_scope, r.menu_check_strictly, r.dept_check_strictly, + r.status, r.del_flag, r.create_time, r.remark + from sys_role r + left join sys_user_role ur on ur.role_id = r.role_id + left join sys_user u on u.user_id = ur.user_id + left join sys_dept d on u.dept_id = d.dept_id + + + + + + + + + + + + + + + + + + + + insert into sys_role( + role_id, + role_name, + role_key, + role_sort, + data_scope, + menu_check_strictly, + dept_check_strictly, + status, + remark, + create_by, + create_time + )values( + #{roleId}, + #{roleName}, + #{roleKey}, + #{roleSort}, + #{dataScope}, + #{menuCheckStrictly}, + #{deptCheckStrictly}, + #{status}, + #{remark}, + #{createBy}, + sysdate() + ) + + + + update sys_role + + role_name = #{roleName}, + role_key = #{roleKey}, + role_sort = #{roleSort}, + data_scope = #{dataScope}, + menu_check_strictly = #{menuCheckStrictly}, + dept_check_strictly = #{deptCheckStrictly}, + status = #{status}, + remark = #{remark}, + update_by = #{updateBy}, + update_time = sysdate() + + where role_id = #{roleId} + + + + update sys_role set del_flag = '2' where role_id = #{roleId} + + + + update sys_role set del_flag = '2' where role_id in + + #{roleId} + + + + \ No newline at end of file diff --git a/portal/backend/portal-system/target/classes/mapper/system/SysRoleMenuMapper.xml b/portal/backend/portal-system/target/classes/mapper/system/SysRoleMenuMapper.xml new file mode 100644 index 0000000..0204ab9 --- /dev/null +++ b/portal/backend/portal-system/target/classes/mapper/system/SysRoleMenuMapper.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + delete from sys_role_menu where role_id=#{roleId} + + + + delete from sys_role_menu where role_id in + + #{roleId} + + + + + insert into sys_role_menu(role_id, menu_id) values + + (#{item.roleId},#{item.menuId}) + + + + \ No newline at end of file diff --git a/portal/backend/portal-system/target/classes/mapper/system/SysUserMapper.xml b/portal/backend/portal-system/target/classes/mapper/system/SysUserMapper.xml new file mode 100644 index 0000000..cd19bcb --- /dev/null +++ b/portal/backend/portal-system/target/classes/mapper/system/SysUserMapper.xml @@ -0,0 +1,227 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + select u.user_id, u.dept_id, u.user_name, u.nick_name, u.email, u.avatar, u.phonenumber, u.password, u.sex, u.status, u.del_flag, u.login_ip, u.login_date, u.pwd_update_date, u.create_by, u.create_time, u.remark, + d.dept_id, d.parent_id, d.ancestors, d.dept_name, d.order_num, d.leader, d.status as dept_status, + r.role_id, r.role_name, r.role_key, r.role_sort, r.data_scope, r.status as role_status + from sys_user u + left join sys_dept d on u.dept_id = d.dept_id + left join sys_user_role ur on u.user_id = ur.user_id + left join sys_role r on r.role_id = ur.role_id + + + + + + + + + + + + + + + + + + + + insert into sys_user( + user_id, + dept_id, + user_name, + nick_name, + email, + avatar, + phonenumber, + sex, + password, + status, + pwd_update_date, + create_by, + remark, + create_time + )values( + #{userId}, + #{deptId}, + #{userName}, + #{nickName}, + #{email}, + #{avatar}, + #{phonenumber}, + #{sex}, + #{password}, + #{status}, + #{pwdUpdateDate}, + #{createBy}, + #{remark}, + sysdate() + ) + + + + update sys_user + + dept_id = #{deptId}, + nick_name = #{nickName}, + email = #{email}, + phonenumber = #{phonenumber}, + sex = #{sex}, + avatar = #{avatar}, + password = #{password}, + status = #{status}, + login_ip = #{loginIp}, + login_date = #{loginDate}, + update_by = #{updateBy}, + remark = #{remark}, + update_time = sysdate() + + where user_id = #{userId} + + + + update sys_user set status = #{status}, update_time = sysdate() where user_id = #{userId} + + + + update sys_user set avatar = #{avatar}, update_time = sysdate() where user_id = #{userId} + + + + update sys_user set login_ip = #{loginIp}, login_date = #{loginDate} where user_id = #{userId} + + + + update sys_user set pwd_update_date = sysdate(), password = #{password}, update_time = sysdate() where user_id = #{userId} + + + + update sys_user set del_flag = '2' where user_id = #{userId} + + + + update sys_user set del_flag = '2' where user_id in + + #{userId} + + + + \ No newline at end of file diff --git a/portal/backend/portal-system/target/classes/mapper/system/SysUserPostMapper.xml b/portal/backend/portal-system/target/classes/mapper/system/SysUserPostMapper.xml new file mode 100644 index 0000000..875aed2 --- /dev/null +++ b/portal/backend/portal-system/target/classes/mapper/system/SysUserPostMapper.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + delete from sys_user_post where user_id=#{userId} + + + + + + delete from sys_user_post where user_id in + + #{userId} + + + + + insert into sys_user_post(user_id, post_id) values + + (#{item.userId},#{item.postId}) + + + + \ No newline at end of file diff --git a/portal/backend/portal-system/target/classes/mapper/system/SysUserRoleMapper.xml b/portal/backend/portal-system/target/classes/mapper/system/SysUserRoleMapper.xml new file mode 100644 index 0000000..a9c0f2a --- /dev/null +++ b/portal/backend/portal-system/target/classes/mapper/system/SysUserRoleMapper.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + delete from sys_user_role where user_id=#{userId} + + + + + + delete from sys_user_role where user_id in + + #{userId} + + + + + insert into sys_user_role(user_id, role_id) values + + (#{item.userId},#{item.roleId}) + + + + + delete from sys_user_role where user_id=#{userId} and role_id=#{roleId} + + + + delete from sys_user_role where role_id=#{roleId} and user_id in + + #{userId} + + + \ No newline at end of file diff --git a/portal/backend/portal-system/target/classes/org/lingniu/portal/system/domain/SysCache.class b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/domain/SysCache.class new file mode 100644 index 0000000..197e044 Binary files /dev/null and b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/domain/SysCache.class differ diff --git a/portal/backend/portal-system/target/classes/org/lingniu/portal/system/domain/SysConfig.class b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/domain/SysConfig.class new file mode 100644 index 0000000..3736a01 Binary files /dev/null and b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/domain/SysConfig.class differ diff --git a/portal/backend/portal-system/target/classes/org/lingniu/portal/system/domain/SysLogininfor.class b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/domain/SysLogininfor.class new file mode 100644 index 0000000..857104c Binary files /dev/null and b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/domain/SysLogininfor.class differ diff --git a/portal/backend/portal-system/target/classes/org/lingniu/portal/system/domain/SysNotice.class b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/domain/SysNotice.class new file mode 100644 index 0000000..12405b5 Binary files /dev/null and b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/domain/SysNotice.class differ diff --git a/portal/backend/portal-system/target/classes/org/lingniu/portal/system/domain/SysOperLog.class b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/domain/SysOperLog.class new file mode 100644 index 0000000..0e86ea6 Binary files /dev/null and b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/domain/SysOperLog.class differ diff --git a/portal/backend/portal-system/target/classes/org/lingniu/portal/system/domain/SysPost.class b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/domain/SysPost.class new file mode 100644 index 0000000..ae4fd55 Binary files /dev/null and b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/domain/SysPost.class differ diff --git a/portal/backend/portal-system/target/classes/org/lingniu/portal/system/domain/SysRoleDept.class b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/domain/SysRoleDept.class new file mode 100644 index 0000000..71e8088 Binary files /dev/null and b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/domain/SysRoleDept.class differ diff --git a/portal/backend/portal-system/target/classes/org/lingniu/portal/system/domain/SysRoleMenu.class b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/domain/SysRoleMenu.class new file mode 100644 index 0000000..9da39d8 Binary files /dev/null and b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/domain/SysRoleMenu.class differ diff --git a/portal/backend/portal-system/target/classes/org/lingniu/portal/system/domain/SysUserOnline.class b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/domain/SysUserOnline.class new file mode 100644 index 0000000..dc12092 Binary files /dev/null and b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/domain/SysUserOnline.class differ diff --git a/portal/backend/portal-system/target/classes/org/lingniu/portal/system/domain/SysUserPost.class b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/domain/SysUserPost.class new file mode 100644 index 0000000..bae8916 Binary files /dev/null and b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/domain/SysUserPost.class differ diff --git a/portal/backend/portal-system/target/classes/org/lingniu/portal/system/domain/SysUserRole.class b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/domain/SysUserRole.class new file mode 100644 index 0000000..0946d07 Binary files /dev/null and b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/domain/SysUserRole.class differ diff --git a/portal/backend/portal-system/target/classes/org/lingniu/portal/system/domain/vo/MetaVo.class b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/domain/vo/MetaVo.class new file mode 100644 index 0000000..9861825 Binary files /dev/null and b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/domain/vo/MetaVo.class differ diff --git a/portal/backend/portal-system/target/classes/org/lingniu/portal/system/domain/vo/RouterVo.class b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/domain/vo/RouterVo.class new file mode 100644 index 0000000..f55e2da Binary files /dev/null and b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/domain/vo/RouterVo.class differ diff --git a/portal/backend/portal-system/target/classes/org/lingniu/portal/system/mapper/SysConfigMapper.class b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/mapper/SysConfigMapper.class new file mode 100644 index 0000000..2d9ac90 Binary files /dev/null and b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/mapper/SysConfigMapper.class differ diff --git a/portal/backend/portal-system/target/classes/org/lingniu/portal/system/mapper/SysDeptMapper.class b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/mapper/SysDeptMapper.class new file mode 100644 index 0000000..d86695a Binary files /dev/null and b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/mapper/SysDeptMapper.class differ diff --git a/portal/backend/portal-system/target/classes/org/lingniu/portal/system/mapper/SysDictDataMapper.class b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/mapper/SysDictDataMapper.class new file mode 100644 index 0000000..a72288b Binary files /dev/null and b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/mapper/SysDictDataMapper.class differ diff --git a/portal/backend/portal-system/target/classes/org/lingniu/portal/system/mapper/SysDictTypeMapper.class b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/mapper/SysDictTypeMapper.class new file mode 100644 index 0000000..b588178 Binary files /dev/null and b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/mapper/SysDictTypeMapper.class differ diff --git a/portal/backend/portal-system/target/classes/org/lingniu/portal/system/mapper/SysLogininforMapper.class b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/mapper/SysLogininforMapper.class new file mode 100644 index 0000000..bba9a9a Binary files /dev/null and b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/mapper/SysLogininforMapper.class differ diff --git a/portal/backend/portal-system/target/classes/org/lingniu/portal/system/mapper/SysMenuMapper.class b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/mapper/SysMenuMapper.class new file mode 100644 index 0000000..dfd2bd9 Binary files /dev/null and b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/mapper/SysMenuMapper.class differ diff --git a/portal/backend/portal-system/target/classes/org/lingniu/portal/system/mapper/SysNoticeMapper.class b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/mapper/SysNoticeMapper.class new file mode 100644 index 0000000..34be8eb Binary files /dev/null and b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/mapper/SysNoticeMapper.class differ diff --git a/portal/backend/portal-system/target/classes/org/lingniu/portal/system/mapper/SysOperLogMapper.class b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/mapper/SysOperLogMapper.class new file mode 100644 index 0000000..7b4d12b Binary files /dev/null and b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/mapper/SysOperLogMapper.class differ diff --git a/portal/backend/portal-system/target/classes/org/lingniu/portal/system/mapper/SysPostMapper.class b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/mapper/SysPostMapper.class new file mode 100644 index 0000000..4393879 Binary files /dev/null and b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/mapper/SysPostMapper.class differ diff --git a/portal/backend/portal-system/target/classes/org/lingniu/portal/system/mapper/SysRoleDeptMapper.class b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/mapper/SysRoleDeptMapper.class new file mode 100644 index 0000000..28de468 Binary files /dev/null and b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/mapper/SysRoleDeptMapper.class differ diff --git a/portal/backend/portal-system/target/classes/org/lingniu/portal/system/mapper/SysRoleMapper.class b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/mapper/SysRoleMapper.class new file mode 100644 index 0000000..7da13c5 Binary files /dev/null and b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/mapper/SysRoleMapper.class differ diff --git a/portal/backend/portal-system/target/classes/org/lingniu/portal/system/mapper/SysRoleMenuMapper.class b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/mapper/SysRoleMenuMapper.class new file mode 100644 index 0000000..f0353ba Binary files /dev/null and b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/mapper/SysRoleMenuMapper.class differ diff --git a/portal/backend/portal-system/target/classes/org/lingniu/portal/system/mapper/SysUserMapper.class b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/mapper/SysUserMapper.class new file mode 100644 index 0000000..2101e69 Binary files /dev/null and b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/mapper/SysUserMapper.class differ diff --git a/portal/backend/portal-system/target/classes/org/lingniu/portal/system/mapper/SysUserPostMapper.class b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/mapper/SysUserPostMapper.class new file mode 100644 index 0000000..659b015 Binary files /dev/null and b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/mapper/SysUserPostMapper.class differ diff --git a/portal/backend/portal-system/target/classes/org/lingniu/portal/system/mapper/SysUserRoleMapper.class b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/mapper/SysUserRoleMapper.class new file mode 100644 index 0000000..47f6ce2 Binary files /dev/null and b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/mapper/SysUserRoleMapper.class differ diff --git a/portal/backend/portal-system/target/classes/org/lingniu/portal/system/service/ISysConfigService.class b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/service/ISysConfigService.class new file mode 100644 index 0000000..6540f83 Binary files /dev/null and b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/service/ISysConfigService.class differ diff --git a/portal/backend/portal-system/target/classes/org/lingniu/portal/system/service/ISysDeptService.class b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/service/ISysDeptService.class new file mode 100644 index 0000000..83e660d Binary files /dev/null and b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/service/ISysDeptService.class differ diff --git a/portal/backend/portal-system/target/classes/org/lingniu/portal/system/service/ISysDictDataService.class b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/service/ISysDictDataService.class new file mode 100644 index 0000000..5cf5b8a Binary files /dev/null and b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/service/ISysDictDataService.class differ diff --git a/portal/backend/portal-system/target/classes/org/lingniu/portal/system/service/ISysDictTypeService.class b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/service/ISysDictTypeService.class new file mode 100644 index 0000000..27f7ae7 Binary files /dev/null and b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/service/ISysDictTypeService.class differ diff --git a/portal/backend/portal-system/target/classes/org/lingniu/portal/system/service/ISysLogininforService.class b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/service/ISysLogininforService.class new file mode 100644 index 0000000..d1e3d93 Binary files /dev/null and b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/service/ISysLogininforService.class differ diff --git a/portal/backend/portal-system/target/classes/org/lingniu/portal/system/service/ISysMenuService.class b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/service/ISysMenuService.class new file mode 100644 index 0000000..e3f96b5 Binary files /dev/null and b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/service/ISysMenuService.class differ diff --git a/portal/backend/portal-system/target/classes/org/lingniu/portal/system/service/ISysNoticeService.class b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/service/ISysNoticeService.class new file mode 100644 index 0000000..76031e8 Binary files /dev/null and b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/service/ISysNoticeService.class differ diff --git a/portal/backend/portal-system/target/classes/org/lingniu/portal/system/service/ISysOperLogService.class b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/service/ISysOperLogService.class new file mode 100644 index 0000000..a4c983b Binary files /dev/null and b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/service/ISysOperLogService.class differ diff --git a/portal/backend/portal-system/target/classes/org/lingniu/portal/system/service/ISysPostService.class b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/service/ISysPostService.class new file mode 100644 index 0000000..3acebab Binary files /dev/null and b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/service/ISysPostService.class differ diff --git a/portal/backend/portal-system/target/classes/org/lingniu/portal/system/service/ISysRoleService.class b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/service/ISysRoleService.class new file mode 100644 index 0000000..13703e7 Binary files /dev/null and b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/service/ISysRoleService.class differ diff --git a/portal/backend/portal-system/target/classes/org/lingniu/portal/system/service/ISysUserOnlineService.class b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/service/ISysUserOnlineService.class new file mode 100644 index 0000000..605f26d Binary files /dev/null and b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/service/ISysUserOnlineService.class differ diff --git a/portal/backend/portal-system/target/classes/org/lingniu/portal/system/service/ISysUserService.class b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/service/ISysUserService.class new file mode 100644 index 0000000..6c3529b Binary files /dev/null and b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/service/ISysUserService.class differ diff --git a/portal/backend/portal-system/target/classes/org/lingniu/portal/system/service/impl/SysConfigServiceImpl.class b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/service/impl/SysConfigServiceImpl.class new file mode 100644 index 0000000..9f301e8 Binary files /dev/null and b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/service/impl/SysConfigServiceImpl.class differ diff --git a/portal/backend/portal-system/target/classes/org/lingniu/portal/system/service/impl/SysDeptServiceImpl.class b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/service/impl/SysDeptServiceImpl.class new file mode 100644 index 0000000..9c901e2 Binary files /dev/null and b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/service/impl/SysDeptServiceImpl.class differ diff --git a/portal/backend/portal-system/target/classes/org/lingniu/portal/system/service/impl/SysDictDataServiceImpl.class b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/service/impl/SysDictDataServiceImpl.class new file mode 100644 index 0000000..1fd5dd0 Binary files /dev/null and b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/service/impl/SysDictDataServiceImpl.class differ diff --git a/portal/backend/portal-system/target/classes/org/lingniu/portal/system/service/impl/SysDictTypeServiceImpl.class b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/service/impl/SysDictTypeServiceImpl.class new file mode 100644 index 0000000..328aa2f Binary files /dev/null and b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/service/impl/SysDictTypeServiceImpl.class differ diff --git a/portal/backend/portal-system/target/classes/org/lingniu/portal/system/service/impl/SysLogininforServiceImpl.class b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/service/impl/SysLogininforServiceImpl.class new file mode 100644 index 0000000..b335fd1 Binary files /dev/null and b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/service/impl/SysLogininforServiceImpl.class differ diff --git a/portal/backend/portal-system/target/classes/org/lingniu/portal/system/service/impl/SysMenuServiceImpl.class b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/service/impl/SysMenuServiceImpl.class new file mode 100644 index 0000000..18d296c Binary files /dev/null and b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/service/impl/SysMenuServiceImpl.class differ diff --git a/portal/backend/portal-system/target/classes/org/lingniu/portal/system/service/impl/SysNoticeServiceImpl.class b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/service/impl/SysNoticeServiceImpl.class new file mode 100644 index 0000000..d50d979 Binary files /dev/null and b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/service/impl/SysNoticeServiceImpl.class differ diff --git a/portal/backend/portal-system/target/classes/org/lingniu/portal/system/service/impl/SysOperLogServiceImpl.class b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/service/impl/SysOperLogServiceImpl.class new file mode 100644 index 0000000..7b027bd Binary files /dev/null and b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/service/impl/SysOperLogServiceImpl.class differ diff --git a/portal/backend/portal-system/target/classes/org/lingniu/portal/system/service/impl/SysPostServiceImpl.class b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/service/impl/SysPostServiceImpl.class new file mode 100644 index 0000000..bf87f1f Binary files /dev/null and b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/service/impl/SysPostServiceImpl.class differ diff --git a/portal/backend/portal-system/target/classes/org/lingniu/portal/system/service/impl/SysRoleServiceImpl.class b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/service/impl/SysRoleServiceImpl.class new file mode 100644 index 0000000..4f99c78 Binary files /dev/null and b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/service/impl/SysRoleServiceImpl.class differ diff --git a/portal/backend/portal-system/target/classes/org/lingniu/portal/system/service/impl/SysUserOnlineServiceImpl.class b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/service/impl/SysUserOnlineServiceImpl.class new file mode 100644 index 0000000..1d53a23 Binary files /dev/null and b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/service/impl/SysUserOnlineServiceImpl.class differ diff --git a/portal/backend/portal-system/target/classes/org/lingniu/portal/system/service/impl/SysUserServiceImpl.class b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/service/impl/SysUserServiceImpl.class new file mode 100644 index 0000000..a58ccf6 Binary files /dev/null and b/portal/backend/portal-system/target/classes/org/lingniu/portal/system/service/impl/SysUserServiceImpl.class differ diff --git a/portal/backend/portal-system/target/maven-archiver/pom.properties b/portal/backend/portal-system/target/maven-archiver/pom.properties new file mode 100644 index 0000000..7b7994a --- /dev/null +++ b/portal/backend/portal-system/target/maven-archiver/pom.properties @@ -0,0 +1,5 @@ +#Generated by Maven +#Tue Feb 10 15:14:05 CST 2026 +groupId=org.lingniu +artifactId=portal-system +version=1.0.0 diff --git a/portal/backend/portal-system/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst b/portal/backend/portal-system/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst new file mode 100644 index 0000000..93c0f5b --- /dev/null +++ b/portal/backend/portal-system/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst @@ -0,0 +1,52 @@ +org\lingniu\portal\system\domain\SysUserOnline.class +org\lingniu\portal\system\service\ISysOperLogService.class +org\lingniu\portal\system\mapper\SysDeptMapper.class +org\lingniu\portal\system\service\ISysDeptService.class +org\lingniu\portal\system\mapper\SysUserRoleMapper.class +org\lingniu\portal\system\domain\SysUserPost.class +org\lingniu\portal\system\service\ISysUserOnlineService.class +org\lingniu\portal\system\mapper\SysOperLogMapper.class +org\lingniu\portal\system\service\ISysMenuService.class +org\lingniu\portal\system\domain\vo\MetaVo.class +org\lingniu\portal\system\service\impl\SysUserServiceImpl.class +org\lingniu\portal\system\domain\SysNotice.class +org\lingniu\portal\system\service\ISysNoticeService.class +org\lingniu\portal\system\domain\SysRoleDept.class +org\lingniu\portal\system\mapper\SysDictDataMapper.class +org\lingniu\portal\system\service\impl\SysDictDataServiceImpl.class +org\lingniu\portal\system\mapper\SysUserPostMapper.class +org\lingniu\portal\system\service\ISysConfigService.class +org\lingniu\portal\system\service\ISysUserService.class +org\lingniu\portal\system\mapper\SysPostMapper.class +org\lingniu\portal\system\service\impl\SysNoticeServiceImpl.class +org\lingniu\portal\system\domain\SysConfig.class +org\lingniu\portal\system\domain\SysRoleMenu.class +org\lingniu\portal\system\mapper\SysNoticeMapper.class +org\lingniu\portal\system\service\impl\SysLogininforServiceImpl.class +org\lingniu\portal\system\domain\vo\RouterVo.class +org\lingniu\portal\system\mapper\SysDictTypeMapper.class +org\lingniu\portal\system\domain\SysCache.class +org\lingniu\portal\system\mapper\SysRoleMenuMapper.class +org\lingniu\portal\system\service\ISysPostService.class +org\lingniu\portal\system\mapper\SysLogininforMapper.class +org\lingniu\portal\system\service\impl\SysDeptServiceImpl.class +org\lingniu\portal\system\service\impl\SysOperLogServiceImpl.class +org\lingniu\portal\system\service\impl\SysRoleServiceImpl.class +org\lingniu\portal\system\service\impl\SysMenuServiceImpl.class +org\lingniu\portal\system\service\impl\SysPostServiceImpl.class +org\lingniu\portal\system\service\ISysDictTypeService.class +org\lingniu\portal\system\domain\SysOperLog.class +org\lingniu\portal\system\service\ISysDictDataService.class +org\lingniu\portal\system\service\impl\SysDictTypeServiceImpl.class +org\lingniu\portal\system\service\ISysLogininforService.class +org\lingniu\portal\system\mapper\SysConfigMapper.class +org\lingniu\portal\system\mapper\SysMenuMapper.class +org\lingniu\portal\system\domain\SysPost.class +org\lingniu\portal\system\domain\SysUserRole.class +org\lingniu\portal\system\service\ISysRoleService.class +org\lingniu\portal\system\mapper\SysRoleDeptMapper.class +org\lingniu\portal\system\service\impl\SysConfigServiceImpl.class +org\lingniu\portal\system\service\impl\SysUserOnlineServiceImpl.class +org\lingniu\portal\system\mapper\SysRoleMapper.class +org\lingniu\portal\system\domain\SysLogininfor.class +org\lingniu\portal\system\mapper\SysUserMapper.class diff --git a/portal/backend/portal-system/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst b/portal/backend/portal-system/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst new file mode 100644 index 0000000..251a167 --- /dev/null +++ b/portal/backend/portal-system/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst @@ -0,0 +1,52 @@ +D:\RuoYi-Vue\portal-system\src\main\java\org\lingniu\portal\system\domain\SysCache.java +D:\RuoYi-Vue\portal-system\src\main\java\org\lingniu\portal\system\domain\SysConfig.java +D:\RuoYi-Vue\portal-system\src\main\java\org\lingniu\portal\system\domain\SysLogininfor.java +D:\RuoYi-Vue\portal-system\src\main\java\org\lingniu\portal\system\domain\SysNotice.java +D:\RuoYi-Vue\portal-system\src\main\java\org\lingniu\portal\system\domain\SysOperLog.java +D:\RuoYi-Vue\portal-system\src\main\java\org\lingniu\portal\system\domain\SysPost.java +D:\RuoYi-Vue\portal-system\src\main\java\org\lingniu\portal\system\domain\SysRoleDept.java +D:\RuoYi-Vue\portal-system\src\main\java\org\lingniu\portal\system\domain\SysRoleMenu.java +D:\RuoYi-Vue\portal-system\src\main\java\org\lingniu\portal\system\domain\SysUserOnline.java +D:\RuoYi-Vue\portal-system\src\main\java\org\lingniu\portal\system\domain\SysUserPost.java +D:\RuoYi-Vue\portal-system\src\main\java\org\lingniu\portal\system\domain\SysUserRole.java +D:\RuoYi-Vue\portal-system\src\main\java\org\lingniu\portal\system\domain\vo\MetaVo.java +D:\RuoYi-Vue\portal-system\src\main\java\org\lingniu\portal\system\domain\vo\RouterVo.java +D:\RuoYi-Vue\portal-system\src\main\java\org\lingniu\portal\system\mapper\SysConfigMapper.java +D:\RuoYi-Vue\portal-system\src\main\java\org\lingniu\portal\system\mapper\SysDeptMapper.java +D:\RuoYi-Vue\portal-system\src\main\java\org\lingniu\portal\system\mapper\SysDictDataMapper.java +D:\RuoYi-Vue\portal-system\src\main\java\org\lingniu\portal\system\mapper\SysDictTypeMapper.java +D:\RuoYi-Vue\portal-system\src\main\java\org\lingniu\portal\system\mapper\SysLogininforMapper.java +D:\RuoYi-Vue\portal-system\src\main\java\org\lingniu\portal\system\mapper\SysMenuMapper.java +D:\RuoYi-Vue\portal-system\src\main\java\org\lingniu\portal\system\mapper\SysNoticeMapper.java +D:\RuoYi-Vue\portal-system\src\main\java\org\lingniu\portal\system\mapper\SysOperLogMapper.java +D:\RuoYi-Vue\portal-system\src\main\java\org\lingniu\portal\system\mapper\SysPostMapper.java +D:\RuoYi-Vue\portal-system\src\main\java\org\lingniu\portal\system\mapper\SysRoleDeptMapper.java +D:\RuoYi-Vue\portal-system\src\main\java\org\lingniu\portal\system\mapper\SysRoleMapper.java +D:\RuoYi-Vue\portal-system\src\main\java\org\lingniu\portal\system\mapper\SysRoleMenuMapper.java +D:\RuoYi-Vue\portal-system\src\main\java\org\lingniu\portal\system\mapper\SysUserMapper.java +D:\RuoYi-Vue\portal-system\src\main\java\org\lingniu\portal\system\mapper\SysUserPostMapper.java +D:\RuoYi-Vue\portal-system\src\main\java\org\lingniu\portal\system\mapper\SysUserRoleMapper.java +D:\RuoYi-Vue\portal-system\src\main\java\org\lingniu\portal\system\service\impl\SysConfigServiceImpl.java +D:\RuoYi-Vue\portal-system\src\main\java\org\lingniu\portal\system\service\impl\SysDeptServiceImpl.java +D:\RuoYi-Vue\portal-system\src\main\java\org\lingniu\portal\system\service\impl\SysDictDataServiceImpl.java +D:\RuoYi-Vue\portal-system\src\main\java\org\lingniu\portal\system\service\impl\SysDictTypeServiceImpl.java +D:\RuoYi-Vue\portal-system\src\main\java\org\lingniu\portal\system\service\impl\SysLogininforServiceImpl.java +D:\RuoYi-Vue\portal-system\src\main\java\org\lingniu\portal\system\service\impl\SysMenuServiceImpl.java +D:\RuoYi-Vue\portal-system\src\main\java\org\lingniu\portal\system\service\impl\SysNoticeServiceImpl.java +D:\RuoYi-Vue\portal-system\src\main\java\org\lingniu\portal\system\service\impl\SysOperLogServiceImpl.java +D:\RuoYi-Vue\portal-system\src\main\java\org\lingniu\portal\system\service\impl\SysPostServiceImpl.java +D:\RuoYi-Vue\portal-system\src\main\java\org\lingniu\portal\system\service\impl\SysRoleServiceImpl.java +D:\RuoYi-Vue\portal-system\src\main\java\org\lingniu\portal\system\service\impl\SysUserOnlineServiceImpl.java +D:\RuoYi-Vue\portal-system\src\main\java\org\lingniu\portal\system\service\impl\SysUserServiceImpl.java +D:\RuoYi-Vue\portal-system\src\main\java\org\lingniu\portal\system\service\ISysConfigService.java +D:\RuoYi-Vue\portal-system\src\main\java\org\lingniu\portal\system\service\ISysDeptService.java +D:\RuoYi-Vue\portal-system\src\main\java\org\lingniu\portal\system\service\ISysDictDataService.java +D:\RuoYi-Vue\portal-system\src\main\java\org\lingniu\portal\system\service\ISysDictTypeService.java +D:\RuoYi-Vue\portal-system\src\main\java\org\lingniu\portal\system\service\ISysLogininforService.java +D:\RuoYi-Vue\portal-system\src\main\java\org\lingniu\portal\system\service\ISysMenuService.java +D:\RuoYi-Vue\portal-system\src\main\java\org\lingniu\portal\system\service\ISysNoticeService.java +D:\RuoYi-Vue\portal-system\src\main\java\org\lingniu\portal\system\service\ISysOperLogService.java +D:\RuoYi-Vue\portal-system\src\main\java\org\lingniu\portal\system\service\ISysPostService.java +D:\RuoYi-Vue\portal-system\src\main\java\org\lingniu\portal\system\service\ISysRoleService.java +D:\RuoYi-Vue\portal-system\src\main\java\org\lingniu\portal\system\service\ISysUserOnlineService.java +D:\RuoYi-Vue\portal-system\src\main\java\org\lingniu\portal\system\service\ISysUserService.java diff --git a/portal/backend/portal-system/target/portal-system-1.0.0.jar b/portal/backend/portal-system/target/portal-system-1.0.0.jar new file mode 100644 index 0000000..2489c90 Binary files /dev/null and b/portal/backend/portal-system/target/portal-system-1.0.0.jar differ diff --git a/portal/frontend/portal/.env.development b/portal/frontend/portal/.env.development new file mode 100644 index 0000000..fc423a1 --- /dev/null +++ b/portal/frontend/portal/.env.development @@ -0,0 +1,15 @@ +# 页面标题 +VITE_APP_TITLE = 羚牛OneOs运营管理 + +# 开发环境配置 +VITE_APP_ENV = 'development' + +# 羚牛OneOs运营管理/开发环境 +VITE_APP_BASE_API = '/dev-api2' + + +VITE_APP_CLIENT_ID=2c6f1d9ff78641c78d72a848 +VITE_APP_REGISTRATION_ID=portal +VITE_APP_STORAGE_TYPE=localStorage +VITE_APP_IDP_LOGOUT_URL=http://localhost/logout +VITE_APP_HOME_PAGE=http://localhost:81/index diff --git a/portal/frontend/portal/.env.production b/portal/frontend/portal/.env.production new file mode 100644 index 0000000..70501b8 --- /dev/null +++ b/portal/frontend/portal/.env.production @@ -0,0 +1,18 @@ +# 页面标题 +VITE_APP_TITLE = 羚牛OneOs运营管理 + +# 生产环境配置 +VITE_APP_ENV = 'production' + +# 羚牛OneOs运营管理/生产环境 +VITE_APP_BASE_API = '/portal-api' + +# 是否在打包时开启压缩,支持 gzip 和 brotli +VITE_BUILD_COMPRESS = gzip + + +VITE_APP_CLIENT_ID=2c6f1d9ff78641c78d72a848 +VITE_APP_REGISTRATION_ID=portal +VITE_APP_STORAGE_TYPE=localStorage +VITE_APP_IDP_LOGOUT_URL=http://106.14.217.120/idp-ui/logout +VITE_APP_HOME_PAGE=http://106.14.217.120/portal-ui/index \ No newline at end of file diff --git a/portal/frontend/portal/.gitignore b/portal/frontend/portal/.gitignore new file mode 100644 index 0000000..78a752d --- /dev/null +++ b/portal/frontend/portal/.gitignore @@ -0,0 +1,23 @@ +.DS_Store +node_modules/ +dist/ +npm-debug.log* +yarn-debug.log* +yarn-error.log* +**/*.log + +tests/**/coverage/ +tests/e2e/reports +selenium-debug.log + +# Editor directories and files +.idea +.vscode +*.suo +*.ntvs* +*.njsproj +*.sln +*.local + +package-lock.json +yarn.lock diff --git a/portal/frontend/portal/README.md b/portal/frontend/portal/README.md new file mode 100644 index 0000000..e69de29 diff --git a/portal/frontend/portal/auto-imports.d.ts b/portal/frontend/portal/auto-imports.d.ts new file mode 100644 index 0000000..ab12676 --- /dev/null +++ b/portal/frontend/portal/auto-imports.d.ts @@ -0,0 +1,90 @@ +/* eslint-disable */ +/* prettier-ignore */ +// @ts-nocheck +// noinspection JSUnusedGlobalSymbols +// Generated by unplugin-auto-import +// biome-ignore lint: disable +export {} +declare global { + const EffectScope: typeof import('vue')['EffectScope'] + const acceptHMRUpdate: typeof import('pinia')['acceptHMRUpdate'] + const computed: typeof import('vue')['computed'] + const createApp: typeof import('vue')['createApp'] + const createPinia: typeof import('pinia')['createPinia'] + const customRef: typeof import('vue')['customRef'] + const defineAsyncComponent: typeof import('vue')['defineAsyncComponent'] + const defineComponent: typeof import('vue')['defineComponent'] + const defineEmits: typeof import('vue')['defineEmits'] + const defineProps: typeof import('vue')['defineProps'] + const defineStore: typeof import('pinia')['defineStore'] + const effectScope: typeof import('vue')['effectScope'] + const getActivePinia: typeof import('pinia')['getActivePinia'] + const getCurrentInstance: typeof import('vue')['getCurrentInstance'] + const getCurrentScope: typeof import('vue')['getCurrentScope'] + const h: typeof import('vue')['h'] + const inject: typeof import('vue')['inject'] + const isProxy: typeof import('vue')['isProxy'] + const isReactive: typeof import('vue')['isReactive'] + const isReadonly: typeof import('vue')['isReadonly'] + const isRef: typeof import('vue')['isRef'] + const mapActions: typeof import('pinia')['mapActions'] + const mapGetters: typeof import('pinia')['mapGetters'] + const mapState: typeof import('pinia')['mapState'] + const mapStores: typeof import('pinia')['mapStores'] + const mapWritableState: typeof import('pinia')['mapWritableState'] + const markRaw: typeof import('vue')['markRaw'] + const nextTick: typeof import('vue')['nextTick'] + const onActivated: typeof import('vue')['onActivated'] + const onBeforeMount: typeof import('vue')['onBeforeMount'] + const onBeforeRouteLeave: typeof import('vue-router')['onBeforeRouteLeave'] + const onBeforeRouteUpdate: typeof import('vue-router')['onBeforeRouteUpdate'] + const onBeforeUnmount: typeof import('vue')['onBeforeUnmount'] + const onBeforeUpdate: typeof import('vue')['onBeforeUpdate'] + const onDeactivated: typeof import('vue')['onDeactivated'] + const onErrorCaptured: typeof import('vue')['onErrorCaptured'] + const onMounted: typeof import('vue')['onMounted'] + const onRenderTracked: typeof import('vue')['onRenderTracked'] + const onRenderTriggered: typeof import('vue')['onRenderTriggered'] + const onScopeDispose: typeof import('vue')['onScopeDispose'] + const onServerPrefetch: typeof import('vue')['onServerPrefetch'] + const onUnmounted: typeof import('vue')['onUnmounted'] + const onUpdated: typeof import('vue')['onUpdated'] + const onWatcherCleanup: typeof import('vue')['onWatcherCleanup'] + const provide: typeof import('vue')['provide'] + const reactive: typeof import('vue')['reactive'] + const readonly: typeof import('vue')['readonly'] + const ref: typeof import('vue')['ref'] + const resolveComponent: typeof import('vue')['resolveComponent'] + const setActivePinia: typeof import('pinia')['setActivePinia'] + const setMapStoreSuffix: typeof import('pinia')['setMapStoreSuffix'] + const shallowReactive: typeof import('vue')['shallowReactive'] + const shallowReadonly: typeof import('vue')['shallowReadonly'] + const shallowRef: typeof import('vue')['shallowRef'] + const storeToRefs: typeof import('pinia')['storeToRefs'] + const toRaw: typeof import('vue')['toRaw'] + const toRef: typeof import('vue')['toRef'] + const toRefs: typeof import('vue')['toRefs'] + const toValue: typeof import('vue')['toValue'] + const triggerRef: typeof import('vue')['triggerRef'] + const unref: typeof import('vue')['unref'] + const useAttrs: typeof import('vue')['useAttrs'] + const useCssModule: typeof import('vue')['useCssModule'] + const useCssVars: typeof import('vue')['useCssVars'] + const useId: typeof import('vue')['useId'] + const useLink: typeof import('vue-router')['useLink'] + const useModel: typeof import('vue')['useModel'] + const useRoute: typeof import('vue-router')['useRoute'] + const useRouter: typeof import('vue-router')['useRouter'] + const useSlots: typeof import('vue')['useSlots'] + const useTemplateRef: typeof import('vue')['useTemplateRef'] + const watch: typeof import('vue')['watch'] + const watchEffect: typeof import('vue')['watchEffect'] + const watchPostEffect: typeof import('vue')['watchPostEffect'] + const watchSyncEffect: typeof import('vue')['watchSyncEffect'] +} +// for type re-export +declare global { + // @ts-ignore + export type { Component, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue' + import('vue') +} diff --git a/portal/frontend/portal/html/ie.html b/portal/frontend/portal/html/ie.html new file mode 100644 index 0000000..052ffcd --- /dev/null +++ b/portal/frontend/portal/html/ie.html @@ -0,0 +1,46 @@ + + + + + + 请升级您的浏览器 + + + + + + +

请升级您的浏览器,以便我们更好的为您提供服务!

+

您正在使用 Internet Explorer 的早期版本(IE11以下版本或使用该内核的浏览器)。这意味着在升级浏览器前,您将无法访问此网站。

+
+

请注意:微软公司对Windows XP 及 Internet Explorer 早期版本的支持已经结束

+

自 2016 年 1 月 12 日起,Microsoft 不再为 IE 11 以下版本提供相应支持和更新。没有关键的浏览器安全更新,您的电脑可能易受有害病毒、间谍软件和其他恶意软件的攻击,它们可以窃取或损害您的业务数据和信息。请参阅 微软对 Internet Explorer 早期版本的支持将于 2016 年 1 月 12 日结束的说明

+
+

您可以选择更先进的浏览器

+

推荐使用以下浏览器的最新版本。如果您的电脑已有以下浏览器的最新版本则直接使用该浏览器访问即可。

+ +
+ + \ No newline at end of file diff --git a/portal/frontend/portal/index.html b/portal/frontend/portal/index.html new file mode 100644 index 0000000..d1d824e --- /dev/null +++ b/portal/frontend/portal/index.html @@ -0,0 +1,215 @@ + + + + + + + + + + %VITE_APP_TITLE% + + + + + +
+
+
+
+
+
正在加载系统资源,请耐心等待
+
+
+ + + + \ No newline at end of file diff --git a/portal/frontend/portal/package.json b/portal/frontend/portal/package.json new file mode 100644 index 0000000..a15fcbe --- /dev/null +++ b/portal/frontend/portal/package.json @@ -0,0 +1,54 @@ +{ + "name": "lingniu", + "version": "1.0.0", + "description": "羚牛OneOs运营管理", + "author": "lingniu", + "license": "MIT", + "type": "module", + "scripts": { + "dev": "vite", + "build:prod": "vite build", + "build:stage": "vite build --mode staging", + "preview": "vite preview" + }, + "dependencies": { + "@element-plus/icons-vue": "2.3.2", + "@vueup/vue-quill": "1.2.0", + "@vueuse/core": "14.1.0", + "axios": "1.13.2", + "clipboard": "2.0.11", + "echarts": "5.6.0", + "element-plus": "2.13.1", + "file-saver": "2.0.5", + "fuse.js": "7.1.0", + "js-beautify": "1.15.4", + "js-cookie": "3.0.5", + "jsencrypt": "3.3.2", + "nprogress": "0.2.0", + "oauth2-login-sdk": "link:C:/Users/admin/AppData/Local/pnpm/global/5/node_modules/oauth2-login-sdk", + "pinia": "3.0.4", + "splitpanes": "4.0.4", + "vue": "3.5.26", + "vue-cropper": "1.1.1", + "vue-router": "4.6.4", + "vuedraggable": "4.1.0" + }, + "devDependencies": { + "@vitejs/plugin-vue": "5.2.4", + "@vue/tsconfig": "0.5.1", + "sass-embedded": "1.97.2", + "typescript": "5.6.3", + "unplugin-auto-import": "0.18.6", + "unplugin-vue-setup-extend-plus": "1.0.1", + "vite": "6.4.1", + "vite-plugin-compression": "0.5.1", + "vite-plugin-svg-icons": "2.0.1", + "vue-tsc": "2.1.10" + }, + "overrides": { + "quill": "2.0.2" + }, + "resolutions": { + "quill": "2.0.2" + } +} diff --git a/portal/frontend/portal/public/favicon.ico b/portal/frontend/portal/public/favicon.ico new file mode 100644 index 0000000..001cb48 Binary files /dev/null and b/portal/frontend/portal/public/favicon.ico differ diff --git a/portal/frontend/portal/src/App.vue b/portal/frontend/portal/src/App.vue new file mode 100644 index 0000000..7a309a9 --- /dev/null +++ b/portal/frontend/portal/src/App.vue @@ -0,0 +1,15 @@ + + + diff --git a/portal/frontend/portal/src/api/login.ts b/portal/frontend/portal/src/api/login.ts new file mode 100644 index 0000000..a9d9dbd --- /dev/null +++ b/portal/frontend/portal/src/api/login.ts @@ -0,0 +1,16 @@ +import request from '@/utils/request' +import {UserInfo,unifiedLoginSDK} from "oauth2-login-sdk"; +// 登录方法 + +// 获取用户详细信息 +export function getInfo(): Promise { + return request({ + url: '/idp/getUserInfo', + method: 'get' + }) +} + +// 退出方法 +export function logout() { + return unifiedLoginSDK.logout() +} diff --git a/portal/frontend/portal/src/api/menu.ts b/portal/frontend/portal/src/api/menu.ts new file mode 100644 index 0000000..7a3b6de --- /dev/null +++ b/portal/frontend/portal/src/api/menu.ts @@ -0,0 +1,10 @@ +import request from '@/utils/request' +import type { RouterVo, AjaxResult } from '@/types' + +// 获取路由 +export const getRouters = (): Promise> => { + return request({ + url: 'idp/routes', + method: 'get' + }) +} diff --git a/portal/frontend/portal/src/api/monitor/cache.ts b/portal/frontend/portal/src/api/monitor/cache.ts new file mode 100644 index 0000000..93a4537 --- /dev/null +++ b/portal/frontend/portal/src/api/monitor/cache.ts @@ -0,0 +1,58 @@ +import request from '@/utils/request' +import type { SysCache, AjaxResult } from '@/types' + +// 查询缓存详细 +export function getCache(): Promise> { + return request({ + url: '/monitor/cache', + method: 'get' + }) +} + +// 查询缓存名称列表 +export function listCacheName(): Promise> { + return request({ + url: '/monitor/cache/getNames', + method: 'get' + }) +} + +// 查询缓存键名列表 +export function listCacheKey(cacheName: string): Promise> { + return request({ + url: '/monitor/cache/getKeys/' + cacheName, + method: 'get' + }) +} + +// 查询缓存内容 +export function getCacheValue(cacheName: string, cacheKey: string): Promise> { + return request({ + url: '/monitor/cache/getValue/' + cacheName + '/' + cacheKey, + method: 'get' + }) +} + +// 清理指定名称缓存 +export function clearCacheName(cacheName: string): Promise { + return request({ + url: '/monitor/cache/clearCacheName/' + cacheName, + method: 'delete' + }) +} + +// 清理指定键名缓存 +export function clearCacheKey(cacheKey: string): Promise { + return request({ + url: '/monitor/cache/clearCacheKey/' + cacheKey, + method: 'delete' + }) +} + +// 清理全部缓存 +export function clearCacheAll(): Promise { + return request({ + url: '/monitor/cache/clearCacheAll', + method: 'delete' + }) +} diff --git a/portal/frontend/portal/src/api/monitor/job.ts b/portal/frontend/portal/src/api/monitor/job.ts new file mode 100644 index 0000000..28057b0 --- /dev/null +++ b/portal/frontend/portal/src/api/monitor/job.ts @@ -0,0 +1,71 @@ +import request from '@/utils/request' +import type { JobQueryParams, SysJob, AjaxResult, TableDataInfo } from '@/types' + +// 查询定时任务调度列表 +export function listJob(query: JobQueryParams): Promise> { + return request({ + url: '/monitor/job/list', + method: 'get', + params: query + }) +} + +// 查询定时任务调度详细 +export function getJob(jobId: number): Promise> { + return request({ + url: '/monitor/job/' + jobId, + method: 'get' + }) +} + +// 新增定时任务调度 +export function addJob(data: SysJob): Promise { + return request({ + url: '/monitor/job', + method: 'post', + data: data + }) +} + +// 修改定时任务调度 +export function updateJob(data: SysJob): Promise { + return request({ + url: '/monitor/job', + method: 'put', + data: data + }) +} + +// 删除定时任务调度 +export function delJob(jobId: number | number[]): Promise { + return request({ + url: '/monitor/job/' + jobId, + method: 'delete' + }) +} + +// 任务状态修改 +export function changeJobStatus(jobId: number, status: string): Promise { + const data = { + jobId, + status + } + return request({ + url: '/monitor/job/changeStatus', + method: 'put', + data: data + }) +} + +// 定时任务立即执行一次 +export function runJob(jobId: number, jobGroup: string): Promise { + const data = { + jobId, + jobGroup + } + return request({ + url: '/monitor/job/run', + method: 'put', + data: data + }) +} diff --git a/portal/frontend/portal/src/api/monitor/jobLog.ts b/portal/frontend/portal/src/api/monitor/jobLog.ts new file mode 100644 index 0000000..7ef73a5 --- /dev/null +++ b/portal/frontend/portal/src/api/monitor/jobLog.ts @@ -0,0 +1,28 @@ +import request from '@/utils/request' +import type { JobLogQueryParams, SysJobLog, AjaxResult, TableDataInfo } from '@/types' + +// 查询调度日志列表 +export function listJobLog(query: JobLogQueryParams): Promise> { + return request({ + url: '/monitor/jobLog/list', + method: 'get', + params: query + }) +} + +// 删除调度日志 +export function delJobLog(jobLogId: number | number[]): Promise { + return request({ + url: '/monitor/jobLog/' + jobLogId, + method: 'delete' + }) +} + +// 清空调度日志 +export function cleanJobLog(): Promise { + return request({ + url: '/monitor/jobLog/clean', + method: 'delete' + }) +} + diff --git a/portal/frontend/portal/src/api/monitor/logininfor.ts b/portal/frontend/portal/src/api/monitor/logininfor.ts new file mode 100644 index 0000000..438e8b3 --- /dev/null +++ b/portal/frontend/portal/src/api/monitor/logininfor.ts @@ -0,0 +1,35 @@ +import request from '@/utils/request' +import type { LogininforQueryParams, SysLogininfor, AjaxResult, TableDataInfo } from '@/types' + +// 查询登录日志列表 +export function list(query: LogininforQueryParams): Promise> { + return request({ + url: '/monitor/logininfor/list', + method: 'get', + params: query + }) +} + +// 删除登录日志 +export function delLogininfor(infoId: number | number[]): Promise { + return request({ + url: '/monitor/logininfor/' + infoId, + method: 'delete' + }) +} + +// 解锁用户登录状态 +export function unlockLogininfor(userName: string): Promise { + return request({ + url: '/monitor/logininfor/unlock/' + userName, + method: 'get' + }) +} + +// 清空登录日志 +export function cleanLogininfor(): Promise { + return request({ + url: '/monitor/logininfor/clean', + method: 'delete' + }) +} diff --git a/portal/frontend/portal/src/api/monitor/online.ts b/portal/frontend/portal/src/api/monitor/online.ts new file mode 100644 index 0000000..080d186 --- /dev/null +++ b/portal/frontend/portal/src/api/monitor/online.ts @@ -0,0 +1,19 @@ +import request from '@/utils/request' +import type { OnlineQueryParams, SysUserOnline, AjaxResult, TableDataInfo } from '@/types' + +// 查询在线用户列表 +export function list(query: OnlineQueryParams): Promise> { + return request({ + url: '/monitor/online/list', + method: 'get', + params: query + }) +} + +// 强退用户 +export function forceLogout(tokenId: string): Promise { + return request({ + url: '/monitor/online/' + tokenId, + method: 'delete' + }) +} diff --git a/portal/frontend/portal/src/api/monitor/operlog.ts b/portal/frontend/portal/src/api/monitor/operlog.ts new file mode 100644 index 0000000..c4a6b24 --- /dev/null +++ b/portal/frontend/portal/src/api/monitor/operlog.ts @@ -0,0 +1,27 @@ +import request from '@/utils/request' +import type { OperlogQueryParams, SysOperLog, AjaxResult, TableDataInfo } from '@/types' + +// 查询操作日志列表 +export function list(query: OperlogQueryParams): Promise> { + return request({ + url: '/monitor/operlog/list', + method: 'get', + params: query + }) +} + +// 删除操作日志 +export function delOperlog(operId: number | number[]): Promise { + return request({ + url: '/monitor/operlog/' + operId, + method: 'delete' + }) +} + +// 清空操作日志 +export function cleanOperlog(): Promise { + return request({ + url: '/monitor/operlog/clean', + method: 'delete' + }) +} diff --git a/portal/frontend/portal/src/api/monitor/server.ts b/portal/frontend/portal/src/api/monitor/server.ts new file mode 100644 index 0000000..9690d42 --- /dev/null +++ b/portal/frontend/portal/src/api/monitor/server.ts @@ -0,0 +1,10 @@ +import request from '@/utils/request' +import type { AjaxResult } from '@/types' + +// 获取服务信息 +export function getServer(): Promise> { + return request({ + url: '/monitor/server', + method: 'get' + }) +} diff --git a/portal/frontend/portal/src/api/system/config.ts b/portal/frontend/portal/src/api/system/config.ts new file mode 100644 index 0000000..ddbd63e --- /dev/null +++ b/portal/frontend/portal/src/api/system/config.ts @@ -0,0 +1,61 @@ +import request from '@/utils/request' +import type { ConfigQueryParams, SysConfig, AjaxResult, TableDataInfo } from '@/types' + +// 查询参数列表 +export function listConfig(query: ConfigQueryParams): Promise> { + return request({ + url: '/system/config/list', + method: 'get', + params: query + }) +} + +// 查询参数详细 +export function getConfig(configId: number): Promise> { + return request({ + url: '/system/config/' + configId, + method: 'get' + }) +} + +// 根据参数键名查询参数值 +export function getConfigKey(configKey: string): Promise { + return request({ + url: '/system/config/configKey/' + configKey, + method: 'get' + }) +} + +// 新增参数配置 +export function addConfig(data: SysConfig): Promise { + return request({ + url: '/system/config', + method: 'post', + data: data + }) +} + +// 修改参数配置 +export function updateConfig(data: SysConfig): Promise { + return request({ + url: '/system/config', + method: 'put', + data: data + }) +} + +// 删除参数配置 +export function delConfig(configId: number | number[]): Promise { + return request({ + url: '/system/config/' + configId, + method: 'delete' + }) +} + +// 刷新参数缓存 +export function refreshCache(): Promise { + return request({ + url: '/system/config/refreshCache', + method: 'delete' + }) +} diff --git a/portal/frontend/portal/src/api/system/dept.ts b/portal/frontend/portal/src/api/system/dept.ts new file mode 100644 index 0000000..17d4807 --- /dev/null +++ b/portal/frontend/portal/src/api/system/dept.ts @@ -0,0 +1,53 @@ +import request from '@/utils/request' +import type { DeptQueryParams, SysDept, AjaxResult } from '@/types' + +// 查询部门列表 +export function listDept(query?: DeptQueryParams): Promise> { + return request({ + url: '/system/dept/list', + method: 'get', + params: query + }) +} + +// 查询部门列表(排除节点) +export function listDeptExcludeChild(deptId: number): Promise> { + return request({ + url: '/system/dept/list/exclude/' + deptId, + method: 'get' + }) +} + +// 查询部门详细 +export function getDept(deptId: number): Promise> { + return request({ + url: '/system/dept/' + deptId, + method: 'get' + }) +} + +// 新增部门 +export function addDept(data: SysDept): Promise { + return request({ + url: '/system/dept', + method: 'post', + data: data + }) +} + +// 修改部门 +export function updateDept(data: SysDept): Promise { + return request({ + url: '/system/dept', + method: 'put', + data: data + }) +} + +// 删除部门 +export function delDept(deptId: number): Promise { + return request({ + url: '/system/dept/' + deptId, + method: 'delete' + }) +} diff --git a/portal/frontend/portal/src/api/system/dict/data.ts b/portal/frontend/portal/src/api/system/dict/data.ts new file mode 100644 index 0000000..9185ce6 --- /dev/null +++ b/portal/frontend/portal/src/api/system/dict/data.ts @@ -0,0 +1,53 @@ +import request from '@/utils/request' +import type { DictDataQueryParams, SysDictData, AjaxResult, TableDataInfo } from '@/types' + +// 查询字典数据列表 +export function listData(query: DictDataQueryParams): Promise> { + return request({ + url: '/system/dict/data/list', + method: 'get', + params: query + }) +} + +// 查询字典数据详细 +export function getData(dictCode: number): Promise> { + return request({ + url: '/system/dict/data/' + dictCode, + method: 'get' + }) +} + +// 根据字典类型查询字典数据信息 +export function getDicts(dictType: string): Promise> { + return request({ + url: '/system/dict/data/type/' + dictType, + method: 'get' + }) +} + +// 新增字典数据 +export function addData(data: SysDictData): Promise { + return request({ + url: '/system/dict/data', + method: 'post', + data: data + }) +} + +// 修改字典数据 +export function updateData(data: SysDictData): Promise { + return request({ + url: '/system/dict/data', + method: 'put', + data: data + }) +} + +// 删除字典数据 +export function delData(dictCode: number | number[]): Promise { + return request({ + url: '/system/dict/data/' + dictCode, + method: 'delete' + }) +} diff --git a/portal/frontend/portal/src/api/system/dict/type.ts b/portal/frontend/portal/src/api/system/dict/type.ts new file mode 100644 index 0000000..ee7dcf9 --- /dev/null +++ b/portal/frontend/portal/src/api/system/dict/type.ts @@ -0,0 +1,61 @@ +import request from '@/utils/request' +import type { DictTypeQueryParams, SysDictType, AjaxResult, TableDataInfo } from '@/types' + +// 查询字典类型列表 +export function listType(query: DictTypeQueryParams): Promise> { + return request({ + url: '/system/dict/type/list', + method: 'get', + params: query + }) +} + +// 查询字典类型详细 +export function getType(dictId: number): Promise> { + return request({ + url: '/system/dict/type/' + dictId, + method: 'get' + }) +} + +// 新增字典类型 +export function addType(data: SysDictType): Promise { + return request({ + url: '/system/dict/type', + method: 'post', + data: data + }) +} + +// 修改字典类型 +export function updateType(data: SysDictType): Promise { + return request({ + url: '/system/dict/type', + method: 'put', + data: data + }) +} + +// 删除字典类型 +export function delType(dictId: number | number[]): Promise { + return request({ + url: '/system/dict/type/' + dictId, + method: 'delete' + }) +} + +// 刷新字典缓存 +export function refreshCache(): Promise { + return request({ + url: '/system/dict/type/refreshCache', + method: 'delete' + }) +} + +// 获取字典选择框列表 +export function optionselect(): Promise> { + return request({ + url: '/system/dict/type/optionselect', + method: 'get' + }) +} diff --git a/portal/frontend/portal/src/api/system/menu.ts b/portal/frontend/portal/src/api/system/menu.ts new file mode 100644 index 0000000..a6d0dc5 --- /dev/null +++ b/portal/frontend/portal/src/api/system/menu.ts @@ -0,0 +1,61 @@ +import request from '@/utils/request' +import type { MenuQueryParams, SysMenu, TreeSelect, AjaxResult, RoleMenuTreeselectResult } from '@/types' + +// 查询菜单列表 +export function listMenu(query?: MenuQueryParams): Promise> { + return request({ + url: '/system/menu/list', + method: 'get', + params: query + }) +} + +// 查询菜单详细 +export function getMenu(menuId: number): Promise> { + return request({ + url: '/system/menu/' + menuId, + method: 'get' + }) +} + +// 查询菜单下拉树结构 +export function treeselect(): Promise> { + return request({ + url: '/system/menu/treeselect', + method: 'get' + }) +} + +// 根据角色ID查询菜单下拉树结构 +export function roleMenuTreeselect(roleId: number): Promise { + return request({ + url: '/system/menu/roleMenuTreeselect/' + roleId, + method: 'get' + }) +} + +// 新增菜单 +export function addMenu(data: SysMenu): Promise { + return request({ + url: '/system/menu', + method: 'post', + data: data + }) +} + +// 修改菜单 +export function updateMenu(data: SysMenu): Promise { + return request({ + url: '/system/menu', + method: 'put', + data: data + }) +} + +// 删除菜单 +export function delMenu(menuId: number): Promise { + return request({ + url: '/system/menu/' + menuId, + method: 'delete' + }) +} diff --git a/portal/frontend/portal/src/api/system/notice.ts b/portal/frontend/portal/src/api/system/notice.ts new file mode 100644 index 0000000..e6ba17f --- /dev/null +++ b/portal/frontend/portal/src/api/system/notice.ts @@ -0,0 +1,45 @@ +import request from '@/utils/request' +import type { NoticeQueryParams, SysNotice, AjaxResult, TableDataInfo } from '@/types' + +// 查询公告列表 +export function listNotice(query: NoticeQueryParams): Promise> { + return request({ + url: '/system/notice/list', + method: 'get', + params: query + }) +} + +// 查询公告详细 +export function getNotice(noticeId: number): Promise> { + return request({ + url: '/system/notice/' + noticeId, + method: 'get' + }) +} + +// 新增公告 +export function addNotice(data: SysNotice): Promise { + return request({ + url: '/system/notice', + method: 'post', + data: data + }) +} + +// 修改公告 +export function updateNotice(data: SysNotice): Promise { + return request({ + url: '/system/notice', + method: 'put', + data: data + }) +} + +// 删除公告 +export function delNotice(noticeId: number | number[]): Promise { + return request({ + url: '/system/notice/' + noticeId, + method: 'delete' + }) +} diff --git a/portal/frontend/portal/src/api/system/post.ts b/portal/frontend/portal/src/api/system/post.ts new file mode 100644 index 0000000..c56c3eb --- /dev/null +++ b/portal/frontend/portal/src/api/system/post.ts @@ -0,0 +1,45 @@ +import request from '@/utils/request' +import type { PostQueryParams, SysPost, AjaxResult, TableDataInfo } from '@/types' + +// 查询岗位列表 +export function listPost(query: PostQueryParams): Promise> { + return request({ + url: '/system/post/list', + method: 'get', + params: query + }) +} + +// 查询岗位详细 +export function getPost(postId: number): Promise> { + return request({ + url: '/system/post/' + postId, + method: 'get' + }) +} + +// 新增岗位 +export function addPost(data: SysPost): Promise { + return request({ + url: '/system/post', + method: 'post', + data: data + }) +} + +// 修改岗位 +export function updatePost(data: SysPost): Promise { + return request({ + url: '/system/post', + method: 'put', + data: data + }) +} + +// 删除岗位 +export function delPost(postId: number | number[]): Promise { + return request({ + url: '/system/post/' + postId, + method: 'delete' + }) +} diff --git a/portal/frontend/portal/src/api/system/role.ts b/portal/frontend/portal/src/api/system/role.ts new file mode 100644 index 0000000..b1bd92d --- /dev/null +++ b/portal/frontend/portal/src/api/system/role.ts @@ -0,0 +1,120 @@ +import request from '@/utils/request' +import type { RoleQueryParams, AuthUserQueryParams, SysRole, SysUser, SysUserRole, AuthUserSelectParams, AjaxResult, RoleDeptTreeResult, TableDataInfo } from '@/types' + +// 查询角色列表 +export function listRole(query: RoleQueryParams): Promise> { + return request({ + url: '/system/role/list', + method: 'get', + params: query + }) +} + +// 查询角色详细 +export function getRole(roleId: number): Promise> { + return request({ + url: '/system/role/' + roleId, + method: 'get' + }) +} + +// 新增角色 +export function addRole(data: SysRole): Promise { + return request({ + url: '/system/role', + method: 'post', + data: data + }) +} + +// 修改角色 +export function updateRole(data: SysRole): Promise { + return request({ + url: '/system/role', + method: 'put', + data: data + }) +} + +// 角色数据权限 +export function dataScope(data: SysRole): Promise { + return request({ + url: '/system/role/dataScope', + method: 'put', + data: data + }) +} + +// 角色状态修改 +export function changeRoleStatus(roleId: number, status: string): Promise { + const data = { + roleId, + status + } + return request({ + url: '/system/role/changeStatus', + method: 'put', + data: data + }) +} + +// 删除角色 +export function delRole(roleId: number | number[]): Promise { + return request({ + url: '/system/role/' + roleId, + method: 'delete' + }) +} + +// 查询角色已授权用户列表 +export function allocatedUserList(query: AuthUserQueryParams): Promise> { + return request({ + url: '/system/role/authUser/allocatedList', + method: 'get', + params: query + }) +} + +// 查询角色未授权用户列表 +export function unallocatedUserList(query: AuthUserQueryParams): Promise> { + return request({ + url: '/system/role/authUser/unallocatedList', + method: 'get', + params: query + }) +} + +// 取消用户授权角色 +export function authUserCancel(data: SysUserRole): Promise { + return request({ + url: '/system/role/authUser/cancel', + method: 'put', + data: data + }) +} + +// 批量取消用户授权角色 +export function authUserCancelAll(data: AuthUserSelectParams): Promise { + return request({ + url: '/system/role/authUser/cancelAll', + method: 'put', + params: data + }) +} + +// 授权用户选择 +export function authUserSelectAll(data: AuthUserSelectParams): Promise { + return request({ + url: '/system/role/authUser/selectAll', + method: 'put', + params: data + }) +} + +// 根据角色ID查询部门树结构 +export function deptTreeSelect(roleId: number): Promise { + return request({ + url: '/system/role/deptTree/' + roleId, + method: 'get' + }) +} diff --git a/portal/frontend/portal/src/api/system/user.ts b/portal/frontend/portal/src/api/system/user.ts new file mode 100644 index 0000000..96e07ee --- /dev/null +++ b/portal/frontend/portal/src/api/system/user.ts @@ -0,0 +1,137 @@ +import request from '@/utils/request' +import { parseStrEmpty } from "@/utils/platform" +import type { UserQueryParams, UserFormDataResult, UserProfileResult, UserAuthRoleResult, UserProfileAvatarResult, SysUser, SysUserRole, SysUserRoles, AjaxResult, TableDataInfo, TreeSelect } from '@/types' + +// 查询用户列表 +export function listUser(query: UserQueryParams): Promise> { + return request({ + url: '/system/user/list', + method: 'get', + params: query + }) +} + +// 查询用户详细 +export function getUser(userId?: number): Promise { + return request({ + url: '/system/user/' + parseStrEmpty(userId), + method: 'get' + }) +} + +// 新增用户 +export function addUser(data: SysUser): Promise { + return request({ + url: '/system/user', + method: 'post', + data: data + }) +} + +// 修改用户 +export function updateUser(data: SysUser): Promise { + return request({ + url: '/system/user', + method: 'put', + data: data + }) +} + +// 删除用户 +export function delUser(userId: number | number[]): Promise { + return request({ + url: '/system/user/' + userId, + method: 'delete' + }) +} + +// 用户密码重置 +export function resetUserPwd(userId: number, password: string): Promise { + const data = { + userId, + password + } + return request({ + url: '/system/user/resetPwd', + method: 'put', + data: data + }) +} + +// 用户状态修改 +export function changeUserStatus(userId: number, status: string): Promise { + const data = { + userId, + status + } + return request({ + url: '/system/user/changeStatus', + method: 'put', + data: data + }) +} + +// 查询用户个人信息 +export function getUserProfile(): Promise { + return request({ + url: '/system/user/profile', + method: 'get' + }) +} + +// 修改用户个人信息 +export function updateUserProfile(data: SysUser): Promise { + return request({ + url: '/system/user/profile', + method: 'put', + data: data + }) +} + +// 用户密码重置 +export function updateUserPwd(oldPassword: string, newPassword: string): Promise { + const data = { + oldPassword, + newPassword + } + return request({ + url: '/system/user/profile/updatePwd', + method: 'put', + data: data + }) +} + +// 用户头像上传 +export function uploadAvatar(file: FormData | File): Promise { + return request({ + url: '/system/user/profile/avatar', + method: 'post', + headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, + data: file + }) +} + +// 查询授权角色 +export function getAuthRole(userId: number): Promise { + return request({ + url: '/system/user/authRole/' + userId, + method: 'get' + }) +} + +// 保存授权角色 +export function updateAuthRole(data: SysUserRoles): Promise { + return request({ + url: '/system/user/authRole', + method: 'put', + params: data + }) +} + +// 查询部门下拉树结构 +export function deptTreeSelect(): Promise> { + return request({ + url: '/system/user/deptTree', + method: 'get' + }) +} diff --git a/portal/frontend/portal/src/api/tool/gen.ts b/portal/frontend/portal/src/api/tool/gen.ts new file mode 100644 index 0000000..44d74a9 --- /dev/null +++ b/portal/frontend/portal/src/api/tool/gen.ts @@ -0,0 +1,88 @@ +import request from '@/utils/request' +import type { GenQueryParams, GenTable, GenTableInfoResult, AjaxResult, TableDataInfo } from '@/types' + +// 查询生成表数据 +export function listTable(query: GenQueryParams): Promise> { + return request({ + url: '/tool/gen/list', + method: 'get', + params: query + }) +} + +// 查询db数据库列表 +export function listDbTable(query: GenQueryParams): Promise> { + return request({ + url: '/tool/gen/db/list', + method: 'get', + params: query + }) +} + +// 查询表详细信息 +export function getGenTable(tableId: number): Promise> { + return request({ + url: '/tool/gen/' + tableId, + method: 'get' + }) +} + +// 修改代码生成信息 +export function updateGenTable(data: GenTable): Promise { + return request({ + url: '/tool/gen', + method: 'put', + data: data + }) +} + +// 导入表 +export function importTable(data: any): Promise { + return request({ + url: '/tool/gen/importTable', + method: 'post', + params: data + }) +} + +// 创建表 +export function createTable(data: any): Promise { + return request({ + url: '/tool/gen/createTable', + method: 'post', + params: data + }) +} + +// 预览生成代码 +export function previewTable(tableId: number): Promise> { + return request({ + url: '/tool/gen/preview/' + tableId, + method: 'get' + }) +} + +// 删除表数据 +export function delTable(tableId: number | number[]): Promise { + return request({ + url: '/tool/gen/' + tableId, + method: 'delete' + }) +} + +// 生成代码(自定义路径) +export function genCode(tableName: string): Promise { + return request({ + url: '/tool/gen/genCode/' + tableName, + method: 'get' + }) +} + +// 同步数据库 +export function synchDb(tableName: string): Promise { + return request({ + url: '/tool/gen/synchDb/' + tableName, + method: 'get' + }) +} + diff --git a/portal/frontend/portal/src/assets/401_images/401.gif b/portal/frontend/portal/src/assets/401_images/401.gif new file mode 100644 index 0000000..cd6e0d9 Binary files /dev/null and b/portal/frontend/portal/src/assets/401_images/401.gif differ diff --git a/portal/frontend/portal/src/assets/404_images/404.png b/portal/frontend/portal/src/assets/404_images/404.png new file mode 100644 index 0000000..3d8e230 Binary files /dev/null and b/portal/frontend/portal/src/assets/404_images/404.png differ diff --git a/portal/frontend/portal/src/assets/404_images/404_cloud.png b/portal/frontend/portal/src/assets/404_images/404_cloud.png new file mode 100644 index 0000000..c6281d0 Binary files /dev/null and b/portal/frontend/portal/src/assets/404_images/404_cloud.png differ diff --git a/portal/frontend/portal/src/assets/icons/svg/404.svg b/portal/frontend/portal/src/assets/icons/svg/404.svg new file mode 100644 index 0000000..6df5019 --- /dev/null +++ b/portal/frontend/portal/src/assets/icons/svg/404.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/portal/frontend/portal/src/assets/icons/svg/bug.svg b/portal/frontend/portal/src/assets/icons/svg/bug.svg new file mode 100644 index 0000000..05a150d --- /dev/null +++ b/portal/frontend/portal/src/assets/icons/svg/bug.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/portal/frontend/portal/src/assets/icons/svg/build.svg b/portal/frontend/portal/src/assets/icons/svg/build.svg new file mode 100644 index 0000000..97c4688 --- /dev/null +++ b/portal/frontend/portal/src/assets/icons/svg/build.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/portal/frontend/portal/src/assets/icons/svg/button.svg b/portal/frontend/portal/src/assets/icons/svg/button.svg new file mode 100644 index 0000000..904fddc --- /dev/null +++ b/portal/frontend/portal/src/assets/icons/svg/button.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/portal/frontend/portal/src/assets/icons/svg/cascader.svg b/portal/frontend/portal/src/assets/icons/svg/cascader.svg new file mode 100644 index 0000000..e256024 --- /dev/null +++ b/portal/frontend/portal/src/assets/icons/svg/cascader.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/portal/frontend/portal/src/assets/icons/svg/chart.svg b/portal/frontend/portal/src/assets/icons/svg/chart.svg new file mode 100644 index 0000000..27728fb --- /dev/null +++ b/portal/frontend/portal/src/assets/icons/svg/chart.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/portal/frontend/portal/src/assets/icons/svg/checkbox.svg b/portal/frontend/portal/src/assets/icons/svg/checkbox.svg new file mode 100644 index 0000000..013fd3a --- /dev/null +++ b/portal/frontend/portal/src/assets/icons/svg/checkbox.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/portal/frontend/portal/src/assets/icons/svg/clipboard.svg b/portal/frontend/portal/src/assets/icons/svg/clipboard.svg new file mode 100644 index 0000000..90923ff --- /dev/null +++ b/portal/frontend/portal/src/assets/icons/svg/clipboard.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/portal/frontend/portal/src/assets/icons/svg/code.svg b/portal/frontend/portal/src/assets/icons/svg/code.svg new file mode 100644 index 0000000..5f9c5ab --- /dev/null +++ b/portal/frontend/portal/src/assets/icons/svg/code.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/portal/frontend/portal/src/assets/icons/svg/color.svg b/portal/frontend/portal/src/assets/icons/svg/color.svg new file mode 100644 index 0000000..44a81aa --- /dev/null +++ b/portal/frontend/portal/src/assets/icons/svg/color.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/portal/frontend/portal/src/assets/icons/svg/component.svg b/portal/frontend/portal/src/assets/icons/svg/component.svg new file mode 100644 index 0000000..29c3458 --- /dev/null +++ b/portal/frontend/portal/src/assets/icons/svg/component.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/portal/frontend/portal/src/assets/icons/svg/dashboard.svg b/portal/frontend/portal/src/assets/icons/svg/dashboard.svg new file mode 100644 index 0000000..5317d37 --- /dev/null +++ b/portal/frontend/portal/src/assets/icons/svg/dashboard.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/portal/frontend/portal/src/assets/icons/svg/date-range.svg b/portal/frontend/portal/src/assets/icons/svg/date-range.svg new file mode 100644 index 0000000..fda571e --- /dev/null +++ b/portal/frontend/portal/src/assets/icons/svg/date-range.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/portal/frontend/portal/src/assets/icons/svg/date.svg b/portal/frontend/portal/src/assets/icons/svg/date.svg new file mode 100644 index 0000000..52dc73e --- /dev/null +++ b/portal/frontend/portal/src/assets/icons/svg/date.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/portal/frontend/portal/src/assets/icons/svg/dict.svg b/portal/frontend/portal/src/assets/icons/svg/dict.svg new file mode 100644 index 0000000..4849377 --- /dev/null +++ b/portal/frontend/portal/src/assets/icons/svg/dict.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/portal/frontend/portal/src/assets/icons/svg/documentation.svg b/portal/frontend/portal/src/assets/icons/svg/documentation.svg new file mode 100644 index 0000000..7043122 --- /dev/null +++ b/portal/frontend/portal/src/assets/icons/svg/documentation.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/portal/frontend/portal/src/assets/icons/svg/download.svg b/portal/frontend/portal/src/assets/icons/svg/download.svg new file mode 100644 index 0000000..c896951 --- /dev/null +++ b/portal/frontend/portal/src/assets/icons/svg/download.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/portal/frontend/portal/src/assets/icons/svg/drag.svg b/portal/frontend/portal/src/assets/icons/svg/drag.svg new file mode 100644 index 0000000..4185d3c --- /dev/null +++ b/portal/frontend/portal/src/assets/icons/svg/drag.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/portal/frontend/portal/src/assets/icons/svg/druid.svg b/portal/frontend/portal/src/assets/icons/svg/druid.svg new file mode 100644 index 0000000..a2b4b4e --- /dev/null +++ b/portal/frontend/portal/src/assets/icons/svg/druid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/portal/frontend/portal/src/assets/icons/svg/edit.svg b/portal/frontend/portal/src/assets/icons/svg/edit.svg new file mode 100644 index 0000000..d26101f --- /dev/null +++ b/portal/frontend/portal/src/assets/icons/svg/edit.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/portal/frontend/portal/src/assets/icons/svg/education.svg b/portal/frontend/portal/src/assets/icons/svg/education.svg new file mode 100644 index 0000000..7bfb01d --- /dev/null +++ b/portal/frontend/portal/src/assets/icons/svg/education.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/portal/frontend/portal/src/assets/icons/svg/email.svg b/portal/frontend/portal/src/assets/icons/svg/email.svg new file mode 100644 index 0000000..74d25e2 --- /dev/null +++ b/portal/frontend/portal/src/assets/icons/svg/email.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/portal/frontend/portal/src/assets/icons/svg/enter.svg b/portal/frontend/portal/src/assets/icons/svg/enter.svg new file mode 100644 index 0000000..f7cabf2 --- /dev/null +++ b/portal/frontend/portal/src/assets/icons/svg/enter.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/portal/frontend/portal/src/assets/icons/svg/example.svg b/portal/frontend/portal/src/assets/icons/svg/example.svg new file mode 100644 index 0000000..46f42b5 --- /dev/null +++ b/portal/frontend/portal/src/assets/icons/svg/example.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/portal/frontend/portal/src/assets/icons/svg/excel.svg b/portal/frontend/portal/src/assets/icons/svg/excel.svg new file mode 100644 index 0000000..74d97b8 --- /dev/null +++ b/portal/frontend/portal/src/assets/icons/svg/excel.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/portal/frontend/portal/src/assets/icons/svg/exit-fullscreen.svg b/portal/frontend/portal/src/assets/icons/svg/exit-fullscreen.svg new file mode 100644 index 0000000..485c128 --- /dev/null +++ b/portal/frontend/portal/src/assets/icons/svg/exit-fullscreen.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/portal/frontend/portal/src/assets/icons/svg/eye-open.svg b/portal/frontend/portal/src/assets/icons/svg/eye-open.svg new file mode 100644 index 0000000..88dcc98 --- /dev/null +++ b/portal/frontend/portal/src/assets/icons/svg/eye-open.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/portal/frontend/portal/src/assets/icons/svg/eye.svg b/portal/frontend/portal/src/assets/icons/svg/eye.svg new file mode 100644 index 0000000..16ed2d8 --- /dev/null +++ b/portal/frontend/portal/src/assets/icons/svg/eye.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/portal/frontend/portal/src/assets/icons/svg/form.svg b/portal/frontend/portal/src/assets/icons/svg/form.svg new file mode 100644 index 0000000..dcbaa18 --- /dev/null +++ b/portal/frontend/portal/src/assets/icons/svg/form.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/portal/frontend/portal/src/assets/icons/svg/fullscreen.svg b/portal/frontend/portal/src/assets/icons/svg/fullscreen.svg new file mode 100644 index 0000000..0e86b6f --- /dev/null +++ b/portal/frontend/portal/src/assets/icons/svg/fullscreen.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/portal/frontend/portal/src/assets/icons/svg/github.svg b/portal/frontend/portal/src/assets/icons/svg/github.svg new file mode 100644 index 0000000..db0a0d4 --- /dev/null +++ b/portal/frontend/portal/src/assets/icons/svg/github.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/portal/frontend/portal/src/assets/icons/svg/guide.svg b/portal/frontend/portal/src/assets/icons/svg/guide.svg new file mode 100644 index 0000000..b271001 --- /dev/null +++ b/portal/frontend/portal/src/assets/icons/svg/guide.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/portal/frontend/portal/src/assets/icons/svg/icon.svg b/portal/frontend/portal/src/assets/icons/svg/icon.svg new file mode 100644 index 0000000..82be8ee --- /dev/null +++ b/portal/frontend/portal/src/assets/icons/svg/icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/portal/frontend/portal/src/assets/icons/svg/input.svg b/portal/frontend/portal/src/assets/icons/svg/input.svg new file mode 100644 index 0000000..ab91381 --- /dev/null +++ b/portal/frontend/portal/src/assets/icons/svg/input.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/portal/frontend/portal/src/assets/icons/svg/international.svg b/portal/frontend/portal/src/assets/icons/svg/international.svg new file mode 100644 index 0000000..e9b56ee --- /dev/null +++ b/portal/frontend/portal/src/assets/icons/svg/international.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/portal/frontend/portal/src/assets/icons/svg/job.svg b/portal/frontend/portal/src/assets/icons/svg/job.svg new file mode 100644 index 0000000..2a93a25 --- /dev/null +++ b/portal/frontend/portal/src/assets/icons/svg/job.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/portal/frontend/portal/src/assets/icons/svg/language.svg b/portal/frontend/portal/src/assets/icons/svg/language.svg new file mode 100644 index 0000000..0082b57 --- /dev/null +++ b/portal/frontend/portal/src/assets/icons/svg/language.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/portal/frontend/portal/src/assets/icons/svg/link.svg b/portal/frontend/portal/src/assets/icons/svg/link.svg new file mode 100644 index 0000000..48197ba --- /dev/null +++ b/portal/frontend/portal/src/assets/icons/svg/link.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/portal/frontend/portal/src/assets/icons/svg/list.svg b/portal/frontend/portal/src/assets/icons/svg/list.svg new file mode 100644 index 0000000..20259ed --- /dev/null +++ b/portal/frontend/portal/src/assets/icons/svg/list.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/portal/frontend/portal/src/assets/icons/svg/lock.svg b/portal/frontend/portal/src/assets/icons/svg/lock.svg new file mode 100644 index 0000000..74fee54 --- /dev/null +++ b/portal/frontend/portal/src/assets/icons/svg/lock.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/portal/frontend/portal/src/assets/icons/svg/log.svg b/portal/frontend/portal/src/assets/icons/svg/log.svg new file mode 100644 index 0000000..d879d33 --- /dev/null +++ b/portal/frontend/portal/src/assets/icons/svg/log.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/portal/frontend/portal/src/assets/icons/svg/logininfor.svg b/portal/frontend/portal/src/assets/icons/svg/logininfor.svg new file mode 100644 index 0000000..267f844 --- /dev/null +++ b/portal/frontend/portal/src/assets/icons/svg/logininfor.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/portal/frontend/portal/src/assets/icons/svg/message.svg b/portal/frontend/portal/src/assets/icons/svg/message.svg new file mode 100644 index 0000000..14ca817 --- /dev/null +++ b/portal/frontend/portal/src/assets/icons/svg/message.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/portal/frontend/portal/src/assets/icons/svg/money.svg b/portal/frontend/portal/src/assets/icons/svg/money.svg new file mode 100644 index 0000000..c1580de --- /dev/null +++ b/portal/frontend/portal/src/assets/icons/svg/money.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/portal/frontend/portal/src/assets/icons/svg/monitor.svg b/portal/frontend/portal/src/assets/icons/svg/monitor.svg new file mode 100644 index 0000000..bc308cb --- /dev/null +++ b/portal/frontend/portal/src/assets/icons/svg/monitor.svg @@ -0,0 +1,2 @@ + \ No newline at end of file diff --git a/portal/frontend/portal/src/assets/icons/svg/moon.svg b/portal/frontend/portal/src/assets/icons/svg/moon.svg new file mode 100644 index 0000000..ec72d77 --- /dev/null +++ b/portal/frontend/portal/src/assets/icons/svg/moon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/portal/frontend/portal/src/assets/icons/svg/more-up.svg b/portal/frontend/portal/src/assets/icons/svg/more-up.svg new file mode 100644 index 0000000..d30ac11 --- /dev/null +++ b/portal/frontend/portal/src/assets/icons/svg/more-up.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/portal/frontend/portal/src/assets/icons/svg/nested.svg b/portal/frontend/portal/src/assets/icons/svg/nested.svg new file mode 100644 index 0000000..06713a8 --- /dev/null +++ b/portal/frontend/portal/src/assets/icons/svg/nested.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/portal/frontend/portal/src/assets/icons/svg/number.svg b/portal/frontend/portal/src/assets/icons/svg/number.svg new file mode 100644 index 0000000..ad5ce9a --- /dev/null +++ b/portal/frontend/portal/src/assets/icons/svg/number.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/portal/frontend/portal/src/assets/icons/svg/online.svg b/portal/frontend/portal/src/assets/icons/svg/online.svg new file mode 100644 index 0000000..330a202 --- /dev/null +++ b/portal/frontend/portal/src/assets/icons/svg/online.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/portal/frontend/portal/src/assets/icons/svg/password.svg b/portal/frontend/portal/src/assets/icons/svg/password.svg new file mode 100644 index 0000000..6c64def --- /dev/null +++ b/portal/frontend/portal/src/assets/icons/svg/password.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/portal/frontend/portal/src/assets/icons/svg/pdf.svg b/portal/frontend/portal/src/assets/icons/svg/pdf.svg new file mode 100644 index 0000000..957aa0c --- /dev/null +++ b/portal/frontend/portal/src/assets/icons/svg/pdf.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/portal/frontend/portal/src/assets/icons/svg/people.svg b/portal/frontend/portal/src/assets/icons/svg/people.svg new file mode 100644 index 0000000..2bd54ae --- /dev/null +++ b/portal/frontend/portal/src/assets/icons/svg/people.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/portal/frontend/portal/src/assets/icons/svg/peoples.svg b/portal/frontend/portal/src/assets/icons/svg/peoples.svg new file mode 100644 index 0000000..aab852e --- /dev/null +++ b/portal/frontend/portal/src/assets/icons/svg/peoples.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/portal/frontend/portal/src/assets/icons/svg/phone.svg b/portal/frontend/portal/src/assets/icons/svg/phone.svg new file mode 100644 index 0000000..ab8e8c4 --- /dev/null +++ b/portal/frontend/portal/src/assets/icons/svg/phone.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/portal/frontend/portal/src/assets/icons/svg/post.svg b/portal/frontend/portal/src/assets/icons/svg/post.svg new file mode 100644 index 0000000..2922c61 --- /dev/null +++ b/portal/frontend/portal/src/assets/icons/svg/post.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/portal/frontend/portal/src/assets/icons/svg/qq.svg b/portal/frontend/portal/src/assets/icons/svg/qq.svg new file mode 100644 index 0000000..ee13d4e --- /dev/null +++ b/portal/frontend/portal/src/assets/icons/svg/qq.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/portal/frontend/portal/src/assets/icons/svg/question.svg b/portal/frontend/portal/src/assets/icons/svg/question.svg new file mode 100644 index 0000000..cf75bd4 --- /dev/null +++ b/portal/frontend/portal/src/assets/icons/svg/question.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/portal/frontend/portal/src/assets/icons/svg/radio.svg b/portal/frontend/portal/src/assets/icons/svg/radio.svg new file mode 100644 index 0000000..0cde345 --- /dev/null +++ b/portal/frontend/portal/src/assets/icons/svg/radio.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/portal/frontend/portal/src/assets/icons/svg/rate.svg b/portal/frontend/portal/src/assets/icons/svg/rate.svg new file mode 100644 index 0000000..aa3b14d --- /dev/null +++ b/portal/frontend/portal/src/assets/icons/svg/rate.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/portal/frontend/portal/src/assets/icons/svg/redis-list.svg b/portal/frontend/portal/src/assets/icons/svg/redis-list.svg new file mode 100644 index 0000000..98a15b2 --- /dev/null +++ b/portal/frontend/portal/src/assets/icons/svg/redis-list.svg @@ -0,0 +1,2 @@ + \ No newline at end of file diff --git a/portal/frontend/portal/src/assets/icons/svg/redis.svg b/portal/frontend/portal/src/assets/icons/svg/redis.svg new file mode 100644 index 0000000..2f1d62d --- /dev/null +++ b/portal/frontend/portal/src/assets/icons/svg/redis.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/portal/frontend/portal/src/assets/icons/svg/row.svg b/portal/frontend/portal/src/assets/icons/svg/row.svg new file mode 100644 index 0000000..0780992 --- /dev/null +++ b/portal/frontend/portal/src/assets/icons/svg/row.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/portal/frontend/portal/src/assets/icons/svg/search.svg b/portal/frontend/portal/src/assets/icons/svg/search.svg new file mode 100644 index 0000000..84233dd --- /dev/null +++ b/portal/frontend/portal/src/assets/icons/svg/search.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/portal/frontend/portal/src/assets/icons/svg/select.svg b/portal/frontend/portal/src/assets/icons/svg/select.svg new file mode 100644 index 0000000..d628382 --- /dev/null +++ b/portal/frontend/portal/src/assets/icons/svg/select.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/portal/frontend/portal/src/assets/icons/svg/server.svg b/portal/frontend/portal/src/assets/icons/svg/server.svg new file mode 100644 index 0000000..eb287e3 --- /dev/null +++ b/portal/frontend/portal/src/assets/icons/svg/server.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/portal/frontend/portal/src/assets/icons/svg/shopping.svg b/portal/frontend/portal/src/assets/icons/svg/shopping.svg new file mode 100644 index 0000000..87513e7 --- /dev/null +++ b/portal/frontend/portal/src/assets/icons/svg/shopping.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/portal/frontend/portal/src/assets/icons/svg/size.svg b/portal/frontend/portal/src/assets/icons/svg/size.svg new file mode 100644 index 0000000..1a409f5 --- /dev/null +++ b/portal/frontend/portal/src/assets/icons/svg/size.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/portal/frontend/portal/src/assets/icons/svg/skill.svg b/portal/frontend/portal/src/assets/icons/svg/skill.svg new file mode 100644 index 0000000..a3b7312 --- /dev/null +++ b/portal/frontend/portal/src/assets/icons/svg/skill.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/portal/frontend/portal/src/assets/icons/svg/slider.svg b/portal/frontend/portal/src/assets/icons/svg/slider.svg new file mode 100644 index 0000000..fbe4f39 --- /dev/null +++ b/portal/frontend/portal/src/assets/icons/svg/slider.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/portal/frontend/portal/src/assets/icons/svg/star.svg b/portal/frontend/portal/src/assets/icons/svg/star.svg new file mode 100644 index 0000000..6cf86e6 --- /dev/null +++ b/portal/frontend/portal/src/assets/icons/svg/star.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/portal/frontend/portal/src/assets/icons/svg/sunny.svg b/portal/frontend/portal/src/assets/icons/svg/sunny.svg new file mode 100644 index 0000000..cc628bf --- /dev/null +++ b/portal/frontend/portal/src/assets/icons/svg/sunny.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/portal/frontend/portal/src/assets/icons/svg/swagger.svg b/portal/frontend/portal/src/assets/icons/svg/swagger.svg new file mode 100644 index 0000000..05d4e7b --- /dev/null +++ b/portal/frontend/portal/src/assets/icons/svg/swagger.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/portal/frontend/portal/src/assets/icons/svg/switch.svg b/portal/frontend/portal/src/assets/icons/svg/switch.svg new file mode 100644 index 0000000..0ba61e3 --- /dev/null +++ b/portal/frontend/portal/src/assets/icons/svg/switch.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/portal/frontend/portal/src/assets/icons/svg/system.svg b/portal/frontend/portal/src/assets/icons/svg/system.svg new file mode 100644 index 0000000..5992593 --- /dev/null +++ b/portal/frontend/portal/src/assets/icons/svg/system.svg @@ -0,0 +1,2 @@ + \ No newline at end of file diff --git a/portal/frontend/portal/src/assets/icons/svg/tab.svg b/portal/frontend/portal/src/assets/icons/svg/tab.svg new file mode 100644 index 0000000..b4b48e4 --- /dev/null +++ b/portal/frontend/portal/src/assets/icons/svg/tab.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/portal/frontend/portal/src/assets/icons/svg/table.svg b/portal/frontend/portal/src/assets/icons/svg/table.svg new file mode 100644 index 0000000..0e3dc9d --- /dev/null +++ b/portal/frontend/portal/src/assets/icons/svg/table.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/portal/frontend/portal/src/assets/icons/svg/textarea.svg b/portal/frontend/portal/src/assets/icons/svg/textarea.svg new file mode 100644 index 0000000..2709f29 --- /dev/null +++ b/portal/frontend/portal/src/assets/icons/svg/textarea.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/portal/frontend/portal/src/assets/icons/svg/theme.svg b/portal/frontend/portal/src/assets/icons/svg/theme.svg new file mode 100644 index 0000000..5982a2f --- /dev/null +++ b/portal/frontend/portal/src/assets/icons/svg/theme.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/portal/frontend/portal/src/assets/icons/svg/time-range.svg b/portal/frontend/portal/src/assets/icons/svg/time-range.svg new file mode 100644 index 0000000..13c1202 --- /dev/null +++ b/portal/frontend/portal/src/assets/icons/svg/time-range.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/portal/frontend/portal/src/assets/icons/svg/time.svg b/portal/frontend/portal/src/assets/icons/svg/time.svg new file mode 100644 index 0000000..b376e32 --- /dev/null +++ b/portal/frontend/portal/src/assets/icons/svg/time.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/portal/frontend/portal/src/assets/icons/svg/tool.svg b/portal/frontend/portal/src/assets/icons/svg/tool.svg new file mode 100644 index 0000000..48e0e35 --- /dev/null +++ b/portal/frontend/portal/src/assets/icons/svg/tool.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/portal/frontend/portal/src/assets/icons/svg/tree-table.svg b/portal/frontend/portal/src/assets/icons/svg/tree-table.svg new file mode 100644 index 0000000..8aafdb8 --- /dev/null +++ b/portal/frontend/portal/src/assets/icons/svg/tree-table.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/portal/frontend/portal/src/assets/icons/svg/tree.svg b/portal/frontend/portal/src/assets/icons/svg/tree.svg new file mode 100644 index 0000000..dd4b7dd --- /dev/null +++ b/portal/frontend/portal/src/assets/icons/svg/tree.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/portal/frontend/portal/src/assets/icons/svg/upload.svg b/portal/frontend/portal/src/assets/icons/svg/upload.svg new file mode 100644 index 0000000..bae49c0 --- /dev/null +++ b/portal/frontend/portal/src/assets/icons/svg/upload.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/portal/frontend/portal/src/assets/icons/svg/user.svg b/portal/frontend/portal/src/assets/icons/svg/user.svg new file mode 100644 index 0000000..0ba0716 --- /dev/null +++ b/portal/frontend/portal/src/assets/icons/svg/user.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/portal/frontend/portal/src/assets/icons/svg/validCode.svg b/portal/frontend/portal/src/assets/icons/svg/validCode.svg new file mode 100644 index 0000000..cfb1021 --- /dev/null +++ b/portal/frontend/portal/src/assets/icons/svg/validCode.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/portal/frontend/portal/src/assets/icons/svg/wechat.svg b/portal/frontend/portal/src/assets/icons/svg/wechat.svg new file mode 100644 index 0000000..c586e55 --- /dev/null +++ b/portal/frontend/portal/src/assets/icons/svg/wechat.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/portal/frontend/portal/src/assets/icons/svg/zip.svg b/portal/frontend/portal/src/assets/icons/svg/zip.svg new file mode 100644 index 0000000..f806fc4 --- /dev/null +++ b/portal/frontend/portal/src/assets/icons/svg/zip.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/portal/frontend/portal/src/assets/images/dark.svg b/portal/frontend/portal/src/assets/images/dark.svg new file mode 100644 index 0000000..f646bd7 --- /dev/null +++ b/portal/frontend/portal/src/assets/images/dark.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/portal/frontend/portal/src/assets/images/light.svg b/portal/frontend/portal/src/assets/images/light.svg new file mode 100644 index 0000000..ab7cc08 --- /dev/null +++ b/portal/frontend/portal/src/assets/images/light.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/portal/frontend/portal/src/assets/images/profile.jpg b/portal/frontend/portal/src/assets/images/profile.jpg new file mode 100644 index 0000000..bb8923b Binary files /dev/null and b/portal/frontend/portal/src/assets/images/profile.jpg differ diff --git a/portal/frontend/portal/src/assets/logo/logo.png b/portal/frontend/portal/src/assets/logo/logo.png new file mode 100644 index 0000000..33ee417 Binary files /dev/null and b/portal/frontend/portal/src/assets/logo/logo.png differ diff --git a/portal/frontend/portal/src/assets/styles/btn.scss b/portal/frontend/portal/src/assets/styles/btn.scss new file mode 100644 index 0000000..fee3ee1 --- /dev/null +++ b/portal/frontend/portal/src/assets/styles/btn.scss @@ -0,0 +1,99 @@ +@use './variables.module.scss' as *; + +@mixin colorBtn($color) { + background: $color; + + &:hover { + color: $color; + + &:before, + &:after { + background: $color; + } + } +} + +.blue-btn { + @include colorBtn($blue) +} + +.light-blue-btn { + @include colorBtn($light-blue) +} + +.red-btn { + @include colorBtn($red) +} + +.pink-btn { + @include colorBtn($pink) +} + +.green-btn { + @include colorBtn($green) +} + +.tiffany-btn { + @include colorBtn($tiffany) +} + +.yellow-btn { + @include colorBtn($yellow) +} + +.pan-btn { + font-size: 14px; + color: #fff; + padding: 14px 36px; + border-radius: 8px; + border: none; + outline: none; + transition: 600ms ease all; + position: relative; + display: inline-block; + + &:hover { + background: #fff; + + &:before, + &:after { + width: 100%; + transition: 600ms ease all; + } + } + + &:before, + &:after { + content: ''; + position: absolute; + top: 0; + right: 0; + height: 2px; + width: 0; + transition: 400ms ease all; + } + + &::after { + right: inherit; + top: inherit; + left: 0; + bottom: 0; + } +} + +.custom-button { + display: inline-block; + line-height: 1; + white-space: nowrap; + cursor: pointer; + background: #fff; + color: #fff; + -webkit-appearance: none; + text-align: center; + box-sizing: border-box; + outline: 0; + margin: 0; + padding: 10px 15px; + font-size: 14px; + border-radius: 4px; +} diff --git a/portal/frontend/portal/src/assets/styles/element-ui.scss b/portal/frontend/portal/src/assets/styles/element-ui.scss new file mode 100644 index 0000000..0f175f2 --- /dev/null +++ b/portal/frontend/portal/src/assets/styles/element-ui.scss @@ -0,0 +1,96 @@ +// cover some element-ui styles + +.el-breadcrumb__inner, +.el-breadcrumb__inner a { + font-weight: 400 !important; +} + +.el-upload { + input[type="file"] { + display: none !important; + } +} + +.el-upload__input { + display: none; +} + +.cell { + .el-tag { + margin-right: 0px; + } +} + +.small-padding { + .cell { + padding-left: 5px; + padding-right: 5px; + } +} + +.fixed-width { + .el-button--mini { + padding: 7px 10px; + width: 60px; + } +} + +.status-col { + .cell { + padding: 0 10px; + text-align: center; + + .el-tag { + margin-right: 0px; + } + } +} + +// to fixed https://github.com/ElemeFE/element/issues/2461 +.el-dialog { + transform: none; + left: 0; + position: relative; + margin: 0 auto; +} + +// refine element ui upload +.upload-container { + .el-upload { + width: 100%; + + .el-upload-dragger { + width: 100%; + height: 200px; + } + } +} + +// dropdown +.el-dropdown-menu { + a { + display: block + } +} + +// fix date-picker ui bug in filter-item +.el-range-editor.el-input__inner { + display: inline-flex !important; +} + +// to fix el-date-picker css style +.el-range-separator { + box-sizing: content-box; +} + +.el-menu--collapse + > div + > .el-submenu + > .el-submenu__title + .el-submenu__icon-arrow { + display: none; +} + +.el-dropdown .el-dropdown-link{ + color: var(--el-color-primary) !important; +} \ No newline at end of file diff --git a/portal/frontend/portal/src/assets/styles/index.scss b/portal/frontend/portal/src/assets/styles/index.scss new file mode 100644 index 0000000..7e27fc6 --- /dev/null +++ b/portal/frontend/portal/src/assets/styles/index.scss @@ -0,0 +1,179 @@ +@use './mixin.scss'; +@use './transition.scss'; +@use './element-ui.scss'; +@use './sidebar.scss'; +@use './btn.scss'; +@use './platform.scss'; + +body { + height: 100%; + margin: 0; + -moz-osx-font-smoothing: grayscale; + -webkit-font-smoothing: antialiased; + text-rendering: optimizeLegibility; + font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Arial, sans-serif; +} + +label { + font-weight: 700; +} + +html { + height: 100%; + box-sizing: border-box; +} + +#app { + height: 100%; +} + +*, +*:before, +*:after { + box-sizing: inherit; +} + +.no-padding { + padding: 0px !important; +} + +.padding-content { + padding: 4px 0; +} + +a:focus, +a:active { + outline: none; +} + +a, +a:focus, +a:hover { + cursor: pointer; + color: inherit; + text-decoration: none; +} + +div:focus { + outline: none; +} + +.fr { + float: right; +} + +.fl { + float: left; +} + +.pr-5 { + padding-right: 5px; +} + +.pl-5 { + padding-left: 5px; +} + +.block { + display: block; +} + +.pointer { + cursor: pointer; +} + +.inlineBlock { + display: block; +} + +.clearfix { + &:after { + visibility: hidden; + display: block; + font-size: 0; + content: " "; + clear: both; + height: 0; + } +} + +aside { + background: #eef1f6; + padding: 8px 24px; + margin-bottom: 20px; + border-radius: 2px; + display: block; + line-height: 32px; + font-size: 16px; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; + color: #2c3e50; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + + a { + color: #337ab7; + cursor: pointer; + + &:hover { + color: rgb(32, 160, 255); + } + } +} + +//main-container全局样式 +.app-container { + padding: 20px; +} + +.components-container { + margin: 30px 50px; + position: relative; +} + +.text-center { + text-align: center +} + +.sub-navbar { + height: 50px; + line-height: 50px; + position: relative; + width: 100%; + text-align: right; + padding-right: 20px; + transition: 600ms ease position; + background: linear-gradient(90deg, rgba(32, 182, 249, 1) 0%, rgba(32, 182, 249, 1) 0%, rgba(33, 120, 241, 1) 100%, rgba(33, 120, 241, 1) 100%); + + .subtitle { + font-size: 20px; + color: #fff; + } + + &.draft { + background: #d0d0d0; + } + + &.deleted { + background: #d0d0d0; + } +} + +.link-type, +.link-type:focus { + color: #337ab7; + cursor: pointer; + + &:hover { + color: rgb(32, 160, 255); + } +} + +.filter-container { + padding-bottom: 10px; + + .filter-item { + display: inline-block; + vertical-align: middle; + margin-bottom: 10px; + } +} diff --git a/portal/frontend/portal/src/assets/styles/mixin.scss b/portal/frontend/portal/src/assets/styles/mixin.scss new file mode 100644 index 0000000..06fa061 --- /dev/null +++ b/portal/frontend/portal/src/assets/styles/mixin.scss @@ -0,0 +1,66 @@ +@mixin clearfix { + &:after { + content: ""; + display: table; + clear: both; + } +} + +@mixin scrollBar { + &::-webkit-scrollbar-track-piece { + background: #d3dce6; + } + + &::-webkit-scrollbar { + width: 6px; + } + + &::-webkit-scrollbar-thumb { + background: #99a9bf; + border-radius: 20px; + } +} + +@mixin relative { + position: relative; + width: 100%; + height: 100%; +} + +@mixin pct($pct) { + width: #{$pct}; + position: relative; + margin: 0 auto; +} + +@mixin triangle($width, $height, $color, $direction) { + $width: $width/2; + $color-border-style: $height solid $color; + $transparent-border-style: $width solid transparent; + height: 0; + width: 0; + + @if $direction==up { + border-bottom: $color-border-style; + border-left: $transparent-border-style; + border-right: $transparent-border-style; + } + + @else if $direction==right { + border-left: $color-border-style; + border-top: $transparent-border-style; + border-bottom: $transparent-border-style; + } + + @else if $direction==down { + border-top: $color-border-style; + border-left: $transparent-border-style; + border-right: $transparent-border-style; + } + + @else if $direction==left { + border-right: $color-border-style; + border-top: $transparent-border-style; + border-bottom: $transparent-border-style; + } +} diff --git a/portal/frontend/portal/src/assets/styles/platform.scss b/portal/frontend/portal/src/assets/styles/platform.scss new file mode 100644 index 0000000..f3024e9 --- /dev/null +++ b/portal/frontend/portal/src/assets/styles/platform.scss @@ -0,0 +1,308 @@ +/** + * 通用css样式布局处理 + * Copyright (c) 2026 lingniu + */ + +/** 基础通用 **/ +.pt5 { + padding-top: 5px; +} +.pr5 { + padding-right: 5px; +} +.pb5 { + padding-bottom: 5px; +} +.mt5 { + margin-top: 5px; +} +.mr5 { + margin-right: 5px; +} +.mb5 { + margin-bottom: 5px; +} +.mb8 { + margin-bottom: 8px; +} +.ml5 { + margin-left: 5px; +} +.mt10 { + margin-top: 10px; +} +.mr10 { + margin-right: 10px; +} +.mb10 { + margin-bottom: 10px; +} +.ml10 { + margin-left: 10px; +} +.mt20 { + margin-top: 20px; +} +.mr20 { + margin-right: 20px; +} +.mb20 { + margin-bottom: 20px; +} +.ml20 { + margin-left: 20px; +} + +.h1, .h2, .h3, .h4, .h5, .h6, h1, h2, h3, h4, h5, h6 { + font-family: inherit; + font-weight: 500; + line-height: 1.1; + color: inherit; +} + +.el-form--inline { + .el-form-item { + .el-input, .el-cascader, .el-select, .el-autocomplete { + width: 200px; + } + } +} + +.el-form .el-form-item__label { + font-weight: 700; +} +.el-dialog:not(.is-fullscreen) { + margin-top: 6vh !important; +} + +.el-dialog.scrollbar .el-dialog__body { + overflow: auto; + overflow-x: hidden; + max-height: 70vh; + padding: 10px 20px 0; +} + +.el-table { + .el-table__header-wrapper, .el-table__fixed-header-wrapper { + th { + word-break: break-word; + background-color: #f8f8f9 !important; + color: #515a6e; + height: 40px !important; + font-size: 13px; + } + } + .el-table__body-wrapper { + .el-button [class*="el-icon-"] + span { + margin-left: 1px; + } + } +} + +/** 表单布局 **/ +.form-header { + font-size:15px; + color:#6379bb; + border-bottom:1px solid #ddd; + margin:8px 10px 25px 10px; + padding-bottom:5px +} + +/** 表格布局 **/ +.pagination-container { + display: flex; + justify-content: flex-end; + margin-top: 20px; + background-color: transparent !important; +} + +/* 弹窗中的分页器 */ +.el-dialog .pagination-container { + position: static !important; + margin: 10px 0 0 0; + padding: 0 !important; + + .el-pagination { + position: static; + } +} + +/* 移动端适配 */ +@media (max-width: 768px) { + .pagination-container { + .el-pagination { + > .el-pagination__jump { + display: none !important; + } + > .el-pagination__sizes { + display: none !important; + } + } + } +} + +/* tree border */ +.tree-border { + margin-top: 5px; + border: 1px solid var(--el-border-color-light, #e5e6e7); + background: var(--el-bg-color, #FFFFFF) none; + border-radius:4px; + width: 100%; +} + +.el-table .fixed-width .el-button--small { + padding-left: 0; + padding-right: 0; + width: inherit; +} + +/* horizontal el menu */ +.el-menu--horizontal .el-menu-item .svg-icon + span, +.el-menu--horizontal .el-sub-menu__title .svg-icon + span { + margin-left: 3px; +} + +.el-menu--horizontal .el-menu--popup { + min-width: 120px !important; +} + +/** 表格更多操作下拉样式 */ +.el-table .el-dropdown-link { + cursor: pointer; + color: #409EFF; + margin-left: 10px; +} + +.el-table .el-dropdown, .el-icon-arrow-down { + font-size: 12px; +} + +.el-tree-node__content > .el-checkbox { + margin-right: 8px; +} + +.list-group-striped > .list-group-item { + border-left: 0; + border-right: 0; + border-radius: 0; + padding-left: 0; + padding-right: 0; +} + +.list-group { + padding-left: 0px; + list-style: none; +} + +.list-group-item { + border-bottom: 1px solid #e7eaec; + border-top: 1px solid #e7eaec; + margin-bottom: -1px; + padding: 11px 0px; + font-size: 13px; +} + +.pull-right { + float: right !important; +} + +.el-card__header { + padding: 14px 15px 7px !important; + min-height: 40px; +} + +.el-card__body { + padding: 15px 20px 20px 20px !important; +} + +.card-box { + margin-bottom: 10px; +} + +/* button color */ +.el-button--cyan.is-active, +.el-button--cyan:active { + background: #20B2AA; + border-color: #20B2AA; + color: #FFFFFF; +} + +.el-button--cyan:focus, +.el-button--cyan:hover { + background: #48D1CC; + border-color: #48D1CC; + color: #FFFFFF; +} + +.el-button--cyan { + background-color: #20B2AA; + border-color: #20B2AA; + color: #FFFFFF; +} + +/* text color */ +.text-navy { + color: #1ab394; +} + +.text-primary { + color: inherit; +} + +.text-success { + color: #1c84c6; +} + +.text-info { + color: #23c6c8; +} + +.text-warning { + color: #f8ac59; +} + +.text-danger { + color: #ed5565; +} + +.text-muted { + color: #888888; +} + +/* image */ +.img-circle { + border-radius: 50%; +} + +.img-lg { + width: 120px; + height: 120px; +} + +.avatar-upload-preview { + position: absolute; + top: 50%; + transform: translate(50%, -50%); + width: 200px; + height: 200px; + border-radius: 50%; + box-shadow: 0 0 4px #ccc; + overflow: hidden; +} + +/* 拖拽列样式 */ +.sortable-ghost{ + opacity: .8; + color: #fff!important; + background: #42b983!important; +} + +/* 表格右侧工具栏样式 */ +.top-right-btn { + margin-left: auto; +} + +/* 分割面板样式 */ +.splitpanes.default-theme .splitpanes__pane { + background-color: var(--splitpanes-default-bg) !important; +} diff --git a/portal/frontend/portal/src/assets/styles/sidebar.scss b/portal/frontend/portal/src/assets/styles/sidebar.scss new file mode 100644 index 0000000..5ee4ac8 --- /dev/null +++ b/portal/frontend/portal/src/assets/styles/sidebar.scss @@ -0,0 +1,238 @@ +@use './variables.module.scss' as vars; + +#app { + + .main-container { + min-height: 100%; + transition: margin-left .28s; + margin-left: vars.$base-sidebar-width; + position: relative; + } + + .sidebarHide { + margin-left: 0!important; + } + + .sidebar-container { + transition: width 0.28s; + width: vars.$base-sidebar-width !important; + height: 100%; + position: fixed; + font-size: 0px; + top: 0; + bottom: 0; + left: 0; + z-index: 1001; + overflow: hidden; + -webkit-box-shadow: 2px 0 6px rgba(0,21,41,.35); + box-shadow: 0px 0px 8px 0px rgba(0, 0, 0, 0.1); + + // reset element-ui css + .horizontal-collapse-transition { + transition: 0s width ease-in-out, 0s padding-left ease-in-out, 0s padding-right ease-in-out; + } + + .scrollbar-wrapper { + overflow-x: hidden !important; + } + + .el-scrollbar__bar.is-vertical { + right: 0px; + } + + .el-scrollbar { + height: 100%; + } + + &.has-logo { + .el-scrollbar { + height: calc(100% - 50px); + } + } + + .is-horizontal { + display: none; + } + + a { + display: inline-block; + width: 100%; + overflow: hidden; + } + + .svg-icon { + margin-right: 10px !important; + } + + .el-menu { + border: none; + height: 100%; + width: 100% !important; + } + + .el-menu-item, .menu-title { + overflow: hidden !important; + text-overflow: ellipsis !important; + white-space: nowrap !important; + } + + .el-menu-item .el-menu-tooltip__trigger { + display: inline-block !important; + } + + // menu hover + .sub-menu-title-noDropdown, + .el-sub-menu__title { + &:hover { + background-color: rgba(0, 0, 0, 0.06) !important; + } + } + + & .theme-dark .is-active > .el-sub-menu__title { + color: vars.$base-menu-color-active !important; + } + + & .nest-menu .el-sub-menu>.el-sub-menu__title, + & .el-sub-menu .el-menu-item { + min-width: vars.$base-sidebar-width !important; + + &:hover { + background-color: rgba(0, 0, 0, 0.06) !important; + } + } + + & .theme-dark .nest-menu .el-sub-menu>.el-sub-menu__title, + & .theme-dark .el-sub-menu .el-menu-item { + background-color: vars.$base-sub-menu-background; + + &:hover { + background-color: vars.$base-sub-menu-hover !important; + } + } + } + + .hideSidebar { + .sidebar-container { + width: 54px !important; + } + + .main-container { + margin-left: 54px; + } + + .sub-menu-title-noDropdown { + padding: 0 !important; + position: relative; + + .el-tooltip { + padding: 0 !important; + + .svg-icon { + margin-left: 20px; + } + } + } + + .el-sub-menu { + overflow: hidden; + + &>.el-sub-menu__title { + padding: 0 !important; + + .svg-icon { + margin-left: 20px; + } + + } + } + + .el-menu--collapse { + .el-sub-menu { + &>.el-sub-menu__title { + &>span { + height: 0; + width: 0; + overflow: hidden; + visibility: hidden; + display: inline-block; + } + &>i { + height: 0; + width: 0; + overflow: hidden; + visibility: hidden; + display: inline-block; + } + } + } + } + } + + .el-menu--collapse .el-menu .el-sub-menu { + min-width: vars.$base-sidebar-width !important; + } + + // mobile responsive + .mobile { + .main-container { + margin-left: 0px; + } + + .sidebar-container { + transition: transform .28s; + width: vars.$base-sidebar-width !important; + } + + &.hideSidebar { + .sidebar-container { + pointer-events: none; + transition-duration: 0.3s; + transform: translate3d(-(vars.$base-sidebar-width), 0, 0); + } + } + } + + .withoutAnimation { + + .main-container, + .sidebar-container { + transition: none; + } + } +} + +// when menu collapsed +.el-menu--vertical { + &>.el-menu { + .svg-icon { + margin-right: 16px; + } + } + + .nest-menu .el-sub-menu>.el-sub-menu__title, + .el-menu-item { + &:hover { + // you can use $sub-menuHover + background-color: rgba(0, 0, 0, 0.06) !important; + } + } + + // the scroll bar appears when the sub-menu is too long + >.el-menu--popup { + max-height: 100vh; + overflow-y: auto; + + &::-webkit-scrollbar-track-piece { + background: #d3dce6; + } + + &::-webkit-scrollbar { + width: 6px; + } + + &::-webkit-scrollbar-thumb { + background: #99a9bf; + border-radius: 20px; + } + } +} diff --git a/portal/frontend/portal/src/assets/styles/transition.scss b/portal/frontend/portal/src/assets/styles/transition.scss new file mode 100644 index 0000000..1f74a7e --- /dev/null +++ b/portal/frontend/portal/src/assets/styles/transition.scss @@ -0,0 +1,80 @@ +// global transition css + +/* fade */ +.fade-enter-active, +.fade-leave-active { + transition: opacity 0.28s; +} + +.fade-enter-from, +.fade-leave-active { + opacity: 0; +} + +/* fade-transform */ +.fade-transform--move, +.fade-transform-leave-active, +.fade-transform-enter-active { + transition: all .5s; +} + +.fade-transform-enter-from { + opacity: 0; + transform: translateX(-30px); +} + +.fade-transform-leave-to { + opacity: 0; + transform: translateX(30px); +} + +/* breadcrumb transition */ +.breadcrumb-enter-active, +.breadcrumb-leave-active { + transition: all .5s; +} + +.breadcrumb-enter-from, +.breadcrumb-leave-active { + opacity: 0; + transform: translateX(20px); +} + +.breadcrumb-move { + transition: all .5s; +} + +.breadcrumb-leave-active { + position: absolute; +} + +/* 黑暗模式下过渡效果 */ +::view-transition-new(root), ::view-transition-old(root) { + animation: none !important; + backface-visibility: hidden; + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; +} + +.dark::view-transition-old(root) { + z-index: 2147483646; + background: var(--bg-color-dark); +} + +.dark::view-transition-new(root) { + z-index: 1; + background: var(--bg-color); +} + +::view-transition-old(root) { + z-index: 1; + background: var(--bg-color); +} + +::view-transition-new(root) { + z-index: 2147483646; + background: var(--bg-color-dark); +} diff --git a/portal/frontend/portal/src/assets/styles/variables.module.scss b/portal/frontend/portal/src/assets/styles/variables.module.scss new file mode 100644 index 0000000..3a833b5 --- /dev/null +++ b/portal/frontend/portal/src/assets/styles/variables.module.scss @@ -0,0 +1,271 @@ +// base color +$blue: #324157; +$light-blue: #333c46; +$red: #C03639; +$pink: #E65D6E; +$green: #30B08F; +$tiffany: #4AB7BD; +$yellow: #FEC171; +$panGreen: #30B08F; + +// 默认主题变量 +$menuText: #bfcbd9; +$menuActiveText: #409eff; +$menuBg: #304156; +$menuHover: #263445; + +// 浅色主题theme-light +$menuLightBg: #ffffff; +$menuLightHover: #f0f1f5; +$menuLightText: #303133; +$menuLightActiveText: #409EFF; + +// 基础变量 +$base-sidebar-width: 200px; +$sideBarWidth: 200px; + +// 菜单暗色变量 +$base-menu-color: #bfcbd9; +$base-menu-color-active: #f4f4f5; +$base-menu-background: #304156; +$base-sub-menu-background: #1f2d3d; +$base-sub-menu-hover: #001528; + +// 组件变量 +$--color-primary: #409EFF; +$--color-success: #67C23A; +$--color-warning: #E6A23C; +$--color-danger: #F56C6C; +$--color-info: #909399; + +:export { + menuText: $menuText; + menuActiveText: $menuActiveText; + menuBg: $menuBg; + menuHover: $menuHover; + menuLightBg: $menuLightBg; + menuLightHover: $menuLightHover; + menuLightText: $menuLightText; + menuLightActiveText: $menuLightActiveText; + sideBarWidth: $sideBarWidth; + // 导出基础颜色 + blue: $blue; + lightBlue: $light-blue; + red: $red; + pink: $pink; + green: $green; + tiffany: $tiffany; + yellow: $yellow; + panGreen: $panGreen; + // 导出组件颜色 + colorPrimary: $--color-primary; + colorSuccess: $--color-success; + colorWarning: $--color-warning; + colorDanger: $--color-danger; + colorInfo: $--color-info; +} + +// CSS变量定义 +:root { + /* 亮色模式变量 */ + --sidebar-bg: #{$menuBg}; + --sidebar-text: #{$menuText}; + --menu-hover: #{$menuHover}; + + --navbar-bg: #ffffff; + --navbar-text: #303133; + + /* splitpanes default-theme 变量 */ + --splitpanes-default-bg: #ffffff; + +} + +// 暗黑模式变量 +html.dark { + /* 默认通用 */ + --el-bg-color: #141414; + --el-bg-color-overlay: #1d1e1f; + --el-text-color-primary: #ffffff; + --el-text-color-regular: #d0d0d0; + --el-border-color: #434343; + --el-border-color-light: #434343; + + /* primary */ + --primary-bg: #18212b; + + /* 侧边栏 */ + --sidebar-bg: #141414; + --sidebar-text: #ffffff; + --menu-hover: #2d2d2d; + --menu-active-text: #{$menuActiveText}; + + /* 顶部导航栏 */ + --navbar-bg: #141414; + --navbar-text: #ffffff; + --navbar-hover: #141414; + + /* 标签栏 */ + --tags-bg: #141414; + --tags-item-bg: #1d1e1f; + --tags-item-border: #303030; + --tags-item-text: #d0d0d0; + --tags-item-hover: #2d2d2d; + --tags-close-hover: #64666a; + + /* splitpanes 组件暗黑模式变量 */ + --splitpanes-bg: #141414; + --splitpanes-border: #303030; + --splitpanes-splitter-bg: #1d1e1f; + --splitpanes-splitter-hover-bg: #2d2d2d; + + /* blockquote 暗黑模式变量 */ + --blockquote-bg: #1d1e1f; + --blockquote-border: #303030; + --blockquote-text: #d0d0d0; + + /* Cron 时间表达式 模式变量 */ + --cron-border: #303030; + + /* splitpanes default-theme 暗黑模式变量 */ + --splitpanes-default-bg: #141414; + + /* 侧边栏菜单覆盖 */ + .sidebar-container { + .el-menu-item:not(.is-active), .menu-title { + color: var(--el-text-color-regular); + } + & .theme-dark .nest-menu .el-sub-menu>.el-sub-menu__title, + & .theme-dark .el-sub-menu .el-menu-item { + background-color: var(--el-bg-color) !important; + } + } + + .topmenu-container { + .el-menu-item, + .el-sub-menu .el-sub-menu__title { + color: var(--el-text-color-regular) !important; + } + } + + .topbar-menu.el-menu--horizontal > .el-sub-menu .el-sub-menu__title{ + color: var(--el-text-color-regular) !important; + } + + /* 顶部栏栏菜单覆盖 */ + .el-menu--horizontal { + .el-menu-item, .el-sub-menu { + &:not(.is-disabled) { + &:hover, + &:focus { + background-color: var(--navbar-hover) !important; + .el-sub-menu__title { + background-color: var(--navbar-hover) !important; + } + } + } + } + } + + /* 分割窗格覆盖 */ + .splitpanes { + background-color: var(--splitpanes-bg); + + .splitpanes__pane { + background-color: var(--splitpanes-bg); + border-color: var(--splitpanes-border); + } + + .splitpanes__splitter { + background-color: var(--splitpanes-splitter-bg); + border-color: var(--splitpanes-border); + + &:hover { + background-color: var(--splitpanes-splitter-hover-bg); + } + + &:before, + &:after { + background-color: var(--splitpanes-border); + } + } + } + + /* 按钮样式覆盖 */ + .el-button--primary.is-plain { + background-color: var(--primary-bg); + border: 1px solid var(--el-color-primary-light-2); + color: var(--el-color-primary-light-2); + + &:hover { + background-color: var(--el-button-hover-bg-color); + border-color: var(--el-button-hover-border-color); + color: var(--el-button-hover-text-color); + } + + &.is-disabled { + background-color: var(--link-active-bg-color); + border-color: var(--el-color-primary-light-3); + color: var(--el-color-primary-light-3); + opacity: 0.5; + } + } + + /* primary tag 样式覆盖 */ + .el-tag--primary { + background-color: var(--primary-bg); + border: 1px solid var(--el-border-color-light); + color: var(--el-color-primary); + } + + /* 表格样式覆盖 */ + .el-table { + --el-table-header-bg-color: var(--el-bg-color-overlay) !important; + --el-table-header-text-color: var(--el-text-color-regular) !important; + --el-table-border-color: var(--el-border-color-light) !important; + --el-table-row-hover-bg-color: var(--el-bg-color-overlay) !important; + + .el-table__header-wrapper, .el-table__fixed-header-wrapper { + th { + background-color: var(--el-bg-color-overlay, #f8f8f9) !important; + color: var(--el-text-color-regular, #515a6e); + } + } + } + + /* 树组件高亮样式覆盖 */ + .el-tree { + .el-tree-node.is-current > .el-tree-node__content { + background-color: var(--el-bg-color-overlay) !important; + color: var(--el-color-primary); + } + + .el-tree-node__content:hover { + background-color: var(--el-bg-color-overlay); + } + } + + /* 下拉菜单样式覆盖 */ + .el-dropdown-menu__item:not(.is-disabled):focus, .el-dropdown-menu__item:not(.is-disabled):hover{ + background-color: var(--navbar-hover) !important; + } + + /* blockquote样式覆盖 */ + blockquote { + background-color: var(--blockquote-bg) !important; + border-left-color: var(--blockquote-border) !important; + color: var(--blockquote-text) !important; + } + + /* 时间表达式标题样式覆盖 */ + .popup-result .title { + background: var(--cron-border); + } + + /* 底部版权样式覆盖 */ + .copyright { + background-color: var(--el-bg-color) !important; + color: var(--el-text-color-regular) !important; + border-top: 1px solid var(--el-bg-color) !important; + } +} + diff --git a/portal/frontend/portal/src/components/Breadcrumb/index.vue b/portal/frontend/portal/src/components/Breadcrumb/index.vue new file mode 100644 index 0000000..19bf96c --- /dev/null +++ b/portal/frontend/portal/src/components/Breadcrumb/index.vue @@ -0,0 +1,97 @@ + + + + + \ No newline at end of file diff --git a/portal/frontend/portal/src/components/Crontab/day.vue b/portal/frontend/portal/src/components/Crontab/day.vue new file mode 100644 index 0000000..07ffe90 --- /dev/null +++ b/portal/frontend/portal/src/components/Crontab/day.vue @@ -0,0 +1,174 @@ + + + + \ No newline at end of file diff --git a/portal/frontend/portal/src/components/Crontab/hour.vue b/portal/frontend/portal/src/components/Crontab/hour.vue new file mode 100644 index 0000000..2eacf44 --- /dev/null +++ b/portal/frontend/portal/src/components/Crontab/hour.vue @@ -0,0 +1,133 @@ + + + + + \ No newline at end of file diff --git a/portal/frontend/portal/src/components/Crontab/index.vue b/portal/frontend/portal/src/components/Crontab/index.vue new file mode 100644 index 0000000..16a05f6 --- /dev/null +++ b/portal/frontend/portal/src/components/Crontab/index.vue @@ -0,0 +1,314 @@ + + + + + \ No newline at end of file diff --git a/portal/frontend/portal/src/components/Crontab/min.vue b/portal/frontend/portal/src/components/Crontab/min.vue new file mode 100644 index 0000000..dc61554 --- /dev/null +++ b/portal/frontend/portal/src/components/Crontab/min.vue @@ -0,0 +1,126 @@ + + + + \ No newline at end of file diff --git a/portal/frontend/portal/src/components/Crontab/month.vue b/portal/frontend/portal/src/components/Crontab/month.vue new file mode 100644 index 0000000..288226d --- /dev/null +++ b/portal/frontend/portal/src/components/Crontab/month.vue @@ -0,0 +1,141 @@ + + + + + \ No newline at end of file diff --git a/portal/frontend/portal/src/components/Crontab/result.vue b/portal/frontend/portal/src/components/Crontab/result.vue new file mode 100644 index 0000000..ee32e0d --- /dev/null +++ b/portal/frontend/portal/src/components/Crontab/result.vue @@ -0,0 +1,543 @@ + + + \ No newline at end of file diff --git a/portal/frontend/portal/src/components/Crontab/second.vue b/portal/frontend/portal/src/components/Crontab/second.vue new file mode 100644 index 0000000..69e3fc2 --- /dev/null +++ b/portal/frontend/portal/src/components/Crontab/second.vue @@ -0,0 +1,129 @@ + + + + + \ No newline at end of file diff --git a/portal/frontend/portal/src/components/Crontab/week.vue b/portal/frontend/portal/src/components/Crontab/week.vue new file mode 100644 index 0000000..6b6e3b4 --- /dev/null +++ b/portal/frontend/portal/src/components/Crontab/week.vue @@ -0,0 +1,197 @@ + + + + + \ No newline at end of file diff --git a/portal/frontend/portal/src/components/Crontab/year.vue b/portal/frontend/portal/src/components/Crontab/year.vue new file mode 100644 index 0000000..cbc5852 --- /dev/null +++ b/portal/frontend/portal/src/components/Crontab/year.vue @@ -0,0 +1,143 @@ + + + + + \ No newline at end of file diff --git a/portal/frontend/portal/src/components/DictTag/index.vue b/portal/frontend/portal/src/components/DictTag/index.vue new file mode 100644 index 0000000..b71e68f --- /dev/null +++ b/portal/frontend/portal/src/components/DictTag/index.vue @@ -0,0 +1,89 @@ + + + + + diff --git a/portal/frontend/portal/src/components/Editor/index.vue b/portal/frontend/portal/src/components/Editor/index.vue new file mode 100644 index 0000000..e154056 --- /dev/null +++ b/portal/frontend/portal/src/components/Editor/index.vue @@ -0,0 +1,277 @@ + + + + + diff --git a/portal/frontend/portal/src/components/FileUpload/index.vue b/portal/frontend/portal/src/components/FileUpload/index.vue new file mode 100644 index 0000000..8ff9d10 --- /dev/null +++ b/portal/frontend/portal/src/components/FileUpload/index.vue @@ -0,0 +1,265 @@ + + + + diff --git a/portal/frontend/portal/src/components/Hamburger/index.vue b/portal/frontend/portal/src/components/Hamburger/index.vue new file mode 100644 index 0000000..15a58a6 --- /dev/null +++ b/portal/frontend/portal/src/components/Hamburger/index.vue @@ -0,0 +1,42 @@ + + + + + diff --git a/portal/frontend/portal/src/components/HeaderSearch/index.vue b/portal/frontend/portal/src/components/HeaderSearch/index.vue new file mode 100644 index 0000000..82015ff --- /dev/null +++ b/portal/frontend/portal/src/components/HeaderSearch/index.vue @@ -0,0 +1,252 @@ + + + + + diff --git a/portal/frontend/portal/src/components/IconSelect/index.vue b/portal/frontend/portal/src/components/IconSelect/index.vue new file mode 100644 index 0000000..a9d230e --- /dev/null +++ b/portal/frontend/portal/src/components/IconSelect/index.vue @@ -0,0 +1,111 @@ + + + + + \ No newline at end of file diff --git a/portal/frontend/portal/src/components/IconSelect/requireIcons.ts b/portal/frontend/portal/src/components/IconSelect/requireIcons.ts new file mode 100644 index 0000000..de6b46a --- /dev/null +++ b/portal/frontend/portal/src/components/IconSelect/requireIcons.ts @@ -0,0 +1,8 @@ +const icons: string[] = [] +const modules = import.meta.glob('./../../assets/icons/svg/*.svg') +for (const path in modules) { + const p = path.split('assets/icons/svg/')[1].split('.svg')[0] + icons.push(p) +} + +export default icons diff --git a/portal/frontend/portal/src/components/ImagePreview/index.vue b/portal/frontend/portal/src/components/ImagePreview/index.vue new file mode 100644 index 0000000..e1bdd85 --- /dev/null +++ b/portal/frontend/portal/src/components/ImagePreview/index.vue @@ -0,0 +1,93 @@ + + + + + diff --git a/portal/frontend/portal/src/components/ImageUpload/index.vue b/portal/frontend/portal/src/components/ImageUpload/index.vue new file mode 100644 index 0000000..f0adf77 --- /dev/null +++ b/portal/frontend/portal/src/components/ImageUpload/index.vue @@ -0,0 +1,269 @@ + + + + + diff --git a/portal/frontend/portal/src/components/Pagination/index.vue b/portal/frontend/portal/src/components/Pagination/index.vue new file mode 100644 index 0000000..64cbca7 --- /dev/null +++ b/portal/frontend/portal/src/components/Pagination/index.vue @@ -0,0 +1,105 @@ + + + + + \ No newline at end of file diff --git a/portal/frontend/portal/src/components/ParentView/index.vue b/portal/frontend/portal/src/components/ParentView/index.vue new file mode 100644 index 0000000..7bf6148 --- /dev/null +++ b/portal/frontend/portal/src/components/ParentView/index.vue @@ -0,0 +1,3 @@ + diff --git a/portal/frontend/portal/src/components/RightToolbar/index.vue b/portal/frontend/portal/src/components/RightToolbar/index.vue new file mode 100644 index 0000000..11f0512 --- /dev/null +++ b/portal/frontend/portal/src/components/RightToolbar/index.vue @@ -0,0 +1,186 @@ + + + + + diff --git a/portal/frontend/portal/src/components/Screenfull/index.vue b/portal/frontend/portal/src/components/Screenfull/index.vue new file mode 100644 index 0000000..5508a1f --- /dev/null +++ b/portal/frontend/portal/src/components/Screenfull/index.vue @@ -0,0 +1,22 @@ + + + + + \ No newline at end of file diff --git a/portal/frontend/portal/src/components/SizeSelect/index.vue b/portal/frontend/portal/src/components/SizeSelect/index.vue new file mode 100644 index 0000000..707c1b3 --- /dev/null +++ b/portal/frontend/portal/src/components/SizeSelect/index.vue @@ -0,0 +1,50 @@ + + + + + \ No newline at end of file diff --git a/portal/frontend/portal/src/components/SvgIcon/index.vue b/portal/frontend/portal/src/components/SvgIcon/index.vue new file mode 100644 index 0000000..e6ff2a3 --- /dev/null +++ b/portal/frontend/portal/src/components/SvgIcon/index.vue @@ -0,0 +1,53 @@ + + + + + diff --git a/portal/frontend/portal/src/components/SvgIcon/svgicon.ts b/portal/frontend/portal/src/components/SvgIcon/svgicon.ts new file mode 100644 index 0000000..086c470 --- /dev/null +++ b/portal/frontend/portal/src/components/SvgIcon/svgicon.ts @@ -0,0 +1,11 @@ +import { App } from 'vue' +import * as components from '@element-plus/icons-vue' + +export default { + install: (app: App) => { + for (const key in components) { + const componentConfig = (components as any)[key] + app.component(componentConfig.name, componentConfig) + } + } +} diff --git a/portal/frontend/portal/src/components/TopNav/index.vue b/portal/frontend/portal/src/components/TopNav/index.vue new file mode 100644 index 0000000..be5363d --- /dev/null +++ b/portal/frontend/portal/src/components/TopNav/index.vue @@ -0,0 +1,215 @@ + + + + + diff --git a/portal/frontend/portal/src/components/iFrame/index.vue b/portal/frontend/portal/src/components/iFrame/index.vue new file mode 100644 index 0000000..b0febe9 --- /dev/null +++ b/portal/frontend/portal/src/components/iFrame/index.vue @@ -0,0 +1,31 @@ + + + diff --git a/portal/frontend/portal/src/layout/components/Navbar.vue b/portal/frontend/portal/src/layout/components/Navbar.vue new file mode 100644 index 0000000..3326cd3 --- /dev/null +++ b/portal/frontend/portal/src/layout/components/Navbar.vue @@ -0,0 +1,274 @@ + + + + + diff --git a/portal/frontend/portal/src/layout/components/Settings/index.vue b/portal/frontend/portal/src/layout/components/Settings/index.vue new file mode 100644 index 0000000..c332eb1 --- /dev/null +++ b/portal/frontend/portal/src/layout/components/Settings/index.vue @@ -0,0 +1,317 @@ + + + + + \ No newline at end of file diff --git a/portal/frontend/portal/src/layout/components/Sidebar/Link.vue b/portal/frontend/portal/src/layout/components/Sidebar/Link.vue new file mode 100644 index 0000000..c659267 --- /dev/null +++ b/portal/frontend/portal/src/layout/components/Sidebar/Link.vue @@ -0,0 +1,40 @@ + + + diff --git a/portal/frontend/portal/src/layout/components/Sidebar/Logo.vue b/portal/frontend/portal/src/layout/components/Sidebar/Logo.vue new file mode 100644 index 0000000..4594c4b --- /dev/null +++ b/portal/frontend/portal/src/layout/components/Sidebar/Logo.vue @@ -0,0 +1,101 @@ + + + + + \ No newline at end of file diff --git a/portal/frontend/portal/src/layout/components/Sidebar/SidebarItem.vue b/portal/frontend/portal/src/layout/components/Sidebar/SidebarItem.vue new file mode 100644 index 0000000..d32b2e5 --- /dev/null +++ b/portal/frontend/portal/src/layout/components/Sidebar/SidebarItem.vue @@ -0,0 +1,100 @@ + + + diff --git a/portal/frontend/portal/src/layout/components/Sidebar/index.vue b/portal/frontend/portal/src/layout/components/Sidebar/index.vue new file mode 100644 index 0000000..f419c52 --- /dev/null +++ b/portal/frontend/portal/src/layout/components/Sidebar/index.vue @@ -0,0 +1,104 @@ + + + + + diff --git a/portal/frontend/portal/src/layout/components/TagsView/ScrollPane.vue b/portal/frontend/portal/src/layout/components/TagsView/ScrollPane.vue new file mode 100644 index 0000000..a26cdeb --- /dev/null +++ b/portal/frontend/portal/src/layout/components/TagsView/ScrollPane.vue @@ -0,0 +1,110 @@ + + + + + \ No newline at end of file diff --git a/portal/frontend/portal/src/layout/components/TagsView/index.vue b/portal/frontend/portal/src/layout/components/TagsView/index.vue new file mode 100644 index 0000000..e81e71d --- /dev/null +++ b/portal/frontend/portal/src/layout/components/TagsView/index.vue @@ -0,0 +1,371 @@ + + + + + + + \ No newline at end of file diff --git a/portal/frontend/portal/src/layout/components/TopBar/index.vue b/portal/frontend/portal/src/layout/components/TopBar/index.vue new file mode 100644 index 0000000..1ffb454 --- /dev/null +++ b/portal/frontend/portal/src/layout/components/TopBar/index.vue @@ -0,0 +1,99 @@ + + + + + diff --git a/portal/frontend/portal/src/layout/components/index.ts b/portal/frontend/portal/src/layout/components/index.ts new file mode 100644 index 0000000..464f38e --- /dev/null +++ b/portal/frontend/portal/src/layout/components/index.ts @@ -0,0 +1,4 @@ +export { default as AppMain } from './AppMain.vue' +export { default as Navbar } from './Navbar.vue' +export { default as Settings } from './Settings/index.vue' +export { default as TagsView } from './TagsView/index.vue' diff --git a/portal/frontend/portal/src/layout/index.vue b/portal/frontend/portal/src/layout/index.vue new file mode 100644 index 0000000..ccde3ef --- /dev/null +++ b/portal/frontend/portal/src/layout/index.vue @@ -0,0 +1,116 @@ + + + + + \ No newline at end of file diff --git a/portal/frontend/portal/src/main.ts b/portal/frontend/portal/src/main.ts new file mode 100644 index 0000000..b8fef87 --- /dev/null +++ b/portal/frontend/portal/src/main.ts @@ -0,0 +1,95 @@ +import { createApp } from 'vue' +import Cookies from 'js-cookie' + +import ElementPlus from 'element-plus' +import 'element-plus/dist/index.css' +import 'element-plus/theme-chalk/dark/css-vars.css' +import locale from 'element-plus/es/locale/lang/zh-cn' + +import '@/assets/styles/index.scss' // global css + +import App from './App.vue' +import store from './store' +import router from './router' +import directive from './directive' // directive + +// 注册指令 +import plugins from './plugins' // plugins +import { download } from '@/utils/request' + +// svg图标 +import 'virtual:svg-icons-register' +import SvgIcon from '@/components/SvgIcon/index.vue' +import elementIcons from '@/components/SvgIcon/svgicon' + +import './permission' // permission control + +import { useDict } from '@/utils/dict' +import { getConfigKey } from "@/api/system/config" +import { parseTime, resetForm, addDateRange, handleTree, selectDictLabel, selectDictLabels } from '@/utils/platform' + +// 分页组件 +import Pagination from '@/components/Pagination/index.vue' +// 自定义表格工具组件 +import RightToolbar from '@/components/RightToolbar/index.vue' +// 富文本组件 +import Editor from "@/components/Editor/index.vue" +// 文件上传组件 +import FileUpload from "@/components/FileUpload/index.vue" +// 图片上传组件 +import ImageUpload from "@/components/ImageUpload/index.vue" +// 图片预览组件 +import ImagePreview from "@/components/ImagePreview/index.vue" +// 字典标签组件 +import DictTag from '@/components/DictTag/index.vue' +import unifiedLoginSDK from "oauth2-login-sdk" + +// 初始化配置 +unifiedLoginSDK.init({ + clientId: import.meta.env.VITE_APP_CLIENT_ID, + registrationId: import.meta.env.VITE_APP_REGISTRATION_ID, + storageType: import.meta.env.VITE_APP_STORAGE_TYPE, + basepath: import.meta.env.VITE_APP_BASE_API, + idpLogoutUrl: import.meta.env.VITE_APP_IDP_LOGOUT_URL, + homePage: import.meta.env.VITE_APP_HOME_PAGE +}) + +const app = createApp(App) + +// 全局方法挂载 +app.config.globalProperties.useDict = useDict +app.config.globalProperties.download = download +app.config.globalProperties.parseTime = parseTime +app.config.globalProperties.resetForm = resetForm +app.config.globalProperties.handleTree = handleTree +app.config.globalProperties.addDateRange = addDateRange +app.config.globalProperties.getConfigKey = getConfigKey +app.config.globalProperties.selectDictLabel = selectDictLabel +app.config.globalProperties.selectDictLabels = selectDictLabels + +// 全局组件挂载 +app.component('DictTag', DictTag) +app.component('Pagination', Pagination) +app.component('FileUpload', FileUpload) +app.component('ImageUpload', ImageUpload) +app.component('ImagePreview', ImagePreview) +app.component('RightToolbar', RightToolbar) +app.component('Editor', Editor) + +app.use(router) +app.use(store) +app.use(plugins) +app.use(elementIcons) +app.component('svg-icon', SvgIcon) + +directive(app) + +// 使用element-plus 并且设置全局的大小 +// @ts-ignore +app.use(ElementPlus, { + locale: locale, + // 支持 large、default、small + size: Cookies.get('size') || 'default' +}) + +app.mount('#app') diff --git a/portal/frontend/portal/src/permission.ts b/portal/frontend/portal/src/permission.ts new file mode 100644 index 0000000..bfa791b --- /dev/null +++ b/portal/frontend/portal/src/permission.ts @@ -0,0 +1,72 @@ +import router from './router' +import { ElMessage } from 'element-plus' +import NProgress from 'nprogress' +import 'nprogress/nprogress.css' +import { getToken } from '@/utils/auth' +import { isHttp, isPathMatch } from '@/utils/validate' +import { isRelogin } from '@/utils/request' +import useUserStore from '@/store/modules/user' +import useSettingsStore from '@/store/modules/settings' +import usePermissionStore from '@/store/modules/permission' +import unifiedLoginSDK from "oauth2-login-sdk" + + +NProgress.configure({ showSpinner: false }) + +const whiteList = ['/login', '/register'] + +const isWhiteList = (path: string): boolean => { + return whiteList.some((pattern: string) => isPathMatch(pattern, path)) +} + +router.beforeEach((to, from, next) => { + if (getToken()) { + to.meta.title && useSettingsStore().setTitle(to.meta.title as string) + /* has token*/ + if (to.path === '/login') { + next({ path: '/' }) + } else if (isWhiteList(to.path)) { + next() + } else { + if (useUserStore().roles.length === 0) { + isRelogin.show = true + // 判断当前用户是否已拉取完user_info信息 + useUserStore().getInfo().then(() => { + isRelogin.show = false + usePermissionStore().generateRoutes().then((accessRoutes: any[]) => { + // 根据roles权限生成可访问的路由表 + accessRoutes.forEach((route: any) => { + if (!isHttp(route.path)) { + router.addRoute(route) // 动态添加可访问路由表 + } + }) + next({ ...to, replace: true }) // hack方法 确保addRoutes已完成 + }) + }).catch((err: any) => { + useUserStore().logOut().then(() => { + ElMessage.error(err as string) + next({ path: '/' }) + }) + }) + } else { + next() + } + } + } else { + // 没有token + if (isWhiteList(to.path)) { + // 在免登录白名单,直接进入 + next() + } else { + if (to.path === '/callback') { + unifiedLoginSDK.handleCallback().then() + }else{ + unifiedLoginSDK.login().then() + } + } + } +}) + +router.afterEach(() => { + NProgress.done() +}) diff --git a/portal/frontend/portal/src/plugins/auth.ts b/portal/frontend/portal/src/plugins/auth.ts new file mode 100644 index 0000000..ae5f2c7 --- /dev/null +++ b/portal/frontend/portal/src/plugins/auth.ts @@ -0,0 +1,60 @@ +import useUserStore from '@/store/modules/user' + +function authPermission(permission: string): boolean { + const all_permission = "*:*:*" + const permissions = useUserStore().permissions + if (permission && permission.length > 0) { + return permissions.some((v: string) => { + return all_permission === v || v === permission + }) + } else { + return false + } +} + +function authRole(role: string): boolean { + const super_admin = "admin" + const roles = useUserStore().roles + if (role && role.length > 0) { + return roles.some((v: string) => { + return super_admin === v || v === role + }) + } else { + return false + } +} + +export default { + // 验证用户是否具备某权限 + hasPermi(permission: string): boolean { + return authPermission(permission) + }, + // 验证用户是否含有指定权限,只需包含其中一个 + hasPermiOr(permissions: string[]): boolean { + return permissions.some(item => { + return authPermission(item) + }) + }, + // 验证用户是否含有指定权限,必须全部拥有 + hasPermiAnd(permissions: string[]): boolean { + return permissions.every(item => { + return authPermission(item) + }) + }, + // 验证用户是否具备某角色 + hasRole(role: string): boolean { + return authRole(role) + }, + // 验证用户是否含有指定角色,只需包含其中一个 + hasRoleOr(roles: string[]): boolean { + return roles.some(item => { + return authRole(item) + }) + }, + // 验证用户是否含有指定角色,必须全部拥有 + hasRoleAnd(roles: string[]): boolean { + return roles.every(item => { + return authRole(item) + }) + } +} diff --git a/portal/frontend/portal/src/plugins/cache.ts b/portal/frontend/portal/src/plugins/cache.ts new file mode 100644 index 0000000..686dab8 --- /dev/null +++ b/portal/frontend/portal/src/plugins/cache.ts @@ -0,0 +1,88 @@ +interface CacheInterface { + set(key: string, value: string): void + get(key: string): string | null + setJSON(key: string, jsonValue: any): void + getJSON(key: string): any + remove(key: string): void +} + +const sessionCache: CacheInterface = { + set (key: string, value: string) { + if (!sessionStorage) { + return + } + if (key != null && value != null) { + sessionStorage.setItem(key, value) + } + }, + get (key: string): string | null { + if (!sessionStorage) { + return null + } + if (key == null) { + return null + } + return sessionStorage.getItem(key) + }, + setJSON (key: string, jsonValue: any) { + if (jsonValue != null) { + this.set(key, JSON.stringify(jsonValue)) + } + }, + getJSON (key: string): any { + const value = this.get(key) + if (value != null) { + return JSON.parse(value) + } + return null + }, + remove (key: string) { + sessionStorage.removeItem(key) + } +} + +const localCache: CacheInterface = { + set (key: string, value: string) { + if (!localStorage) { + return + } + if (key != null && value != null) { + localStorage.setItem(key, value) + } + }, + get (key: string): string | null { + if (!localStorage) { + return null + } + if (key == null) { + return null + } + return localStorage.getItem(key) + }, + setJSON (key: string, jsonValue: any) { + if (jsonValue != null) { + this.set(key, JSON.stringify(jsonValue)) + } + }, + getJSON (key: string): any { + const value = this.get(key) + if (value != null) { + return JSON.parse(value) + } + return null + }, + remove (key: string) { + localStorage.removeItem(key) + } +} + +export default { + /** + * 会话级缓存 + */ + session: sessionCache, + /** + * 本地缓存 + */ + local: localCache +} diff --git a/portal/frontend/portal/src/plugins/download.ts b/portal/frontend/portal/src/plugins/download.ts new file mode 100644 index 0000000..8078254 --- /dev/null +++ b/portal/frontend/portal/src/plugins/download.ts @@ -0,0 +1,78 @@ +import axios from 'axios' +import { ElLoading, ElMessage } from 'element-plus' +import { saveAs } from 'file-saver' +import { getToken } from '@/utils/auth' +import errorCode from '@/utils/errorCode' +import { blobValidate } from '@/utils/platform' + +const baseURL = import.meta.env.VITE_APP_BASE_API +let downloadLoadingInstance: ReturnType + +export default { + name(name: string, isDelete = true) { + const url = baseURL + "/common/download?fileName=" + encodeURIComponent(name) + "&delete=" + isDelete + axios({ + method: 'get', + url: url, + responseType: 'blob', + headers: { 'Authorization': 'Bearer ' + getToken() } + }).then((res: any) => { + const isBlob = blobValidate(res.data) + if (isBlob) { + const blob = new Blob([res.data]) + this.saveAs(blob, decodeURIComponent(res.headers['download-filename'])) + } else { + this.printErrMsg(res.data) + } + }) + }, + resource(resource: string) { + const url = baseURL + "/common/download/resource?resource=" + encodeURIComponent(resource) + axios({ + method: 'get', + url: url, + responseType: 'blob', + headers: { 'Authorization': 'Bearer ' + getToken() } + }).then((res: any) => { + const isBlob = blobValidate(res.data) + if (isBlob) { + const blob = new Blob([res.data]) + this.saveAs(blob, decodeURIComponent(res.headers['download-filename'])) + } else { + this.printErrMsg(res.data) + } + }) + }, + zip(url: string, name: string) { + const downloadUrl = baseURL + url + downloadLoadingInstance = ElLoading.service({ text: "正在下载数据,请稍候", background: "rgba(0, 0, 0, 0.7)", }) + axios({ + method: 'get', + url: downloadUrl, + responseType: 'blob', + headers: { 'Authorization': 'Bearer ' + getToken() } + }).then((res: any) => { + const isBlob = blobValidate(res.data) + if (isBlob) { + const blob = new Blob([res.data], { type: 'application/zip' }) + this.saveAs(blob, name) + } else { + this.printErrMsg(res.data) + } + downloadLoadingInstance.close() + }).catch((r: any) => { + console.error(r) + ElMessage.error('下载文件出现错误,请联系管理员!') + downloadLoadingInstance.close() + }) + }, + saveAs(text: Blob, name: string, opts?: any) { + saveAs(text, name, opts) + }, + async printErrMsg(data: any) { + const resText = await data.text() + const rspObj = JSON.parse(resText) + const errMsg = errorCode[rspObj.code] || rspObj.msg || errorCode['default'] + ElMessage.error(errMsg) + } +} diff --git a/portal/frontend/portal/src/plugins/index.ts b/portal/frontend/portal/src/plugins/index.ts new file mode 100644 index 0000000..aace780 --- /dev/null +++ b/portal/frontend/portal/src/plugins/index.ts @@ -0,0 +1,19 @@ +import { App } from 'vue' +import tab from './tab' +import auth from './auth' +import cache from './cache' +import modal from './modal' +import download from './download' + +export default function installPlugins(app: App){ + // 页签操作 + app.config.globalProperties.$tab = tab + // 认证对象 + app.config.globalProperties.$auth = auth + // 缓存对象 + app.config.globalProperties.$cache = cache + // 模态框对象 + app.config.globalProperties.$modal = modal + // 下载文件 + app.config.globalProperties.$download = download +} diff --git a/portal/frontend/portal/src/plugins/modal.ts b/portal/frontend/portal/src/plugins/modal.ts new file mode 100644 index 0000000..12ffa37 --- /dev/null +++ b/portal/frontend/portal/src/plugins/modal.ts @@ -0,0 +1,82 @@ +import { ElMessage, ElMessageBox, ElNotification, ElLoading } from 'element-plus' + +let loadingInstance: ReturnType + +export default { + // 消息提示 + msg(content: string) { + ElMessage.info(content) + }, + // 错误消息 + msgError(content: string) { + ElMessage.error(content) + }, + // 成功消息 + msgSuccess(content: string) { + ElMessage.success(content) + }, + // 警告消息 + msgWarning(content: string) { + ElMessage.warning(content) + }, + // 弹出提示 + alert(content: string) { + ElMessageBox.alert(content, "系统提示") + }, + // 错误提示 + alertError(content: string) { + ElMessageBox.alert(content, "系统提示", { type: 'error' }) + }, + // 成功提示 + alertSuccess(content: string) { + ElMessageBox.alert(content, "系统提示", { type: 'success' }) + }, + // 警告提示 + alertWarning(content: string) { + ElMessageBox.alert(content, "系统提示", { type: 'warning' }) + }, + // 通知提示 + notify(content: string) { + ElNotification.info(content) + }, + // 错误通知 + notifyError(content: string) { + ElNotification.error(content) + }, + // 成功通知 + notifySuccess(content: string) { + ElNotification.success(content) + }, + // 警告通知 + notifyWarning(content: string) { + ElNotification.warning(content) + }, + // 确认窗体 + confirm(content: string) { + return ElMessageBox.confirm(content, "系统提示", { + confirmButtonText: '确定', + cancelButtonText: '取消', + type: "warning", + }) + }, + // 提交内容 + prompt(content: string) { + return ElMessageBox.prompt(content, "系统提示", { + confirmButtonText: '确定', + cancelButtonText: '取消', + type: "warning", + }) + }, + // 打开遮罩层 + loading(content: string) { + loadingInstance = ElLoading.service({ + lock: true, + text: content, + background: "rgba(0, 0, 0, 0.7)", + }) + }, + // 关闭遮罩层 + closeLoading() { + loadingInstance.close() + } +} diff --git a/portal/frontend/portal/src/plugins/tab.ts b/portal/frontend/portal/src/plugins/tab.ts new file mode 100644 index 0000000..0475f98 --- /dev/null +++ b/portal/frontend/portal/src/plugins/tab.ts @@ -0,0 +1,71 @@ +import useTagsViewStore from '@/store/modules/tagsView' +import router from '@/router' + +export default { + // 刷新当前tab页签 + refreshPage(obj?: any) { + const { path, query, matched } = router.currentRoute.value + if (obj === undefined) { + matched.forEach((m: any) => { + if (m.components && m.components.default && m.components.default.name) { + if (!['Layout', 'ParentView'].includes(m.components.default.name)) { + obj = { name: m.components.default.name, path: path, query: query } + } + } + }) + } + return useTagsViewStore().delCachedView(obj).then(() => { + const { path, query } = obj + router.replace({ + path: '/redirect' + path, + query: query + }) + }) + }, + // 关闭当前tab页签,打开新页签 + closeOpenPage(obj?: any) { + useTagsViewStore().delView(router.currentRoute.value) + if (obj !== undefined) { + return router.push(obj) + } + }, + // 关闭指定tab页签 + closePage(obj?: any) { + if (obj === undefined) { + return useTagsViewStore().delView(router.currentRoute.value).then(({ visitedViews }: { visitedViews: any[] }) => { + const latestView = visitedViews.slice(-1)[0] + if (latestView) { + return router.push(latestView.fullPath) + } + return router.push('/') + }) + } + return useTagsViewStore().delView(obj) + }, + // 关闭所有tab页签 + closeAllPage() { + return useTagsViewStore().delAllViews() + }, + // 关闭左侧tab页签 + closeLeftPage(obj?: any) { + return useTagsViewStore().delLeftTags(obj || router.currentRoute.value) + }, + // 关闭右侧tab页签 + closeRightPage(obj?: any) { + return useTagsViewStore().delRightTags(obj || router.currentRoute.value) + }, + // 关闭其他tab页签 + closeOtherPage(obj?: any) { + return useTagsViewStore().delOthersViews(obj || router.currentRoute.value) + }, + // 打开tab页签 + openPage(title: string, url: string, params?: any) { + const obj = { path: url, meta: { title: title } } as any + useTagsViewStore().addView(obj) + return router.push({ path: url, query: params }) + }, + // 修改tab页签 + updatePage(obj: any) { + return useTagsViewStore().updateVisitedView(obj) + } +} diff --git a/portal/frontend/portal/src/router/index.ts b/portal/frontend/portal/src/router/index.ts new file mode 100644 index 0000000..d41a160 --- /dev/null +++ b/portal/frontend/portal/src/router/index.ts @@ -0,0 +1,164 @@ +import { createWebHistory, createRouter } from 'vue-router' +/* Layout */ +import Layout from '@/layout/index.vue' + +/** + * Note: 路由配置项 + * + * hidden: true // 当设置 true 的时候该路由不会再侧边栏出现 如401,login等页面,或者如一些编辑页面/edit/1 + * alwaysShow: true // 当你一个路由下面的 children 声明的路由大于1个时,自动会变成嵌套的模式--如组件页面 + * // 只有一个时,会将那个子路由当做根路由显示在侧边栏--如引导页面 + * // 若你想不管路由下面的 children 声明的个数都显示你的根路由 + * // 你可以设置 alwaysShow: true,这样它就会忽略之前定义的规则,一直显示根路由 + * redirect: noRedirect // 当设置 noRedirect 的时候该路由在面包屑导航中不可被点击 + * name:'router-name' // 设定路由的名字,一定要填写不然使用时会出现各种问题 + * query: '{"id": 1, "name": "ry"}' // 访问路由的默认传递参数 + * roles: ['admin', 'common'] // 访问路由的角色权限 + * permissions: ['a:a:a', 'b:b:b'] // 访问路由的菜单权限 + * meta : { + noCache: true // 如果设置为true,则不会被 缓存(默认 false) + title: 'title' // 设置该路由在侧边栏和面包屑中展示的名字 + icon: 'svg-name' // 设置该路由的图标,对应路径src/assets/icons/svg + breadcrumb: false // 如果设置为false,则不会在breadcrumb面包屑中显示 + activeMenu: '/system/user' // 当路由设置了该属性,则会高亮相对应的侧边栏。 + } + */ + +// 公共路由 +export const constantRoutes = [ + { + path: '/redirect', + component: Layout, + hidden: true, + children: [ + { + path: '/redirect/:path(.*)', + component: () => import('@/views/redirect/index.vue') + } + ] + }, + { + path: "/:pathMatch(.*)*", + component: () => import('@/views/error/404.vue'), + hidden: true + }, + { + path: '/401', + component: () => import('@/views/error/401.vue'), + hidden: true + }, + { + path: '', + component: Layout, + redirect: '/index', + children: [ + { + path: '/index', + component: () => import('@/views/index.vue'), + name: 'Index', + meta: { title: '首页', icon: 'dashboard', affix: true } + } + ] + }, + { + path: '/user', + component: Layout, + hidden: true, + redirect: 'noredirect', + children: [ + { + path: 'profile/:activeTab?', + component: () => import('@/views/system/user/profile/index.vue'), + name: 'Profile', + meta: { title: '个人中心', icon: 'user' } + } + ] + } +] + +// 动态路由,基于用户权限动态去加载 +export const dynamicRoutes = [ + { + path: '/system/user-auth', + component: Layout, + hidden: true, + permissions: ['system:user:edit'], + children: [ + { + path: 'role/:userId(\\d+)', + component: () => import('@/views/system/user/authRole.vue'), + name: 'AuthRole', + meta: { title: '分配角色', activeMenu: '/system/user' } + } + ] + }, + { + path: '/system/role-auth', + component: Layout, + hidden: true, + permissions: ['system:role:edit'], + children: [ + { + path: 'user/:roleId(\\d+)', + component: () => import('@/views/system/role/authUser.vue'), + name: 'AuthUser', + meta: { title: '分配用户', activeMenu: '/system/role' } + } + ] + }, + { + path: '/system/dict-data', + component: Layout, + hidden: true, + permissions: ['system:dict:list'], + children: [ + { + path: 'index/:dictId(\\d+)', + component: () => import('@/views/system/dict/data.vue'), + name: 'Data', + meta: { title: '字典数据', activeMenu: '/system/dict' } + } + ] + }, + { + path: '/monitor/job-log', + component: Layout, + hidden: true, + permissions: ['monitor:job:list'], + children: [ + { + path: 'index/:jobId(\\d+)', + component: () => import('@/views/monitor/job/log.vue'), + name: 'JobLog', + meta: { title: '调度日志', activeMenu: '/monitor/job' } + } + ] + }, + { + path: '/tool/gen-edit', + component: Layout, + hidden: true, + permissions: ['tool:gen:edit'], + children: [ + { + path: 'index/:tableId(\\d+)', + component: () => import('@/views/tool/gen/editTable.vue'), + name: 'GenEdit', + meta: { title: '修改生成配置', activeMenu: '/tool/gen' } + } + ] + } +] + +const router = createRouter({ + history: createWebHistory(), + routes: constantRoutes, + scrollBehavior(to, from, savedPosition) { + if (savedPosition) { + return savedPosition + } + return { top: 0 } + }, +}) + +export default router diff --git a/portal/frontend/portal/src/settings.ts b/portal/frontend/portal/src/settings.ts new file mode 100644 index 0000000..1f3acd6 --- /dev/null +++ b/portal/frontend/portal/src/settings.ts @@ -0,0 +1,57 @@ +export default { + /** + * 网页标题 + */ + title: import.meta.env.VITE_APP_TITLE, + + /** + * 侧边栏主题 深色主题theme-dark,浅色主题theme-light + */ + sideTheme: 'theme-dark', + + /** + * 是否系统布局配置 + */ + showSettings: true, + + /** + * 菜单导航模式 1、纯左侧 2、混合(左侧+顶部) 3、纯顶部 + */ + navType: 1, + + /** + * 是否显示 tagsView + */ + tagsView: true, + + /** + * 显示页签图标 + */ + tagsIcon: false, + + /** + * 是否固定头部 + */ + fixedHeader: true, + + /** + * 是否显示logo + */ + sidebarLogo: true, + + /** + * 是否显示动态标题 + */ + dynamicTitle: false, + + /** + * 是否显示底部版权 + */ + footerVisible: false, + + /** + * 底部版权文本内容 + */ + footerContent: 'Copyright © 2026-2032 lingniu. All Rights Reserved.' +} + diff --git a/portal/frontend/portal/src/store/index.ts b/portal/frontend/portal/src/store/index.ts new file mode 100644 index 0000000..944c3c6 --- /dev/null +++ b/portal/frontend/portal/src/store/index.ts @@ -0,0 +1,3 @@ +const store = createPinia() + +export default store diff --git a/portal/frontend/portal/src/store/modules/app.ts b/portal/frontend/portal/src/store/modules/app.ts new file mode 100644 index 0000000..1de6d2d --- /dev/null +++ b/portal/frontend/portal/src/store/modules/app.ts @@ -0,0 +1,59 @@ +import Cookies from 'js-cookie' +import { defineStore } from 'pinia' + +interface SidebarState { + opened: boolean + withoutAnimation: boolean + hide: boolean +} + +interface AppState { + sidebar: SidebarState + device: string + size: string +} + +const useAppStore = defineStore( + 'app', + { + state: (): AppState => ({ + sidebar: { + opened: Cookies.get('sidebarStatus') ? !!+Cookies.get('sidebarStatus')! : true, + withoutAnimation: false, + hide: false + }, + device: 'desktop', + size: Cookies.get('size') || 'default' + }), + actions: { + toggleSideBar(withoutAnimation?: boolean) { + if (this.sidebar.hide) { + return false + } + this.sidebar.opened = !this.sidebar.opened + this.sidebar.withoutAnimation = withoutAnimation || false + if (this.sidebar.opened) { + Cookies.set('sidebarStatus', '1') + } else { + Cookies.set('sidebarStatus', '0') + } + }, + closeSideBar({ withoutAnimation }: { withoutAnimation?: boolean }) { + Cookies.set('sidebarStatus', '0') + this.sidebar.opened = false + this.sidebar.withoutAnimation = withoutAnimation || false + }, + toggleDevice(device: string) { + this.device = device + }, + setSize(size: string) { + this.size = size + Cookies.set('size', size) + }, + toggleSideBarHide(status: boolean) { + this.sidebar.hide = status + } + } + }) + +export default useAppStore diff --git a/portal/frontend/portal/src/store/modules/dict.ts b/portal/frontend/portal/src/store/modules/dict.ts new file mode 100644 index 0000000..46ef506 --- /dev/null +++ b/portal/frontend/portal/src/store/modules/dict.ts @@ -0,0 +1,67 @@ +interface DictItem { + key: string + value: any[] +} + +interface DictState { + dict: DictItem[] +} + +const useDictStore = defineStore( + 'dict', + { + state: (): DictState => ({ + dict: [] + }), + actions: { + // 获取字典 + getDict(_key: string): any[] | null { + if (_key == null && _key == "") { + return null + } + try { + for (let i = 0; i < this.dict.length; i++) { + if (this.dict[i].key == _key) { + return this.dict[i].value + } + } + } catch (e) { + return null + } + return null + }, + // 设置字典 + setDict(_key: string, value: any[]) { + if (_key !== null && _key !== "") { + this.dict.push({ + key: _key, + value: value + }) + } + }, + // 删除字典 + removeDict(_key: string): boolean { + let bln = false + try { + for (let i = 0; i < this.dict.length; i++) { + if (this.dict[i].key == _key) { + this.dict.splice(i, 1) + return true + } + } + } catch (e) { + bln = false + } + return bln + }, + // 清空字典 + cleanDict() { + this.dict = [] + }, + // 初始字典 + initDict() { + } + } + }) + +export default useDictStore diff --git a/portal/frontend/portal/src/store/modules/permission.ts b/portal/frontend/portal/src/store/modules/permission.ts new file mode 100644 index 0000000..2c6d40e --- /dev/null +++ b/portal/frontend/portal/src/store/modules/permission.ts @@ -0,0 +1,127 @@ +import auth from '@/plugins/auth' +import router, { constantRoutes, dynamicRoutes } from '@/router' +import { getRouters } from '@/api/menu' +import Layout from '@/layout/index.vue' +import ParentView from '@/components/ParentView/index.vue' +import InnerLink from '@/layout/components/InnerLink/index.vue' + +// 匹配views里面所有的.vue文件 +const modules = import.meta.glob('./../../views/**/*.vue') + +const usePermissionStore = defineStore( + 'permission', + { + state: () => ({ + routes: [] as any[], + addRoutes: [] as any[], + defaultRoutes: [] as any[], + topbarRouters: [] as any[], + sidebarRouters: [] as any[] + }), + actions: { + setRoutes(routes: any[]) { + this.addRoutes = routes + this.routes = constantRoutes.concat(routes) + }, + setDefaultRoutes(routes: any[]) { + this.defaultRoutes = constantRoutes.concat(routes) + }, + setTopbarRoutes(routes: any[]) { + this.topbarRouters = routes + }, + setSidebarRouters(routes: any[]) { + this.sidebarRouters = routes + }, + generateRoutes(roles?: any[]): Promise { + return new Promise(resolve => { + // 向后端请求路由数据 + getRouters().then(res => { + const sdata = JSON.parse(JSON.stringify(res.data)) + const rdata = JSON.parse(JSON.stringify(res.data)) + const defaultData = JSON.parse(JSON.stringify(res.data)) + const sidebarRoutes = filterAsyncRouter(sdata) + const rewriteRoutes = filterAsyncRouter(rdata, false, true) + const defaultRoutes = filterAsyncRouter(defaultData) + const asyncRoutes = filterDynamicRoutes(dynamicRoutes) + asyncRoutes.forEach(route => { router.addRoute(route) }) + this.setRoutes(rewriteRoutes) + this.setSidebarRouters(constantRoutes.concat(sidebarRoutes)) + this.setDefaultRoutes(sidebarRoutes) + this.setTopbarRoutes(defaultRoutes) + resolve(rewriteRoutes) + }) + }) + } + } + }) + +// 遍历后台传来的路由字符串,转换为组件对象 +function filterAsyncRouter(asyncRouterMap: any[], lastRouter = false, type = false) { + return asyncRouterMap.filter(route => { + if (type && route.children) { + route.children = filterChildren(route.children) + } + if (route.component) { + // Layout ParentView 组件特殊处理 + if (route.component === 'Layout') { + route.component = Layout + } else if (route.component === 'ParentView') { + route.component = ParentView + } else if (route.component === 'InnerLink') { + route.component = InnerLink + } else { + route.component = loadView(route.component) + } + } + if (route.children != null && route.children && route.children.length) { + route.children = filterAsyncRouter(route.children, route, type) + } else { + delete route['children'] + delete route['redirect'] + } + return true + }) +} + +function filterChildren(childrenMap: any[], lastRouter: any = false) { + var children: any[] = [] + childrenMap.forEach(el => { + el.path = lastRouter ? lastRouter.path + '/' + el.path : el.path + if (el.children && el.children.length && el.component === 'ParentView') { + children = children.concat(filterChildren(el.children, el)) + } else { + children.push(el) + } + }) + return children +} + +// 动态路由遍历,验证是否具备权限 +export function filterDynamicRoutes(routes: any[]): any[] { + const res: any[] = [] + routes.forEach(route => { + if (route.permissions) { + if (auth.hasPermiOr(route.permissions)) { + res.push(route) + } + } else if (route.roles) { + if (auth.hasRoleOr(route.roles)) { + res.push(route) + } + } + }) + return res +} + +export const loadView = (view: string): any => { + let res + for (const path in modules) { + const dir = path.split('views/')[1].split('.vue')[0] + if (dir === view) { + res = () => modules[path]() + } + } + return res +} + +export default usePermissionStore diff --git a/portal/frontend/portal/src/store/modules/settings.ts b/portal/frontend/portal/src/store/modules/settings.ts new file mode 100644 index 0000000..96a947c --- /dev/null +++ b/portal/frontend/portal/src/store/modules/settings.ts @@ -0,0 +1,67 @@ +import defaultSettings from '@/settings' +import { useDark, useToggle } from '@vueuse/core' +import { useDynamicTitle } from '@/utils/dynamicTitle' + +const isDark = useDark() +const toggleDark = useToggle(isDark) + +const { sideTheme, showSettings, navType, tagsView, tagsIcon, fixedHeader, sidebarLogo, dynamicTitle, footerVisible, footerContent } = defaultSettings + +const storageSetting = JSON.parse(localStorage.getItem('layout-setting') || '{}') || {} + +interface SettingsState { + title: string + theme: string + sideTheme: string + showSettings: boolean + navType: number + tagsView: boolean + tagsIcon: boolean + fixedHeader: boolean + sidebarLogo: boolean + dynamicTitle: boolean + footerVisible: boolean + footerContent: string + isDark: boolean +} + +const useSettingsStore = defineStore( + 'settings', + { + state: (): SettingsState => ({ + title: '', + theme: storageSetting.theme || '#409EFF', + sideTheme: storageSetting.sideTheme || sideTheme, + showSettings: showSettings, + navType: storageSetting.navType === undefined ? navType : storageSetting.navType, + tagsView: storageSetting.tagsView === undefined ? tagsView : storageSetting.tagsView, + tagsIcon: storageSetting.tagsIcon === undefined ? tagsIcon : storageSetting.tagsIcon, + fixedHeader: storageSetting.fixedHeader === undefined ? fixedHeader : storageSetting.fixedHeader, + sidebarLogo: storageSetting.sidebarLogo === undefined ? sidebarLogo : storageSetting.sidebarLogo, + dynamicTitle: storageSetting.dynamicTitle === undefined ? dynamicTitle : storageSetting.dynamicTitle, + footerVisible: storageSetting.footerVisible === undefined ? footerVisible : storageSetting.footerVisible, + footerContent: footerContent, + isDark: isDark.value + }), + actions: { + // 修改布局设置 + changeSetting(data: { key: string; value: any }) { + const { key, value } = data + if (this.hasOwnProperty(key)) { + (this as any)[key] = value + } + }, + // 设置网页标题 + setTitle(title: string) { + this.title = title + useDynamicTitle() + }, + // 切换暗黑模式 + toggleTheme() { + this.isDark = !this.isDark + toggleDark() + } + } + }) + +export default useSettingsStore diff --git a/portal/frontend/portal/src/store/modules/tagsView.ts b/portal/frontend/portal/src/store/modules/tagsView.ts new file mode 100644 index 0000000..1c87d30 --- /dev/null +++ b/portal/frontend/portal/src/store/modules/tagsView.ts @@ -0,0 +1,219 @@ +interface ViewMeta { + title?: string + noCache?: boolean + affix?: boolean + link?: string + [key: string]: any +} + +interface View { + path: string + name?: string + meta: ViewMeta + [key: string]: any +} + +const useTagsViewStore = defineStore( + 'tags-view', + { + state: () => ({ + visitedViews: [] as View[], + cachedViews: [] as string[], + iframeViews: [] as View[] + }), + + actions: { + addView(view: any) { + this.addVisitedView(view) + this.addCachedView(view) + }, + + addIframeView(view: any) { + if (this.iframeViews.some((v: any) => v.path === view.path)) return + this.iframeViews.push( + Object.assign({}, view, { + title: view.meta?.title || 'no-name' + }) + ) + }, + + addVisitedView(view: any) { + if (this.visitedViews.some((v: any) => v.path === view.path)) return + this.visitedViews.push( + Object.assign({}, view, { + title: view.meta?.title || 'no-name' + }) + ) + }, + + addCachedView(view: any) { + if (this.cachedViews.includes(view.name)) return + if (!view.meta?.noCache) { + this.cachedViews.push(view.name) + } + }, + + delView(view: any) { + return new Promise<{ visitedViews: View[], cachedViews: string[] }>(resolve => { + this.delVisitedView(view) + this.delCachedView(view) + resolve({ + visitedViews: [...this.visitedViews], + cachedViews: [...this.cachedViews] + }) + }) + }, + + delVisitedView(view: any) { + return new Promise(resolve => { + for (const [i, v] of this.visitedViews.entries()) { + if (v.path === view.path) { + this.visitedViews.splice(i, 1) + break + } + } + this.iframeViews = this.iframeViews.filter((item: any) => item.path !== view.path) + resolve([...this.visitedViews]) + }) + }, + + delIframeView(view: any) { + return new Promise(resolve => { + this.iframeViews = this.iframeViews.filter((item: any) => item.path !== view.path) + resolve([...this.iframeViews]) + }) + }, + + delCachedView(view: any) { + return new Promise(resolve => { + const index = this.cachedViews.indexOf(view.name) + index > -1 && this.cachedViews.splice(index, 1) + resolve([...this.cachedViews]) + }) + }, + + delOthersViews(view: any) { + return new Promise<{ visitedViews: View[], cachedViews: string[] }>(resolve => { + this.delOthersVisitedViews(view) + this.delOthersCachedViews(view) + resolve({ + visitedViews: [...this.visitedViews], + cachedViews: [...this.cachedViews] + }) + }) + }, + + delOthersVisitedViews(view: any) { + return new Promise(resolve => { + this.visitedViews = this.visitedViews.filter((v: any) => { + return v.meta?.affix || v.path === view.path + }) + this.iframeViews = this.iframeViews.filter((item: any) => item.path === view.path) + resolve([...this.visitedViews]) + }) + }, + + delOthersCachedViews(view: any) { + return new Promise(resolve => { + const index = this.cachedViews.indexOf(view.name) + if (index > -1) { + this.cachedViews = this.cachedViews.slice(index, index + 1) + } else { + this.cachedViews = [] + } + resolve([...this.cachedViews]) + }) + }, + + delAllViews(view?: any) { + return new Promise<{ visitedViews: View[], cachedViews: string[] }>(resolve => { + this.delAllVisitedViews(view) + this.delAllCachedViews(view) + resolve({ + visitedViews: [...this.visitedViews], + cachedViews: [...this.cachedViews] + }) + }) + }, + + delAllVisitedViews(view: any) { + return new Promise(resolve => { + const affixTags = this.visitedViews.filter((tag: any) => tag.meta?.affix) + this.visitedViews = affixTags + this.iframeViews = [] + resolve([...this.visitedViews]) + }) + }, + + delAllCachedViews(view: any) { + return new Promise(resolve => { + this.cachedViews = [] + resolve([...this.cachedViews]) + }) + }, + + updateVisitedView(view: any) { + for (let v of this.visitedViews) { + if (v.path === view.path) { + v = Object.assign(v, view) + break + } + } + }, + + delRightTags(view: any) { + return new Promise(resolve => { + const index = this.visitedViews.findIndex((v: any) => v.path === view.path) + if (index === -1) { + return + } + this.visitedViews = this.visitedViews.filter((item: any, idx: number) => { + if (idx <= index || (item.meta && item.meta.affix)) { + return true + } + if (item.name) { + const i = this.cachedViews.indexOf(item.name) + if (i > -1) { + this.cachedViews.splice(i, 1) + } + } + if (item.meta?.link) { + const fi = this.iframeViews.findIndex((v: any) => v.path === item.path) + this.iframeViews.splice(fi, 1) + } + return false + }) + resolve([...this.visitedViews]) + }) + }, + + delLeftTags(view: any) { + return new Promise(resolve => { + const index = this.visitedViews.findIndex((v: any) => v.path === view.path) + if (index === -1) { + return + } + this.visitedViews = this.visitedViews.filter((item: any, idx: number) => { + if (idx >= index || (item.meta && item.meta.affix)) { + return true + } + if (item.name) { + const i = this.cachedViews.indexOf(item.name) + if (i > -1) { + this.cachedViews.splice(i, 1) + } + } + if (item.meta?.link) { + const fi = this.iframeViews.findIndex((v: any) => v.path === item.path) + this.iframeViews.splice(fi, 1) + } + return false + }) + resolve([...this.visitedViews]) + }) + } + } + } +) + +export default useTagsViewStore diff --git a/portal/frontend/portal/src/store/modules/user.ts b/portal/frontend/portal/src/store/modules/user.ts new file mode 100644 index 0000000..bc8fe61 --- /dev/null +++ b/portal/frontend/portal/src/store/modules/user.ts @@ -0,0 +1,79 @@ + +import { getInfo } from '@/api/login' +import { getToken } from '@/utils/auth' +import { isHttp, isEmpty } from "@/utils/validate" +import defAva from '@/assets/images/profile.jpg' +import unifiedLoginSDK from "oauth2-login-sdk"; + +interface UserState { + token: string | undefined + id: string | number + name: string + nickName: string + avatar: string + roles: string[] + permissions: string[] +} + +const useUserStore = defineStore( + 'user', + { + state: (): UserState => ({ + token: getToken(), + id: '', + name: '', + nickName: '', + avatar: '', + roles: [], + permissions: [] + }), + actions: { + // 获取用户信息 + getInfo() { + return new Promise((resolve, reject) => { + getInfo().then(res => { + // const user = res.user + const user = res + // let avatar = user.avatar || '' + let avatar = '' + if (!isHttp(avatar)) { + avatar = (isEmpty(avatar)) ? defAva : import.meta.env.VITE_APP_BASE_API + avatar + } + if (res.roles && res.roles.length > 0) { // 验证返回的roles是否是一个非空数组 + this.roles = res.roles + this.permissions = res.permissions + } else { + this.roles = ['ROLE_DEFAULT'] + } + this.id = user.userId || '' + this.name = user.username || '' + this.nickName = user.nickName || '' + this.avatar = avatar + /* 初始密码提示 */ + // if(res.isDefaultModifyPwd) { + // ElMessageBox.confirm('您的密码还是初始密码,请修改密码!', '安全提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(() => { + // router.push({ name: 'Profile', params: { activeTab: 'resetPwd' } }) + // }).catch(() => {}) + // } + // /* 过期密码提示 */ + // if(!res.isDefaultModifyPwd && res.isPasswordExpired) { + // ElMessageBox.confirm('您的密码已过期,请尽快修改密码!', '安全提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(() => { + // router.push({ name: 'Profile', params: { activeTab: 'resetPwd' } }) + // }).catch(() => {}) + // } + resolve(res) + }).catch(error => { + reject(error) + }) + }) + }, + // 退出系统 + logOut() { + return new Promise((resolve, reject) => { + unifiedLoginSDK.logout() + }) + } + } + }) + +export default useUserStore diff --git a/portal/frontend/portal/src/types/api/common.ts b/portal/frontend/portal/src/types/api/common.ts new file mode 100644 index 0000000..e6fc621 --- /dev/null +++ b/portal/frontend/portal/src/types/api/common.ts @@ -0,0 +1,85 @@ +/** API 通用响应类型 */ +export interface AjaxResult { + /** 状态码 */ + code: number + /** 返回内容 */ + msg: string + /** 数据对象 */ + data?: T +} + +/** API 表格响应类型 */ +export interface TableDataInfo { + /** 状态码 */ + code: number + /** 返回内容 */ + msg: string + /** 总记录数 */ + total: number + /** 列表数据 */ + rows: T[] +} + +/** 分页参数类型 */ +export interface PageDomain { + /** 当前记录起始索引 */ + pageNum?: number + /** 每页显示记录数 */ + pageSize?: number + /** 排序列 */ + orderByColumn?: string + /** 排序的方向desc或者asc */ + isAsc?: string + /** 分页参数合理化 */ + reasonable?: boolean +} + +/** Entity基类 */ +export interface BaseEntity { + /** 搜索值 */ + searchValue?: string + /** 创建者 */ + createBy?: string + /** 创建时间 */ + createTime?: string + /** 更新者 */ + updateBy?: string + /** 更新时间 */ + updateTime?: string + /** 备注 */ + remark?: string + /** 请求参数 */ + params?: Record +} + +/** Treeselect树结构类型 */ +export interface TreeSelect { + /** 节点ID */ + id?: number + /** 节点名称 */ + label?: string + /** 节点禁用 */ + disabled: boolean + /** 子节点 */ + children: TreeSelect[] +} + +/** 显隐列信息 */ +export interface TableShowColumns { + // 显示名称 + label: string + // 是否显示 + visible: boolean +} + +/** 文件上传响应数据 */ +export interface UploadFileResult extends AjaxResult { + /** 文件名称(包含路径) */ + fileName: string + /** 文件名称(不含路径) */ + newFileName: string + /** 完整URL地址 */ + url: string + /** 原始文件名称 */ + originalFilename: string +} diff --git a/portal/frontend/portal/src/types/api/index.ts b/portal/frontend/portal/src/types/api/index.ts new file mode 100644 index 0000000..2ffa461 --- /dev/null +++ b/portal/frontend/portal/src/types/api/index.ts @@ -0,0 +1,29 @@ +/** + * API 类型统一导出 + */ +export * from "./common"; + +// 登录模块 +export * from "./login"; +export * from "./menu"; + +// System 模块 +export * from "./system/user"; +export * from "./system/role"; +export * from "./system/menu"; +export * from "./system/dept"; +export * from "./system/post"; +export * from "./system/dict"; +export * from "./system/config"; +export * from "./system/notice"; + +// monitor 模块 +export * from "./monitor/cache"; +export * from "./monitor/job"; +export * from "./monitor/jobLog"; +export * from "./monitor/logininfor"; +export * from "./monitor/operlog"; +export * from "./monitor/online"; + +// 代码生成模块 +export * from "./tool/gen"; diff --git a/portal/frontend/portal/src/types/api/login.ts b/portal/frontend/portal/src/types/api/login.ts new file mode 100644 index 0000000..de8db26 --- /dev/null +++ b/portal/frontend/portal/src/types/api/login.ts @@ -0,0 +1,50 @@ +import type { AjaxResult } from './common' +import type { SysUser } from './system/user' + +// 登录响应 +export interface LoginInfoResult extends AjaxResult { + /** 令牌 */ + token: string +} + +/** 用户信息响应 */ +export interface UserInfoResult extends AjaxResult { + /** 用户信息 */ + user: SysUser + /** 角色数据 */ + roles: string[] + /** 权限数据 */ + permissions: string[] + /** 初始密码是否提醒修改 */ + isDefaultModifyPwd?: boolean + /** 密码是否过期 */ + isPasswordExpired?: boolean +} + +/** 验证码响应 */ +export interface CaptchaInfoResult extends AjaxResult { + /** 验证码缓存key */ + uuid: string; + /** 验证码图片Base64 */ + img: string; + /** 验证码开关 */ + captchaEnabled: boolean +} + +/** 注册提交信息 */ +export interface RegisterForm { + username: string + password: string + confirmPassword: string + code: string + uuid: string +} + +/** 登录提交信息 */ +export interface LoginForm { + username: string + password: string + rememberMe?: boolean | string + code: string + uuid: string +} diff --git a/portal/frontend/portal/src/types/api/menu.ts b/portal/frontend/portal/src/types/api/menu.ts new file mode 100644 index 0000000..c3d3db9 --- /dev/null +++ b/portal/frontend/portal/src/types/api/menu.ts @@ -0,0 +1,33 @@ +/** 路由信息 */ +export interface RouterVo { + /** 路由名字 */ + name?: string; + /** 路由地址 */ + path?: string; + /** 是否隐藏路由,当设置 true 的时候该路由不会再侧边栏出现 */ + hidden?: boolean; + /** 重定向地址,当设置 noRedirect 的时候该路由在面包屑导航中不可被点击 */ + redirect?: string; + /** 组件地址 */ + component?: string; + /** 路由参数:如 {"id": 1, "name": "ry"} */ + query?: string; + /** 当你一个路由下面的 children 声明的路由大于1个时,自动会变成嵌套的模式--如组件页面 */ + alwaysShow?: boolean; + /** 其他元素 */ + meta?: MetaVo; + /** 子路由 */ + children?: RouterVo[] +} + +/** 路由其他元素信息 */ +export interface MetaVo { + /** 设置该路由在侧边栏和面包屑中展示的名字 */ + title?: string + /** 设置该路由的图标,对应路径src/assets/icons/svg */ + icon?: string + /** 设置为true,则不会被 缓存 */ + noCache?: boolean + /** 内链地址(http(s)://开头) */ + link?: string +} diff --git a/portal/frontend/portal/src/types/api/monitor/cache.ts b/portal/frontend/portal/src/types/api/monitor/cache.ts new file mode 100644 index 0000000..7e076d9 --- /dev/null +++ b/portal/frontend/portal/src/types/api/monitor/cache.ts @@ -0,0 +1,11 @@ +/** 定时任务信息 */ +export interface SysCache { + /** 缓存名称 */ + cacheName?: string; + /** 缓存键名 */ + cacheKey?: string; + /** 缓存内容 */ + cacheValue?: string; + /** 备注 */ + remark?: string; +} diff --git a/portal/frontend/portal/src/types/api/monitor/job.ts b/portal/frontend/portal/src/types/api/monitor/job.ts new file mode 100644 index 0000000..04c713a --- /dev/null +++ b/portal/frontend/portal/src/types/api/monitor/job.ts @@ -0,0 +1,33 @@ +import type { PageDomain, BaseEntity } from "../common"; + +/** 定时任务分页查询参数 */ +export interface JobQueryParams extends PageDomain { + /** 任务名称 */ + jobName?: string; + /** 任务组名 */ + jobGroup?: string; + /** 任务状态 */ + status?: string; +} + +/** 定时任务信息 */ +export interface SysJob extends BaseEntity { + /** 任务编号 */ + jobId?: number; + /** 任务名称 */ + jobName?: string; + /** 任务组名 */ + jobGroup?: string; + /** 调用目标字符串 */ + invokeTarget?: string; + /** 执行表达式 */ + cronExpression?: string; + /** 下次执行时间 */ + nextValidTime?: Date; + /** 计划策略 */ + misfirePolicy?: '1' | '2' | '3'; + /** 并发执行(0允许 1禁止) */ + concurrent?: '0' | '1'; + /** 状态(0正常 1暂停) */ + status?: '0' | '1'; +} diff --git a/portal/frontend/portal/src/types/api/monitor/jobLog.ts b/portal/frontend/portal/src/types/api/monitor/jobLog.ts new file mode 100644 index 0000000..7a61a46 --- /dev/null +++ b/portal/frontend/portal/src/types/api/monitor/jobLog.ts @@ -0,0 +1,34 @@ +import type { PageDomain, BaseEntity } from "../common"; + +/** 定时任务日志分页查询参数 */ +export interface JobLogQueryParams extends PageDomain { + /** 任务名称 */ + jobName?: string; + /** 任务组名 */ + jobGroup?: string; + /** 执行状态 */ + status?: string; + /** 执行时间 */ + params?: { + beginTime?: string; + endTime?: string; + }; +} + +/** 定时任务日志信息 */ +export interface SysJobLog extends BaseEntity { + /** 任务日志编号 */ + jobLogId?: number; + /** 任务名称 */ + jobName?: string; + /** 任务组名 */ + jobGroup?: string; + /** 调用目标字符串 */ + invokeTarget?: string; + /** 日志信息 */ + jobMessage?: string; + /** 异常信息 */ + exceptionInfo?: string; + /** 状态(0正常 1失败) */ + status?: '0' | '1'; +} diff --git a/portal/frontend/portal/src/types/api/monitor/logininfor.ts b/portal/frontend/portal/src/types/api/monitor/logininfor.ts new file mode 100644 index 0000000..0b81e83 --- /dev/null +++ b/portal/frontend/portal/src/types/api/monitor/logininfor.ts @@ -0,0 +1,38 @@ +import type { PageDomain, BaseEntity } from "../common"; + +/** 登录日志分页查询参数 */ +export interface LogininforQueryParams extends PageDomain { + /** 登录地址 */ + ipaddr?: string; + /** 用户名称 */ + userName?: string; + /** 状态 */ + status?: string; + /** 登录时间 */ + params?: { + beginTime?: string; + endTime?: string; + }; +} + +/** 登录日志信息 */ +export interface SysLogininfor extends BaseEntity { + /** 登录日志编号 */ + infoId?: number; + /** 用户账号 */ + userName?: string; + /** 登录地址 */ + ipaddr?: string; + /** 登录地点 */ + loginLocation?: string; + /** 浏览器 */ + browser?: string; + /** 操作系统 */ + os?: string; + /** 提示消息 */ + msg?: string; + /** 访问时间 */ + loginTime?: Date; + /** 状态(0正常 1失败) */ + status?: '0' | '1'; +} diff --git a/portal/frontend/portal/src/types/api/monitor/online.ts b/portal/frontend/portal/src/types/api/monitor/online.ts new file mode 100644 index 0000000..3ca96cd --- /dev/null +++ b/portal/frontend/portal/src/types/api/monitor/online.ts @@ -0,0 +1,27 @@ +/** 在线用户分页查询参数 */ +export interface OnlineQueryParams { + /** 登录地址 */ + ipaddr?: string; + /** 用户名称 */ + userName?: string; +} + +/** 在线用户信息 */ +export interface SysUserOnline { + /** 会话编号 */ + tokenId?: string; + /** 部门名称 */ + deptName?: string; + /** 用户名称 */ + userName?: string; + /** 登录IP */ + ipaddr?: string; + /** 登录地址 */ + loginLocation?: string; + /** 浏览器类型 */ + browser?: string; + /** 操作系统 */ + os?: string; + /** 登录时间 */ + loginTime?: number; +} diff --git a/portal/frontend/portal/src/types/api/monitor/operlog.ts b/portal/frontend/portal/src/types/api/monitor/operlog.ts new file mode 100644 index 0000000..a6530e3 --- /dev/null +++ b/portal/frontend/portal/src/types/api/monitor/operlog.ts @@ -0,0 +1,58 @@ +import type { PageDomain, BaseEntity } from "../common"; + +/** 操作日志分页查询参数 */ +export interface OperlogQueryParams extends PageDomain { + /** 操作地址 */ + operIp?: string; + /** 系统模块 */ + title?: string; + /** 操作人员 */ + operName?: string; + /** 类型 */ + businessType?: number; + /** 状态 */ + status?: string; + /** 登录时间 */ + params?: { + beginTime?: string; + endTime?: string; + }; +} + +/** 操作日志信息 */ +export interface SysOperLog extends BaseEntity { + /** 操作日志编号 */ + operId?: number; + /** 操作模块 */ + title?: string; + /** 业务类型(0=其它,1=新增,2=修改,3=删除,4=授权,5=导出,6=导入,7=强退,8=生成代码,9=清空数据) */ + businessType?: number; + /** 请求方法 */ + method?: string; + /** 请求方式 */ + requestMethod?: string; + /** 操作类别 */ + operatorType?: string; + /** 操作人员 */ + operName?: string; + /** 部门名称 */ + deptName?: string; + /** 请求地址 */ + operUrl?: string; + /** 操作地址 */ + operIp?: string; + /** 操作地点 */ + operLocation?: string; + /** 请求参数 */ + operParam?: string; + /** 返回参数 */ + jsonResult?: string; + /** 错误消息 */ + errorMsg?: string; + /** 操作时间 */ + operTime?: Date; + /** 消耗时间 */ + costTime?: number; + /** 状态(0正常 1异常) */ + status?: '0' | '1'; +} diff --git a/portal/frontend/portal/src/types/api/system/config.ts b/portal/frontend/portal/src/types/api/system/config.ts new file mode 100644 index 0000000..3e02f6d --- /dev/null +++ b/portal/frontend/portal/src/types/api/system/config.ts @@ -0,0 +1,30 @@ +import type { PageDomain, BaseEntity } from "../common"; + +/** 参数配置分页查询参数 */ +export interface ConfigQueryParams extends PageDomain { + /** 参数名称 */ + configName?: string; + /** 参数键名 */ + configKey?: string; + /** 系统内置 */ + configType?: string; + /** 创建时间 */ + params?: { + beginTime?: string; + endTime?: string; + }; +} + +/** 参数配置信息 */ +export interface SysConfig extends BaseEntity { + /** 参数编号 */ + configId?: number; + /** 参数名称 */ + configName?: string; + /** 参数键名 */ + configKey?: string; + /** 参数键值 */ + configValue?: string; + /** 系统内置(Y是 N否) */ + configType?: 'Y' | 'N'; +} diff --git a/portal/frontend/portal/src/types/api/system/dept.ts b/portal/frontend/portal/src/types/api/system/dept.ts new file mode 100644 index 0000000..ff29ba5 --- /dev/null +++ b/portal/frontend/portal/src/types/api/system/dept.ts @@ -0,0 +1,31 @@ +import type { BaseEntity } from "../common"; + +/** 部门查询参数 */ +export interface DeptQueryParams { + /** 部门名称 */ + deptName?: string; + /** 状态 */ + status?: string; +} + +/** 部门信息 */ +export interface SysDept extends BaseEntity { + /** 部门编号 */ + deptId?: number; + /** 父部门ID */ + parentId?: number; + /** 祖级列表 */ + ancestors?: string; + /** 部门名称 */ + deptName?: string; + /** 显示顺序 */ + orderNum?: number; + /** 负责人 */ + leader?: string; + /** 联系电话 */ + phone?: string; + /** 邮箱 */ + email?: string; + /** 状态(0正常 1停用) */ + status?: '0' | '1'; +} diff --git a/portal/frontend/portal/src/types/api/system/dict.ts b/portal/frontend/portal/src/types/api/system/dict.ts new file mode 100644 index 0000000..fb1d7d3 --- /dev/null +++ b/portal/frontend/portal/src/types/api/system/dict.ts @@ -0,0 +1,60 @@ +import type { PageDomain, BaseEntity } from "../common"; + +/** 字典分页查询参数 */ +export interface DictTypeQueryParams extends PageDomain { + /** 字典名称 */ + dictName?: string; + /** 字典类型 */ + dictType?: string; + /** 状态 */ + status?: string; + /** 创建时间 */ + params?: { + beginTime?: string; + endTime?: string; + }; +} + +/** 字典数据查询参数 */ +export interface DictDataQueryParams extends PageDomain { + /** 字典名称 */ + dictName?: string; + /** 字典标签 */ + dictLabel?: string; + /** 状态 */ + status?: string; +} + +/** 字典类型信息 */ +export interface SysDictType extends BaseEntity { + /** 字典编号 */ + dictId?: number; + /** 字典名称 */ + dictName?: string; + /** 字典类型 */ + dictType?: string; + /** 状态(0正常 1停用) */ + status?: '0' | '1'; +} + +/** 字典数据信息 */ +export interface SysDictData extends BaseEntity { + /** 字典编码 */ + dictCode?: number; + /** 字典标签 */ + dictLabel?: string; + /** 字典键值 */ + dictValue?: string; + /** 字典类型 */ + dictType?: string; + /** 样式属性 */ + cssClass?: string; + /** 表格字典样式 */ + listClass?: string; + /** 字典排序 */ + dictSort?: number; + /** 是否默认(Y是 N否) */ + isDefault?: 'Y' | 'N'; + /** 状态(0正常 1停用) */ + status?: '0' | '1'; +} diff --git a/portal/frontend/portal/src/types/api/system/menu.ts b/portal/frontend/portal/src/types/api/system/menu.ts new file mode 100644 index 0000000..6a4cbb6 --- /dev/null +++ b/portal/frontend/portal/src/types/api/system/menu.ts @@ -0,0 +1,50 @@ +import type { BaseEntity, AjaxResult, TreeSelect } from "../common"; + +/** 菜单查询参数 */ +export interface MenuQueryParams { + /** 菜单名称 */ + menuName?: string; + /** 状态 */ + status?: string; +} + +/** 菜单信息 */ +export interface SysMenu extends BaseEntity { + /** 菜单编号 */ + menuId?: number; + /** 父菜单ID */ + parentId?: number; + /** 菜单名称 */ + menuName?: string; + /** 显示顺序 */ + orderNum?: number; + /** 路由地址 */ + path?: string; + /** 组件路径 */ + component?: string; + /** 路由参数 */ + query?: string; + /** 路由名称 */ + routeName?: string; + /** 权限字符串 */ + perms?: string; + /** 菜单图标 */ + icon?: string; + /** 是否为外链(0是 1否) */ + isFrame?: '0' | '1'; + /** 是否缓存(0缓存 1不缓存) */ + isCache?: '0' | '1'; + /** 类型(M目录 C菜单 F按钮) */ + menuType?: 'M' | 'C' | 'F'; + /** 显示状态(0显示 1隐藏) */ + visible?: '0' | '1'; + /** 状态(0正常 1停用) */ + status?: '0' | '1'; +} + +export interface RoleMenuTreeselectResult extends AjaxResult { + /** 已选中的菜单ID列表 */ + checkedKeys: number[] + /** 菜单树形结构 */ + menus: TreeSelect[] +} diff --git a/portal/frontend/portal/src/types/api/system/notice.ts b/portal/frontend/portal/src/types/api/system/notice.ts new file mode 100644 index 0000000..6506356 --- /dev/null +++ b/portal/frontend/portal/src/types/api/system/notice.ts @@ -0,0 +1,25 @@ +import type { PageDomain, BaseEntity } from "../common"; + +/** 通知公告分页查询参数 */ +export interface NoticeQueryParams extends PageDomain { + /** 公告标题 */ + noticeTitle?: string; + /** 操作人员 */ + createBy?: string; + /** 公告类型 */ + noticeType?: string; +} + +/** 通知公告信息 */ +export interface SysNotice extends BaseEntity { + /** 公告编号 */ + noticeId?: number; + /** 公告标题 */ + noticeTitle?: string; + /** 公告类型(1通知 2公告) */ + noticeType?: '1' | '2'; + /** 公告内容 */ + noticeContent?: string; + /** 状态(0正常 1停用) */ + status?: '0' | '1'; +} diff --git a/portal/frontend/portal/src/types/api/system/post.ts b/portal/frontend/portal/src/types/api/system/post.ts new file mode 100644 index 0000000..e49fbd7 --- /dev/null +++ b/portal/frontend/portal/src/types/api/system/post.ts @@ -0,0 +1,25 @@ +import type { PageDomain, BaseEntity } from "../common"; + +/** 岗位分页查询参数 */ +export interface PostQueryParams extends PageDomain { + /** 岗位编码 */ + postCode?: string; + /** 岗位名称 */ + postName?: string; + /** 状态 */ + status?: string; +} + +/** 岗位信息 */ +export interface SysPost extends BaseEntity { + /** 岗位编号 */ + postId?: number; + /** 岗位编码 */ + postCode?: string; + /** 岗位名称 */ + postName?: string; + /** 岗位排序 */ + postSort?: number; + /** 状态(0正常 1停用) */ + status?: '0' | '1'; +} diff --git a/portal/frontend/portal/src/types/api/system/role.ts b/portal/frontend/portal/src/types/api/system/role.ts new file mode 100644 index 0000000..55c5d9b --- /dev/null +++ b/portal/frontend/portal/src/types/api/system/role.ts @@ -0,0 +1,71 @@ +import type { PageDomain, BaseEntity, AjaxResult, TreeSelect } from "../common"; + +/** 角色分页查询参数 */ +export interface RoleQueryParams extends PageDomain { + /** 角色名称 */ + roleName?: string; + /** 角色权限 */ + roleKey?: string; + /** 状态 */ + status?: string; + /** 创建时间 */ + params?: { + beginTime?: string; + endTime?: string; + }; +} + +/** 批量授权用户参数 */ +export interface AuthUserSelectParams { + roleId: number; + userIds: number[]; +} + +/** 用户和角色关联信息 */ +export interface SysUserRole { + /** 用户编号 */ + userId?: number; + /** 角色编号 */ + roleId: number; +} + +/** 用户和多角色关联信息 */ +export interface SysUserRoles { + /** 用户编号 */ + userId?: number; + /** 角色编号组 */ + roleIds?: number[]; +} + + +/** 角色信息 */ +export interface SysRole extends BaseEntity { + /** 角色编号 */ + roleId?: number; + /** 角色名称 */ + roleName?: string; + /** 角色权限 */ + roleKey?: string; + /** 角色排序 */ + roleSort?: number; + /** 数据范围(1:所有数据权限;2:自定义数据权限;3:本部门数据权限;4:本部门及以下数据权限;5:仅本人数据权限) */ + dataScope?: '1' | '2' | '3' | '4' | '5'; + /** 菜单树选择项是否关联显示 */ + menuCheckStrictly?: boolean; + /** 部门树选择项是否关联显示 */ + deptCheckStrictly?: boolean; + /** 角色权限 */ + menuIds?: number[]; + /** 角色权限 */ + deptIds?: number[]; + /** 状态(0正常 1停用) */ + status?: '0' | '1'; +} + +/** 角色部门树响应 */ +export interface RoleDeptTreeResult extends AjaxResult { + /** 选中部门ID */ + checkedKeys: number[] + /** 部门树列表 */ + depts: TreeSelect[] +} diff --git a/portal/frontend/portal/src/types/api/system/user.ts b/portal/frontend/portal/src/types/api/system/user.ts new file mode 100644 index 0000000..de0675f --- /dev/null +++ b/portal/frontend/portal/src/types/api/system/user.ts @@ -0,0 +1,107 @@ +import type { SysRole } from './role' +import type { SysDept } from './dept' +import type { SysPost } from './post' +import type { PageDomain, AjaxResult, BaseEntity } from "../common"; + +/** 用户分页查询参数 */ +export interface UserQueryParams extends PageDomain { + /** 用户名称 */ + userName?: string; + /** 手机号码 */ + phonenumber?: string; + /** 状态(0正常 1停用) */ + status?: '0' | '1'; + /** 部门编号 */ + deptId?: number; + /** 创建时间 */ + params?: { + beginTime?: string; + endTime?: string; + }; +} + +/** 角色授权用户分页查询参数 */ +export interface AuthUserQueryParams extends UserQueryParams { + /** 角色编号 */ + roleId?: number; +} + +/** 用户信息 */ +export interface SysUser extends BaseEntity { + /** 用户ID */ + userId?: number; + /** 部门ID */ + deptId?: number; + /** 用户账号 */ + userName?: string; + /** 用户昵称 */ + nickName?: string; + /** 用户邮箱 */ + email?: string; + /** 手机号码 */ + phonenumber?: string; + /** 用户性别(0男 1女 2未知) */ + sex?: '0' | '1' | '2'; + /** 用户头像 */ + avatar?: string; + /** 密码 */ + password?: string; + /** 账号状态(0正常 1停用) */ + status?: '0' | '1'; + /** 部门对象 */ + dept?: SysDept; + /** 角色对象 */ + roles?: SysRole[]; + /** 角色组 */ + roleIds?: number[]; + /** 岗位组 */ + postIds?: number[]; +} + +/** 注册信息 */ +export interface SysRegister { + /** 用户账号 */ + userName?: string; + /** 密码 */ + password?: string; + /** 验证码 */ + code?: string; + /** 唯一标识 */ + uuid?: string; +} + +/** 用户详情查询响应 */ +export interface UserFormDataResult extends AjaxResult { + /** 用户信息 */ + data?: SysUser + /** 用户的岗位ID列表 */ + postIds?: number[] + /** 用户的角色ID列表 */ + roleIds?: number[] + /** 所有角色列表 */ + roles: SysRole[] + /** 所有岗位列表 */ + posts: SysPost[] +} + +/** 用户个人资料响应 */ +export interface UserProfileResult extends AjaxResult { + /** 角色分组 */ + roleGroup: string + /** 岗位分组 */ + postGroup: string +} + +/** 用户头像上传响应 */ +export interface UserProfileAvatarResult extends AjaxResult { + /** 头像地址 */ + imgUrl: string +} + +/** 用户授权角色响应 */ +export interface UserAuthRoleResult extends AjaxResult { + /** 用户信息 */ + user: SysUser + /** 角色列表 */ + roles: SysRole[] +} diff --git a/portal/frontend/portal/src/types/api/tool/gen.ts b/portal/frontend/portal/src/types/api/tool/gen.ts new file mode 100644 index 0000000..08135f3 --- /dev/null +++ b/portal/frontend/portal/src/types/api/tool/gen.ts @@ -0,0 +1,112 @@ +import type { PageDomain, BaseEntity } from "../common"; + +/** 数据表分页查询参数 */ +export interface GenQueryParams extends PageDomain { + /** 表名称 */ + tableName?: string; + /** 表描述 */ + tableComment?: string; + /** 创建时间 */ + params?: { + beginTime?: string; + endTime?: string; + }; +} + +/** 代码生成业务信息 */ +export interface GenTable extends BaseEntity { + /** 主键编号 */ + tableId?: number; + /** 表名称 */ + tableName?: string; + /** 表描述 */ + tableComment?: string; + /** 关联父表的表名 */ + subTableName?: string; + /** 本表关联父表的外键名 */ + subTableFkName?: string; + /** 实体类名称(首字母大写) */ + className?: string; + /** 使用的模板(crud单表操作 tree树表操作 sub主子表操作) */ + tplCategory?: 'crud' | 'tree' | 'sub' + /** 前端类型(element-ui模版 element-plus模版) */ + tplWebType?: 'element-ui' | 'element-plus' + /** 生成包路径 */ + packageName?: string; + /** 生成模块名 */ + moduleName?: string; + /** 生成业务名 */ + businessName?: string; + /** 生成功能名 */ + functionName?: string; + /** 生成作者 */ + functionAuthor?: string; + /** 生成代码方式(0zip压缩包 1自定义路径) */ + genType?: '0' | '1' + /** 生成路径(不填默认项目路径) */ + genPath?: string; + /** 其它生成选项 */ + options?: string; + /** 树编码字段 */ + treeCode?: string; + /** 树父编码字段 */ + treeParentCode?: string; + /** 树名称字段 */ + treeName?: string; + /** 上级菜单ID字段 */ + parentMenuId?: string; + /** 上级菜单名称字段 */ + parentMenuName?: string; + /** 表列信息 */ + columns?: GenTableColumn[] +} + +/** 生成表列字段信息 */ +export interface GenTableColumn extends BaseEntity { + /** 主键编号 */ + columnId?: number + /** 归属表编号 */ + tableId?: number + /** 列名称 */ + columnName?: string + /** 列描述 */ + columnComment?: string + /** 列类型 */ + columnType?: string + /** Java类型 */ + javaType?: string + /** Java字段名 */ + javaField?: string + /** 是否主键(1是) */ + isPk?: '1' | '0' + /** 是否自增(1是) */ + isIncrement?: '1' | '0' + /** 是否必填(1是) */ + isRequired?: '1' | '0' + /** 是否为插入字段(1是) */ + isInsert?: '1' | '0' + /** 是否编辑字段(1是) */ + isEdit?: '1' | '0' + /** 是否列表字段(1是) */ + isList?: '1' | '0' + /** 是否查询字段(1是) */ + isQuery?: '1' | '0' + /** 查询方式(EQ等于、NE不等于、GT大于、LT小于、LIKE模糊、BETWEEN范围) */ + queryType?: 'EQ' | 'NE' | 'GT' | 'LT' | 'LIKE' | 'BETWEEN' + /** 显示类型(input文本框、textarea文本域、select下拉框、checkbox复选框、radio单选框、datetime日期控件、image图片上传控件、upload文件上传控件、editor富文本控件) */ + htmlType?: string + /** 字典类型 */ + dictType?: string + /** 排序 */ + sort?: number +} + +/** 代码生成信息响应 */ +export interface GenTableInfoResult { + /** 表信息 */ + info: GenTable + /** 列信息列表 */ + rows: GenTableColumn[] + /** 所有表信息 */ + tables: GenTable[] +} diff --git a/portal/frontend/portal/src/types/components.d.ts b/portal/frontend/portal/src/types/components.d.ts new file mode 100644 index 0000000..35d2077 --- /dev/null +++ b/portal/frontend/portal/src/types/components.d.ts @@ -0,0 +1,11 @@ +/* eslint-disable */ +// @ts-nocheck +// Generated by unplugin-vue-components +// Read more: https://github.com/vuejs/core/pull/3399 +export {} + +/* prettier-ignore */ +declare module 'vue' { + export interface GlobalComponents { + } +} diff --git a/portal/frontend/portal/src/types/global.d.ts b/portal/frontend/portal/src/types/global.d.ts new file mode 100644 index 0000000..4d55a97 --- /dev/null +++ b/portal/frontend/portal/src/types/global.d.ts @@ -0,0 +1,134 @@ +/** 模块类型声明 */ +declare module '*.vue' { + import type { DefineComponent } from 'vue' + const component: DefineComponent<{}, {}, any> + export default component +} + +/** Vite 环境变量类型 */ +interface ImportMetaEnv { + readonly VITE_APP_TITLE: string + readonly VITE_APP_BASE_API: string + readonly VITE_APP_ENV: string +} + +interface ImportMeta { + readonly env: ImportMetaEnv +} + +// element-plus +declare module 'element-plus' + +// vue +declare module 'vue' { + interface ComponentInternalInstance { proxy: any } +} + +// nprogress +declare module 'nprogress' { + export interface NProgressOptions { + minimum?: number + template?: string + easing?: string + speed?: number + trickle?: boolean + trickleSpeed?: number + showSpinner?: boolean + parent?: string + barSelector?: string + } + + export interface NProgress { + start(): NProgress + set(n: number): NProgress + inc(amount?: number): NProgress + done(force?: boolean): NProgress + remove(): void + configure(options: NProgressOptions): NProgress + status: number | null + } + + const nprogress: NProgress + export default nprogress +} + +// js-cookie +declare module 'js-cookie' { + export default Cookies +} + +// file-saver +declare module 'file-saver' { + export function saveAs(data: Blob | string, filename?: string, options?: any): void + export function saveAs(data: Blob | string, filename?: string, disableAutoBOM?: boolean): void + export default saveAs +} + +// jsencrypt +declare module 'jsencrypt/bin/jsencrypt.min' { + import JSEncrypt from 'jsencrypt' + export default JSEncrypt +} + +// axios +declare module 'axios' + +// vue-quill +declare module '@vueup/vue-quill' + +// sortablejs +declare module 'sortablejs' { + export interface SortableEvent { + oldIndex: number; + newIndex: number; + } + + export interface SortableOptions { + ghostClass?: string; + onEnd?: (evt: SortableEvent) => void; + } + + export default class Sortable { + static create(el: HTMLElement, options: SortableOptions): Sortable; + } +} + +// fuse +declare module 'fuse.js' { + export interface FuseOptions { + keys: string[]; + threshold?: number; + includeScore?: boolean; + includeMatches?: boolean; + minMatchCharLength?: number; + shouldSort?: boolean; + // 添加其他你需要的选项 + } + + export default class Fuse { + constructor(list: T[], options?: FuseOptions) + search(pattern: string): T[] + } +} + +// vuedraggable +declare module 'vuedraggable/dist/vuedraggable.common' { + import { DefineComponent } from 'vue' + const draggable: DefineComponent + export default draggable +} + +// vue-cropper +declare module 'vue-cropper' { + import { DefineComponent } from 'vue' + const VueCropper: DefineComponent + export { VueCropper } +} + +// splitpanes +declare module 'splitpanes' { + import { DefineComponent } from 'vue' + + export const Splitpanes: DefineComponent + export const Pane: DefineComponent +} diff --git a/portal/frontend/portal/src/types/index.ts b/portal/frontend/portal/src/types/index.ts new file mode 100644 index 0000000..792b1a3 --- /dev/null +++ b/portal/frontend/portal/src/types/index.ts @@ -0,0 +1,5 @@ +/** + * 类型统一导出 + */ + +export * from "./api"; diff --git a/portal/frontend/portal/src/utils/auth.ts b/portal/frontend/portal/src/utils/auth.ts new file mode 100644 index 0000000..f6c8be8 --- /dev/null +++ b/portal/frontend/portal/src/utils/auth.ts @@ -0,0 +1,14 @@ +import unifiedLoginSDK from "oauth2-login-sdk" +// const TokenKey = 'Admin-Token' + +export function getToken(): string | undefined { + return unifiedLoginSDK.getToken()||'' +} + +export function setToken(token: string): string | undefined { + return '' +} + +export function removeToken(): void { + +} diff --git a/portal/frontend/portal/src/utils/dict.ts b/portal/frontend/portal/src/utils/dict.ts new file mode 100644 index 0000000..5672bef --- /dev/null +++ b/portal/frontend/portal/src/utils/dict.ts @@ -0,0 +1,24 @@ +import useDictStore from '@/store/modules/dict' +import { getDicts } from '@/api/system/dict/data' + +/** + * 获取字典数据 + */ +export function useDict(...args: string[]) { + const res = ref>({}) + return (() => { + args.forEach((dictType) => { + res.value[dictType] = [] + const dicts = useDictStore().getDict(dictType) + if (dicts) { + res.value[dictType] = dicts + } else { + getDicts(dictType).then(resp => { + res.value[dictType] = (resp.data || []).map((p: any) => ({ label: p.dictLabel, value: p.dictValue, elTagType: p.listClass, elTagClass: p.cssClass })) + useDictStore().setDict(dictType, res.value[dictType]) + }) + } + }) + return toRefs(res.value) + })() +} diff --git a/portal/frontend/portal/src/utils/dynamicTitle.ts b/portal/frontend/portal/src/utils/dynamicTitle.ts new file mode 100644 index 0000000..66a503a --- /dev/null +++ b/portal/frontend/portal/src/utils/dynamicTitle.ts @@ -0,0 +1,14 @@ +import defaultSettings from '@/settings' +import useSettingsStore from '@/store/modules/settings' + +/** + * 动态修改标题 + */ +export function useDynamicTitle(): void { + const settingsStore = useSettingsStore() + if (settingsStore.dynamicTitle) { + document.title = settingsStore.title + ' - ' + defaultSettings.title + } else { + document.title = defaultSettings.title + } +} diff --git a/portal/frontend/portal/src/utils/errorCode.ts b/portal/frontend/portal/src/utils/errorCode.ts new file mode 100644 index 0000000..ab2832d --- /dev/null +++ b/portal/frontend/portal/src/utils/errorCode.ts @@ -0,0 +1,8 @@ +const errorCode: Record = { + '401': '认证失败,无法访问系统资源', + '403': '当前操作没有权限', + '404': '访问资源不存在', + 'default': '系统未知错误,请反馈给管理员' +} + +export default errorCode diff --git a/portal/frontend/portal/src/utils/generator/config.js b/portal/frontend/portal/src/utils/generator/config.js new file mode 100644 index 0000000..449715f --- /dev/null +++ b/portal/frontend/portal/src/utils/generator/config.js @@ -0,0 +1,452 @@ +export const formConf = { + formRef: 'formRef', + formModel: 'formData', + size: 'default', + labelPosition: 'right', + labelWidth: 100, + formRules: 'rules', + gutter: 15, + disabled: false, + span: 24, + formBtns: true, +} + +export const inputComponents = [ + { + label: '单行文本', + tag: 'el-input', + tagIcon: 'input', + type: 'text', + placeholder: '请输入', + defaultValue: undefined, + span: 24, + labelWidth: null, + style: { width: '100%' }, + clearable: true, + prepend: '', + append: '', + 'prefix-icon': '', + 'suffix-icon': '', + maxlength: null, + 'show-word-limit': false, + readonly: false, + disabled: false, + required: true, + regList: [], + changeTag: true, + document: 'https://element-plus.org/zh-CN/component/input', + }, + { + label: '多行文本', + tag: 'el-input', + tagIcon: 'textarea', + type: 'textarea', + placeholder: '请输入', + defaultValue: undefined, + span: 24, + labelWidth: null, + autosize: { + minRows: 4, + maxRows: 4, + }, + style: { width: '100%' }, + maxlength: null, + 'show-word-limit': false, + readonly: false, + disabled: false, + required: true, + regList: [], + changeTag: true, + document: 'https://element-plus.org/zh-CN/component/input', + }, + { + label: '密码', + tag: 'el-input', + tagIcon: 'password', + type: 'password', + placeholder: '请输入', + defaultValue: undefined, + span: 24, + 'show-password': true, + labelWidth: null, + style: { width: '100%' }, + clearable: true, + prepend: '', + append: '', + 'prefix-icon': '', + 'suffix-icon': '', + maxlength: null, + 'show-word-limit': false, + readonly: false, + disabled: false, + required: true, + regList: [], + changeTag: true, + document: 'https://element-plus.org/zh-CN/component/input', + }, + { + label: '计数器', + tag: 'el-input-number', + tagIcon: 'number', + placeholder: '', + defaultValue: undefined, + span: 24, + labelWidth: null, + min: undefined, + max: undefined, + step: undefined, + 'step-strictly': false, + precision: undefined, + 'controls-position': '', + disabled: false, + required: true, + regList: [], + changeTag: true, + document: 'https://element-plus.org/zh-CN/component/input-number', + }, +] + +export const selectComponents = [ + { + label: '下拉选择', + tag: 'el-select', + tagIcon: 'select', + placeholder: '请选择', + defaultValue: undefined, + span: 24, + labelWidth: null, + style: { width: '100%' }, + clearable: true, + disabled: false, + required: true, + filterable: false, + multiple: false, + options: [ + { + label: '选项一', + value: 1, + }, + { + label: '选项二', + value: 2, + }, + ], + regList: [], + changeTag: true, + document: 'https://element-plus.org/zh-CN/component/select', + }, + { + label: '级联选择', + tag: 'el-cascader', + tagIcon: 'cascader', + placeholder: '请选择', + defaultValue: [], + span: 24, + labelWidth: null, + style: { width: '100%' }, + props: { + props: { + multiple: false, + }, + }, + 'show-all-levels': true, + disabled: false, + clearable: true, + filterable: false, + required: true, + options: [ + { + id: 1, + value: 1, + label: '选项1', + children: [ + { + id: 2, + value: 2, + label: '选项1-1', + }, + ], + }, + ], + dataType: 'dynamic', + labelKey: 'label', + valueKey: 'value', + childrenKey: 'children', + separator: '/', + regList: [], + changeTag: true, + document: 'https://element-plus.org/zh-CN/component/cascader', + }, + { + label: '单选框组', + tag: 'el-radio-group', + tagIcon: 'radio', + defaultValue: 0, + span: 24, + labelWidth: null, + style: {}, + optionType: 'default', + border: false, + size: 'default', + disabled: false, + required: true, + options: [ + { + label: '选项一', + value: 1, + }, + { + label: '选项二', + value: 2, + }, + ], + regList: [], + changeTag: true, + document: 'https://element-plus.org/zh-CN/component/radio', + }, + { + label: '多选框组', + tag: 'el-checkbox-group', + tagIcon: 'checkbox', + defaultValue: [], + span: 24, + labelWidth: null, + style: {}, + optionType: 'default', + border: false, + size: 'default', + disabled: false, + required: true, + options: [ + { + label: '选项一', + value: 1, + }, + { + label: '选项二', + value: 2, + }, + ], + regList: [], + changeTag: true, + document: 'https://element-plus.org/zh-CN/component/checkbox', + }, + { + label: '开关', + tag: 'el-switch', + tagIcon: 'switch', + defaultValue: false, + span: 24, + labelWidth: null, + style: {}, + disabled: false, + required: true, + 'active-text': '', + 'inactive-text': '', + 'active-color': null, + 'inactive-color': null, + 'active-value': true, + 'inactive-value': false, + regList: [], + changeTag: true, + document: 'https://element-plus.org/zh-CN/component/switch', + }, + { + label: '滑块', + tag: 'el-slider', + tagIcon: 'slider', + defaultValue: null, + span: 24, + labelWidth: null, + disabled: false, + required: true, + min: 0, + max: 100, + step: 1, + 'show-stops': false, + range: false, + regList: [], + changeTag: true, + document: 'https://element-plus.org/zh-CN/component/slider', + }, + { + label: '时间选择', + tag: 'el-time-picker', + tagIcon: 'time', + placeholder: '请选择', + defaultValue: '', + span: 24, + labelWidth: null, + style: { width: '100%' }, + disabled: false, + clearable: true, + required: true, + format: 'HH:mm:ss', + 'value-format': 'HH:mm:ss', + regList: [], + changeTag: true, + document: 'https://element-plus.org/zh-CN/component/time-picker', + }, + { + label: '时间范围', + tag: 'el-time-picker', + tagIcon: 'time-range', + defaultValue: null, + span: 24, + labelWidth: null, + style: { width: '100%' }, + disabled: false, + clearable: true, + required: true, + 'is-range': true, + 'range-separator': '至', + 'start-placeholder': '开始时间', + 'end-placeholder': '结束时间', + format: 'HH:mm:ss', + 'value-format': 'HH:mm:ss', + regList: [], + changeTag: true, + document: 'https://element-plus.org/zh-CN/component/time-picker', + }, + { + label: '日期选择', + tag: 'el-date-picker', + tagIcon: 'date', + placeholder: '请选择', + defaultValue: null, + type: 'date', + span: 24, + labelWidth: null, + style: { width: '100%' }, + disabled: false, + clearable: true, + required: true, + format: 'YYYY-MM-DD', + 'value-format': 'YYYY-MM-DD', + readonly: false, + regList: [], + changeTag: true, + document: 'https://element-plus.org/zh-CN/component/date-picker', + }, + { + label: '日期范围', + tag: 'el-date-picker', + tagIcon: 'date-range', + defaultValue: null, + span: 24, + labelWidth: null, + style: { width: '100%' }, + type: 'daterange', + 'range-separator': '至', + 'start-placeholder': '开始日期', + 'end-placeholder': '结束日期', + disabled: false, + clearable: true, + required: true, + format: 'YYYY-MM-DD', + 'value-format': 'YYYY-MM-DD', + readonly: false, + regList: [], + changeTag: true, + document: 'https://element-plus.org/zh-CN/component/date-picker', + }, + { + label: '评分', + tag: 'el-rate', + tagIcon: 'rate', + defaultValue: 0, + span: 24, + labelWidth: null, + style: {}, + max: 5, + 'allow-half': false, + 'show-text': false, + 'show-score': false, + disabled: false, + required: true, + regList: [], + changeTag: true, + document: 'https://element-plus.org/zh-CN/component/rate', + }, + { + label: '颜色选择', + tag: 'el-color-picker', + tagIcon: 'color', + defaultValue: null, + labelWidth: null, + 'show-alpha': false, + 'color-format': '', + disabled: false, + required: true, + size: 'default', + regList: [], + changeTag: true, + document: 'https://element-plus.org/zh-CN/component/color-picker', + }, + { + label: '上传', + tag: 'el-upload', + tagIcon: 'upload', + action: 'https://jsonplaceholder.typicode.com/posts/', + defaultValue: null, + labelWidth: null, + disabled: false, + required: true, + accept: '', + name: 'file', + 'auto-upload': true, + showTip: false, + buttonText: '点击上传', + fileSize: 2, + sizeUnit: 'MB', + 'list-type': 'text', + multiple: false, + regList: [], + changeTag: true, + document: 'https://element-plus.org/zh-CN/component/upload', + tip: '只能上传不超过 2MB 的文件', + style: { width: '100%' }, + }, +] + +export const layoutComponents = [ + { + layout: 'rowFormItem', + tagIcon: 'row', + type: 'default', + justify: 'start', + align: 'top', + label: '行容器', + layoutTree: true, + children: [], + document: 'https://element-plus.org/zh-CN/component/layout', + }, + { + layout: 'colFormItem', + label: '按钮', + changeTag: true, + labelWidth: null, + tag: 'el-button', + tagIcon: 'button', + span: 24, + default: '主要按钮', + type: 'primary', + icon: 'Search', + size: 'default', + disabled: false, + document: 'https://element-plus.org/zh-CN/component/button', + }, +] + +// 组件rule的触发方式,无触发方式的组件不生成rule +export const trigger = { + 'el-input': 'blur', + 'el-input-number': 'blur', + 'el-select': 'change', + 'el-radio-group': 'change', + 'el-checkbox-group': 'change', + 'el-cascader': 'change', + 'el-time-picker': 'change', + 'el-date-picker': 'change', + 'el-rate': 'change', +} diff --git a/portal/frontend/portal/src/utils/generator/css.js b/portal/frontend/portal/src/utils/generator/css.js new file mode 100644 index 0000000..c1c62e6 --- /dev/null +++ b/portal/frontend/portal/src/utils/generator/css.js @@ -0,0 +1,18 @@ +const styles = { + 'el-rate': '.el-rate{display: inline-block; vertical-align: text-top;}', + 'el-upload': '.el-upload__tip{line-height: 1.2;}' +} + +function addCss(cssList, el) { + const css = styles[el.tag] + css && cssList.indexOf(css) === -1 && cssList.push(css) + if (el.children) { + el.children.forEach(el2 => addCss(cssList, el2)) + } +} + +export function makeUpCss(conf) { + const cssList = [] + conf.fields.forEach(el => addCss(cssList, el)) + return cssList.join('\n') +} diff --git a/portal/frontend/portal/src/utils/generator/drawingDefault.js b/portal/frontend/portal/src/utils/generator/drawingDefault.js new file mode 100644 index 0000000..3345c84 --- /dev/null +++ b/portal/frontend/portal/src/utils/generator/drawingDefault.js @@ -0,0 +1,37 @@ +export const drawingDefaultValue = [] + +export function initDrawingDefaultValue() { + if (drawingDefaultValue.length === 0) { + drawingDefaultValue.push({ + layout: 'colFormItem', + tagIcon: 'input', + label: '手机号', + vModel: 'mobile', + formId: 6, + tag: 'el-input', + placeholder: '请输入手机号', + defaultValue: '', + span: 24, + style: {width: '100%'}, + clearable: true, + prepend: '', + append: '', + 'prefix-icon': 'Cellphone', + 'suffix-icon': '', + maxlength: 11, + 'show-word-limit': true, + readonly: false, + disabled: false, + required: true, + changeTag: true, + regList: [{ + pattern: '/^1(3|4|5|7|8|9)\\d{9}$/', + message: '手机号格式错误' + }] + }) + } +} + +export function cleanDrawingDefaultValue() { + drawingDefaultValue.splice(0, drawingDefaultValue.length) +} diff --git a/portal/frontend/portal/src/utils/generator/html.js b/portal/frontend/portal/src/utils/generator/html.js new file mode 100644 index 0000000..4b29841 --- /dev/null +++ b/portal/frontend/portal/src/utils/generator/html.js @@ -0,0 +1,359 @@ +/* eslint-disable max-len */ +import { trigger } from './config' + +let confGlobal +let someSpanIsNot24 + +export function dialogWrapper(str) { + return ` + ${str} + + ` +} + +export function vueTemplate(str) { + return `` +} + +export function vueScript(str) { + return `` +} + +export function cssStyle(cssStr) { + return `` +} + +function buildFormTemplate(conf, child, type) { + let labelPosition = '' + if (conf.labelPosition !== 'right') { + labelPosition = `label-position="${conf.labelPosition}"` + } + const disabled = conf.disabled ? `:disabled="${conf.disabled}"` : '' + let str = ` + ${child} + ${buildFromBtns(conf, type)} + ` + if (someSpanIsNot24) { + str = ` + ${str} + ` + } + return str +} + +function buildFromBtns(conf, type) { + let str = '' + if (conf.formBtns && type === 'file') { + str = ` + 提交 + 重置 + ` + if (someSpanIsNot24) { + str = ` + ${str} + ` + } + } + return str +} + +// span不为24的用el-col包裹 +function colWrapper(element, str) { + if (someSpanIsNot24 || element.span !== 24) { + return ` + ${str} + ` + } + return str +} + +const layouts = { + colFormItem(element) { + let labelWidth = '' + if (element.labelWidth && element.labelWidth !== confGlobal.labelWidth) { + labelWidth = `label-width="${element.labelWidth}px"` + } + const required = !trigger[element.tag] && element.required ? 'required' : '' + const tagDom = tags[element.tag] ? tags[element.tag](element) : null + let str = ` + ${tagDom} + ` + str = colWrapper(element, str) + return str + }, + rowFormItem(element) { + const type = element.type === 'default' ? '' : `type="${element.type}"` + const justify = element.type === 'default' ? '' : `justify="${element.justify}"` + const align = element.type === 'default' ? '' : `align="${element.align}"` + const gutter = element.gutter ? `gutter="${element.gutter}"` : '' + const children = element.children.map(el => layouts[el.layout](el)) + let str = ` + ${children.join('\n')} + ` + str = colWrapper(element, str) + return str + } +} + +const tags = { + 'el-button': el => { + const { + tag, disabled + } = attrBuilder(el) + const type = el.type ? `type="${el.type}"` : '' + const icon = el.icon ? `icon="${el.icon}"` : '' + const size = el.size ? `size="${el.size}"` : '' + let child = buildElButtonChild(el) + + if (child) child = `\n${child}\n` // 换行 + return `<${el.tag} ${type} ${icon} ${size} ${disabled}>${child}` + }, + 'el-input': el => { + const { + disabled, vModel, clearable, placeholder, width + } = attrBuilder(el) + const maxlength = el.maxlength ? `:maxlength="${el.maxlength}"` : '' + const showWordLimit = el['show-word-limit'] ? 'show-word-limit' : '' + const readonly = el.readonly ? 'readonly' : '' + const prefixIcon = el['prefix-icon'] ? `prefix-icon='${el['prefix-icon']}'` : '' + const suffixIcon = el['suffix-icon'] ? `suffix-icon='${el['suffix-icon']}'` : '' + const showPassword = el['show-password'] ? 'show-password' : '' + const type = el.type ? `type="${el.type}"` : '' + const autosize = el.autosize && el.autosize.minRows + ? `:autosize="{minRows: ${el.autosize.minRows}, maxRows: ${el.autosize.maxRows}}"` + : '' + let child = buildElInputChild(el) + + if (child) child = `\n${child}\n` // 换行 + return `<${el.tag} ${vModel} ${type} ${placeholder} ${maxlength} ${showWordLimit} ${readonly} ${disabled} ${clearable} ${prefixIcon} ${suffixIcon} ${showPassword} ${autosize} ${width}>${child}` + }, + 'el-input-number': el => { + const { disabled, vModel, placeholder } = attrBuilder(el) + const controlsPosition = el['controls-position'] ? `controls-position=${el['controls-position']}` : '' + const min = el.min ? `:min='${el.min}'` : '' + const max = el.max ? `:max='${el.max}'` : '' + const step = el.step ? `:step='${el.step}'` : '' + const stepStrictly = el['step-strictly'] ? 'step-strictly' : '' + const precision = el.precision ? `:precision='${el.precision}'` : '' + + return `<${el.tag} ${vModel} ${placeholder} ${step} ${stepStrictly} ${precision} ${controlsPosition} ${min} ${max} ${disabled}>` + }, + 'el-select': el => { + const { + disabled, vModel, clearable, placeholder, width + } = attrBuilder(el) + const filterable = el.filterable ? 'filterable' : '' + const multiple = el.multiple ? 'multiple' : '' + let child = buildElSelectChild(el) + + if (child) child = `\n${child}\n` // 换行 + return `<${el.tag} ${vModel} ${placeholder} ${disabled} ${multiple} ${filterable} ${clearable} ${width}>${child}` + }, + 'el-radio-group': el => { + const { disabled, vModel } = attrBuilder(el) + const size = `size="${el.size}"` + let child = buildElRadioGroupChild(el) + + if (child) child = `\n${child}\n` // 换行 + return `<${el.tag} ${vModel} ${size} ${disabled}>${child}` + }, + 'el-checkbox-group': el => { + const { disabled, vModel } = attrBuilder(el) + const size = `size="${el.size}"` + const min = el.min ? `:min="${el.min}"` : '' + const max = el.max ? `:max="${el.max}"` : '' + let child = buildElCheckboxGroupChild(el) + + if (child) child = `\n${child}\n` // 换行 + return `<${el.tag} ${vModel} ${min} ${max} ${size} ${disabled}>${child}` + }, + 'el-switch': el => { + const { disabled, vModel } = attrBuilder(el) + const activeText = el['active-text'] ? `active-text="${el['active-text']}"` : '' + const inactiveText = el['inactive-text'] ? `inactive-text="${el['inactive-text']}"` : '' + const activeColor = el['active-color'] ? `active-color="${el['active-color']}"` : '' + const inactiveColor = el['inactive-color'] ? `inactive-color="${el['inactive-color']}"` : '' + const activeValue = el['active-value'] !== true ? `:active-value='${JSON.stringify(el['active-value'])}'` : '' + const inactiveValue = el['inactive-value'] !== false ? `:inactive-value='${JSON.stringify(el['inactive-value'])}'` : '' + + return `<${el.tag} ${vModel} ${activeText} ${inactiveText} ${activeColor} ${inactiveColor} ${activeValue} ${inactiveValue} ${disabled}>` + }, + 'el-cascader': el => { + const { + disabled, vModel, clearable, placeholder, width + } = attrBuilder(el) + const options = el.options ? `:options="${el.vModel}Options"` : '' + const props = el.props ? `:props="${el.vModel}Props"` : '' + const showAllLevels = el['show-all-levels'] ? '' : ':show-all-levels="false"' + const filterable = el.filterable ? 'filterable' : '' + const separator = el.separator === '/' ? '' : `separator="${el.separator}"` + + return `<${el.tag} ${vModel} ${options} ${props} ${width} ${showAllLevels} ${placeholder} ${separator} ${filterable} ${clearable} ${disabled}>` + }, + 'el-slider': el => { + const { disabled, vModel } = attrBuilder(el) + const min = el.min ? `:min='${el.min}'` : '' + const max = el.max ? `:max='${el.max}'` : '' + const step = el.step ? `:step='${el.step}'` : '' + const range = el.range ? 'range' : '' + const showStops = el['show-stops'] ? `:show-stops="${el['show-stops']}"` : '' + + return `<${el.tag} ${min} ${max} ${step} ${vModel} ${range} ${showStops} ${disabled}>` + }, + 'el-time-picker': el => { + const { + disabled, vModel, clearable, placeholder, width + } = attrBuilder(el) + const startPlaceholder = el['start-placeholder'] ? `start-placeholder="${el['start-placeholder']}"` : '' + const endPlaceholder = el['end-placeholder'] ? `end-placeholder="${el['end-placeholder']}"` : '' + const rangeSeparator = el['range-separator'] ? `range-separator="${el['range-separator']}"` : '' + const isRange = el['is-range'] ? 'is-range' : '' + const format = el.format ? `format="${el.format}"` : '' + const valueFormat = el['value-format'] ? `value-format="${el['value-format']}"` : '' + const pickerOptions = el['picker-options'] ? `:picker-options='${JSON.stringify(el['picker-options'])}'` : '' + + return `<${el.tag} ${vModel} ${isRange} ${format} ${valueFormat} ${pickerOptions} ${width} ${placeholder} ${startPlaceholder} ${endPlaceholder} ${rangeSeparator} ${clearable} ${disabled}>` + }, + 'el-date-picker': el => { + const { + disabled, vModel, clearable, placeholder, width + } = attrBuilder(el) + const startPlaceholder = el['start-placeholder'] ? `start-placeholder="${el['start-placeholder']}"` : '' + const endPlaceholder = el['end-placeholder'] ? `end-placeholder="${el['end-placeholder']}"` : '' + const rangeSeparator = el['range-separator'] ? `range-separator="${el['range-separator']}"` : '' + const format = el.format ? `format="${el.format}"` : '' + const valueFormat = el['value-format'] ? `value-format="${el['value-format']}"` : '' + const type = el.type === 'date' ? '' : `type="${el.type}"` + const readonly = el.readonly ? 'readonly' : '' + + return `<${el.tag} ${type} ${vModel} ${format} ${valueFormat} ${width} ${placeholder} ${startPlaceholder} ${endPlaceholder} ${rangeSeparator} ${clearable} ${readonly} ${disabled}>` + }, + 'el-rate': el => { + const { disabled, vModel } = attrBuilder(el) + const max = el.max ? `:max='${el.max}'` : '' + const allowHalf = el['allow-half'] ? 'allow-half' : '' + const showText = el['show-text'] ? 'show-text' : '' + const showScore = el['show-score'] ? 'show-score' : '' + + return `<${el.tag} ${vModel} ${allowHalf} ${showText} ${showScore} ${disabled}>` + }, + 'el-color-picker': el => { + const { disabled, vModel } = attrBuilder(el) + const size = `size="${el.size}"` + const showAlpha = el['show-alpha'] ? 'show-alpha' : '' + const colorFormat = el['color-format'] ? `color-format="${el['color-format']}"` : '' + + return `<${el.tag} ${vModel} ${size} ${showAlpha} ${colorFormat} ${disabled}>` + }, + 'el-upload': el => { + const disabled = el.disabled ? ':disabled=\'true\'' : '' + const action = el.action ? `:action="${el.vModel}Action"` : '' + const multiple = el.multiple ? 'multiple' : '' + const listType = el['list-type'] !== 'text' ? `list-type="${el['list-type']}"` : '' + const accept = el.accept ? `accept="${el.accept}"` : '' + const name = el.name !== 'file' ? `name="${el.name}"` : '' + const autoUpload = el['auto-upload'] === false ? ':auto-upload="false"' : '' + const beforeUpload = `:before-upload="${el.vModel}BeforeUpload"` + const fileList = `:file-list="${el.vModel}fileList"` + const ref = `ref="${el.vModel}"` + let child = buildElUploadChild(el) + + if (child) child = `\n${child}\n` // 换行 + return `<${el.tag} ${ref} ${fileList} ${action} ${autoUpload} ${multiple} ${beforeUpload} ${listType} ${accept} ${name} ${disabled}>${child}` + } +} + +function attrBuilder(el) { + return { + vModel: `v-model="${confGlobal.formModel}.${el.vModel}"`, + clearable: el.clearable ? 'clearable' : '', + placeholder: el.placeholder ? `placeholder="${el.placeholder}"` : '', + width: el.style && el.style.width ? ':style="{width: \'100%\'}"' : '', + disabled: el.disabled ? ':disabled=\'true\'' : '' + } +} + +// el-buttin 子级 +function buildElButtonChild(conf) { + const children = [] + if (conf.default) { + children.push(conf.default) + } + return children.join('\n') +} + +// el-input innerHTML +function buildElInputChild(conf) { + const children = [] + if (conf.prepend) { + children.push(``) + } + if (conf.append) { + children.push(``) + } + return children.join('\n') +} + +function buildElSelectChild(conf) { + const children = [] + if (conf.options && conf.options.length) { + children.push(``) + } + return children.join('\n') +} + +function buildElRadioGroupChild(conf) { + const children = [] + if (conf.options && conf.options.length) { + const tag = conf.optionType === 'button' ? 'el-radio-button' : 'el-radio' + const border = conf.border ? 'border' : '' + children.push(`<${tag} v-for="(item, index) in ${conf.vModel}Options" :key="index" :value="item.value" :disabled="item.disabled" ${border}>{{item.label}}`) + } + return children.join('\n') +} + +function buildElCheckboxGroupChild(conf) { + const children = [] + if (conf.options && conf.options.length) { + const tag = conf.optionType === 'button' ? 'el-checkbox-button' : 'el-checkbox' + const border = conf.border ? 'border' : '' + children.push(`<${tag} v-for="(item, index) in ${conf.vModel}Options" :key="index" :label="item.value" :value="item.label" :disabled="item.disabled" ${border} />`) + } + return children.join('\n') +} + +function buildElUploadChild(conf) { + const list = [] + if (conf['list-type'] === 'picture-card') list.push('') + else list.push(`${conf.buttonText}`) + if (conf.showTip) list.push(`
只能上传不超过 ${conf.fileSize}${conf.sizeUnit} 的${conf.accept}文件
`) + return list.join('\n') +} + +export function makeUpHtml(conf, type) { + const htmlList = [] + confGlobal = conf + someSpanIsNot24 = conf.fields.some(item => item.span !== 24) + conf.fields.forEach(el => { + htmlList.push(layouts[el.layout](el)) + }) + const htmlStr = htmlList.join('\n') + + let temp = buildFormTemplate(conf, htmlStr, type) + if (type === 'dialog') { + temp = dialogWrapper(temp) + } + confGlobal = null + return temp +} diff --git a/portal/frontend/portal/src/utils/generator/icon.json b/portal/frontend/portal/src/utils/generator/icon.json new file mode 100644 index 0000000..2d9999a --- /dev/null +++ b/portal/frontend/portal/src/utils/generator/icon.json @@ -0,0 +1 @@ +["platform-eleme","eleme","delete-solid","delete","s-tools","setting","user-solid","user","phone","phone-outline","more","more-outline","star-on","star-off","s-goods","goods","warning","warning-outline","question","info","remove","circle-plus","success","error","zoom-in","zoom-out","remove-outline","circle-plus-outline","circle-check","circle-close","s-help","help","minus","plus","check","close","picture","picture-outline","picture-outline-round","upload","upload2","download","camera-solid","camera","video-camera-solid","video-camera","message-solid","bell","s-cooperation","s-order","s-platform","s-fold","s-unfold","s-operation","s-promotion","s-home","s-release","s-ticket","s-management","s-open","s-shop","s-marketing","s-flag","s-comment","s-finance","s-claim","s-custom","s-opportunity","s-data","s-check","s-grid","menu","share","d-caret","caret-left","caret-right","caret-bottom","caret-top","bottom-left","bottom-right","back","right","bottom","top","top-left","top-right","arrow-left","arrow-right","arrow-down","arrow-up","d-arrow-left","d-arrow-right","video-pause","video-play","refresh","refresh-right","refresh-left","finished","sort","sort-up","sort-down","rank","loading","view","c-scale-to-original","date","edit","edit-outline","folder","folder-opened","folder-add","folder-remove","folder-delete","folder-checked","tickets","document-remove","document-delete","document-copy","document-checked","document","document-add","printer","paperclip","takeaway-box","search","monitor","attract","mobile","scissors","umbrella","headset","brush","mouse","coordinate","magic-stick","reading","data-line","data-board","pie-chart","data-analysis","collection-tag","film","suitcase","suitcase-1","receiving","collection","files","notebook-1","notebook-2","toilet-paper","office-building","school","table-lamp","house","no-smoking","smoking","shopping-cart-full","shopping-cart-1","shopping-cart-2","shopping-bag-1","shopping-bag-2","sold-out","sell","present","box","bank-card","money","coin","wallet","discount","price-tag","news","guide","male","female","thumb","cpu","link","connection","open","turn-off","set-up","chat-round","chat-line-round","chat-square","chat-dot-round","chat-dot-square","chat-line-square","message","postcard","position","turn-off-microphone","microphone","close-notification","bangzhu","time","odometer","crop","aim","switch-button","full-screen","copy-document","mic","stopwatch","medal-1","medal","trophy","trophy-1","first-aid-kit","discover","place","location","location-outline","location-information","add-location","delete-location","map-location","alarm-clock","timer","watch-1","watch","lock","unlock","key","service","mobile-phone","bicycle","truck","ship","basketball","football","soccer","baseball","wind-power","light-rain","lightning","heavy-rain","sunrise","sunrise-1","sunset","sunny","cloudy","partly-cloudy","cloudy-and-sunny","moon","moon-night","dish","dish-1","food","chicken","fork-spoon","knife-fork","burger","tableware","sugar","dessert","ice-cream","hot-water","water-cup","coffee-cup","cold-drink","goblet","goblet-full","goblet-square","goblet-square-full","refrigerator","grape","watermelon","cherry","apple","pear","orange","coffee","ice-tea","ice-drink","milk-tea","potato-strips","lollipop","ice-cream-square","ice-cream-round"] \ No newline at end of file diff --git a/portal/frontend/portal/src/utils/generator/js.js b/portal/frontend/portal/src/utils/generator/js.js new file mode 100644 index 0000000..dc38bfe --- /dev/null +++ b/portal/frontend/portal/src/utils/generator/js.js @@ -0,0 +1,370 @@ +import { titleCase } from '@/utils/index' +import { trigger } from './config' +// 文件大小设置 +const units = { + KB: '1024', + MB: '1024 / 1024', + GB: '1024 / 1024 / 1024', +} +/** + * @name: 生成js需要的数据 + * @description: 生成js需要的数据 + * @param {*} conf + * @param {*} type 弹窗或表单 + * @return {*} + */ +export function makeUpJs(conf, type) { + conf = JSON.parse(JSON.stringify(conf)) + const dataList = [] + const ruleList = [] + const optionsList = [] + const propsList = [] + const methodList = [] + const uploadVarList = [] + + conf.fields.forEach((el) => { + buildAttributes( + el, + dataList, + ruleList, + optionsList, + methodList, + propsList, + uploadVarList + ) + }) + + const script = buildexport( + conf, + type, + dataList.join('\n'), + ruleList.join('\n'), + optionsList.join('\n'), + uploadVarList.join('\n'), + propsList.join('\n'), + methodList.join('\n') + ) + + return script +} +/** + * @name: 生成参数 + * @description: 生成参数,包括表单数据表单验证数据,多选选项数据,上传数据等 + * @return {*} + */ +function buildAttributes( + el, + dataList, + ruleList, + optionsList, + methodList, + propsList, + uploadVarList +){ + buildData(el, dataList) + buildRules(el, ruleList) + + if (el.options && el.options.length) { + buildOptions(el, optionsList) + if (el.dataType === 'dynamic') { + const model = `${el.vModel}Options` + const options = titleCase(model) + buildOptionMethod(`get${options}`, model, methodList) + } + } + + if (el.props && el.props.props) { + buildProps(el, propsList) + } + + if (el.action && el.tag === 'el-upload') { + uploadVarList.push( + ` + // 上传请求路径 + const ${el.vModel}Action = ref('${el.action}') + // 上传文件列表 + const ${el.vModel}fileList = ref([])` + ) + methodList.push(buildBeforeUpload(el)) + if (!el['auto-upload']) { + methodList.push(buildSubmitUpload(el)) + } + } + + if (el.children) { + el.children.forEach((el2) => { + buildAttributes( + el2, + dataList, + ruleList, + optionsList, + methodList, + propsList, + uploadVarList + ) + }) + } +} +/** + * @name: 生成表单数据formData + * @description: 生成表单数据formData + * @param {*} conf + * @param {*} dataList 数据列表 + * @return {*} + */ +function buildData(conf, dataList) { + if (conf.vModel === undefined) return + let defaultValue + if (typeof conf.defaultValue === 'string' && !conf.multiple) { + defaultValue = `'${conf.defaultValue}'` + } else { + defaultValue = `${JSON.stringify(conf.defaultValue)}` + } + dataList.push(`${conf.vModel}: ${defaultValue},`) +} +/** + * @name: 生成表单验证数据rule + * @description: 生成表单验证数据rule + * @param {*} conf + * @param {*} ruleList 验证数据列表 + * @return {*} + */ +function buildRules(conf, ruleList) { + if (conf.vModel === undefined) return + const rules = [] + if (trigger[conf.tag]) { + if (conf.required) { + const type = Array.isArray(conf.defaultValue) ? "type: 'array'," : '' + let message = Array.isArray(conf.defaultValue) + ? `请至少选择一个${conf.vModel}` + : conf.placeholder + if (message === undefined) message = `${conf.label}不能为空` + rules.push( + `{ required: true, ${type} message: '${message}', trigger: '${ + trigger[conf.tag] + }' }` + ) + } + if (conf.regList && Array.isArray(conf.regList)) { + conf.regList.forEach((item) => { + if (item.pattern) { + rules.push( + `{ pattern: new RegExp(${item.pattern}), message: '${ + item.message + }', trigger: '${trigger[conf.tag]}' }` + ) + } + }) + } + ruleList.push(`${conf.vModel}: [${rules.join(',')}],`) + } +} +/** + * @name: 生成选项数据 + * @description: 生成选项数据,单选多选下拉等 + * @param {*} conf + * @param {*} optionsList 选项数据列表 + * @return {*} + */ +function buildOptions(conf, optionsList) { + if (conf.vModel === undefined) return + if (conf.dataType === 'dynamic') { + conf.options = [] + } + const str = `const ${conf.vModel}Options = ref(${JSON.stringify(conf.options)})` + optionsList.push(str) +} +/** + * @name: 生成方法 + * @description: 生成方法 + * @param {*} methodName 方法名 + * @param {*} model + * @param {*} methodList 方法列表 + * @return {*} + */ +function buildOptionMethod(methodName, model, methodList) { + const str = `function ${methodName}() { + // TODO 发起请求获取数据 + ${model}.value + }` + methodList.push(str) +} +/** + * @name: 生成表单组件需要的props设置 + * @description: 生成表单组件需要的props设置,如;级联组件 + * @param {*} conf + * @param {*} propsList + * @return {*} + */ +function buildProps(conf, propsList) { + if (conf.dataType === 'dynamic') { + conf.valueKey !== 'value' && (conf.props.props.value = conf.valueKey) + conf.labelKey !== 'label' && (conf.props.props.label = conf.labelKey) + conf.childrenKey !== 'children' && + (conf.props.props.children = conf.childrenKey) + } + const str = ` + // props设置 + const ${conf.vModel}Props = ref(${JSON.stringify(conf.props.props)})` + propsList.push(str) +} +/** + * @name: 生成上传组件的相关内容 + * @description: 生成上传组件的相关内容 + * @param {*} conf + * @return {*} + */ +function buildBeforeUpload(conf) { + const unitNum = units[conf.sizeUnit] + let rightSizeCode = '' + let acceptCode = '' + const returnList = [] + if (conf.fileSize) { + rightSizeCode = `let isRightSize = file.size / ${unitNum} < ${conf.fileSize} + if(!isRightSize){ + proxy.$modal.msgError('文件大小超过 ${conf.fileSize}${conf.sizeUnit}') + }` + returnList.push('isRightSize') + } + if (conf.accept) { + acceptCode = `let isAccept = new RegExp('${conf.accept}').test(file.type) + if(!isAccept){ + proxy.$modal.msgError('应该选择${conf.accept}类型的文件') + }` + returnList.push('isAccept') + } + const str = ` + /** + * @name: 上传之前的文件判断 + * @description: 上传之前的文件判断,判断文件大小文件类型等 + * @param {*} file + * @return {*} + */ + function ${conf.vModel}BeforeUpload(file) { + ${rightSizeCode} + ${acceptCode} + return ${returnList.join('&&')} + }` + return returnList.length ? str : '' +} +/** + * @name: 生成提交表单方法 + * @description: 生成提交表单方法 + * @param {Object} conf vModel 表单ref + * @return {*} + */ +function buildSubmitUpload(conf) { + const str = `function submitUpload() { + this.$refs['${conf.vModel}'].submit() + }` + return str +} +/** + * @name: 组装js代码 + * @description: 组装js代码方法 + * @return {*} + */ +function buildexport( + conf, + type, + data, + rules, + selectOptions, + uploadVar, + props, + methods +) { + let str = ` + const { proxy } = getCurrentInstance() + const ${conf.formRef} = ref() + const data = reactive({ + ${conf.formModel}: { + ${data} + }, + ${conf.formRules}: { + ${rules} + } + }) + + const {${conf.formModel}, ${conf.formRules}} = toRefs(data) + + ${selectOptions} + + ${uploadVar} + + ${props} + + ${methods} + ` + + if(type === 'dialog') { + str += ` + // 弹窗设置 + const dialogVisible = defineModel() + // 弹窗确认回调 + const emit = defineEmits(['confirm']) + /** + * @name: 弹窗打开后执行 + * @description: 弹窗打开后执行方法 + * @return {*} + */ + function onOpen(){ + + } + /** + * @name: 弹窗关闭时执行 + * @description: 弹窗关闭方法,重置表单 + * @return {*} + */ + function onClose(){ + ${conf.formRef}.value.resetFields() + } + /** + * @name: 弹窗取消 + * @description: 弹窗取消方法 + * @return {*} + */ + function close(){ + dialogVisible.value = false + } + /** + * @name: 弹窗表单提交 + * @description: 弹窗表单提交方法 + * @return {*} + */ + function handelConfirm(){ + ${conf.formRef}.value.validate((valid) => { + if (!valid) return + // TODO 提交表单 + + close() + // 回调父级组件 + emit('confirm') + }) + } + ` + } else { + str += ` + /** + * @name: 表单提交 + * @description: 表单提交方法 + * @return {*} + */ + function submitForm() { + ${conf.formRef}.value.validate((valid) => { + if (!valid) return + // TODO 提交表单 + }) + } + /** + * @name: 表单重置 + * @description: 表单重置方法 + * @return {*} + */ + function resetForm() { + ${conf.formRef}.value.resetFields() + } + ` + } + return str +} diff --git a/portal/frontend/portal/src/utils/generator/render.js b/portal/frontend/portal/src/utils/generator/render.js new file mode 100644 index 0000000..d6d4414 --- /dev/null +++ b/portal/frontend/portal/src/utils/generator/render.js @@ -0,0 +1,156 @@ +import { defineComponent, h } from 'vue' +import { makeMap } from '@/utils/index' + +const isAttr = makeMap( + 'accept,accept-charset,accesskey,action,align,alt,async,autocomplete,' + + 'autofocus,autoplay,autosave,bgcolor,border,buffered,challenge,charset,' + + 'checked,cite,class,code,codebase,color,cols,colspan,content,http-equiv,' + + 'name,contenteditable,contextmenu,controls,coords,data,datetime,default,' + + 'defer,dir,dirname,disabled,download,draggable,dropzone,enctype,method,for,' + + 'form,formaction,headers,height,hidden,high,href,hreflang,http-equiv,' + + 'icon,id,ismap,itemprop,keytype,kind,label,lang,language,list,loop,low,' + + 'manifest,max,maxlength,media,method,GET,POST,min,multiple,email,file,' + + 'muted,name,novalidate,open,optimum,pattern,ping,placeholder,poster,' + + 'preload,radiogroup,readonly,rel,required,reversed,rows,rowspan,sandbox,' + + 'scope,scoped,seamless,selected,shape,size,type,text,password,sizes,span,' + + 'spellcheck,src,srcdoc,srclang,srcset,start,step,style,summary,tabindex,' + + 'target,title,type,usemap,value,width,wrap' + 'prefix-icon' +) +const isNotProps = makeMap( + 'layout,prepend,regList,tag,document,changeTag,defaultValue' +) + +function useVModel(props, emit) { + return { + modelValue: props.defaultValue, + 'onUpdate:modelValue': (val) => emit('update:modelValue', val), + } +} +const componentChild = { + 'el-button': { + default(h, conf, key) { + return conf[key] + }, + }, + 'el-select': { + options(h, conf, key) { + return conf.options.map(item => h(resolveComponent('el-option'), { + label: item.label, + value: item.value, + })) + } + }, + 'el-radio-group': { + options(h, conf, key) { + return conf.optionType === 'button' ? conf.options.map(item => h(resolveComponent('el-checkbox-button'), { + label: item.value, + }, () => item.label)) : conf.options.map(item => h(resolveComponent('el-radio'), { + label: item.value, + border: conf.border, + }, () => item.label)) + } + }, + 'el-checkbox-group': { + options(h, conf, key) { + return conf.optionType === 'button' ? conf.options.map(item => h(resolveComponent('el-checkbox-button'), { + label: item.value, + }, () => item.label)) : conf.options.map(item => h(resolveComponent('el-checkbox'), { + label: item.value, + border: conf.border, + }, () => item.label)) + } + }, + 'el-upload': { + 'list-type': (h, conf, key) => { + const option = {} + // if (conf.showTip) { + // tip = h('div', { + // class: "el-upload__tip" + // }, () => '只能上传不超过' + conf.fileSize + conf.sizeUnit + '的' + conf.accept + '文件') + // } + if (conf['list-type'] === 'picture-card') { + return h(resolveComponent('el-icon'), option, () => h(resolveComponent('Plus'))) + } else { + // option.size = "small" + option.type = "primary" + option.icon = "Upload" + return h(resolveComponent('el-button'), option, () => conf.buttonText) + } + }, + + } +} +const componentSlot = { + 'el-upload': { + 'tip': (h, conf, key) => { + if (conf.showTip) { + return () => h('div', { + class: "el-upload__tip" + }, '只能上传不超过' + conf.fileSize + conf.sizeUnit + '的' + conf.accept + '文件') + } + }, + } +} +export default defineComponent({ + + // 使用 render 函数 + render() { + const dataObject = { + attrs: {}, + props: {}, + on: {}, + style: {} + } + const confClone = JSON.parse(JSON.stringify(this.conf)) + const children = [] + const slot = {} + const childObjs = componentChild[confClone.tag] + if (childObjs) { + Object.keys(childObjs).forEach(key => { + const childFunc = childObjs[key] + if (confClone[key]) { + children.push(childFunc(h, confClone, key)) + } + }) + } + const slotObjs = componentSlot[confClone.tag] + if (slotObjs) { + Object.keys(slotObjs).forEach(key => { + const childFunc = slotObjs[key] + if (confClone[key]) { + slot[key] = childFunc(h, confClone, key) + } + }) + } + Object.keys(confClone).forEach(key => { + const val = confClone[key] + if (dataObject[key]) { + dataObject[key] = val + } else if (isAttr(key)) { + dataObject.attrs[key] = val + } else if (!isNotProps(key)) { + dataObject.props[key] = val + } + }) + if(children.length > 0){ + slot.default = () => children + } + + return h(resolveComponent(this.conf.tag), + { + modelValue: this.$attrs.modelValue, + ...dataObject.props, + ...dataObject.attrs, + style: { + ...dataObject.style + }, + } + , slot ?? null) + }, + props: { + conf: { + type: Object, + required: true, + }, + } +}) \ No newline at end of file diff --git a/portal/frontend/portal/src/utils/index.ts b/portal/frontend/portal/src/utils/index.ts new file mode 100644 index 0000000..dae520e --- /dev/null +++ b/portal/frontend/portal/src/utils/index.ts @@ -0,0 +1,348 @@ +import { parseTime } from './platform' + +/** + * 表格时间格式化 + */ +export function formatDate(cellValue: any): string { + if (cellValue == null || cellValue == "") return "" + const date = new Date(cellValue) + const year = date.getFullYear() + const month = date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1 + const day = date.getDate() < 10 ? '0' + date.getDate() : date.getDate() + const hours = date.getHours() < 10 ? '0' + date.getHours() : date.getHours() + const minutes = date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes() + const seconds = date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds() + return year + '-' + month + '-' + day + ' ' + hours + ':' + minutes + ':' + seconds +} + +/** + * @param time + * @param option + * @returns {string} + */ +export function formatTime(time: number | string, option?: string): string { + if (('' + time).length === 10) { + time = parseInt(time as string) * 1000 + } else { + time = +time + } + const d = new Date(time) + const now = Date.now() + + const diff = (now - d.getTime()) / 1000 + + if (diff < 30) { + return '刚刚' + } else if (diff < 3600) { + // less 1 hour + return Math.ceil(diff / 60) + '分钟前' + } else if (diff < 3600 * 24) { + return Math.ceil(diff / 3600) + '小时前' + } else if (diff < 3600 * 24 * 2) { + return '1天前' + } + if (option) { + return parseTime(time, option) || '' + } else { + return ( + d.getMonth() + + 1 + + '月' + + d.getDate() + + '日' + + d.getHours() + + '时' + + d.getMinutes() + + '分' + ) + } +} + +/** + * @param url + * @returns {Object} + */ +export function getQueryObject(url?: string | null): Record { + url = url == null ? window.location.href : url + const search = url.substring(url.lastIndexOf('?') + 1) + const obj: Record = {} + const reg = /([^?&=]+)=([^?&=]*)/g + search.replace(reg, (rs, $1: string, $2: string) => { + const name = decodeURIComponent($1) + let val = decodeURIComponent($2) + val = String(val) + obj[name] = val + return rs + }) + return obj +} + +/** + * @param input value + * @returns {number} output value + */ +export function byteLength(str: string): number { + // returns the byte length of an utf8 string + let s = str.length + for (let i = str.length - 1; i >= 0; i--) { + const code = str.charCodeAt(i) + if (code > 0x7f && code <= 0x7ff) s++ + else if (code > 0x7ff && code <= 0xffff) s += 2 + if (code >= 0xDC00 && code <= 0xDFFF) i-- + } + return s +} + +/** + * @param actual + * @returns {Array} + */ +export function cleanArray(actual: any[]): any[] { + const newArray: any[] = [] + for (let i = 0; i < actual.length; i++) { + if (actual[i]) { + newArray.push(actual[i]) + } + } + return newArray +} + +/** + * @param json + * @returns {Array} + */ +export function param(json: Record): string { + if (!json) return '' + return cleanArray( + Object.keys(json).map(key => { + if (json[key] === undefined) return '' + return encodeURIComponent(key) + '=' + encodeURIComponent(json[key]) + }) + ).join('&') +} + +/** + * @param url + * @returns {Object} + */ +export function param2Obj(url: string): Record { + const search = decodeURIComponent(url.split('?')[1]).replace(/\+/g, ' ') + if (!search) { + return {} + } + const obj: Record = {} + const searchArr = search.split('&') + searchArr.forEach(v => { + const index = v.indexOf('=') + if (index !== -1) { + const name = v.substring(0, index) + const val = v.substring(index + 1, v.length) + obj[name] = val + } + }) + return obj +} + +/** + * @param val + * @returns {string} + */ +export function html2Text(val: string): string { + const div = document.createElement('div') + div.innerHTML = val + return div.textContent || div.innerText || '' +} + +/** + * Merges two objects, giving the last one precedence + * @param target + * @param source + * @returns {Object} + */ +export function objectMerge(target: any, source: any): any { + if (typeof target !== 'object') { + target = {} + } + if (Array.isArray(source)) { + return source.slice() + } + Object.keys(source).forEach(property => { + const sourceProperty = source[property] + if (typeof sourceProperty === 'object') { + target[property] = objectMerge(target[property], sourceProperty) + } else { + target[property] = sourceProperty + } + }) + return target +} + +/** + * @param element + * @param className + */ +export function toggleClass(element: HTMLElement, className: string): void { + if (!element || !className) { + return + } + let classString = element.className + const nameIndex = classString.indexOf(className) + if (nameIndex === -1) { + classString += ' ' + className + } else { + classString = + classString.substr(0, nameIndex) + + classString.substr(nameIndex + className.length) + } + element.className = classString +} + +/** + * @param type + * @returns {Date} + */ +export function getTime(type?: string): number { + if (type === 'start') { + return new Date().getTime() - 3600 * 1000 * 24 * 90 + } else { + return new Date(new Date().toDateString()).getTime() + } +} + +/** + * This is just a simple version of deep copy + * Has a lot of edge cases bug + * If you want to use a perfect deep copy, use lodash's _.cloneDeep + * @param source + * @returns {Object} + */ +export function deepClone(source: T): T { + if (!source && typeof source !== 'object') { + throw new Error('error arguments') + } + const targetObj = (source as any).constructor === Array ? [] : {} + Object.keys(source as any).forEach(keys => { + if ((source as any)[keys] && typeof (source as any)[keys] === 'object') { + (targetObj as any)[keys] = deepClone((source as any)[keys]) + } else { + (targetObj as any)[keys] = (source as any)[keys] + } + }) + return targetObj as T +} + +/** + * @param arr + * @returns {Array} + */ +export function uniqueArr(arr: T[]): T[] { + return Array.from(new Set(arr)) +} + +/** + * @returns {string} + */ +export function createUniqueString(): string { + const timestamp = +new Date() + '' + const randomNum = parseInt((1 + Math.random()) * 65536 + '') + '' + return (+(randomNum + timestamp)).toString(32) +} + +/** + * Check if an element has a class + * @param elm + * @param cls + * @returns {boolean} + */ +export function hasClass(ele: HTMLElement, cls: string): boolean { + return !!ele.className.match(new RegExp('(\\s|^)' + cls + '(\\s|$)')) +} + +/** + * Add class to element + * @param ele + * @param cls + */ +export function addClass(ele: HTMLElement, cls: string): void { + if (!hasClass(ele, cls)) ele.className += ' ' + cls +} + +/** + * Remove class from element + * @param ele + * @param cls + */ +export function removeClass(ele: HTMLElement, cls: string): void { + if (hasClass(ele, cls)) { + const reg = new RegExp('(\\s|^)' + cls + '(\\s|$)') + ele.className = ele.className.replace(reg, ' ') + } +} + +export function makeMap(str: string, expectsLowerCase?: boolean): (val: string) => boolean { + const map = Object.create(null) + const list = str.split(',') + for (let i = 0; i < list.length; i++) { + map[list[i]] = true + } + return expectsLowerCase + ? (val: string) => map[val.toLowerCase()] + : (val: string) => map[val] +} + +export const exportDefault = 'export default ' + +export const beautifierConf = { + html: { + indent_size: '2', + indent_char: ' ', + max_preserve_newlines: '-1', + preserve_newlines: false, + keep_array_indentation: false, + break_chained_methods: false, + indent_scripts: 'separate', + brace_style: 'end-expand', + space_before_conditional: true, + unescape_strings: false, + jslint_happy: false, + end_with_newline: true, + wrap_line_length: '110', + indent_inner_html: true, + comma_first: false, + e4x: true, + indent_empty_lines: true + }, + js: { + indent_size: '2', + indent_char: ' ', + max_preserve_newlines: '-1', + preserve_newlines: false, + keep_array_indentation: false, + break_chained_methods: false, + indent_scripts: 'normal', + brace_style: 'end-expand', + space_before_conditional: true, + unescape_strings: false, + jslint_happy: true, + end_with_newline: true, + wrap_line_length: '110', + indent_inner_html: true, + comma_first: false, + e4x: true, + indent_empty_lines: true + } +} + +// 首字母大小 +export function titleCase(str: string): string { + return str.replace(/( |^)[a-z]/g, L => L.toUpperCase()) +} + +// 下划转驼峰 +export function camelCase(str: string): string { + return str.replace(/_[a-z]/g, str1 => str1.substr(-1).toUpperCase()) +} + +export function isNumberStr(str: string): boolean { + return /^[+-]?(0|([1-9]\d*))(\.\d+)?$/g.test(str) +} diff --git a/portal/frontend/portal/src/utils/jsencrypt.ts b/portal/frontend/portal/src/utils/jsencrypt.ts new file mode 100644 index 0000000..1f832cb --- /dev/null +++ b/portal/frontend/portal/src/utils/jsencrypt.ts @@ -0,0 +1,29 @@ +import JSEncrypt from 'jsencrypt/bin/jsencrypt.min' + +// 密钥对生成 http://web.chacuo.net/netrsakeypair + +const publicKey = 'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdH\n' + + 'nzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ==' + +const privateKey = 'MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAqhHyZfSsYourNxaY\n' + + '7Nt+PrgrxkiA50efORdI5U5lsW79MmFnusUA355oaSXcLhu5xxB38SMSyP2KvuKN\n' + + 'PuH3owIDAQABAkAfoiLyL+Z4lf4Myxk6xUDgLaWGximj20CUf+5BKKnlrK+Ed8gA\n' + + 'kM0HqoTt2UZwA5E2MzS4EI2gjfQhz5X28uqxAiEA3wNFxfrCZlSZHb0gn2zDpWow\n' + + 'cSxQAgiCstxGUoOqlW8CIQDDOerGKH5OmCJ4Z21v+F25WaHYPxCFMvwxpcw99Ecv\n' + + 'DQIgIdhDTIqD2jfYjPTY8Jj3EDGPbH2HHuffvflECt3Ek60CIQCFRlCkHpi7hthh\n' + + 'YhovyloRYsM+IS9h/0BzlEAuO0ktMQIgSPT3aFAgJYwKpqRYKlLDVcflZFCKY7u3\n' + + 'UP8iWi1Qw0Y=' + +// 加密 +export function encrypt(txt: string): string | false { + const encryptor = new JSEncrypt() + encryptor.setPublicKey(publicKey) // 设置公钥 + return encryptor.encrypt(txt) // 对数据进行加密 +} + +// 解密 +export function decrypt(txt: string): string | false { + const encryptor = new JSEncrypt() + encryptor.setPrivateKey(privateKey) // 设置私钥 + return encryptor.decrypt(txt) // 对数据进行解密 +} diff --git a/portal/frontend/portal/src/utils/permission.ts b/portal/frontend/portal/src/utils/permission.ts new file mode 100644 index 0000000..df75cf3 --- /dev/null +++ b/portal/frontend/portal/src/utils/permission.ts @@ -0,0 +1,51 @@ +import useUserStore from '@/store/modules/user' + +/** + * 字符权限校验 + * @param value 校验值 + * @returns {Boolean} + */ +export function checkPermi(value: string[]): boolean { + if (value && value instanceof Array && value.length > 0) { + const permissions = useUserStore().permissions + const permissionDatas = value + const all_permission = "*:*:*" + + const hasPermission = permissions.some((permission: string) => { + return all_permission === permission || permissionDatas.includes(permission) + }) + + if (!hasPermission) { + return false + } + return true + } else { + console.error(`need roles! Like checkPermi="['system:user:add','system:user:edit']"`) + return false + } +} + +/** + * 角色权限校验 + * @param value 校验值 + * @returns {Boolean} + */ +export function checkRole(value: string[]): boolean { + if (value && value instanceof Array && value.length > 0) { + const roles = useUserStore().roles + const permissionRoles = value + const super_admin = "admin" + + const hasRole = roles.some((role: string) => { + return super_admin === role || permissionRoles.includes(role) + }) + + if (!hasRole) { + return false + } + return true + } else { + console.error(`need roles! Like checkRole="['admin','editor']"`) + return false + } +} diff --git a/portal/frontend/portal/src/utils/platform.ts b/portal/frontend/portal/src/utils/platform.ts new file mode 100644 index 0000000..3dbf5d2 --- /dev/null +++ b/portal/frontend/portal/src/utils/platform.ts @@ -0,0 +1,230 @@ +/** + * 通用js方法封装处理 + * Copyright (c) 2026 lingniu + */ + +// 日期格式化 +export function parseTime(time: any, pattern?: string): string | null { + if (arguments.length === 0 || !time) { + return null + } + const format = pattern || '{y}-{m}-{d} {h}:{i}:{s}' + let date: Date + if (typeof time === 'object') { + date = time + } else { + if ((typeof time === 'string') && (/^[0-9]+$/.test(time))) { + time = parseInt(time) + } else if (typeof time === 'string') { + time = time.replace(new RegExp(/-/gm), '/').replace('T', ' ').replace(new RegExp(/\.[\d]{3}/gm), '') + } + if ((typeof time === 'number') && (time.toString().length === 10)) { + time = time * 1000 + } + date = new Date(time) + } + const formatObj: Record = { + y: date.getFullYear(), + m: date.getMonth() + 1, + d: date.getDate(), + h: date.getHours(), + i: date.getMinutes(), + s: date.getSeconds(), + a: date.getDay() + } + const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => { + let value = formatObj[key] + // Note: getDay() returns 0 on Sunday + if (key === 'a') { return ['日', '一', '二', '三', '四', '五', '六'][value] } + if (result.length > 0 && value < 10) { + value = '0' + value + } + return value || 0 + }) + return time_str +} + +// 表单重置 +export function resetForm(refName: string): void { + // @ts-ignore + if ((this as any).$refs[refName]) { + // @ts-ignore + (this as any).$refs[refName].resetFields() + } +} + +// 添加日期范围 +export function addDateRange(params: any, dateRange: string[], propName?: string): any { + let search = params + search.params = typeof (search.params) === 'object' && search.params !== null && !Array.isArray(search.params) ? search.params : {} + dateRange = Array.isArray(dateRange) ? dateRange : [] + if (typeof (propName) === 'undefined') { + search.params['beginTime'] = dateRange[0] + search.params['endTime'] = dateRange[1] + } else { + search.params['begin' + propName] = dateRange[0] + search.params['end' + propName] = dateRange[1] + } + return search +} + +// 回显数据字典 +export function selectDictLabel(datas: any, value: any): string { + if (value === undefined) { + return "" + } + const actions: string[] = [] + Object.keys(datas).some((key) => { + if (datas[key].value == ('' + value)) { + actions.push(datas[key].label) + return true + } + }) + if (actions.length === 0) { + actions.push(value) + } + return actions.join('') +} + +// 回显数据字典(字符串、数组) +export function selectDictLabels(datas: any, value: any, separator?: string): string { + if (value === undefined || value.length === 0) { + return "" + } + if (Array.isArray(value)) { + value = value.join(",") + } + const actions: string[] = [] + const currentSeparator = undefined === separator ? "," : separator + const temp = value.split(currentSeparator) + Object.keys(value.split(currentSeparator)).some((val) => { + let match = false + Object.keys(datas).some((key) => { + if (datas[key].value == ('' + temp[val])) { + actions.push(datas[key].label + currentSeparator) + match = true + } + }) + if (!match) { + actions.push(temp[val] + currentSeparator) + } + }) + return actions.join('').substring(0, actions.join('').length - 1) +} + +// 字符串格式化(%s ) +export function sprintf(str: string, ...args: any[]): string { + let flag = true, i = 1 + str = str.replace(/%s/g, function () { + const arg = args[i++] + if (typeof arg === 'undefined') { + flag = false + return '' + } + return arg + }) + return flag ? str : '' +} + +// 转换字符串,undefined,null等转化为"" +export function parseStrEmpty(str: any): string { + if (!str || str == "undefined" || str == "null") { + return "" + } + return str +} + +// 数据合并 +export function mergeRecursive(source: any, target: any): any { + for (const p in target) { + try { + if (target[p].constructor == Object) { + source[p] = mergeRecursive(source[p], target[p]) + } else { + source[p] = target[p] + } + } catch (e) { + source[p] = target[p] + } + } + return source +} + +/** + * 构造树型结构数据 + * @param data 数据源 + * @param id id字段 默认 'id' + * @param parentId 父节点字段 默认 'parentId' + * @param children 孩子节点字段 默认 'children' + */ +export function handleTree(data: any[], id?: string, parentId?: string, children?: string): any[] { + const config = { + id: id || 'id', + parentId: parentId || 'parentId', + childrenList: children || 'children' + } + + const childrenListMap: Record = {} + const tree: any[] = [] + for (const d of data) { + const id = d[config.id] + childrenListMap[id] = d + if (!d[config.childrenList]) { + d[config.childrenList] = [] + } + } + + for (const d of data) { + const parentId = d[config.parentId] + const parentObj = childrenListMap[parentId] + if (!parentObj) { + tree.push(d) + } else { + parentObj[config.childrenList].push(d) + } + } + return tree +} + +/** +* 参数处理 +* @param params 参数 +*/ +export function tansParams(params: Record): string { + let result = '' + for (const propName of Object.keys(params)) { + const value = params[propName] + const part = encodeURIComponent(propName) + "=" + if (value !== null && value !== "" && typeof (value) !== "undefined") { + if (typeof value === 'object') { + for (const key of Object.keys(value)) { + if (value[key] !== null && value[key] !== "" && typeof (value[key]) !== 'undefined') { + const params = propName + '[' + key + ']' + const subPart = encodeURIComponent(params) + "=" + result += subPart + encodeURIComponent(value[key]) + "&" + } + } + } else { + result += part + encodeURIComponent(value) + "&" + } + } + } + return result +} + +// 返回项目路径 +export function getNormalPath(p: string): string { + if (p.length === 0 || !p || p == 'undefined') { + return p + } + let res = p.replace('//', '/') + if (res[res.length - 1] === '/') { + return res.slice(0, res.length - 1) + } + return res +} + +// 验证是否为blob格式 +export function blobValidate(data: Blob): boolean { + return data.type !== 'application/json' +} diff --git a/portal/frontend/portal/src/utils/request.ts b/portal/frontend/portal/src/utils/request.ts new file mode 100644 index 0000000..79cc4cb --- /dev/null +++ b/portal/frontend/portal/src/utils/request.ts @@ -0,0 +1,154 @@ +import axios from 'axios' +import { ElNotification , ElMessageBox, ElMessage, ElLoading } from 'element-plus' +import { getToken } from '@/utils/auth' +import errorCode from '@/utils/errorCode' +import { tansParams, blobValidate } from '@/utils/platform' +import cache from '@/plugins/cache' +import { saveAs } from 'file-saver' +import useUserStore from '@/store/modules/user' +import unifiedLoginSDK from "oauth2-login-sdk"; + +let downloadLoadingInstance: ReturnType +// 是否显示重新登录 +export const isRelogin = { show: false } + +axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8' +// 创建axios实例 +const service = axios.create({ + // axios中请求配置有baseURL选项,表示请求URL公共部分 + baseURL: import.meta.env.VITE_APP_BASE_API, + // 超时 + timeout: 10000 +}) + +// request拦截器 +service.interceptors.request.use((config: any) => { + // 是否需要设置 token + const isToken = (config.headers || {}).isToken === false + // 是否需要防止数据重复提交 + const isRepeatSubmit = (config.headers || {}).repeatSubmit === false + // 间隔时间(ms),小于此时间视为重复提交 + const interval = (config.headers || {}).interval || 1000 + if (getToken() && !isToken) { + config.headers['Authorization'] = getToken() // 让每个请求携带自定义token 请根据实际情况自行修改 + } + // get请求映射params参数 + if (config.method === 'get' && config.params) { + let url = config.url + '?' + tansParams(config.params) + url = url.slice(0, -1) + config.params = {} + config.url = url + } + if (!isRepeatSubmit && (config.method === 'post' || config.method === 'put')) { + const requestObj = { + url: config.url, + data: typeof config.data === 'object' ? JSON.stringify(config.data) : config.data, + time: new Date().getTime() + } + const requestSize = Object.keys(JSON.stringify(requestObj)).length // 请求数据大小 + const limitSize = 5 * 1024 * 1024 // 限制存放数据5M + if (requestSize >= limitSize) { + console.warn(`[${config.url}]: ` + '请求数据大小超出允许的5M限制,无法进行防重复提交验证。') + return config + } + const sessionObj = cache.session.getJSON('sessionObj') + if (sessionObj === undefined || sessionObj === null || sessionObj === '') { + cache.session.setJSON('sessionObj', requestObj) + } else { + const s_url = sessionObj.url // 请求地址 + const s_data = sessionObj.data // 请求数据 + const s_time = sessionObj.time // 请求时间 + if (s_data === requestObj.data && requestObj.time - s_time < interval && s_url === requestObj.url) { + const message = '数据正在处理,请勿重复提交' + console.warn(`[${s_url}]: ` + message) + return Promise.reject(new Error(message)) + } else { + cache.session.setJSON('sessionObj', requestObj) + } + } + } + return config +}, (error: any) => { + console.log(error) + Promise.reject(error) +}) + +// 响应拦截器 +service.interceptors.response.use((res: any) => { + // 未设置状态码则默认成功状态 + const code = res.data.code || 200 + // 获取错误信息 + const msg = errorCode[code] || res.data.msg || errorCode['default'] + // 二进制数据则直接返回 + if (res.request.responseType === 'blob' || res.request.responseType === 'arraybuffer') { + return res.data + } + if (code === 401) { + if (!isRelogin.show) { + isRelogin.show = true + ElMessageBox.confirm('登录状态已过期,您可以继续留在该页面,或者重新登录', '系统提示', { confirmButtonText: '重新登录', cancelButtonText: '取消', type: 'warning' }).then(() => { + isRelogin.show = false + useUserStore().logOut().then(() => { + location.href = '/index' + }) + }).catch(() => { + isRelogin.show = false + }) + } + return Promise.reject('无效的会话,或者会话已过期,请重新登录。') + } else if (code === 500) { + ElMessage({ message: msg, type: 'error' }) + return Promise.reject(new Error(msg)) + } else if (code === 601) { + ElMessage({ message: msg, type: 'warning' }) + return Promise.reject(new Error(msg)) + } else if (code !== 200) { + ElNotification.error({ title: msg }) + return Promise.reject('error') + } else { + return Promise.resolve(res.data) + } + }, + (error: any) => { + console.log('err' + error) + let { message } = error + if (message == "Network Error") { + message = "后端接口连接异常" + } else if (message.includes("timeout")) { + message = "系统接口请求超时" + } else if (message.includes("Request failed with status code")) { + message = "系统接口" + message.slice(-3) + "异常" + } + ElMessage({ message: message, type: 'error', duration: 5 * 1000 }) + return Promise.reject(error) + } +) + +// 通用下载方法 +export function download(url: string, params: any, filename: string, config?: any) { + downloadLoadingInstance = ElLoading.service({ text: "正在下载数据,请稍候", background: "rgba(0, 0, 0, 0.7)", }) + return service.post(url, params, { + transformRequest: [(params: any) => { return tansParams(params) }], + headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, + responseType: 'blob', + ...config + }).then(async (data: any) => { + const isBlob = blobValidate(data) + if (isBlob) { + const blob = new Blob([data]) + saveAs(blob, filename) + } else { + const resText = await data.text() + const rspObj = JSON.parse(resText) + const errMsg = errorCode[rspObj.code] || rspObj.msg || errorCode['default'] + ElMessage.error(errMsg) + } + downloadLoadingInstance.close() + }).catch((r: any) => { + console.error(r) + ElMessage.error('下载文件出现错误,请联系管理员!') + downloadLoadingInstance.close() + }) +} + +export default service diff --git a/portal/frontend/portal/src/utils/scroll-to.ts b/portal/frontend/portal/src/utils/scroll-to.ts new file mode 100644 index 0000000..9078347 --- /dev/null +++ b/portal/frontend/portal/src/utils/scroll-to.ts @@ -0,0 +1,64 @@ +declare global { + interface Math { + easeInOutQuad(t: number, b: number, c: number, d: number): number + } +} + +Math.easeInOutQuad = function(t: number, b: number, c: number, d: number): number { + t /= d / 2 + if (t < 1) { + return c / 2 * t * t + b + } + t-- + return -c / 2 * (t * (t - 2) - 1) + b +} + +// requestAnimationFrame for Smart Animating http://goo.gl/sx5sts +const requestAnimFrame = (function() { + return window.requestAnimationFrame || (window as any).webkitRequestAnimationFrame || (window as any).mozRequestAnimationFrame || function(callback: FrameRequestCallback) { window.setTimeout(callback, 1000 / 60) } +})() + +/** + * Because it's so fucking difficult to detect the scrolling element, just move them all + * @param amount + */ +function move(amount: number): void { + document.documentElement.scrollTop = amount + ;(document.body.parentNode as HTMLElement).scrollTop = amount + document.body.scrollTop = amount +} + +function position(): number { + return document.documentElement.scrollTop || (document.body.parentNode as HTMLElement).scrollTop || document.body.scrollTop +} + +/** + * @param to + * @param duration + * @param callback + */ +export function scrollTo(to: number, duration?: number, callback?: () => void): void { + const start = position() + const change = to - start + const increment = 20 + let currentTime = 0 + duration = (typeof (duration) === 'undefined') ? 500 : duration + const animateScroll = function() { + // increment the time + currentTime += increment + // find the value with the quadratic in-out easing function + const val = Math.easeInOutQuad(currentTime, start, change, duration!) + // move the document.body + move(val) + // do the animation unless its over + if (currentTime < duration) { + requestAnimFrame(animateScroll) + } else { + if (callback && typeof (callback) === 'function') { + // the animation is done so lets callback + callback() + } + } + } + animateScroll() +} diff --git a/portal/frontend/portal/src/utils/theme.ts b/portal/frontend/portal/src/utils/theme.ts new file mode 100644 index 0000000..3e2f518 --- /dev/null +++ b/portal/frontend/portal/src/utils/theme.ts @@ -0,0 +1,49 @@ +// 处理主题样式 +export function handleThemeStyle(theme: string): void { + document.documentElement.style.setProperty('--el-color-primary', theme) + for (let i = 1; i <= 9; i++) { + document.documentElement.style.setProperty(`--el-color-primary-light-${i}`, `${getLightColor(theme, i / 10)}`) + } + for (let i = 1; i <= 9; i++) { + document.documentElement.style.setProperty(`--el-color-primary-dark-${i}`, `${getDarkColor(theme, i / 10)}`) + } +} + +// hex颜色转rgb颜色 +export function hexToRgb(str: string): number[] { + str = str.replace('#', '') + const hexs = str.match(/../g) || [] + for (let i = 0; i < 3; i++) { + hexs[i] = String(parseInt(hexs[i], 16)) + } + return hexs.map(h => parseInt(h)) +} + +// rgb颜色转Hex颜色 +export function rgbToHex(r: number, g: number, b: number): string { + const hexs = [r.toString(16), g.toString(16), b.toString(16)] + for (let i = 0; i < 3; i++) { + if (hexs[i].length == 1) { + hexs[i] = `0${hexs[i]}` + } + } + return `#${hexs.join('')}` +} + +// 变浅颜色值 +export function getLightColor(color: string, level: number): string { + const rgb = hexToRgb(color) + for (let i = 0; i < 3; i++) { + rgb[i] = Math.floor((255 - rgb[i]) * level + rgb[i]) + } + return rgbToHex(rgb[0], rgb[1], rgb[2]) +} + +// 变深颜色值 +export function getDarkColor(color: string, level: number): string { + const rgb = hexToRgb(color) + for (let i = 0; i < 3; i++) { + rgb[i] = Math.floor(rgb[i] * (1 - level)) + } + return rgbToHex(rgb[0], rgb[1], rgb[2]) +} diff --git a/portal/frontend/portal/src/utils/validate.ts b/portal/frontend/portal/src/utils/validate.ts new file mode 100644 index 0000000..ed99eee --- /dev/null +++ b/portal/frontend/portal/src/utils/validate.ts @@ -0,0 +1,114 @@ +/** + * 路径匹配器 + * @param pattern + * @param path + * @returns {Boolean} + */ +export function isPathMatch(pattern: string, path: string): boolean { + const regexPattern = pattern.replace(/\//g, '\\/').replace(/\*\*/g, '.*').replace(/\*/g, '[^\\/]*') + const regex = new RegExp(`^${regexPattern}$`) + return regex.test(path) +} + +/** + * 判断value字符串是否为空 + * @param value + * @returns {Boolean} + */ +export function isEmpty(value: any): boolean { + if (value == null || value == "" || value == undefined || value == "undefined") { + return true + } + return false +} + +/** + * 判断url是否是http或https + * @param url + * @returns {Boolean} + */ +export function isHttp(url: string): boolean { + return url.indexOf('http://') !== -1 || url.indexOf('https://') !== -1 +} + +/** + * 判断path是否为外链 + * @param path + * @returns {Boolean} + */ +export function isExternal(path: string): boolean { + return /^(https?:|mailto:|tel:)/.test(path) +} + +/** + * @param str + * @returns {Boolean} + */ +export function validUsername(str: string): boolean { + const valid_map = ['admin', 'editor'] + return valid_map.indexOf(str.trim()) >= 0 +} + +/** + * @param url + * @returns {Boolean} + */ +export function validURL(url: string): boolean { + const reg = /^(https?|ftp):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(\/($|[a-zA-Z0-9.,?'\\+&%$#=~_-]+))*$/ + return reg.test(url) +} + +/** + * @param str + * @returns {Boolean} + */ +export function validLowerCase(str: string): boolean { + const reg = /^[a-z]+$/ + return reg.test(str) +} + +/** + * @param str + * @returns {Boolean} + */ +export function validUpperCase(str: string): boolean { + const reg = /^[A-Z]+$/ + return reg.test(str) +} + +/** + * @param str + * @returns {Boolean} + */ +export function validAlphabets(str: string): boolean { + const reg = /^[A-Za-z]+$/ + return reg.test(str) +} + +/** + * @param email + * @returns {Boolean} + */ +export function validEmail(email: string): boolean { + const reg = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/ + return reg.test(email) +} + +/** + * @param str + * @returns {Boolean} + */ +export function isString(str: any): boolean { + return typeof str === 'string' || str instanceof String +} + +/** + * @param arg + * @returns {Boolean} + */ +export function isArray(arg: any): boolean { + if (typeof Array.isArray === 'undefined') { + return Object.prototype.toString.call(arg) === '[object Array]' + } + return Array.isArray(arg) +} diff --git a/portal/frontend/portal/src/views/error/401.vue b/portal/frontend/portal/src/views/error/401.vue new file mode 100644 index 0000000..ca11273 --- /dev/null +++ b/portal/frontend/portal/src/views/error/401.vue @@ -0,0 +1,82 @@ + + + + + diff --git a/portal/frontend/portal/src/views/error/404.vue b/portal/frontend/portal/src/views/error/404.vue new file mode 100644 index 0000000..87b822b --- /dev/null +++ b/portal/frontend/portal/src/views/error/404.vue @@ -0,0 +1,227 @@ + + + + + diff --git a/portal/frontend/portal/src/views/index.vue b/portal/frontend/portal/src/views/index.vue new file mode 100644 index 0000000..e5e6a79 --- /dev/null +++ b/portal/frontend/portal/src/views/index.vue @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/portal/frontend/portal/src/views/monitor/cache/index.vue b/portal/frontend/portal/src/views/monitor/cache/index.vue new file mode 100644 index 0000000..1c33d20 --- /dev/null +++ b/portal/frontend/portal/src/views/monitor/cache/index.vue @@ -0,0 +1,132 @@ + + + diff --git a/portal/frontend/portal/src/views/monitor/cache/list.vue b/portal/frontend/portal/src/views/monitor/cache/list.vue new file mode 100644 index 0000000..7451fc1 --- /dev/null +++ b/portal/frontend/portal/src/views/monitor/cache/list.vue @@ -0,0 +1,259 @@ + + + diff --git a/portal/frontend/portal/src/views/monitor/druid/index.vue b/portal/frontend/portal/src/views/monitor/druid/index.vue new file mode 100644 index 0000000..d688394 --- /dev/null +++ b/portal/frontend/portal/src/views/monitor/druid/index.vue @@ -0,0 +1,11 @@ + + + diff --git a/portal/frontend/portal/src/views/monitor/job/index.vue b/portal/frontend/portal/src/views/monitor/job/index.vue new file mode 100644 index 0000000..b022ae7 --- /dev/null +++ b/portal/frontend/portal/src/views/monitor/job/index.vue @@ -0,0 +1,503 @@ + + + diff --git a/portal/frontend/portal/src/views/monitor/job/log.vue b/portal/frontend/portal/src/views/monitor/job/log.vue new file mode 100644 index 0000000..4c03a08 --- /dev/null +++ b/portal/frontend/portal/src/views/monitor/job/log.vue @@ -0,0 +1,285 @@ + + + diff --git a/portal/frontend/portal/src/views/monitor/logininfor/index.vue b/portal/frontend/portal/src/views/monitor/logininfor/index.vue new file mode 100644 index 0000000..72a0b2c --- /dev/null +++ b/portal/frontend/portal/src/views/monitor/logininfor/index.vue @@ -0,0 +1,234 @@ + + + diff --git a/portal/frontend/portal/src/views/monitor/online/index.vue b/portal/frontend/portal/src/views/monitor/online/index.vue new file mode 100644 index 0000000..5d36e88 --- /dev/null +++ b/portal/frontend/portal/src/views/monitor/online/index.vue @@ -0,0 +1,110 @@ + + + diff --git a/portal/frontend/portal/src/views/monitor/operlog/index.vue b/portal/frontend/portal/src/views/monitor/operlog/index.vue new file mode 100644 index 0000000..b15e3bb --- /dev/null +++ b/portal/frontend/portal/src/views/monitor/operlog/index.vue @@ -0,0 +1,311 @@ + + + diff --git a/portal/frontend/portal/src/views/monitor/server/index.vue b/portal/frontend/portal/src/views/monitor/server/index.vue new file mode 100644 index 0000000..95209aa --- /dev/null +++ b/portal/frontend/portal/src/views/monitor/server/index.vue @@ -0,0 +1,187 @@ + + + diff --git a/portal/frontend/portal/src/views/redirect/index.vue b/portal/frontend/portal/src/views/redirect/index.vue new file mode 100644 index 0000000..542ae6a --- /dev/null +++ b/portal/frontend/portal/src/views/redirect/index.vue @@ -0,0 +1,14 @@ + + + \ No newline at end of file diff --git a/portal/frontend/portal/src/views/system/config/index.vue b/portal/frontend/portal/src/views/system/config/index.vue new file mode 100644 index 0000000..a061cf9 --- /dev/null +++ b/portal/frontend/portal/src/views/system/config/index.vue @@ -0,0 +1,317 @@ + + + diff --git a/portal/frontend/portal/src/views/system/dept/index.vue b/portal/frontend/portal/src/views/system/dept/index.vue new file mode 100644 index 0000000..59f0ec7 --- /dev/null +++ b/portal/frontend/portal/src/views/system/dept/index.vue @@ -0,0 +1,285 @@ + + + diff --git a/portal/frontend/portal/src/views/system/dict/data.vue b/portal/frontend/portal/src/views/system/dict/data.vue new file mode 100644 index 0000000..a7ffafc --- /dev/null +++ b/portal/frontend/portal/src/views/system/dict/data.vue @@ -0,0 +1,368 @@ + + + diff --git a/portal/frontend/portal/src/views/system/dict/index.vue b/portal/frontend/portal/src/views/system/dict/index.vue new file mode 100644 index 0000000..5b6cfbf --- /dev/null +++ b/portal/frontend/portal/src/views/system/dict/index.vue @@ -0,0 +1,332 @@ + + + diff --git a/portal/frontend/portal/src/views/system/menu/index.vue b/portal/frontend/portal/src/views/system/menu/index.vue new file mode 100644 index 0000000..50d8534 --- /dev/null +++ b/portal/frontend/portal/src/views/system/menu/index.vue @@ -0,0 +1,453 @@ + + + diff --git a/portal/frontend/portal/src/views/system/notice/index.vue b/portal/frontend/portal/src/views/system/notice/index.vue new file mode 100644 index 0000000..3863c1a --- /dev/null +++ b/portal/frontend/portal/src/views/system/notice/index.vue @@ -0,0 +1,293 @@ + + + diff --git a/portal/frontend/portal/src/views/system/post/index.vue b/portal/frontend/portal/src/views/system/post/index.vue new file mode 100644 index 0000000..6f62d8b --- /dev/null +++ b/portal/frontend/portal/src/views/system/post/index.vue @@ -0,0 +1,288 @@ + + + diff --git a/portal/frontend/portal/src/views/system/role/authUser.vue b/portal/frontend/portal/src/views/system/role/authUser.vue new file mode 100644 index 0000000..64d9cdb --- /dev/null +++ b/portal/frontend/portal/src/views/system/role/authUser.vue @@ -0,0 +1,180 @@ + + + + diff --git a/portal/frontend/portal/src/views/system/role/index.vue b/portal/frontend/portal/src/views/system/role/index.vue new file mode 100644 index 0000000..9a8b17a --- /dev/null +++ b/portal/frontend/portal/src/views/system/role/index.vue @@ -0,0 +1,588 @@ + + + diff --git a/portal/frontend/portal/src/views/system/role/selectUser.vue b/portal/frontend/portal/src/views/system/role/selectUser.vue new file mode 100644 index 0000000..757b87e --- /dev/null +++ b/portal/frontend/portal/src/views/system/role/selectUser.vue @@ -0,0 +1,145 @@ + + + diff --git a/portal/frontend/portal/src/views/system/user/authRole.vue b/portal/frontend/portal/src/views/system/user/authRole.vue new file mode 100644 index 0000000..96ffc34 --- /dev/null +++ b/portal/frontend/portal/src/views/system/user/authRole.vue @@ -0,0 +1,130 @@ + + + diff --git a/portal/frontend/portal/src/views/system/user/index.vue b/portal/frontend/portal/src/views/system/user/index.vue new file mode 100644 index 0000000..bfb61b7 --- /dev/null +++ b/portal/frontend/portal/src/views/system/user/index.vue @@ -0,0 +1,564 @@ + + + diff --git a/portal/frontend/portal/src/views/system/user/profile/index.vue b/portal/frontend/portal/src/views/system/user/profile/index.vue new file mode 100644 index 0000000..c563ebc --- /dev/null +++ b/portal/frontend/portal/src/views/system/user/profile/index.vue @@ -0,0 +1,102 @@ + + + diff --git a/portal/frontend/portal/src/views/system/user/profile/resetPwd.vue b/portal/frontend/portal/src/views/system/user/profile/resetPwd.vue new file mode 100644 index 0000000..93a81c7 --- /dev/null +++ b/portal/frontend/portal/src/views/system/user/profile/resetPwd.vue @@ -0,0 +1,68 @@ + + + diff --git a/portal/frontend/portal/src/views/system/user/profile/userAvatar.vue b/portal/frontend/portal/src/views/system/user/profile/userAvatar.vue new file mode 100644 index 0000000..01cae89 --- /dev/null +++ b/portal/frontend/portal/src/views/system/user/profile/userAvatar.vue @@ -0,0 +1,180 @@ + + + + + \ No newline at end of file diff --git a/portal/frontend/portal/src/views/system/user/profile/userInfo.vue b/portal/frontend/portal/src/views/system/user/profile/userInfo.vue new file mode 100644 index 0000000..d60fc7e --- /dev/null +++ b/portal/frontend/portal/src/views/system/user/profile/userInfo.vue @@ -0,0 +1,70 @@ + + + diff --git a/portal/frontend/portal/src/views/tool/build/CodeTypeDialog.vue b/portal/frontend/portal/src/views/tool/build/CodeTypeDialog.vue new file mode 100644 index 0000000..2b1fef2 --- /dev/null +++ b/portal/frontend/portal/src/views/tool/build/CodeTypeDialog.vue @@ -0,0 +1,71 @@ + + + \ No newline at end of file diff --git a/portal/frontend/portal/src/views/tool/build/DraggableItem.vue b/portal/frontend/portal/src/views/tool/build/DraggableItem.vue new file mode 100644 index 0000000..3b0fe2b --- /dev/null +++ b/portal/frontend/portal/src/views/tool/build/DraggableItem.vue @@ -0,0 +1,68 @@ + + diff --git a/portal/frontend/portal/src/views/tool/build/IconsDialog.vue b/portal/frontend/portal/src/views/tool/build/IconsDialog.vue new file mode 100644 index 0000000..d1b2dac --- /dev/null +++ b/portal/frontend/portal/src/views/tool/build/IconsDialog.vue @@ -0,0 +1,116 @@ + + + diff --git a/portal/frontend/portal/src/views/tool/build/RightPanel.vue b/portal/frontend/portal/src/views/tool/build/RightPanel.vue new file mode 100644 index 0000000..e7ad28c --- /dev/null +++ b/portal/frontend/portal/src/views/tool/build/RightPanel.vue @@ -0,0 +1,922 @@ + + + + + \ No newline at end of file diff --git a/portal/frontend/portal/src/views/tool/build/TreeNodeDialog.vue b/portal/frontend/portal/src/views/tool/build/TreeNodeDialog.vue new file mode 100644 index 0000000..882c7ad --- /dev/null +++ b/portal/frontend/portal/src/views/tool/build/TreeNodeDialog.vue @@ -0,0 +1,95 @@ + + diff --git a/portal/frontend/portal/src/views/tool/build/index.vue b/portal/frontend/portal/src/views/tool/build/index.vue new file mode 100644 index 0000000..10bcf61 --- /dev/null +++ b/portal/frontend/portal/src/views/tool/build/index.vue @@ -0,0 +1,663 @@ + + + + + diff --git a/portal/frontend/portal/src/views/tool/gen/basicInfoForm.vue b/portal/frontend/portal/src/views/tool/gen/basicInfoForm.vue new file mode 100644 index 0000000..4af82a7 --- /dev/null +++ b/portal/frontend/portal/src/views/tool/gen/basicInfoForm.vue @@ -0,0 +1,48 @@ + + + diff --git a/portal/frontend/portal/src/views/tool/gen/createTable.vue b/portal/frontend/portal/src/views/tool/gen/createTable.vue new file mode 100644 index 0000000..5a2250e --- /dev/null +++ b/portal/frontend/portal/src/views/tool/gen/createTable.vue @@ -0,0 +1,46 @@ + + + diff --git a/portal/frontend/portal/src/views/tool/gen/editTable.vue b/portal/frontend/portal/src/views/tool/gen/editTable.vue new file mode 100644 index 0000000..dfae521 --- /dev/null +++ b/portal/frontend/portal/src/views/tool/gen/editTable.vue @@ -0,0 +1,216 @@ + + + diff --git a/portal/frontend/portal/src/views/tool/gen/genInfoForm.vue b/portal/frontend/portal/src/views/tool/gen/genInfoForm.vue new file mode 100644 index 0000000..ac86946 --- /dev/null +++ b/portal/frontend/portal/src/views/tool/gen/genInfoForm.vue @@ -0,0 +1,309 @@ + + + diff --git a/portal/frontend/portal/src/views/tool/gen/importTable.vue b/portal/frontend/portal/src/views/tool/gen/importTable.vue new file mode 100644 index 0000000..8eaedb5 --- /dev/null +++ b/portal/frontend/portal/src/views/tool/gen/importTable.vue @@ -0,0 +1,127 @@ + + + diff --git a/portal/frontend/portal/src/views/tool/gen/index.vue b/portal/frontend/portal/src/views/tool/gen/index.vue new file mode 100644 index 0000000..ec98233 --- /dev/null +++ b/portal/frontend/portal/src/views/tool/gen/index.vue @@ -0,0 +1,309 @@ + + + diff --git a/portal/frontend/portal/src/views/tool/swagger/index.vue b/portal/frontend/portal/src/views/tool/swagger/index.vue new file mode 100644 index 0000000..02dcbb4 --- /dev/null +++ b/portal/frontend/portal/src/views/tool/swagger/index.vue @@ -0,0 +1,9 @@ + + + diff --git a/portal/frontend/portal/tsconfig.json b/portal/frontend/portal/tsconfig.json new file mode 100644 index 0000000..2a7aac0 --- /dev/null +++ b/portal/frontend/portal/tsconfig.json @@ -0,0 +1,40 @@ +{ + "compilerOptions": { + "target": "esnext", + "module": "esnext", + "moduleResolution": "bundler", + "lib": ["esnext", "dom"], + "baseUrl": "./", + "paths": { + "@/*": ["src/*"] + }, + + // 严格性和类型检查相关配置 + "strict": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + + // 模块和兼容性相关配置 + "allowSyntheticDefaultImports": true, + "esModuleInterop": true, + "resolveJsonModule": true, + + // 调试和兼容性相关配置 + "sourceMap": true, + "useDefineForClassFields": true, + "allowJs": true, + "noEmit": true, + + // 类型声明相关配置 + "types": ["node", "vite/client", "element-plus/global"] + }, + "include": [ + "src/**/*.ts", + "src/**/*.vue", + "vite/**/*.ts", + "auto-imports.d.ts", + "vite.config.ts" + ], + "exclude": ["node_modules", "dist", "src/**/*.js"] +} + diff --git a/portal/frontend/portal/vite.config.ts b/portal/frontend/portal/vite.config.ts new file mode 100644 index 0000000..719333f --- /dev/null +++ b/portal/frontend/portal/vite.config.ts @@ -0,0 +1,79 @@ +import { defineConfig, loadEnv } from 'vite' +import path from 'path' +import createVitePlugins from './vite/plugins' + +const baseUrl = 'http://localhost:8080' // 后端接口 + +// https://vitejs.dev/config/ +export default defineConfig(({ mode, command }) => { + const env = loadEnv(mode, process.cwd()) + const { VITE_APP_ENV } = env + return { + // 部署生产环境和开发环境下的URL。 + // 默认情况下,vite 会假设你的应用是被部署在一个域名的根路径上 + base: VITE_APP_ENV === 'production' ? '/portal-ui' : '/', + plugins: createVitePlugins(env, command === 'build'), + resolve: { + // https://cn.vitejs.dev/config/#resolve-alias + alias: { + // 设置路径 + '~': path.resolve(__dirname, './'), + // 设置别名 + '@': path.resolve(__dirname, './src') + }, + // https://cn.vitejs.dev/config/#resolve-extensions + extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue'] + }, + // 打包配置 + build: { + // https://vite.dev/config/build-options.html + sourcemap: command === 'build' ? false : 'inline', + outDir: 'dist', + assetsDir: 'assets', + chunkSizeWarningLimit: 2000, + rollupOptions: { + output: { + chunkFileNames: 'static/js/[name]-[hash].js', + entryFileNames: 'static/js/[name]-[hash].js', + assetFileNames: 'static/[ext]/[name]-[hash].[ext]' + } + } + }, + // vite 相关配置 + server: { + port: 81, + host: true, + open: true, + proxy: { + // https://cn.vitejs.dev/config/#server-proxy + '/dev-api2': { + target: baseUrl, + changeOrigin: true, + rewrite: (p) => p.replace(/^\/dev-api2/, '') + }, + // springdoc proxy + '^/v3/api-docs/(.*)': { + target: baseUrl, + changeOrigin: true, + } + } + }, + css: { + postcss: { + plugins: [ + { + postcssPlugin: 'internal:charset-removal', + AtRule: { + charset: (atRule: any) => { + if (atRule.name === 'charset') { + atRule.remove() + } + } + } + } + ] + } + } + } +}) + diff --git a/portal/frontend/portal/vite/plugins/auto-import.ts b/portal/frontend/portal/vite/plugins/auto-import.ts new file mode 100644 index 0000000..c1d0fc4 --- /dev/null +++ b/portal/frontend/portal/vite/plugins/auto-import.ts @@ -0,0 +1,13 @@ +import autoImport from 'unplugin-auto-import/vite' + +export default function createAutoImport() { + return autoImport({ + imports: [ + 'vue', + 'vue-router', + 'pinia' + ], + dts: true + }) +} + diff --git a/portal/frontend/portal/vite/plugins/compression.ts b/portal/frontend/portal/vite/plugins/compression.ts new file mode 100644 index 0000000..836c4d0 --- /dev/null +++ b/portal/frontend/portal/vite/plugins/compression.ts @@ -0,0 +1,28 @@ +import { PluginOption } from 'vite' +import compression from 'vite-plugin-compression' + +export default function createCompression(env: Record) { + const { VITE_BUILD_COMPRESS } = env + const plugin: PluginOption[] = [] + if (VITE_BUILD_COMPRESS) { + const compressList = VITE_BUILD_COMPRESS.split(',') + if (compressList.includes('gzip')) { + plugin.push( + compression({ + ext: '.gz', + deleteOriginFile: false + }) + ) + } + if (compressList.includes('brotli')) { + plugin.push( + compression({ + ext: '.br', + algorithm: 'brotliCompress', + deleteOriginFile: false + }) + ) + } + } + return plugin +} diff --git a/portal/frontend/portal/vite/plugins/index.ts b/portal/frontend/portal/vite/plugins/index.ts new file mode 100644 index 0000000..28874fd --- /dev/null +++ b/portal/frontend/portal/vite/plugins/index.ts @@ -0,0 +1,16 @@ +import vue from '@vitejs/plugin-vue' + +import createAutoImport from './auto-import' +import createSvgIcon from './svg-icon' +import createCompression from './compression' +import createSetupExtend from './setup-extend' +import { PluginOption } from 'vite' + +export default function createVitePlugins(viteEnv: Record, isBuild = false) { + const vitePlugins: PluginOption[] = [vue()] + vitePlugins.push(createAutoImport()) + vitePlugins.push(createSetupExtend()) + vitePlugins.push(createSvgIcon(isBuild)) + isBuild && vitePlugins.push(...createCompression(viteEnv)) + return vitePlugins +} diff --git a/portal/frontend/portal/vite/plugins/setup-extend.ts b/portal/frontend/portal/vite/plugins/setup-extend.ts new file mode 100644 index 0000000..fb70609 --- /dev/null +++ b/portal/frontend/portal/vite/plugins/setup-extend.ts @@ -0,0 +1,5 @@ +import setupExtend from 'unplugin-vue-setup-extend-plus/vite' + +export default function createSetupExtend() { + return setupExtend({}) +} diff --git a/portal/frontend/portal/vite/plugins/svg-icon.ts b/portal/frontend/portal/vite/plugins/svg-icon.ts new file mode 100644 index 0000000..2862a9e --- /dev/null +++ b/portal/frontend/portal/vite/plugins/svg-icon.ts @@ -0,0 +1,10 @@ +import { createSvgIconsPlugin } from 'vite-plugin-svg-icons' +import path from 'path' + +export default function createSvgIcon(isBuild: boolean) { + return createSvgIconsPlugin({ + iconDirs: [path.resolve(process.cwd(), 'src/assets/icons/svg')], + symbolId: 'icon-[dir]-[name]', + svgoOptions: isBuild + }) +}