Commit 6c8b9d48 authored by 蒋赟昊's avatar 蒋赟昊 👮

init

parent a3b5760b
target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/
### IntelliJ IDEA ###
.idea/modules.xml
.idea/jarRepositories.xml
.idea/compiler.xml
.idea/libraries/
*.iws
*.iml
*.ipr
### Eclipse ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
build/
!**/src/main/**/build/
!**/src/test/**/build/
### VS Code ###
.vscode/
### Mac OS ###
.DS_Store
logs/
*.log
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DBNavigator.Project.DatabaseFileManager">
<open-files />
</component>
<component name="DBNavigator.Project.Settings">
<connections />
<browser-settings>
<general>
<display-mode value="TABBED" />
<navigation-history-size value="100" />
<show-object-details value="false" />
<enable-sticky-paths value="true" />
</general>
<filters>
<object-type-filter>
<object-type name="SCHEMA" enabled="true" />
<object-type name="USER" enabled="true" />
<object-type name="ROLE" enabled="true" />
<object-type name="PRIVILEGE" enabled="true" />
<object-type name="CHARSET" enabled="true" />
<object-type name="TABLE" enabled="true" />
<object-type name="VIEW" enabled="true" />
<object-type name="MATERIALIZED_VIEW" enabled="true" />
<object-type name="NESTED_TABLE" enabled="true" />
<object-type name="COLUMN" enabled="true" />
<object-type name="INDEX" enabled="true" />
<object-type name="CONSTRAINT" enabled="true" />
<object-type name="DATASET_TRIGGER" enabled="true" />
<object-type name="DATABASE_TRIGGER" enabled="true" />
<object-type name="SYNONYM" enabled="true" />
<object-type name="SEQUENCE" enabled="true" />
<object-type name="PROCEDURE" enabled="true" />
<object-type name="FUNCTION" enabled="true" />
<object-type name="PACKAGE" enabled="true" />
<object-type name="TYPE" enabled="true" />
<object-type name="TYPE_ATTRIBUTE" enabled="true" />
<object-type name="ARGUMENT" enabled="true" />
<object-type name="DIMENSION" enabled="true" />
<object-type name="CLUSTER" enabled="true" />
<object-type name="DBLINK" enabled="true" />
</object-type-filter>
</filters>
<sorting>
<object-type name="COLUMN" sorting-type="NAME" />
<object-type name="FUNCTION" sorting-type="NAME" />
<object-type name="PROCEDURE" sorting-type="NAME" />
<object-type name="ARGUMENT" sorting-type="POSITION" />
<object-type name="TYPE ATTRIBUTE" sorting-type="POSITION" />
</sorting>
<default-editors>
<object-type name="VIEW" editor-type="SELECTION" />
<object-type name="PACKAGE" editor-type="SELECTION" />
<object-type name="TYPE" editor-type="SELECTION" />
</default-editors>
</browser-settings>
<navigation-settings>
<lookup-filters>
<lookup-objects>
<object-type name="SCHEMA" enabled="true" />
<object-type name="USER" enabled="false" />
<object-type name="ROLE" enabled="false" />
<object-type name="PRIVILEGE" enabled="false" />
<object-type name="CHARSET" enabled="false" />
<object-type name="TABLE" enabled="true" />
<object-type name="VIEW" enabled="true" />
<object-type name="MATERIALIZED VIEW" enabled="true" />
<object-type name="INDEX" enabled="true" />
<object-type name="CONSTRAINT" enabled="true" />
<object-type name="DATASET TRIGGER" enabled="true" />
<object-type name="DATABASE TRIGGER" enabled="true" />
<object-type name="SYNONYM" enabled="false" />
<object-type name="SEQUENCE" enabled="true" />
<object-type name="PROCEDURE" enabled="true" />
<object-type name="FUNCTION" enabled="true" />
<object-type name="PACKAGE" enabled="true" />
<object-type name="TYPE" enabled="true" />
<object-type name="DIMENSION" enabled="false" />
<object-type name="CLUSTER" enabled="false" />
<object-type name="DBLINK" enabled="true" />
</lookup-objects>
<force-database-load value="false" />
<prompt-connection-selection value="true" />
<prompt-schema-selection value="true" />
</lookup-filters>
</navigation-settings>
<dataset-grid-settings>
<general>
<enable-zooming value="true" />
<enable-column-tooltip value="true" />
</general>
<sorting>
<nulls-first value="true" />
<max-sorting-columns value="4" />
</sorting>
<audit-columns>
<column-names value="" />
<visible value="true" />
<editable value="false" />
</audit-columns>
</dataset-grid-settings>
<dataset-editor-settings>
<text-editor-popup>
<active value="false" />
<active-if-empty value="false" />
<data-length-threshold value="100" />
<popup-delay value="1000" />
</text-editor-popup>
<values-actions-popup>
<show-popup-button value="true" />
<element-count-threshold value="1000" />
<data-length-threshold value="250" />
</values-actions-popup>
<general>
<fetch-block-size value="100" />
<fetch-timeout value="30" />
<trim-whitespaces value="true" />
<convert-empty-strings-to-null value="true" />
<select-content-on-cell-edit value="true" />
<large-value-preview-active value="true" />
</general>
<filters>
<prompt-filter-dialog value="true" />
<default-filter-type value="BASIC" />
</filters>
<qualified-text-editor text-length-threshold="300">
<content-types>
<content-type name="Text" enabled="true" />
<content-type name="Properties" enabled="true" />
<content-type name="XML" enabled="true" />
<content-type name="DTD" enabled="true" />
<content-type name="HTML" enabled="true" />
<content-type name="XHTML" enabled="true" />
<content-type name="CSS" enabled="true" />
<content-type name="Java" enabled="true" />
<content-type name="SQL" enabled="true" />
<content-type name="PL/SQL" enabled="true" />
<content-type name="JavaScript" enabled="true" />
<content-type name="JSON" enabled="true" />
<content-type name="JSON5" enabled="true" />
<content-type name="JSP" enabled="true" />
<content-type name="JSPx" enabled="true" />
<content-type name="Groovy" enabled="true" />
<content-type name="FTL" enabled="true" />
<content-type name="VTL" enabled="true" />
<content-type name="YAML" enabled="true" />
<content-type name="Manifest" enabled="true" />
</content-types>
</qualified-text-editor>
<record-navigation>
<navigation-target value="VIEWER" />
</record-navigation>
</dataset-editor-settings>
<code-editor-settings>
<general>
<show-object-navigation-gutter value="false" />
<show-spec-declaration-navigation-gutter value="true" />
<enable-spellchecking value="true" />
<enable-reference-spellchecking value="false" />
</general>
<confirmations>
<save-changes value="false" />
<revert-changes value="true" />
<exit-on-changes value="ASK" />
</confirmations>
</code-editor-settings>
<code-completion-settings>
<filters>
<basic-filter>
<filter-element type="RESERVED_WORD" id="keyword" selected="true" />
<filter-element type="RESERVED_WORD" id="function" selected="true" />
<filter-element type="RESERVED_WORD" id="parameter" selected="true" />
<filter-element type="RESERVED_WORD" id="datatype" selected="true" />
<filter-element type="RESERVED_WORD" id="exception" selected="true" />
<filter-element type="OBJECT" id="schema" selected="true" />
<filter-element type="OBJECT" id="role" selected="true" />
<filter-element type="OBJECT" id="user" selected="true" />
<filter-element type="OBJECT" id="privilege" selected="true" />
<user-schema>
<filter-element type="OBJECT" id="table" selected="true" />
<filter-element type="OBJECT" id="view" selected="true" />
<filter-element type="OBJECT" id="materialized view" selected="true" />
<filter-element type="OBJECT" id="index" selected="true" />
<filter-element type="OBJECT" id="constraint" selected="true" />
<filter-element type="OBJECT" id="trigger" selected="true" />
<filter-element type="OBJECT" id="synonym" selected="false" />
<filter-element type="OBJECT" id="sequence" selected="true" />
<filter-element type="OBJECT" id="procedure" selected="true" />
<filter-element type="OBJECT" id="function" selected="true" />
<filter-element type="OBJECT" id="package" selected="true" />
<filter-element type="OBJECT" id="type" selected="true" />
<filter-element type="OBJECT" id="dimension" selected="true" />
<filter-element type="OBJECT" id="cluster" selected="true" />
<filter-element type="OBJECT" id="dblink" selected="true" />
</user-schema>
<public-schema>
<filter-element type="OBJECT" id="table" selected="false" />
<filter-element type="OBJECT" id="view" selected="false" />
<filter-element type="OBJECT" id="materialized view" selected="false" />
<filter-element type="OBJECT" id="index" selected="false" />
<filter-element type="OBJECT" id="constraint" selected="false" />
<filter-element type="OBJECT" id="trigger" selected="false" />
<filter-element type="OBJECT" id="synonym" selected="false" />
<filter-element type="OBJECT" id="sequence" selected="false" />
<filter-element type="OBJECT" id="procedure" selected="false" />
<filter-element type="OBJECT" id="function" selected="false" />
<filter-element type="OBJECT" id="package" selected="false" />
<filter-element type="OBJECT" id="type" selected="false" />
<filter-element type="OBJECT" id="dimension" selected="false" />
<filter-element type="OBJECT" id="cluster" selected="false" />
<filter-element type="OBJECT" id="dblink" selected="false" />
</public-schema>
<any-schema>
<filter-element type="OBJECT" id="table" selected="true" />
<filter-element type="OBJECT" id="view" selected="true" />
<filter-element type="OBJECT" id="materialized view" selected="true" />
<filter-element type="OBJECT" id="index" selected="true" />
<filter-element type="OBJECT" id="constraint" selected="true" />
<filter-element type="OBJECT" id="trigger" selected="true" />
<filter-element type="OBJECT" id="synonym" selected="true" />
<filter-element type="OBJECT" id="sequence" selected="true" />
<filter-element type="OBJECT" id="procedure" selected="true" />
<filter-element type="OBJECT" id="function" selected="true" />
<filter-element type="OBJECT" id="package" selected="true" />
<filter-element type="OBJECT" id="type" selected="true" />
<filter-element type="OBJECT" id="dimension" selected="true" />
<filter-element type="OBJECT" id="cluster" selected="true" />
<filter-element type="OBJECT" id="dblink" selected="true" />
</any-schema>
</basic-filter>
<extended-filter>
<filter-element type="RESERVED_WORD" id="keyword" selected="true" />
<filter-element type="RESERVED_WORD" id="function" selected="true" />
<filter-element type="RESERVED_WORD" id="parameter" selected="true" />
<filter-element type="RESERVED_WORD" id="datatype" selected="true" />
<filter-element type="RESERVED_WORD" id="exception" selected="true" />
<filter-element type="OBJECT" id="schema" selected="true" />
<filter-element type="OBJECT" id="user" selected="true" />
<filter-element type="OBJECT" id="role" selected="true" />
<filter-element type="OBJECT" id="privilege" selected="true" />
<user-schema>
<filter-element type="OBJECT" id="table" selected="true" />
<filter-element type="OBJECT" id="view" selected="true" />
<filter-element type="OBJECT" id="materialized view" selected="true" />
<filter-element type="OBJECT" id="index" selected="true" />
<filter-element type="OBJECT" id="constraint" selected="true" />
<filter-element type="OBJECT" id="trigger" selected="true" />
<filter-element type="OBJECT" id="synonym" selected="true" />
<filter-element type="OBJECT" id="sequence" selected="true" />
<filter-element type="OBJECT" id="procedure" selected="true" />
<filter-element type="OBJECT" id="function" selected="true" />
<filter-element type="OBJECT" id="package" selected="true" />
<filter-element type="OBJECT" id="type" selected="true" />
<filter-element type="OBJECT" id="dimension" selected="true" />
<filter-element type="OBJECT" id="cluster" selected="true" />
<filter-element type="OBJECT" id="dblink" selected="true" />
</user-schema>
<public-schema>
<filter-element type="OBJECT" id="table" selected="true" />
<filter-element type="OBJECT" id="view" selected="true" />
<filter-element type="OBJECT" id="materialized view" selected="true" />
<filter-element type="OBJECT" id="index" selected="true" />
<filter-element type="OBJECT" id="constraint" selected="true" />
<filter-element type="OBJECT" id="trigger" selected="true" />
<filter-element type="OBJECT" id="synonym" selected="true" />
<filter-element type="OBJECT" id="sequence" selected="true" />
<filter-element type="OBJECT" id="procedure" selected="true" />
<filter-element type="OBJECT" id="function" selected="true" />
<filter-element type="OBJECT" id="package" selected="true" />
<filter-element type="OBJECT" id="type" selected="true" />
<filter-element type="OBJECT" id="dimension" selected="true" />
<filter-element type="OBJECT" id="cluster" selected="true" />
<filter-element type="OBJECT" id="dblink" selected="true" />
</public-schema>
<any-schema>
<filter-element type="OBJECT" id="table" selected="true" />
<filter-element type="OBJECT" id="view" selected="true" />
<filter-element type="OBJECT" id="materialized view" selected="true" />
<filter-element type="OBJECT" id="index" selected="true" />
<filter-element type="OBJECT" id="constraint" selected="true" />
<filter-element type="OBJECT" id="trigger" selected="true" />
<filter-element type="OBJECT" id="synonym" selected="true" />
<filter-element type="OBJECT" id="sequence" selected="true" />
<filter-element type="OBJECT" id="procedure" selected="true" />
<filter-element type="OBJECT" id="function" selected="true" />
<filter-element type="OBJECT" id="package" selected="true" />
<filter-element type="OBJECT" id="type" selected="true" />
<filter-element type="OBJECT" id="dimension" selected="true" />
<filter-element type="OBJECT" id="cluster" selected="true" />
<filter-element type="OBJECT" id="dblink" selected="true" />
</any-schema>
</extended-filter>
</filters>
<sorting enabled="true">
<sorting-element type="RESERVED_WORD" id="keyword" />
<sorting-element type="RESERVED_WORD" id="datatype" />
<sorting-element type="OBJECT" id="column" />
<sorting-element type="OBJECT" id="table" />
<sorting-element type="OBJECT" id="view" />
<sorting-element type="OBJECT" id="materialized view" />
<sorting-element type="OBJECT" id="index" />
<sorting-element type="OBJECT" id="constraint" />
<sorting-element type="OBJECT" id="trigger" />
<sorting-element type="OBJECT" id="synonym" />
<sorting-element type="OBJECT" id="sequence" />
<sorting-element type="OBJECT" id="procedure" />
<sorting-element type="OBJECT" id="function" />
<sorting-element type="OBJECT" id="package" />
<sorting-element type="OBJECT" id="type" />
<sorting-element type="OBJECT" id="dimension" />
<sorting-element type="OBJECT" id="cluster" />
<sorting-element type="OBJECT" id="dblink" />
<sorting-element type="OBJECT" id="schema" />
<sorting-element type="OBJECT" id="role" />
<sorting-element type="OBJECT" id="user" />
<sorting-element type="RESERVED_WORD" id="function" />
<sorting-element type="RESERVED_WORD" id="parameter" />
</sorting>
<format>
<enforce-code-style-case value="true" />
</format>
</code-completion-settings>
<execution-engine-settings>
<statement-execution>
<fetch-block-size value="100" />
<execution-timeout value="20" />
<debug-execution-timeout value="600" />
<focus-result value="false" />
<prompt-execution value="false" />
</statement-execution>
<script-execution>
<command-line-interfaces />
<execution-timeout value="300" />
</script-execution>
<method-execution>
<execution-timeout value="30" />
<debug-execution-timeout value="600" />
<parameter-history-size value="10" />
</method-execution>
</execution-engine-settings>
<operation-settings>
<transactions>
<uncommitted-changes>
<on-project-close value="ASK" />
<on-disconnect value="ASK" />
<on-autocommit-toggle value="ASK" />
</uncommitted-changes>
<multiple-uncommitted-changes>
<on-commit value="ASK" />
<on-rollback value="ASK" />
</multiple-uncommitted-changes>
</transactions>
<session-browser>
<disconnect-session value="ASK" />
<kill-session value="ASK" />
<reload-on-filter-change value="false" />
</session-browser>
<compiler>
<compile-type value="KEEP" />
<compile-dependencies value="ASK" />
<always-show-controls value="false" />
</compiler>
</operation-settings>
<ddl-file-settings>
<extensions>
<mapping file-type-id="VIEW" extensions="vw" />
<mapping file-type-id="TRIGGER" extensions="trg" />
<mapping file-type-id="PROCEDURE" extensions="prc" />
<mapping file-type-id="FUNCTION" extensions="fnc" />
<mapping file-type-id="PACKAGE" extensions="pkg" />
<mapping file-type-id="PACKAGE_SPEC" extensions="pks" />
<mapping file-type-id="PACKAGE_BODY" extensions="pkb" />
<mapping file-type-id="TYPE" extensions="tpe" />
<mapping file-type-id="TYPE_SPEC" extensions="tps" />
<mapping file-type-id="TYPE_BODY" extensions="tpb" />
</extensions>
<general>
<lookup-ddl-files value="true" />
<create-ddl-files value="false" />
<synchronize-ddl-files value="true" />
<use-qualified-names value="false" />
<make-scripts-rerunnable value="true" />
</general>
</ddl-file-settings>
<general-settings>
<regional-settings>
<date-format value="MEDIUM" />
<number-format value="UNGROUPED" />
<locale value="SYSTEM_DEFAULT" />
<use-custom-formats value="false" />
</regional-settings>
<environment>
<environment-types>
<environment-type id="development" name="Development" description="Development environment" color="-2430209/-12296320" readonly-code="false" readonly-data="false" />
<environment-type id="integration" name="Integration" description="Integration environment" color="-2621494/-12163514" readonly-code="true" readonly-data="false" />
<environment-type id="production" name="Production" description="Productive environment" color="-11574/-10271420" readonly-code="true" readonly-data="true" />
<environment-type id="other" name="Other" description="" color="-1576/-10724543" readonly-code="false" readonly-data="false" />
</environment-types>
<visibility-settings>
<connection-tabs value="true" />
<dialog-headers value="true" />
<object-editor-tabs value="true" />
<script-editor-tabs value="false" />
<execution-result-tabs value="true" />
</visibility-settings>
</environment>
</general-settings>
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding" native2AsciiForPropertiesFiles="true" defaultCharsetForPropertiesFiles="UTF-8">
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/src/main/resources" charset="UTF-8" />
<file url="PROJECT" charset="UTF-8" />
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="MavenProjectsManager">
<option name="originalFiles">
<list>
<option value="$PROJECT_DIR$/pom.xml" />
</list>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>
\ No newline at end of file
_
java:S2259"IA "NullPointerException" could be thrown; "getRequest()" can return null.(
h
java:S2259"RA "NullPointerException" could be thrown; "getRequestAttributes" is nullable here.(
f
java:S2259"IA "NullPointerException" could be thrown; "getRequest()" can return null.(¶8҄2
c
java:S1128"ARemove this unused import 'org.apache.commons.lang3.StringUtils'.(؜8͉2
U
java:S1118 ":Add a private constructor to hide the implicit public one.(옧
\ No newline at end of file
S
java:S1117!"8Rename "body" which hides the field declared at line 20.(‚ý²–ûÿÿÿÿ
œ
java:S1186<"€Add a nested comment explaining why this method is empty, throw an UnsupportedOperationException or complete the implementation.(ÖóûËÿÿÿÿÿ
\ No newline at end of file
c
java:S1192"HDefine a constant instead of duplicating this literal "unknown" 3 times.(
d
java:S2259'"IA "NullPointerException" could be thrown; "inetAddress" is nullable here.(„
P
java:S1118
":Add a private constructor to hide the implicit public one.(
\ No newline at end of file
E
java:S3740("/Provide the parametrized type for this generic.(ˆïɵ
J
java:S3740,"/Provide the parametrized type for this generic.(Ä”ïÎüÿÿÿÿ
J
java:S37400"/Provide the parametrized type for this generic.(£ñ¥Âýÿÿÿÿ
E
java:S37404"/Provide the parametrized type for this generic.(¡©”†
J
java:S37408"/Provide the parametrized type for this generic.(ŠŽÈþÿÿÿÿ
J
java:S3740<"/Provide the parametrized type for this generic.(ÐüñÑüÿÿÿÿ
E
java:S3740@"/Provide the parametrized type for this generic.(ìþµ
E
java:S3740D"/Provide the parametrized type for this generic.(µáÓ·
\ No newline at end of file
Q
java:S3740"/Provide the parametrized type for this generic.(°áÊŽùÿÿÿÿ8ó™„–¼2
\ No newline at end of file
g
java:S1858N"E"getErrorMsg" returns a string, there's no need to call "toString()".(8ꕼ2
L
java:S3740K"/Provide the parametrized type for this generic.(ԅ8ꕼ2
Z
java:S3252R"?Use static access with "com.alibaba.fastjson.JSON" for "parse".(
E
java:S3740!"/Provide the parametrized type for this generic.(ʫ
U
java:S32527"@Use static access with "com.alibaba.fastjson.JSON" for "toJSON".('
u
java:S1128
"SRemove this unused import 'org.springframework.beans.factory.annotation.Autowired'.(82
\ No newline at end of file
I
java:S1612)",Replace this lambda with a method reference.(ܑ8Ӈ2
L
java:S3740&"/Provide the parametrized type for this generic.(82
L
java:S3740 "/Provide the parametrized type for this generic.(ؓ82
L
java:S3740."/Provide the parametrized type for this generic.(82
L
java:S37403"/Provide the parametrized type for this generic.(82
\ No newline at end of file
n java:S127¾"XRefactor the code in order to not assign to this loop counter from within the loop body.(×Ù¤õ
o
java:S22933"YReplace the type specification in this constructor call with the diamond operator ("<>").( ˜î¾
t
java:S22934"YReplace the type specification in this constructor call with the diamond operator ("<>").(׳ƒúÿÿÿÿ
i
java:S3776Ÿ"RRefactor this method to reduce its Cognitive Complexity from 61 to the 15 allowed.(×òŠç
W
java:S1119ÿ";Refactor the code to remove this label and the need for it.(ˆ™Éžþÿÿÿÿ
X
java:S1117Ú"ARename "cachePatterns" which hides the field declared at line 57.(××ó‘
X
java:S1117“"ARename "cachePatterns" which hides the field declared at line 57.(××ó‘
p
java:S2293Í"YReplace the type specification in this constructor call with the diamond operator ("<>").(ïÄèÒ
n
java:S3776ó"RRefactor this method to reduce its Cognitive Complexity from 17 to the 15 allowed.(ý²ìúûÿÿÿÿ
h
java:S5998Ï"LRefactor this repetition that can lead to a stack overflow for large inputs.(¼ä†¢þÿÿÿÿ
u
java:S2293Õ"YReplace the type specification in this constructor call with the diamond operator ("<>").(¨û؃ûÿÿÿÿ
i
java:S3776½"RRefactor this method to reduce its Cognitive Complexity from 18 to the 15 allowed.(ܧê®
\ No newline at end of file
E
java:S3740""/Provide the parametrized type for this generic.(¿§å¨
E
java:S3740""/Provide the parametrized type for this generic.(¿§å¨
P
java:S1118":Add a private constructor to hide the implicit public one.(¯¶ô°
o
java:S2293"YReplace the type specification in this constructor call with the diamond operator ("<>").(Ü€·•
\ No newline at end of file
D
java:S3740"/Provide the parametrized type for this generic.(‚ûëJ
\ No newline at end of file
P
java:S1118!":Add a private constructor to hide the implicit public one.(
` java:S112f"FDefine and throw a dedicated exception instead of using a generic one.(
Z
java:S3252p"?Use static access with "com.alibaba.fastjson.JSON" for "parse".(
V
java:S32521";Use static access with "io.jsonwebtoken.Header" for "TYPE".(눾
Z
java:S32521"?Use static access with "io.jsonwebtoken.Header" for "JWT_TYPE".(눾
p
java:S3008""URename this field "JWT_ISSUER" to match the regular expression '^[a-z][a-zA-Z0-9]*$'.(
E
java:S3740u"/Provide the parametrized type for this generic.(
[ java:S112Y"FDefine and throw a dedicated exception instead of using a generic one.(ц

java:S1488R"jImmediately return this expression instead of assigning it to the temporary variable "base64UrlSignature".(
Q
java:S3740f"/Provide the parametrized type for this generic.(82
\ No newline at end of file
E
java:S3740"/Provide the parametrized type for this generic.(‚Ê«
\ No newline at end of file
e
5src/main/java/com/fxkc/care/config/SwaggerConfig.java,b\f\bf93668eb83e7bcfde17060ea01b5cba2b351d39
c
3src/main/java/com/fxkc/care/config/RedisConfig.java,c\3\c3aa7a3964cde55fe993101e941f921f23891e22
n
>src/main/java/com/fxkc/care/config/RepeatSubmitValidation.java,d\8\d8b4105492134a1957fd1d5f6557cb097d76453d
b
2src/main/java/com/fxkc/care/common/AjaxResult.java,7\7\77eb52d127f223f385892986daba76b11a9105ec
a
1src/main/java/com/fxkc/care/enums/CommonEnum.java,2\3\23c9f4766e2b795b0199609c74bb8a9f71fec1d1
b
2src/main/java/com/fxkc/care/utils/RequestUtil.java,2\4\2452882d32c9d20eb88d97b57e6901d70a746113
j
:src/main/java/com/fxkc/care/config/RepeatSubmitAspect.java,f\5\f51e178c01ebedb3f60da5348b51afc9d4e73979
i
9src/main/java/com/fxkc/care/config/MybatisPlusConfig.java,8\c\8cae1deeadd9812e969d6edc37c1c1946c02287c
l
<src/main/java/com/fxkc/care/config/CustomRequestWrapper.java,5\f\5f0ab11237ecccbdb319ccfdc60518021200a663
o
?src/main/java/com/fxkc/care/config/CustomMetaObjectHandler.java,c\b\cb0cfcc5d709ce3096b7d0b5ee315d8b26ffc5b2
^
.src/main/java/com/fxkc/care/vo/UserInfoVO.java,0\c\0c0a889b2dd21117a86f81b245be3e781e20f4a3
b
2src/main/java/com/fxkc/care/config/CorsConfig.java,0\3\034b53c8d8817415172bfc65da3730d485603896
r
Bsrc/main/java/com/fxkc/care/config/ControllerExceptionHandler.java,a\8\a8ca1ee311f207529d2b30962ddfd5cfeb22cc72
e
5src/main/java/com/fxkc/care/CrowdCareApplication.java,6\9\695a17e31c7d7200de12364f235660950d665f97
7
pom.xml,4\4\442292b8a7efeabbe4cc176709b833b1792140ec
e
5src/main/java/com/fxkc/care/utils/AntPathMatcher.java,a\8\a8fa2e23be05ea35f5340166db44d9e23a7605b4
f
6src/main/java/com/fxkc/care/utils/PathMatcherUtil.java,c\c\cc39d46a841ff17348e2418f1dd36d7b615e1e05
]
-src/main/java/com/fxkc/care/utils/IpUtil.java,6\6\66c7582455f53d22aa18b317d12f9627539c045f
i
9src/main/java/com/fxkc/care/utils/MapBeanConvertUtil.java,b\c\bc7324d6d7dd11ce664980619f66e4ee3a907fd2
j
:src/main/java/com/fxkc/care/controller/TestController.java,9\b\9bc89d84d5fb873f9b923087d237e1e8dffb8c55
e
5src/main/java/com/fxkc/care/filter/GatewayFilter.java,a\6\a622226b8df3886d1fb2d8c11a817b983dc39d0c
^
.src/main/java/com/fxkc/care/utils/JwtUtil.java,c\b\cbb834e68f3ba03d235f0d975730d8e1098544c4
:
.gitignore,a\5\a5cc2925ca8258af241be7e5b0381edf30266302
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Palette2">
<group name="Swing">
<item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="1" hsize-policy="6" anchor="0" fill="1" />
</item>
<item class="com.intellij.uiDesigner.VSpacer" tooltip-text="Vertical Spacer" icon="/com/intellij/uiDesigner/icons/vspacer.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="1" anchor="0" fill="2" />
</item>
<item class="javax.swing.JPanel" icon="/com/intellij/uiDesigner/icons/panel.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3" />
</item>
<item class="javax.swing.JScrollPane" icon="/com/intellij/uiDesigner/icons/scrollPane.svg" removable="false" auto-create-binding="false" can-attach-label="true">
<default-constraints vsize-policy="7" hsize-policy="7" anchor="0" fill="3" />
</item>
<item class="javax.swing.JButton" icon="/com/intellij/uiDesigner/icons/button.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="0" fill="1" />
<initial-values>
<property name="text" value="Button" />
</initial-values>
</item>
<item class="javax.swing.JRadioButton" icon="/com/intellij/uiDesigner/icons/radioButton.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
<initial-values>
<property name="text" value="RadioButton" />
</initial-values>
</item>
<item class="javax.swing.JCheckBox" icon="/com/intellij/uiDesigner/icons/checkBox.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
<initial-values>
<property name="text" value="CheckBox" />
</initial-values>
</item>
<item class="javax.swing.JLabel" icon="/com/intellij/uiDesigner/icons/label.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="0" anchor="8" fill="0" />
<initial-values>
<property name="text" value="Label" />
</initial-values>
</item>
<item class="javax.swing.JTextField" icon="/com/intellij/uiDesigner/icons/textField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JPasswordField" icon="/com/intellij/uiDesigner/icons/passwordField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JFormattedTextField" icon="/com/intellij/uiDesigner/icons/formattedTextField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JTextArea" icon="/com/intellij/uiDesigner/icons/textArea.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTextPane" icon="/com/intellij/uiDesigner/icons/textPane.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JEditorPane" icon="/com/intellij/uiDesigner/icons/editorPane.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JComboBox" icon="/com/intellij/uiDesigner/icons/comboBox.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="2" anchor="8" fill="1" />
</item>
<item class="javax.swing.JTable" icon="/com/intellij/uiDesigner/icons/table.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JList" icon="/com/intellij/uiDesigner/icons/list.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="2" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTree" icon="/com/intellij/uiDesigner/icons/tree.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTabbedPane" icon="/com/intellij/uiDesigner/icons/tabbedPane.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
<preferred-size width="200" height="200" />
</default-constraints>
</item>
<item class="javax.swing.JSplitPane" icon="/com/intellij/uiDesigner/icons/splitPane.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
<preferred-size width="200" height="200" />
</default-constraints>
</item>
<item class="javax.swing.JSpinner" icon="/com/intellij/uiDesigner/icons/spinner.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
</item>
<item class="javax.swing.JSlider" icon="/com/intellij/uiDesigner/icons/slider.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
</item>
<item class="javax.swing.JSeparator" icon="/com/intellij/uiDesigner/icons/separator.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3" />
</item>
<item class="javax.swing.JProgressBar" icon="/com/intellij/uiDesigner/icons/progressbar.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1" />
</item>
<item class="javax.swing.JToolBar" icon="/com/intellij/uiDesigner/icons/toolbar.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1">
<preferred-size width="-1" height="20" />
</default-constraints>
</item>
<item class="javax.swing.JToolBar$Separator" icon="/com/intellij/uiDesigner/icons/toolbarSeparator.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="0" anchor="0" fill="1" />
</item>
<item class="javax.swing.JScrollBar" icon="/com/intellij/uiDesigner/icons/scrollbar.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="0" anchor="0" fill="2" />
</item>
</group>
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="AutoImportSettings">
<option name="autoReloadType" value="SELECTIVE" />
</component>
<component name="ChangeListManager">
<list default="true" id="e4c0d574-8a71-44e7-a1b0-a0b5ee2c62f3" name="Changes" comment="日志配置">
<change beforePath="$PROJECT_DIR$/logs/bds.2024-12-13.0.error.log" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/logs/bds.2024-12-13.0.log" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/logs/bds.2024-12-14.0.error.log" beforeDir="false" afterPath="$PROJECT_DIR$/logs/bds.2024-12-14.0.error.log" afterDir="false" />
<change beforePath="$PROJECT_DIR$/logs/bds.2024-12-14.0.log" beforeDir="false" afterPath="$PROJECT_DIR$/logs/bds.2024-12-14.0.log" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" />
</component>
<component name="FileTemplateManagerImpl">
<option name="RECENT_TEMPLATES">
<list>
<option value="Class" />
</list>
</option>
</component>
<component name="Git.Settings">
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
</component>
<component name="MavenImportPreferences">
<option name="generalSettings">
<MavenGeneralSettings>
<option name="mavenHome" value="$PROJECT_DIR$/../../CodingTools/maven/apache-maven-3.6.3" />
<option name="userSettingsFile" value="D:\CodingTools\maven\apache-maven-3.6.3\conf\settings.xml" />
</MavenGeneralSettings>
</option>
</component>
<component name="ProjectId" id="2q9fsTYz1ZcAG49i7PRbhs9aAik" />
<component name="ProjectViewState">
<option name="hideEmptyMiddlePackages" value="true" />
<option name="showLibraryContents" value="true" />
</component>
<component name="PropertiesComponent"><![CDATA[{
"keyToString": {
"RequestMappingsPanelOrder0": "0",
"RequestMappingsPanelOrder1": "1",
"RequestMappingsPanelWidth0": "75",
"RequestMappingsPanelWidth1": "75",
"RunOnceActivity.OpenProjectViewOnStart": "true",
"RunOnceActivity.ShowReadmeOnStart": "true",
"SONARLINT_PRECOMMIT_ANALYSIS": "true",
"WebServerToolWindowFactoryState": "false",
"last_opened_file_path": "D:/Workspace/crowd_care/src/main/resources",
"node.js.detected.package.eslint": "true",
"node.js.detected.package.tslint": "true",
"node.js.selected.package.eslint": "(autodetect)",
"node.js.selected.package.tslint": "(autodetect)",
"project.structure.last.edited": "Project",
"project.structure.proportion": "0.15",
"project.structure.side.proportion": "0.2",
"settings.editor.selected.configurable": "MavenSettings",
"spring.configuration.checksum": "494292902f76e4e8088cdf70de8cb09d",
"vue.rearranger.settings.migration": "true"
}
}]]></component>
<component name="ReactorSettings">
<option name="notificationShown" value="true" />
</component>
<component name="RecentsManager">
<key name="CopyFile.RECENT_KEYS">
<recent name="D:\Workspace\crowd_care\src\main\resources" />
<recent name="D:\Workspace\crowd_care\src\main\java\com\fxkc\care\config" />
</key>
<key name="CopyClassDialog.RECENTS_KEY">
<recent name="com.fxkc.care.utils" />
<recent name="com.fxkc.care.filter" />
<recent name="com.fxkc.care.config" />
<recent name="com.fxkc.care.common" />
<recent name="com.fxkc.care.enums" />
</key>
</component>
<component name="RunManager">
<configuration name="CrowdCareApplication" type="SpringBootApplicationConfigurationType" factoryName="Spring Boot" nameIsGenerated="true">
<module name="crowd_care" />
<option name="SPRING_BOOT_MAIN_CLASS" value="com.fxkc.care.CrowdCareApplication" />
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
</component>
<component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" />
<component name="TaskManager">
<task active="true" id="Default" summary="Default task">
<changelist id="e4c0d574-8a71-44e7-a1b0-a0b5ee2c62f3" name="Changes" comment="" />
<created>1734079919051</created>
<option name="number" value="Default" />
<option name="presentableId" value="Default" />
<updated>1734079919051</updated>
<workItem from="1734079920148" duration="4512000" />
<workItem from="1734084442688" duration="2246000" />
<workItem from="1734138010805" duration="88000" />
<workItem from="1734138119849" duration="4347000" />
</task>
<task id="LOCAL-00001" summary="日志配置">
<option name="closed" value="true" />
<created>1734141309428</created>
<option name="number" value="00001" />
<option name="presentableId" value="LOCAL-00001" />
<option name="project" value="LOCAL" />
<updated>1734141309428</updated>
</task>
<option name="localTasksCounter" value="2" />
<servers />
</component>
<component name="TypeScriptGeneratedFilesManager">
<option name="version" value="3" />
</component>
<component name="VcsManagerConfiguration">
<MESSAGE value="日志配置" />
<option name="LAST_COMMIT_MESSAGE" value="日志配置" />
</component>
<component name="XDebuggerManager">
<breakpoint-manager>
<breakpoints>
<line-breakpoint enabled="true" type="java-line">
<url>file://$PROJECT_DIR$/src/main/java/com/fxkc/care/filter/GatewayFilter.java</url>
<line>74</line>
<option name="timeStamp" value="1" />
</line-breakpoint>
</breakpoints>
</breakpoint-manager>
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.fxkc</groupId>
<artifactId>crowd_care</artifactId>
<version>1.0-SNAPSHOT</version>
<name>crowd_care</name>
<description>人流关怀系统</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.6</version>
<relativePath/>
</parent>
<properties>
<java.version>1.8</java.version>
<!-- <version>2.6.6</version>-->
<!-- <spring-boot.version>2.6.6</spring-boot.version>-->
<mybatis-plus.version>3.5.4</mybatis-plus.version>
<pagehelper.boot.version>1.4.6</pagehelper.boot.version>
<druid.version>1.1.13</druid.version>
<!-- <jedis.version>3.2.0</jedis.version>-->
<swagger.version>2.9.2</swagger.version>
<java.jwt.version>3.9.0</java.jwt.version>
<jjwt.version>0.9.1</jjwt.version>
<logback.filter.version>3.0.12</logback.filter.version>
<io.swagger.version>1.5.22</io.swagger.version>
<swagger.ui.version>1.9.5</swagger.ui.version>
<hutool.version>5.7.9</hutool.version>
<shiro.version>1.7.1</shiro.version>
<lang3.version>3.9</lang3.version>
<fastjson.version>1.2.20</fastjson.version>
<orai18n.version>12.1.0.2.0</orai18n.version>
<oracle.jdbc.version>11.2.0.3</oracle.jdbc.version>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- <dependency>-->
<!-- <groupId>org.springframework.boot</groupId>-->
<!-- <artifactId>spring-boot-starter-parent</artifactId>-->
<!-- <version>2.6.15</version>-->
<!-- </dependency>-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<!-- <version>${spring-boot.version}</version>-->
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
<!-- <version>${spring-boot.version}</version>-->
</dependency>
<!-- 切面工具-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
<!-- <version>${spring-boot.version}</version>-->
</dependency>
<!-- 添加oracle驱动依赖 -->
<dependency>
<groupId>com.oracle</groupId>
<artifactId>ojdbc6</artifactId>
<version>${oracle.jdbc.version}</version>
</dependency>
<!--数据库连接池配置-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.17</version>
</dependency>
<!--hibernate-validator-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
<!-- <version>${spring-boot.version}</version>-->
</dependency>
<!--redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<!-- <version>${spring-boot.version}</version>-->
</dependency>
<!-- <dependency>-->
<!-- <groupId>redis.clients</groupId>-->
<!-- <artifactId>jedis</artifactId>-->
<!-- <version>${jedis.version}</version>-->
<!-- </dependency>-->
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
<optional>true</optional>
</dependency>
<!--mybatis-plus-->
<!-- <dependency>-->
<!-- <groupId>org.mybatis.spring.boot</groupId>-->
<!-- <artifactId>mybatis-spring-boot-starter</artifactId>-->
<!-- <version>2.1.0</version>-->
<!-- </dependency>-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-extension</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<!--fastjson-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
<!--常用工具-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>${lang3.version}</version>
</dependency>
<!--MD5工具用-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>${shiro.version}</version>
</dependency>
<!--JWT工具用-->
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>${java.jwt.version}</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>${jjwt.version}</version>
</dependency>
<!--github分页插件-->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>${pagehelper.boot.version}</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>${hutool.version}</version>
</dependency>
<!--swagger2-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>${swagger.version}</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>${swagger.version}</version>
</dependency>
<!-- logback-filter工具-->
<dependency>
<groupId>org.codehaus.janino</groupId>
<artifactId>commons-compiler</artifactId>
<version>${logback.filter.version}</version>
</dependency>
<dependency>
<groupId>org.codehaus.janino</groupId>
<artifactId>janino</artifactId>
<version>${logback.filter.version}</version>
</dependency>
<!--swaggerui 几个自定义界面方便查看接口-->
<!--访问路径:http://localhost:8080/doc.html-->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>swagger-bootstrap-ui</artifactId>
<version>${swagger.ui.version}</version>
</dependency>
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-annotations</artifactId>
<version>${io.swagger.version}</version>
</dependency>
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-models</artifactId>
<version>${io.swagger.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<!-- <version>${spring-boot.version}</version>-->
<!-- 打包时会将本地jar一起打包 -->
<configuration>
<includeSystemScope>true</includeSystemScope>
</configuration>
</plugin>
</plugins>
</build>
</project>
\ No newline at end of file
package com.fxkc.care;
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
/**
* @author JYH
* @date 2024/12/13 16:56
* @describe
*/
@EnableAsync
@SpringBootApplication(exclude = DruidDataSourceAutoConfigure.class)
@EnableSwagger2
@EnableTransactionManagement
@ServletComponentScan
@MapperScan("com.fxkc.care.mapper")
@EnableAspectJAutoProxy
@EnableScheduling
public class CrowdCareApplication {
public static void main(String[] args) {
SpringApplication.run(CrowdCareApplication.class, args);
}
}
package com.fxkc.care.common;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.commons.lang3.ObjectUtils;
@Data
@NoArgsConstructor
public class AjaxResult<T> {
private int code;
private String errorMsg;
private T data;
public AjaxResult(Type type, T data, String errorMsg) {
super();
this.code = type.getCode();
this.data = data;
this.errorMsg = ObjectUtils.isEmpty(errorMsg) ? type.getDesc() : errorMsg;
}
public AjaxResult(Type type) {
this.code = type.getCode();
this.errorMsg = type.getDesc();
}
public AjaxResult(Type type, T data) {
this.code = type.getCode();
this.errorMsg = type.getDesc();
this.data = data;
}
public AjaxResult(int code, String message, T data) {
this.code = code;
this.errorMsg = message;
this.data = data;
}
public static <T> AjaxResult<T> success(T data) {
return new AjaxResult(Type.SUCCESS, data, null);
}
public static <T> AjaxResult<T> ok() {
return new AjaxResult(Type.SUCCESS, "操作成功!", null);
}
public static <T> AjaxResult<T> error(String errorMsg) {
return new AjaxResult(Type.ERROR, null, errorMsg);
}
public static <T> AjaxResult<T> error(Type type, String errorMsg) {
return new AjaxResult(type, null, errorMsg);
}
public static <T> AjaxResult<T> error401(String errorMsg) {
return new AjaxResult(Type.UNAUTHORIZED, null, errorMsg);
}
public static <T> AjaxResult<T> error601(String errorMsg) {
return new AjaxResult(Type.INVALID_TOKEN, null, errorMsg);
}
public static <T> AjaxResult<T> error602(String errorMsg) {
return new AjaxResult(Type.OTHER_CLIENT_LOGIN, null, errorMsg);
}
public static <T> AjaxResult<T> error603(String errorMsg) {
return new AjaxResult(Type.TOKEN_TIMEOUT, null, errorMsg);
}
public enum Type {
SUCCESS(1, "success"),
ERROR(0, "error"),
UNAUTHORIZED(401, "unauthorized"),
INVALID_TOKEN(601, "invalidToken"),//不合法的token
OTHER_CLIENT_LOGIN(602, "otherClientLogin"),//异地登录被挤下线
TOKEN_TIMEOUT(603, "tokenTimeout"),//token超时失效
;
private int code;
private String desc;
Type(int code, String desc) {
this.code = code;
this.desc = desc;
}
public int getCode() {
return this.code;
}
public String getDesc() {
return this.desc;
}
}
}
package com.fxkc.care.config;
import com.fxkc.care.common.AjaxResult;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import java.util.List;
import java.util.stream.Collectors;
/**
* 全局异常处理器
*/
@Slf4j
@RestControllerAdvice
public class ControllerExceptionHandler {
/**
* 全局异常.
*
* @param e the e
* @return R
*/
@ExceptionHandler(Exception.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public AjaxResult handleGlobalException(Exception e) {
log.error("全局异常信息 ex={}", e.getMessage(), e);
return AjaxResult.error(e.getLocalizedMessage());
}
@ExceptionHandler(MethodArgumentNotValidException.class)
public AjaxResult methodArgumentNotValidException(Exception e) {
MethodArgumentNotValidException m = (MethodArgumentNotValidException) e;
BindingResult bindingResult = m.getBindingResult();
List<String> collect = bindingResult.getFieldErrors().stream().map(item -> item.getDefaultMessage()).collect(Collectors.toList());
return AjaxResult.error(StringUtils.join(collect, ";"));
}
@ExceptionHandler(HttpMessageNotReadableException.class)
public AjaxResult httpMessageNotReadableException(Exception e) {
return AjaxResult.error("数据输入格式错误!!");
}
@ExceptionHandler(IllegalArgumentException.class)
public AjaxResult illegalArgumentException(Exception e) {
return AjaxResult.error(e.getMessage());
}
}
package com.fxkc.care.config;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
@RequiredArgsConstructor
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOriginPatterns("*")//springboot2.4以上写法 允许向该服务器提交请求的origin,*表示全部允许。
.allowedMethods("GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS")
.allowedHeaders("*")
.allowCredentials(true);// 允许请求携带cookies
}
@Bean
@Primary
public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer(Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer) {
return builder -> builder
.serializerByType(Long.class, ToStringSerializer.instance)
.build();
}
}
package com.fxkc.care.config;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import com.fxkc.care.utils.RequestUtil;
import com.fxkc.care.vo.UserInfoVO;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;
import java.util.Date;
/**
* mybatis-plus自动填充
*/
@Component
public class CustomMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
this.strictInsertFill(metaObject, "createTime", Date.class, new Date());
UserInfoVO userInfo = RequestUtil.getUserInfo();
if (userInfo != null) {
this.strictInsertFill(metaObject, "createUser", String.class, userInfo.getUserName());
} else {
this.strictInsertFill(metaObject, "createUser", String.class, "auto");
}
}
@Override
public void updateFill(MetaObject metaObject) {
this.strictUpdateFill(metaObject, "updateTime", Date.class, new Date());
UserInfoVO userInfo = RequestUtil.getUserInfo();
if (userInfo != null) {
this.strictUpdateFill(metaObject, "updateUser", String.class, userInfo.getUserName());
} else {
this.strictUpdateFill(metaObject, "updateUser", String.class, "auto");
}
}
}
package com.fxkc.care.config;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.tomcat.util.http.fileupload.servlet.ServletFileUpload;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
/**
* 解决HttpServletRequest.getInputStream()和getReader()只能读取一次的问题
*/
public class CustomRequestWrapper extends HttpServletRequestWrapper {
private byte[] body;
public CustomRequestWrapper(HttpServletRequest request) throws IOException {
super(request);
if (ServletFileUpload.isMultipartContent(request)) {
return;
}
StringBuilder sb = new StringBuilder();
String line;
BufferedReader reader = request.getReader();
while ((line = reader.readLine()) != null) {
sb.append(line);
}
String body = sb.toString();
this.body = body.getBytes(StandardCharsets.UTF_8);
}
public String getBody() {
if (ObjectUtils.isNotEmpty(this.body)) {
return new String(this.body, StandardCharsets.UTF_8);
}
return null;
}
@Override
public ServletInputStream getInputStream() {
final ByteArrayInputStream bais = new ByteArrayInputStream(body);
return new ServletInputStream() {
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener readListener) {
}
@Override
public int read() {
return bais.read();
}
};
}
@Override
public BufferedReader getReader() {
return new BufferedReader(new InputStreamReader(this.getInputStream()));
}
}
package com.fxkc.care.config;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@EnableTransactionManagement
@Configuration
public class MybatisPlusConfig {
//分页插件
@Bean
public MybatisPlusInterceptor paginationInterceptor() {
MybatisPlusInterceptor paginationInterceptor = new MybatisPlusInterceptor();
paginationInterceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.ORACLE));
return paginationInterceptor;
}
}
package com.fxkc.care.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
public class RedisConfig {
/**
* 自定义RedisTemplate组件的key value的序列化规则
*/
@Bean(name = "redisTemplate")
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new StringRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(new Jackson2JsonRedisSerializer(Object.class));
return redisTemplate;
}
/**
* 注册自定义RedisCacheConfiguration组件,解决@Cacheable @Cacheput注解在向Redis中保存的Value是java序列化乱码的问题
*
* @return
*/
@Bean
public RedisCacheConfiguration redisCacheConfiguration() {
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
config = config.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(RedisSerializer.json()));
return config;
}
}
package com.fxkc.care.config;
import com.fxkc.care.common.AjaxResult;
import com.fxkc.care.utils.RequestUtil;
import com.fxkc.care.vo.UserInfoVO;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;
@Aspect
@Component
@Slf4j
public class RepeatSubmitAspect {
@Autowired
private RedisTemplate redisTemplate;
@Value("${repeat.submit.period}")
private String repeatSubmitPeriod;
@Around("@annotation(repeatSubmitValidation)")
public Object doAround(ProceedingJoinPoint proceedingJoinPoint, RepeatSubmitValidation repeatSubmitValidation)
throws Throwable {
UserInfoVO userInfo = RequestUtil.getUserInfo();
if (repeatSubmitValidation != null) {
// 类名
String className = proceedingJoinPoint.getTarget().getClass().getName();
// 方法名
String methodName = proceedingJoinPoint.getSignature().getName();
// redis-key
String fullName = userInfo.getLoginName() + "-" + className + "." + methodName;
Long result = redisTemplate.opsForValue().increment(fullName, 1);
if (result > 1) {
log.warn("**************** {} Method Repeat Submit ****************", className + "." + methodName);
return AjaxResult.error("请勿重复提交!");
}
redisTemplate.expire(fullName, Integer.valueOf(repeatSubmitPeriod), TimeUnit.MILLISECONDS);
}
return proceedingJoinPoint.proceed();
}
}
package com.fxkc.care.config;
import java.lang.annotation.*;
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RepeatSubmitValidation {
}
package com.fxkc.care.config;
import com.fxkc.care.enums.CommonEnum;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.ParameterBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Parameter;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import java.util.ArrayList;
import java.util.List;
@Configuration
public class SwaggerConfig {
@Bean
public Docket createRestApi() {
List<Parameter> parameters = new ArrayList<>();
ParameterBuilder parameterBuilder = new ParameterBuilder();
parameterBuilder.name(CommonEnum.USER_TOKEN.getCode()).description("token").modelRef(new ModelRef("string"))
.parameterType("header").required(true).build();
parameters.add(parameterBuilder.build());
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("com.fxkc.care.controller"))
.paths(PathSelectors.any()).build().globalOperationParameters(parameters);
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("人流关怀系统-API接口文档")
.description("人流关怀系统-API接口文档")
.termsOfServiceUrl("")
.version("1.0")
.build();
}
}
package com.fxkc.care.controller;
import com.fxkc.care.common.AjaxResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/**
* @author JYH
* @date 2024/12/14 9:20
* @describe
*/
@RestController
@RequestMapping("test")
public class TestController {
@GetMapping("/getKey")
public AjaxResult test(@RequestParam String key) {
return AjaxResult.success(key);
}
}
package com.fxkc.care.enums;
public enum CommonEnum {
USER_TOKEN("user_token"),
USER_INFO("user_info"),
;
private String code;
CommonEnum(String code) {
this.code = code;
}
public String getCode() {
return code;
}
}
package com.fxkc.care.filter;
import com.alibaba.fastjson.JSONObject;
import com.fxkc.care.common.AjaxResult;
import com.fxkc.care.config.CustomRequestWrapper;
import com.fxkc.care.enums.CommonEnum;
import com.fxkc.care.utils.*;
import com.fxkc.care.vo.UserInfoVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.annotation.Order;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.Base64Utils;
import javax.annotation.Resource;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
@WebFilter(filterName = "gatewayFilter", description = "网关过滤器:jwt的校验、白名单资源的处理", urlPatterns = "/*")
@Order(1)
@Slf4j
public class GatewayFilter implements Filter {
private static final PathMatcherUtil pathMatcherUtil = new AntPathMatcher();
private String filterName;
@Resource
private RedisTemplate redisTemplate;
@Value("${whiteUrlList}")
private String whiteUrlList;
@Override
public void init(FilterConfig filterConfig) {
filterName = filterConfig.getFilterName();
log.info("{} Filter Ready Init...", filterName);
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
String servletPath = req.getServletPath();
String method = req.getMethod();
CustomRequestWrapper wrapperRequest = new CustomRequestWrapper(req);
//1.打印记录API请求
String parameterMapString = "";
if ("get".equalsIgnoreCase(method)) {
Map<String, String[]> parameterMap = request.getParameterMap();
parameterMapString = JSONObject.toJSON(parameterMap).toString();
} else {
parameterMapString = wrapperRequest.getBody();
}
String ipAddr = IpUtil.getIpAddr(req);
log.debug("用户[{}]请求API=[{}][{}],请求参数=[{}]", ipAddr, method, servletPath, parameterMapString);
//2.URL白名单的过滤放行
if (whiteUrlList != null && whiteUrlList.trim().length() > 0) {
for (String whiteUrl : whiteUrlList.split(",")) {
String uriPattern = whiteUrl.trim();
if (pathMatcherUtil.match(uriPattern, servletPath)) {
chain.doFilter(wrapperRequest, response);
return;
}
}
}
//3.从请求头获取首部字段userToken并校验
String userToken = Optional.ofNullable(req.getHeader(CommonEnum.USER_TOKEN.getCode())).orElse(request.getParameter(CommonEnum.USER_TOKEN.getCode()));
try {
AjaxResult checkResult = JwtUtil.checkJwt(userToken);
if (checkResult.getCode() != AjaxResult.Type.SUCCESS.getCode()) {
log.error("jwt校验不通过:{}", checkResult);
((HttpServletResponse) response).sendError(checkResult.getCode(), checkResult.getErrorMsg().toString());
} else {
//从token中获取loginName
String requestJwtPayload = userToken.split("\\.")[1];
JSONObject payloadJSON = (JSONObject) JSONObject.parse(Base64Utils.decode(requestJwtPayload.getBytes()));
String loginName = (String) payloadJSON.get("loginName");
//重置token有效时间
redisTemplate.opsForValue().getOperations().expire("jwtsalt:" + loginName, 24, TimeUnit.HOURS);
//从redis中获取UserInfo放入Request.attributes中,供上下文用
Map<String, Object> userInfoMap = (Map<String, Object>) redisTemplate.opsForHash().get(
CommonEnum.USER_INFO.getCode(), loginName);
UserInfoVO userInfoVO = (UserInfoVO) MapBeanConvertUtil.convertMap2Bean(UserInfoVO.class, userInfoMap);
request.setAttribute(CommonEnum.USER_INFO.getCode(), userInfoVO);
chain.doFilter(wrapperRequest, response);
}
} catch (Exception e) {
log.error("校验jwt异常!", e);
((HttpServletResponse) response).sendError(AjaxResult.Type.INVALID_TOKEN.getCode(), "token不合法Exception!");//不合法的token
}
}
@Override
public void destroy() {
log.info("{}过滤器准备销毁...", filterName);
}
}
package com.fxkc.care.utils;
import org.springframework.util.StringUtils;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* {@link PathMatcherUtil} implementation for Ant-style path patterns.
*
* <p>Part of this mapping code has been kindly borrowed from <a href="http://ant.apache.org">Apache Ant</a>. (borrowed from spring)
*
* <p>The mapping matches URLs using the following rules:<br>
*
* <ul>
* <li>{@code ?} matches one character</li>
* <li>{@code *} matches zero or more characters</li>
* <li>{@code **} matches zero or more <em>directories</em> in a path</li>
* <li>{@code {spring:[a-z]+}} matches the regexp {@code [a-z]+} as a path variable named "spring"</li>
* </ul>
*
* <h3>Examples</h3>
* <ul>
* <li>{@code com/t?st.jsp} &mdash; matches {@code com/test.jsp} but also {@code com/tast.jsp} or {@code com/txst.jsp}</li>
* <li>{@code com/*.jsp} &mdash; matches all {@code .jsp} files in the {@code com} directory</li>
* <li><code>com/&#42;&#42;/test.jsp</code> &mdash; matches all {@code test.jsp} files underneath the {@code com} path</li>
* <li><code>org/springframework/&#42;&#42;/*.jsp</code> &mdash; matches all {@code .jsp} files underneath the {@code org/springframework} path</li>
* <li><code>org/&#42;&#42;/servlet/bla.jsp</code> &mdash; matches {@code org/springframework/servlet/bla.jsp} but also
* {@code org/springframework/testing/servlet/bla.jsp} and {@code org/servlet/bla.jsp}</li>
* <li>{@code com/{filename:\\w+}.jsp} will match {@code com/test.jsp} and assign the value {@code test} to the {@code filename} variable</li>
* </ul>
*
* <p><strong>Note:</strong> a pattern and a path must both be absolute or must both be relative in order for the two to match.
* Therefore it is recommended that users of this implementation to sanitize patterns
* in order to prefix them with "/" as it makes sense in the context in which they're used.
*/
public class AntPathMatcher implements PathMatcherUtil {
/**
* Default path separator: "/"
*/
public static final String DEFAULT_PATH_SEPARATOR = "/";
private static final int CACHE_TURNOFF_THRESHOLD = 65536;
private static final Pattern VARIABLE_PATTERN = Pattern.compile("\\{[^/]+?\\}");
private static final char[] WILDCARD_CHARS = {'*', '?', '{'};
final Map<String, AntPathStringMatcher> stringMatcherCache = new ConcurrentHashMap<String, AntPathStringMatcher>(256);
private final Map<String, String[]> tokenizedPatternCache = new ConcurrentHashMap<String, String[]>(256);
private String pathSeparator;
private PathSeparatorPatternCache pathSeparatorPatternCache;
private boolean caseSensitive = true;
private boolean trimTokens = false;
private volatile Boolean cachePatterns;
/**
* Create a new instance with the {@link #DEFAULT_PATH_SEPARATOR}.
*/
public AntPathMatcher() {
this.pathSeparator = DEFAULT_PATH_SEPARATOR;
this.pathSeparatorPatternCache = new PathSeparatorPatternCache(DEFAULT_PATH_SEPARATOR);
}
/**
* A convenient, alternative constructor to use with a custom path separator.
*
* @param pathSeparator the path separator to use, must not be {@code null}.
* @since 4.1
*/
public AntPathMatcher(String pathSeparator) {
if (pathSeparator == null) {
throw new IllegalArgumentException("'pathSeparator' is required");
}
this.pathSeparator = pathSeparator;
this.pathSeparatorPatternCache = new PathSeparatorPatternCache(pathSeparator);
}
/**
* Set the path separator to use for pattern parsing.
* <p>Default is "/", as in Ant.
*/
public void setPathSeparator(String pathSeparator) {
this.pathSeparator = (pathSeparator != null ? pathSeparator : DEFAULT_PATH_SEPARATOR);
this.pathSeparatorPatternCache = new PathSeparatorPatternCache(this.pathSeparator);
}
/**
* Specify whether to perform pattern matching in a case-sensitive fashion.
* <p>Default is {@code true}. Switch this to {@code false} for case-insensitive matching.
*
* @since 4.2
*/
public void setCaseSensitive(boolean caseSensitive) {
this.caseSensitive = caseSensitive;
}
/**
* Specify whether to trim tokenized paths and patterns.
* <p>Default is {@code false}.
*/
public void setTrimTokens(boolean trimTokens) {
this.trimTokens = trimTokens;
}
/**
* Specify whether to cache parsed pattern metadata for patterns passed
* into this matcher's {@link #match} method. A value of {@code true}
* activates an unlimited pattern cache; a value of {@code false} turns
* the pattern cache off completely.
* <p>Default is for the cache to be on, but with the variant to automatically
* turn it off when encountering too many patterns to cache at runtime
* (the threshold is 65536), assuming that arbitrary permutations of patterns
* are coming in, with little chance for encountering a recurring pattern.
*
* @see #getStringMatcher(String)
* @since 4.0.1
*/
public void setCachePatterns(boolean cachePatterns) {
this.cachePatterns = cachePatterns;
}
private void deactivatePatternCache() {
this.cachePatterns = false;
this.tokenizedPatternCache.clear();
this.stringMatcherCache.clear();
}
@Override
public boolean isPattern(String path) {
return (path.indexOf('*') != -1 || path.indexOf('?') != -1);
}
@Override
public boolean match(String pattern, String path) {
return doMatch(pattern, path, true, null);
}
@Override
public boolean matchStart(String pattern, String path) {
return doMatch(pattern, path, false, null);
}
/**
* Actually match the given {@code path} against the given {@code pattern}.
*
* @param pattern the pattern to match against
* @param path the path String to test
* @param fullMatch whether a full pattern match is required (else a pattern match
* as far as the given base path goes is sufficient)
* @return {@code true} if the supplied {@code path} matched, {@code false} if it didn't
*/
protected boolean doMatch(String pattern, String path, boolean fullMatch, Map<String, String> uriTemplateVariables) {
if (path.startsWith(this.pathSeparator) != pattern.startsWith(this.pathSeparator)) {
return false;
}
String[] pattDirs = tokenizePattern(pattern);
if (fullMatch && this.caseSensitive && !isPotentialMatch(path, pattDirs)) {
return false;
}
String[] pathDirs = tokenizePath(path);
int pattIdxStart = 0;
int pattIdxEnd = pattDirs.length - 1;
int pathIdxStart = 0;
int pathIdxEnd = pathDirs.length - 1;
// Match all elements up to the first **
while (pattIdxStart <= pattIdxEnd && pathIdxStart <= pathIdxEnd) {
String pattDir = pattDirs[pattIdxStart];
if ("**".equals(pattDir)) {
break;
}
if (!matchStrings(pattDir, pathDirs[pathIdxStart], uriTemplateVariables)) {
return false;
}
pattIdxStart++;
pathIdxStart++;
}
if (pathIdxStart > pathIdxEnd) {
// Path is exhausted, only match if rest of pattern is * or **'s
if (pattIdxStart > pattIdxEnd) {
return (pattern.endsWith(this.pathSeparator) == path.endsWith(this.pathSeparator));
}
if (!fullMatch) {
return true;
}
if (pattIdxStart == pattIdxEnd && pattDirs[pattIdxStart].equals("*") && path.endsWith(this.pathSeparator)) {
return true;
}
for (int i = pattIdxStart; i <= pattIdxEnd; i++) {
if (!pattDirs[i].equals("**")) {
return false;
}
}
return true;
} else if (pattIdxStart > pattIdxEnd) {
// String not exhausted, but pattern is. Failure.
return false;
} else if (!fullMatch && "**".equals(pattDirs[pattIdxStart])) {
// Path start definitely matches due to "**" part in pattern.
return true;
}
// up to last '**'
while (pattIdxStart <= pattIdxEnd && pathIdxStart <= pathIdxEnd) {
String pattDir = pattDirs[pattIdxEnd];
if (pattDir.equals("**")) {
break;
}
if (!matchStrings(pattDir, pathDirs[pathIdxEnd], uriTemplateVariables)) {
return false;
}
pattIdxEnd--;
pathIdxEnd--;
}
if (pathIdxStart > pathIdxEnd) {
// String is exhausted
for (int i = pattIdxStart; i <= pattIdxEnd; i++) {
if (!pattDirs[i].equals("**")) {
return false;
}
}
return true;
}
while (pattIdxStart != pattIdxEnd && pathIdxStart <= pathIdxEnd) {
int patIdxTmp = -1;
for (int i = pattIdxStart + 1; i <= pattIdxEnd; i++) {
if (pattDirs[i].equals("**")) {
patIdxTmp = i;
break;
}
}
if (patIdxTmp == pattIdxStart + 1) {
// '**/**' situation, so skip one
pattIdxStart++;
continue;
}
// Find the pattern between padIdxStart & padIdxTmp in str between
// strIdxStart & strIdxEnd
int patLength = (patIdxTmp - pattIdxStart - 1);
int strLength = (pathIdxEnd - pathIdxStart + 1);
int foundIdx = -1;
strLoop:
for (int i = 0; i <= strLength - patLength; i++) {
for (int j = 0; j < patLength; j++) {
String subPat = pattDirs[pattIdxStart + j + 1];
String subStr = pathDirs[pathIdxStart + i + j];
if (!matchStrings(subPat, subStr, uriTemplateVariables)) {
continue strLoop;
}
}
foundIdx = pathIdxStart + i;
break;
}
if (foundIdx == -1) {
return false;
}
pattIdxStart = patIdxTmp;
pathIdxStart = foundIdx + patLength;
}
for (int i = pattIdxStart; i <= pattIdxEnd; i++) {
if (!pattDirs[i].equals("**")) {
return false;
}
}
return true;
}
private boolean isPotentialMatch(String path, String[] pattDirs) {
if (!this.trimTokens) {
int pos = 0;
for (String pattDir : pattDirs) {
int skipped = skipSeparator(path, pos, this.pathSeparator);
pos += skipped;
skipped = skipSegment(path, pos, pattDir);
if (skipped < pattDir.length()) {
return (skipped > 0 || (pattDir.length() > 0 && isWildcardChar(pattDir.charAt(0))));
}
pos += skipped;
}
}
return true;
}
private int skipSegment(String path, int pos, String prefix) {
int skipped = 0;
for (int i = 0; i < prefix.length(); i++) {
char c = prefix.charAt(i);
if (isWildcardChar(c)) {
return skipped;
}
int currPos = pos + skipped;
if (currPos >= path.length()) {
return 0;
}
if (c == path.charAt(currPos)) {
skipped++;
}
}
return skipped;
}
private int skipSeparator(String path, int pos, String separator) {
int skipped = 0;
while (path.startsWith(separator, pos + skipped)) {
skipped += separator.length();
}
return skipped;
}
private boolean isWildcardChar(char c) {
for (char candidate : WILDCARD_CHARS) {
if (c == candidate) {
return true;
}
}
return false;
}
/**
* Tokenize the given path pattern into parts, based on this matcher's settings.
* <p>Performs caching based on {@link #setCachePatterns}, delegating to
* {@link #tokenizePath(String)} for the actual tokenization algorithm.
*
* @param pattern the pattern to tokenize
* @return the tokenized pattern parts
*/
protected String[] tokenizePattern(String pattern) {
String[] tokenized = null;
Boolean cachePatterns = this.cachePatterns;
if (cachePatterns == null || cachePatterns.booleanValue()) {
tokenized = this.tokenizedPatternCache.get(pattern);
}
if (tokenized == null) {
tokenized = tokenizePath(pattern);
if (cachePatterns == null && this.tokenizedPatternCache.size() >= CACHE_TURNOFF_THRESHOLD) {
// Try to adapt to the runtime situation that we're encountering:
// There are obviously too many different patterns coming in here...
// So let's turn off the cache since the patterns are unlikely to be reoccurring.
deactivatePatternCache();
return tokenized;
}
if (cachePatterns == null || cachePatterns.booleanValue()) {
this.tokenizedPatternCache.put(pattern, tokenized);
}
}
return tokenized;
}
/**
* Tokenize the given path String into parts, based on this matcher's settings.
*
* @param path the path to tokenize
* @return the tokenized path parts
*/
protected String[] tokenizePath(String path) {
return StringUtils.tokenizeToStringArray(path, this.pathSeparator, this.trimTokens, true);
}
/**
* Test whether or not a string matches against a pattern.
*
* @param pattern the pattern to match against (never {@code null})
* @param str the String which must be matched against the pattern (never {@code null})
* @return {@code true} if the string matches against the pattern, or {@code false} otherwise
*/
private boolean matchStrings(String pattern, String str, Map<String, String> uriTemplateVariables) {
return getStringMatcher(pattern).matchStrings(str, uriTemplateVariables);
}
/**
* Build or retrieve an {@link AntPathStringMatcher} for the given pattern.
* <p>The default implementation checks this AntPathMatcher's internal cache
* (see {@link #setCachePatterns}), creating a new AntPathStringMatcher instance
* if no cached copy is found.
* <p>When encountering too many patterns to cache at runtime (the threshold is 65536),
* it turns the default cache off, assuming that arbitrary permutations of patterns
* are coming in, with little chance for encountering a recurring pattern.
* <p>This method may be overridden to implement a custom cache strategy.
*
* @param pattern the pattern to match against (never {@code null})
* @return a corresponding AntPathStringMatcher (never {@code null})
* @see #setCachePatterns
*/
protected AntPathStringMatcher getStringMatcher(String pattern) {
AntPathStringMatcher matcher = null;
Boolean cachePatterns = this.cachePatterns;
if (cachePatterns == null || cachePatterns.booleanValue()) {
matcher = this.stringMatcherCache.get(pattern);
}
if (matcher == null) {
matcher = new AntPathStringMatcher(pattern, this.caseSensitive);
if (cachePatterns == null && this.stringMatcherCache.size() >= CACHE_TURNOFF_THRESHOLD) {
// Try to adapt to the runtime situation that we're encountering:
// There are obviously too many different patterns coming in here...
// So let's turn off the cache since the patterns are unlikely to be reoccurring.
deactivatePatternCache();
return matcher;
}
if (cachePatterns == null || cachePatterns.booleanValue()) {
this.stringMatcherCache.put(pattern, matcher);
}
}
return matcher;
}
/**
* Given a pattern and a full path, determine the pattern-mapped part. <p>For example: <ul>
* <li>'{@code /docs/cvs/commit.html}' and '{@code /docs/cvs/commit.html} -> ''</li>
* <li>'{@code /docs/*}' and '{@code /docs/cvs/commit} -> '{@code cvs/commit}'</li>
* <li>'{@code /docs/cvs/*.html}' and '{@code /docs/cvs/commit.html} -> '{@code commit.html}'</li>
* <li>'{@code /docs/**}' and '{@code /docs/cvs/commit} -> '{@code cvs/commit}'</li>
* <li>'{@code /docs/**\/*.html}' and '{@code /docs/cvs/commit.html} -> '{@code cvs/commit.html}'</li>
* <li>'{@code /*.html}' and '{@code /docs/cvs/commit.html} -> '{@code docs/cvs/commit.html}'</li>
* <li>'{@code *.html}' and '{@code /docs/cvs/commit.html} -> '{@code /docs/cvs/commit.html}'</li>
* <li>'{@code *}' and '{@code /docs/cvs/commit.html} -> '{@code /docs/cvs/commit.html}'</li> </ul>
* <p>Assumes that {@link #match} returns {@code true} for '{@code pattern}' and '{@code path}', but
* does <strong>not</strong> enforce this.
*/
@Override
public String extractPathWithinPattern(String pattern, String path) {
String[] patternParts = StringUtils.tokenizeToStringArray(pattern, this.pathSeparator, this.trimTokens, true);
String[] pathParts = StringUtils.tokenizeToStringArray(path, this.pathSeparator, this.trimTokens, true);
StringBuilder builder = new StringBuilder();
boolean pathStarted = false;
for (int segment = 0; segment < patternParts.length; segment++) {
String patternPart = patternParts[segment];
if (patternPart.indexOf('*') > -1 || patternPart.indexOf('?') > -1) {
for (; segment < pathParts.length; segment++) {
if (pathStarted || (segment == 0 && !pattern.startsWith(this.pathSeparator))) {
builder.append(this.pathSeparator);
}
builder.append(pathParts[segment]);
pathStarted = true;
}
}
}
return builder.toString();
}
@Override
public Map<String, String> extractUriTemplateVariables(String pattern, String path) {
Map<String, String> variables = new LinkedHashMap<String, String>();
boolean result = doMatch(pattern, path, true, variables);
if (!result) {
throw new IllegalStateException("Pattern \"" + pattern + "\" is not a match for \"" + path + "\"");
}
return variables;
}
/**
* Combine two patterns into a new pattern.
* <p>This implementation simply concatenates the two patterns, unless
* the first pattern contains a file extension match (e.g., {@code *.html}).
* In that case, the second pattern will be merged into the first. Otherwise,
* an {@code IllegalArgumentException} will be thrown.
* <h3>Examples</h3>
* <table border="1">
* <tr><th>Pattern 1</th><th>Pattern 2</th><th>Result</th></tr>
* <tr><td>{@code null}</td><td>{@code null}</td><td>&nbsp;</td></tr>
* <tr><td>/hotels</td><td>{@code null}</td><td>/hotels</td></tr>
* <tr><td>{@code null}</td><td>/hotels</td><td>/hotels</td></tr>
* <tr><td>/hotels</td><td>/bookings</td><td>/hotels/bookings</td></tr>
* <tr><td>/hotels</td><td>bookings</td><td>/hotels/bookings</td></tr>
* <tr><td>/hotels/*</td><td>/bookings</td><td>/hotels/bookings</td></tr>
* <tr><td>/hotels/&#42;&#42;</td><td>/bookings</td><td>/hotels/&#42;&#42;/bookings</td></tr>
* <tr><td>/hotels</td><td>{hotel}</td><td>/hotels/{hotel}</td></tr>
* <tr><td>/hotels/*</td><td>{hotel}</td><td>/hotels/{hotel}</td></tr>
* <tr><td>/hotels/&#42;&#42;</td><td>{hotel}</td><td>/hotels/&#42;&#42;/{hotel}</td></tr>
* <tr><td>/*.html</td><td>/hotels.html</td><td>/hotels.html</td></tr>
* <tr><td>/*.html</td><td>/hotels</td><td>/hotels.html</td></tr>
* <tr><td>/*.html</td><td>/*.txt</td><td>{@code IllegalArgumentException}</td></tr>
* </table>
*
* @param pattern1 the first pattern
* @param pattern2 the second pattern
* @return the combination of the two patterns
* @throws IllegalArgumentException if the two patterns cannot be combined
*/
@Override
public String combine(String pattern1, String pattern2) {
if (!StringUtils.hasText(pattern1) && !StringUtils.hasText(pattern2)) {
return "";
}
if (!StringUtils.hasText(pattern1)) {
return pattern2;
}
if (!StringUtils.hasText(pattern2)) {
return pattern1;
}
boolean pattern1ContainsUriVar = (pattern1.indexOf('{') != -1);
if (!pattern1.equals(pattern2) && !pattern1ContainsUriVar && match(pattern1, pattern2)) {
// /* + /hotel -> /hotel ; "/*.*" + "/*.html" -> /*.html
// However /user + /user -> /usr/user ; /{foo} + /bar -> /{foo}/bar
return pattern2;
}
// /hotels/* + /booking -> /hotels/booking
// /hotels/* + booking -> /hotels/booking
if (pattern1.endsWith(this.pathSeparatorPatternCache.getEndsOnWildCard())) {
return concat(pattern1.substring(0, pattern1.length() - 2), pattern2);
}
// /hotels/** + /booking -> /hotels/**/booking
// /hotels/** + booking -> /hotels/**/booking
if (pattern1.endsWith(this.pathSeparatorPatternCache.getEndsOnDoubleWildCard())) {
return concat(pattern1, pattern2);
}
int starDotPos1 = pattern1.indexOf("*.");
if (pattern1ContainsUriVar || starDotPos1 == -1 || this.pathSeparator.equals(".")) {
// simply concatenate the two patterns
return concat(pattern1, pattern2);
}
String ext1 = pattern1.substring(starDotPos1 + 1);
int dotPos2 = pattern2.indexOf('.');
String file2 = (dotPos2 == -1 ? pattern2 : pattern2.substring(0, dotPos2));
String ext2 = (dotPos2 == -1 ? "" : pattern2.substring(dotPos2));
boolean ext1All = (ext1.equals(".*") || ext1.equals(""));
boolean ext2All = (ext2.equals(".*") || ext2.equals(""));
if (!ext1All && !ext2All) {
throw new IllegalArgumentException("Cannot combine patterns: " + pattern1 + " vs " + pattern2);
}
String ext = (ext1All ? ext2 : ext1);
return file2 + ext;
}
private String concat(String path1, String path2) {
boolean path1EndsWithSeparator = path1.endsWith(this.pathSeparator);
boolean path2StartsWithSeparator = path2.startsWith(this.pathSeparator);
if (path1EndsWithSeparator && path2StartsWithSeparator) {
return path1 + path2.substring(1);
} else if (path1EndsWithSeparator || path2StartsWithSeparator) {
return path1 + path2;
} else {
return path1 + this.pathSeparator + path2;
}
}
/**
* Given a full path, returns a {@link Comparator} suitable for sorting patterns in order of
* explicitness.
* <p>This{@code Comparator} will {@linkplain Collections#sort(List, Comparator) sort}
* a list so that more specific patterns (without uri templates or wild cards) come before
* generic patterns. So given a list with the following patterns:
* <ol>
* <li>{@code /hotels/new}</li>
* <li>{@code /hotels/{hotel}}</li> <li>{@code /hotels/*}</li>
* </ol>
* the returned comparator will sort this list so that the order will be as indicated.
* <p>The full path given as parameter is used to test for exact matches. So when the given path
* is {@code /hotels/2}, the pattern {@code /hotels/2} will be sorted before {@code /hotels/1}.
*
* @param path the full path to use for comparison
* @return a comparator capable of sorting patterns in order of explicitness
*/
@Override
public Comparator<String> getPatternComparator(String path) {
return new AntPatternComparator(path);
}
/**
* Tests whether or not a string matches against a pattern via a {@link Pattern}.
* <p>The pattern may contain special characters: '*' means zero or more characters; '?' means one and
* only one character; '{' and '}' indicate a URI template pattern. For example <tt>/users/{user}</tt>.
*/
protected static class AntPathStringMatcher {
private static final Pattern GLOB_PATTERN = Pattern.compile("\\?|\\*|\\{((?:\\{[^/]+?\\}|[^/{}]|\\\\[{}])+?)\\}");
private static final String DEFAULT_VARIABLE_PATTERN = "(.*)";
private final Pattern pattern;
private final List<String> variableNames = new LinkedList<String>();
public AntPathStringMatcher(String pattern) {
this(pattern, true);
}
public AntPathStringMatcher(String pattern, boolean caseSensitive) {
StringBuilder patternBuilder = new StringBuilder();
Matcher matcher = GLOB_PATTERN.matcher(pattern);
int end = 0;
while (matcher.find()) {
patternBuilder.append(quote(pattern, end, matcher.start()));
String match = matcher.group();
if ("?".equals(match)) {
patternBuilder.append('.');
} else if ("*".equals(match)) {
patternBuilder.append(".*");
} else if (match.startsWith("{") && match.endsWith("}")) {
int colonIdx = match.indexOf(':');
if (colonIdx == -1) {
patternBuilder.append(DEFAULT_VARIABLE_PATTERN);
this.variableNames.add(matcher.group(1));
} else {
String variablePattern = match.substring(colonIdx + 1, match.length() - 1);
patternBuilder.append('(');
patternBuilder.append(variablePattern);
patternBuilder.append(')');
String variableName = match.substring(1, colonIdx);
this.variableNames.add(variableName);
}
}
end = matcher.end();
}
patternBuilder.append(quote(pattern, end, pattern.length()));
this.pattern = (caseSensitive ? Pattern.compile(patternBuilder.toString()) :
Pattern.compile(patternBuilder.toString(), Pattern.CASE_INSENSITIVE));
}
private String quote(String s, int start, int end) {
if (start == end) {
return "";
}
return Pattern.quote(s.substring(start, end));
}
/**
* Main entry point.
*
* @return {@code true} if the string matches against the pattern, or {@code false} otherwise.
*/
public boolean matchStrings(String str, Map<String, String> uriTemplateVariables) {
Matcher matcher = this.pattern.matcher(str);
if (matcher.matches()) {
if (uriTemplateVariables != null) {
// SPR-8455
if (this.variableNames.size() != matcher.groupCount()) {
throw new IllegalArgumentException("The number of capturing groups in the pattern segment " +
this.pattern + " does not match the number of URI template variables it defines, " +
"which can occur if capturing groups are used in a URI template regex. " +
"Use non-capturing groups instead.");
}
for (int i = 1; i <= matcher.groupCount(); i++) {
String name = this.variableNames.get(i - 1);
String value = matcher.group(i);
uriTemplateVariables.put(name, value);
}
}
return true;
} else {
return false;
}
}
}
/**
* The default {@link Comparator} implementation returned by
* {@link #getPatternComparator(String)}.
* <p>In order, the most "generic" pattern is determined by the following:
* <ul>
* <li>if it's null or a capture all pattern (i.e. it is equal to "/**")</li>
* <li>if the other pattern is an actual match</li>
* <li>if it's a catch-all pattern (i.e. it ends with "**"</li>
* <li>if it's got more "*" than the other pattern</li>
* <li>if it's got more "{foo}" than the other pattern</li>
* <li>if it's shorter than the other pattern</li>
* </ul>
*/
protected static class AntPatternComparator implements Comparator<String> {
private final String path;
public AntPatternComparator(String path) {
this.path = path;
}
/**
* Compare two patterns to determine which should match first, i.e. which
* is the most specific regarding the current path.
*
* @return a negative integer, zero, or a positive integer as pattern1 is
* more specific, equally specific, or less specific than pattern2.
*/
@Override
public int compare(String pattern1, String pattern2) {
PatternInfo info1 = new PatternInfo(pattern1);
PatternInfo info2 = new PatternInfo(pattern2);
if (info1.isLeastSpecific() && info2.isLeastSpecific()) {
return 0;
} else if (info1.isLeastSpecific()) {
return 1;
} else if (info2.isLeastSpecific()) {
return -1;
}
boolean pattern1EqualsPath = pattern1.equals(path);
boolean pattern2EqualsPath = pattern2.equals(path);
if (pattern1EqualsPath && pattern2EqualsPath) {
return 0;
} else if (pattern1EqualsPath) {
return -1;
} else if (pattern2EqualsPath) {
return 1;
}
if (info1.isPrefixPattern() && info2.getDoubleWildcards() == 0) {
return 1;
} else if (info2.isPrefixPattern() && info1.getDoubleWildcards() == 0) {
return -1;
}
if (info1.getTotalCount() != info2.getTotalCount()) {
return info1.getTotalCount() - info2.getTotalCount();
}
if (info1.getLength() != info2.getLength()) {
return info2.getLength() - info1.getLength();
}
if (info1.getSingleWildcards() < info2.getSingleWildcards()) {
return -1;
} else if (info2.getSingleWildcards() < info1.getSingleWildcards()) {
return 1;
}
if (info1.getUriVars() < info2.getUriVars()) {
return -1;
} else if (info2.getUriVars() < info1.getUriVars()) {
return 1;
}
return 0;
}
/**
* Value class that holds information about the pattern, e.g. number of
* occurrences of "*", "**", and "{" pattern elements.
*/
private static class PatternInfo {
private final String pattern;
private int uriVars;
private int singleWildcards;
private int doubleWildcards;
private boolean catchAllPattern;
private boolean prefixPattern;
private Integer length;
public PatternInfo(String pattern) {
this.pattern = pattern;
if (this.pattern != null) {
initCounters();
this.catchAllPattern = this.pattern.equals("/**");
this.prefixPattern = !this.catchAllPattern && this.pattern.endsWith("/**");
}
if (this.uriVars == 0) {
this.length = (this.pattern != null ? this.pattern.length() : 0);
}
}
protected void initCounters() {
int pos = 0;
while (pos < this.pattern.length()) {
if (this.pattern.charAt(pos) == '{') {
this.uriVars++;
pos++;
} else if (this.pattern.charAt(pos) == '*') {
if (pos + 1 < this.pattern.length() && this.pattern.charAt(pos + 1) == '*') {
this.doubleWildcards++;
pos += 2;
} else if (pos > 0 && !this.pattern.substring(pos - 1).equals(".*")) {
this.singleWildcards++;
pos++;
} else {
pos++;
}
} else {
pos++;
}
}
}
public int getUriVars() {
return this.uriVars;
}
public int getSingleWildcards() {
return this.singleWildcards;
}
public int getDoubleWildcards() {
return this.doubleWildcards;
}
public boolean isLeastSpecific() {
return (this.pattern == null || this.catchAllPattern);
}
public boolean isPrefixPattern() {
return this.prefixPattern;
}
public int getTotalCount() {
return this.uriVars + this.singleWildcards + (2 * this.doubleWildcards);
}
/**
* Returns the length of the given pattern, where template variables are considered to be 1 long.
*/
public int getLength() {
if (this.length == null) {
this.length = VARIABLE_PATTERN.matcher(this.pattern).replaceAll("#").length();
}
return this.length;
}
}
}
/**
* A simple cache for patterns that depend on the configured path separator.
*/
private static class PathSeparatorPatternCache {
private final String endsOnWildCard;
private final String endsOnDoubleWildCard;
public PathSeparatorPatternCache(String pathSeparator) {
this.endsOnWildCard = pathSeparator + "*";
this.endsOnDoubleWildCard = pathSeparator + "**";
}
public String getEndsOnWildCard() {
return this.endsOnWildCard;
}
public String getEndsOnDoubleWildCard() {
return this.endsOnDoubleWildCard;
}
}
}
package com.fxkc.care.utils;
import javax.servlet.http.HttpServletRequest;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Enumeration;
public class IpUtil {
/**
* 获取登录用户的IP地址
*/
public static String getIpAddr(HttpServletRequest request) {
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("WL-Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
if (ip.equals("0:0:0:0:0:0:0:1")) {
ip = "127.0.0.1";
}
if (ip.split(",").length > 1) {
ip = ip.split(",")[0];
}
if (ip.equals("127.0.0.1") || ip.equals("0:0:0:0:0:0:0:1")) {
//根据网卡获取本机配置的IP地址
InetAddress inetAddress = null;
try {
inetAddress = InetAddress.getLocalHost();
} catch (UnknownHostException e) {
e.printStackTrace();
}
ip = inetAddress.getHostAddress();
}
return ip;
}
// 获取本机局域网ip
public static String getLocalIp() {
try {
Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
while (interfaces.hasMoreElements()) {
NetworkInterface ni = interfaces.nextElement();
Enumeration<InetAddress> addresses = ni.getInetAddresses();
while (addresses.hasMoreElements()) {
InetAddress addr = addresses.nextElement();
if (!addr.isLinkLocalAddress() && !addr.isLoopbackAddress() && addr.isSiteLocalAddress()) {
return addr.getHostAddress();
}
}
}
} catch (SocketException e) {
e.printStackTrace();
}
return null;
}
}
package com.fxkc.care.utils;
import cn.hutool.extra.spring.SpringUtil;
import com.alibaba.fastjson.JSONObject;
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.auth0.jwt.interfaces.Verification;
import com.fxkc.care.common.AjaxResult;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwsHeader;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.impl.TextCodec;
import io.jsonwebtoken.impl.crypto.DefaultJwtSigner;
import io.jsonwebtoken.impl.crypto.JwtSigner;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.Base64Utils;
import javax.crypto.spec.SecretKeySpec;
import java.security.Key;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
/**
* @Description: JSON WEB TOKEN工具 JWT官网:https://jwt.io/introduction/
*/
@Slf4j
public class JwtUtil {
private static String JWT_ISSUER = "fxkc";//JWT签发者
/**
* 生成JWT
*/
public static String createJwt(Map<String, Object> customParams, String jwtSecret) {
return Jwts.builder()
.setHeader(createJwtHeader())
.addClaims(createJwtPayload(customParams))
.signWith(SignatureAlgorithm.HS256, Base64Utils.encodeToString(jwtSecret.getBytes()))
.compact();
}
private static Map<String, Object> createJwtHeader() {
Map<String, Object> map = new HashMap<>();
map.put(JwsHeader.TYPE, JwsHeader.JWT_TYPE);
map.put(JwsHeader.ALGORITHM, SignatureAlgorithm.HS256.getValue());//算法名称
return map;
}
/**
* 创建payload(载荷)中的声明部分
* 要求:不要放中文
*/
private static Map<String, Object> createJwtPayload(Map<String, Object> customParams) {
Map<String, Object> map = new HashMap<>();
//标准中注册的声明部分(建议但不强制参数)
map.put(Claims.ISSUER, JWT_ISSUER);//jwt签发者
// map.put(Claims.SUBJECT, null);//jwt所面向的用户
// map.put(Claims.AUDIENCE,null);//接收jwt的一方
// map.put(Claims.EXPIRATION, null);//jwt过期时间,交由Redis控制
// map.put(Claims.NOT_BEFORE, null);//定义在某个时间点之前,该jwt都是不可用的
map.put(Claims.ISSUED_AT, new Date().getTime() / 1000);//jwt签发时间,单位s
// map.put(Claims.ID, null);//jwt的唯一身份表示,主要用来作为一次性token,从而回避重放
if (Objects.nonNull(customParams)) {
map.putAll(customParams);
}
return map;
}
/**
* 根据Base64编码的header payload secret创建Signature
*/
private static String createSignature(String base64UrlEncodedHeader, String base64UrlEncodedPayload, String base64UrlEncodedSecret) {
byte[] keyBytes = TextCodec.BASE64.decode(base64UrlEncodedSecret);
Key key = new SecretKeySpec(keyBytes, SignatureAlgorithm.HS256.getJcaName());
JwtSigner signer = new DefaultJwtSigner(SignatureAlgorithm.HS256, key);
String jwt = base64UrlEncodedHeader + '.' + base64UrlEncodedPayload;
String base64UrlSignature = signer.sign(jwt);
return base64UrlSignature;
}
/**
* 解析JWT
*/
public static DecodedJWT parseJWT(String token, String jwtSecret) throws Exception {
DecodedJWT decodedJWT = null;
if (StringUtils.isBlank(token) || StringUtils.isBlank(jwtSecret)) {
return decodedJWT;
}
Verification require = JWT.require(Algorithm.HMAC256(jwtSecret.getBytes()));
decodedJWT = require.build().verify(token);
return decodedJWT;
}
/**
* 校验jwt的有效性
*/
public static AjaxResult checkJwt(String userToken) throws Exception {
//1.如果token为空
if (StringUtils.isBlank(userToken)) {
return AjaxResult.error401("token为空");
}
//2.解析jwt.payload
String requestJwtHead = userToken.split("\\.")[0];
String requestJwtPayload = userToken.split("\\.")[1];
String requestJwtSignature = userToken.split("\\.")[2];
JSONObject payloadJSON = (JSONObject) JSONObject.parse(Base64Utils.decode(requestJwtPayload.getBytes()));
String loginName = (String) payloadJSON.get("loginName");
long requestTimestamp = (long) payloadJSON.get("timestamp");
//3.校验该用户的jwtsalt是否存在
RedisTemplate redisTemplate = (RedisTemplate) SpringUtil.getBean("redisTemplate");
String jwtsaltValue = (String) redisTemplate.opsForValue().get("jwtsalt:" + loginName);
if (StringUtils.isBlank(jwtsaltValue)) {//jwt超时失效,重新登录
return AjaxResult.error603("jwtsalt不存在");
}
//4.校验timestamp是否一致
long redisTimestamp = Long.parseLong(jwtsaltValue.split("\\.")[1]);
if (requestTimestamp != redisTimestamp) {
return AjaxResult.error602("该账号在异地登录");
}
//5.根据该用户的salt,重新计算Signature并判断是否一致
String userSalt = jwtsaltValue.split("\\.")[0];
String createdSignatureByReq = createSignature(requestJwtHead, requestJwtPayload, Base64Utils.encodeToString(userSalt.getBytes()));
if (!requestJwtSignature.equals(createdSignatureByReq)) {
return AjaxResult.error601("Token.Signature校验不合法");
}
return AjaxResult.success("token校验通过");
}
}
package com.fxkc.care.utils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.beanutils.PropertyUtilsBean;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Slf4j
public class MapBeanConvertUtil {
public static Map<String, Object> convertBean2Map(Object obj) {
Map<String, Object> map = new HashMap<String, Object>(0);
try {
PropertyUtilsBean propertyUtilsBean = new PropertyUtilsBean();
PropertyDescriptor[] descriptors = propertyUtilsBean.getPropertyDescriptors(obj);
for (int i = 0; i < descriptors.length; i++) {
String name = descriptors[i].getName();
if (!"class".equals(name)) {
map.put(name, propertyUtilsBean.getNestedProperty(obj, name));
}
}
} catch (Exception e) {
e.printStackTrace();
}
return map;
}
public static Object convertMap2Bean(Class type, Map map) throws IntrospectionException, IllegalAccessException, InstantiationException, InvocationTargetException {
BeanInfo beanInfo = Introspector.getBeanInfo(type); // 获取类属性
Object obj = type.newInstance(); // 创建 JavaBean 对象
// 给 JavaBean 对象的属性赋值
PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
for (int i = 0; i < propertyDescriptors.length; i++) {
PropertyDescriptor descriptor = propertyDescriptors[i];
String propertyName = descriptor.getName();
if (map.containsKey(propertyName)) {
// 下面一句可以 try 起来,这样当一个属性赋值失败的时候就不会影响其他属性赋值。
Object value = map.get(propertyName);
Object[] args = new Object[1];
if (value == null || "null".equals(value.toString())) {
continue;
}
if (value instanceof String) {
args[0] = value.toString();
} else if (value instanceof Integer || value instanceof List) {
args[0] = value;
}
try {
descriptor.getWriteMethod().invoke(obj, args);
} catch (Exception e) {
log.error("{}属性赋值{}时出现异常!", propertyName, value);
}
}
}
return obj;
}
}
package com.fxkc.care.utils;
import java.util.Comparator;
import java.util.Map;
/**
* Strategy interface for {@code String}-based path matching.
*/
public interface PathMatcherUtil {
/**
* Does the given {@code path} represent a pattern that can be matched by an implementation of this interface?
*
* <p>If the return value is {@code false}, then the {@link #match}
* method does not have to be used because direct equality comparisons
* on the static path Strings will lead to the same result.
*
* @param path the path String to check
* @return {@code true} if the given {@code path} represents a pattern
*/
boolean isPattern(String path);
/**
* Match the given {@code path} against the given {@code pattern}, according to this PathMatcher's matching strategy.
*
* @param pattern the pattern to match against
* @param path the path String to test
* @return {@code true} if the supplied {@code path} matched,
* {@code false} if it didn't
*/
boolean match(String pattern, String path);
/**
* Match the given {@code path} against the corresponding part of the given {@code pattern}, according to this PathMatcher's matching strategy.
*
* <p>Determines whether the pattern at least matches as far as the given base path goes, assuming that a full path may then match as well.
*
* @param pattern the pattern to match against
* @param path the path String to test
* @return {@code true} if the supplied {@code path} matched,
* {@code false} if it didn't
*/
boolean matchStart(String pattern, String path);
/**
* Given a pattern and a full path, determine the pattern-mapped part.
*
* <p>This method is supposed to find out which part of the path is matched dynamically through an actual pattern,
* that is, it strips off a statically defined leading path from the given full path,
* returning only the actually pattern-matched part of the path.
*
* <p>For example: For "myroot/*.html" as pattern and "myroot/myfile.html" as full path, this method should return "myfile.html".
* The detailed determination rules are specified to this PathMatcher's matching strategy.
*
* <p>A simple implementation may return the given full path as-is in case of an actual pattern,
* and the empty String in case of the pattern not containing any dynamic parts (i.e. the {@code pattern} parameter being
* a static path that wouldn't qualify as an actual {@link #isPattern pattern}).
* A sophisticated implementation will differentiate between the static parts and the dynamic parts of the given path pattern.
*
* @param pattern the path pattern
* @param path the full path to introspect
* @return the pattern-mapped part of the given {@code path}
* (never {@code null})
*/
String extractPathWithinPattern(String pattern, String path);
/**
* Given a pattern and a full path, extract the URI template variables. URI template variables are expressed through curly brackets ('{' and '}').
*
* <p>For example: For pattern "/hotels/{hotel}" and path "/hotels/1", this method will return a map containing "hotel"->"1".
*
* @param pattern the path pattern, possibly containing URI templates
* @param path the full path to extract template variables from
* @return a map, containing variable names as keys; variables values as values
*/
Map<String, String> extractUriTemplateVariables(String pattern, String path);
/**
* Given a full path, returns a {@link Comparator} suitable for sorting patterns in order of explicitness for that path.
*
* <p>The full algorithm used depends on the underlying implementation, but generally,
* the returned {@code Comparator} will {@linkplain java.util.Collections#sort(java.util.List, Comparator) sort}
* a list so that more specific patterns come before generic patterns.
*
* @param path the full path to use for comparison
* @return a comparator capable of sorting patterns in order of explicitness
*/
Comparator<String> getPatternComparator(String path);
/**
* Combines two patterns into a new pattern that is returned.
*
* <p>The full algorithm used for combining the two pattern depends on the underlying implementation.
*
* @param pattern1 the first pattern
* @param pattern2 the second pattern
* @return the combination of the two patterns
* @throws IllegalArgumentException when the two patterns cannot be combined
*/
String combine(String pattern1, String pattern2);
}
package com.fxkc.care.utils;
import com.fxkc.care.enums.CommonEnum;
import com.fxkc.care.vo.UserInfoVO;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
public class RequestUtil {
public static HttpServletRequest getRequest() {
try {
return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
} catch (Exception var1) {
return null;
}
}
public static String getParameter(String name) {
return getRequest().getParameter(name);
}
public static UserInfoVO getUserInfo() {
return (UserInfoVO) (getRequest() != null ? getRequest().getAttribute(CommonEnum.USER_INFO.getCode()) : null);
}
}
package com.fxkc.care.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
/**
* @author JYH
* @date 2024/12/13 17:36
* @describe
*/
@Data
@ApiModel(value = "用户信息vo")
public class UserInfoVO implements Serializable {
@ApiModelProperty(value = "登录名")
private String loginName;
@ApiModelProperty(value = "用户名")
private String userName;
}
server.port=8009
#\u672C\u5730\u6570\u636E\u5E93\u914D\u7F6E
spring.datasource.driver-class-name=oracle.jdbc.OracleDriver
spring.datasource.name=CROWD_CARE
spring.datasource.url=jdbc:oracle:thin:@10.10.31.139:1521:orcl
spring.datasource.username=CROWD_CARE
spring.datasource.password=123456
#Redis\u914D\u7F6E
spring.redis.database=9
spring.redis.host=localhost
spring.redis.port=6379
spring.redis.password=
#\u63A7\u5236logback level
logback.logging.customLevel=debug
\ No newline at end of file
#\u9879\u76EE\u8DEF\u5F84\u4E0A\u4E0B\u6587
server.servlet.context-path=/
#\u8FD0\u884C\u73AF\u5883
spring.profiles.active=dev
#8\u5C0F\u65F6\u65F6\u5DEE\u95EE\u9898\uFF0C\u89E3\u51B3\u524D\u7AEF\u4F20\u7ED9\u540E\u53F0\u65F6\u95F4\u5DEE\u95EE\u9898
spring.jackson.time-zone=GMT+8
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
spring.mvc.format.date=yyyy-MM-dd
spring.mvc.format.date-time=yyyy-MM-dd HH:mm:ss
spring.mvc.format.time=HH:mm:ss
#\u6570\u636E\u5E93\u53CA\u8FDE\u63A5\u6C60
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
#\u662F\u5426\u5728\u4ECE\u6C60\u4E2D\u53D6\u51FA\u8FDE\u63A5\u524D\u8FDB\u884C\u68C0\u9A8C
spring.datasource.druid.test-on-borrow=false
#\u662F\u5426\u68C0\u6D4B\u7A7A\u95F2\u8FDE\u63A5\u8D85\u65F6
spring.datasource.druid.test-while-idle=true
#\u95F4\u9694\u591A\u4E45\u624D\u8FDB\u884C\u4E00\u6B21\u68C0\u6D4B\uFF0C\u68C0\u6D4B\u9700\u8981\u5173\u95ED\u7684\u7A7A\u95F2\u8FDE\u63A5\uFF0C\u5355\u4F4D\u662F\u6BEB\u79D2
spring.datasource.druid.time-between-eviction-runs-millis=11000
#\u662F\u5426\u5BF9\u5DF2\u5907\u8BED\u53E5\u8FDB\u884C\u6C60\u7BA1\u7406
spring.datasource.druid.pool-prepared-statements=true
spring.datasource.druid.initial-size=8
#\u5728\u4E0D\u65B0\u5EFA\u8FDE\u63A5\u7684\u6761\u4EF6\u4E0B\uFF0C\u6C60\u4E2D\u4FDD\u6301\u7A7A\u95F2\u7684\u6700\u5C11\u8FDE\u63A5\u6570
spring.datasource.druid.min-idle=1
#\u540C\u4E00\u65F6\u95F4\u53EF\u4EE5\u4ECE\u6C60\u5206\u914D\u7684\u6700\u591A\u8FDE\u63A5\u6570\u91CF
spring.datasource.druid.max-active=50
#\u83B7\u53D6\u8FDE\u63A5\u7684\u6700\u5927\u7B49\u5F85\u65F6\u95F4\uFF0C\u4F1A\u542F\u52A8\u516C\u5E73\u9501
spring.datasource.druid.max-wait=20000
#\u8FDE\u63A5\u4FDD\u6301\u7A7A\u95F2\u7684\u6700\u957F\u65F6\u95F4
spring.datasource.druid.min-evictable-idle-time-millis=30000
#Mybatis\u914D\u7F6E
mybatis.mapper-locations=classpath:mapper/**/*.xml
mybatis.type-aliases-package=com.fxkc.care.entity
#\u5206\u9875\u63D2\u4EF6
pagehelper.helper-dialect=oracle
pagehelper.reasonable=true
#support-methods-arguments\u5982\u679C\u4E3Atrue\uFF0C\u6CA1\u6709\u6307\u660EPageHelper.startPage\u4E5F\u4F1A\u81EA\u52A8\u5206\u9875
pagehelper.support-methods-arguments=false
pagehelper.params=count=countSql
#Redis\u914D\u7F6E
#\u8FDE\u63A5\u6C60\u6700\u5927\u8FDE\u63A5\u6570\uFF08\u8D1F\u503C\u8868\u793A\u6CA1\u6709\u9650\u5236\uFF09
spring.redis.jedis.pool.max-active=8
#\u8FDE\u63A5\u6C60\u6700\u5927\u963B\u585E\u7B49\u5F85\u65F6\u95F4\uFF08\u8D1F\u503C\u8868\u793A\u6CA1\u6709\u9650\u5236\uFF09
spring.redis.jedis.pool.max-wait=-1
#\u8FDE\u63A5\u6C60\u6700\u5927\u7A7A\u95F2\u8FDE\u63A5\u6570
spring.redis.jedis.pool.max-idle=8
#\u8FDE\u63A5\u6C60\u6700\u5C0F\u7A7A\u95F2\u8FDE\u63A5\u6570
spring.redis.jedis.pool.min-idle=0
#\u8FDE\u63A5\u8D85\u65F6\u65F6\u95F4
spring.redis.timeout=60000
spring.servlet.multipart.max-file-size=100MB
spring.servlet.multipart.max-request-size=1000MB
#URL\u767D\u540D\u5355\uFF1A\u4E0D\u9700\u8981\u6821\u9A8Ctoken, /favicon.ico
whiteUrlList=/base/login,/base/localLogin,/**/*swagger*/**,/v2/**,/csrf,/swagger-resources/**,/webjars/**,/doc.html/**
#\u91CD\u590D\u63D0\u4EA4\u89C4\u5219\u6BEB\u79D2\u503C\uFF1A\u8BE5\u65F6\u95F4\u8303\u56F4\u5185\u7684\u63D0\u4EA4\u90FD\u89C6\u4E3A\u91CD\u590D\u63D0\u4EA4\u3002\u5355\u4F4D\u6BEB\u79D2\uFF0C\u89C6\u7F51\u7EDC\u5EF6\u8FDF\u73AF\u5883\u8C03\u6574\u3002
repeat.submit.period=3000
#swagger2\u7684\u914D\u7F6E
spring.mvc.pathmatch.matching-strategy=ant_path_matcher
#mybatis\u63A5\u53E3\u8FD4\u56DEmap\u503C\u4E3Anull\u7684\u5B57\u6BB5\u663E\u793A,\u9ED8\u8BA4false
mybatis-plus.configuration.call-setters-on-nulls=true
# \u5168\u5C40\u903B\u8F91\u5220\u9664\u7684\u5B9E\u4F53\u5B57\u6BB5\u540D
mybatis-plus.global-config.db-config.logic-delete-field=deleteFlag
# \u903B\u8F91\u5DF2\u5220\u9664\u503C
mybatis-plus.global-config.db-config.logic-delete-value=1
# \u903B\u8F91\u672A\u5220\u9664\u503C
mybatis-plus.global-config.db-config.logic-not-delete-value=0
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
<property name="LOG_FILE" value="logs/care"/>
<include resource="org/springframework/boot/logging/logback/console-appender.xml"/>
<springProperty scope="context" name="customLevel" source="logback.logging.customLevel"/>
<appender name="TIMEROLL_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<append>true</append>
<filter class="ch.qos.logback.core.filter.EvaluatorFilter">
<evaluator>
<expression>
return formattedMessage.contains("/notice/unreadList")
|| event.getLoggerName().contains("NoticeMapper.selectUnreadList");
</expression>
</evaluator>
<OnMatch>DENY</OnMatch>
<OnMismatch>ACCEPT</OnMismatch>
</filter>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{80} - %msg%n</pattern>
<!--<charset>GBK</charset>-->
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_FILE}.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<maxHistory>90</maxHistory>
<totalSizeCap>1GB</totalSizeCap>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>10MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
</appender>
<appender name="TIMEROLL_ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<append>true</append>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{80} - %msg%n</pattern>
<!--<charset>GBK</charset>-->
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_FILE}.%d{yyyy-MM-dd}.%i.error.log</fileNamePattern>
<maxHistory>90</maxHistory>
<totalSizeCap>1GB</totalSizeCap>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>10MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
</appender>
<appender name="MY_CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<filter class="ch.qos.logback.core.filter.EvaluatorFilter">
<evaluator>
<expression>
return formattedMessage.contains("/notice/unreadList")
|| event.getLoggerName().contains("NoticeMapper.selectUnreadList");
</expression>
</evaluator>
<OnMatch>DENY</OnMatch>
<OnMismatch>ACCEPT</OnMismatch>
</filter>
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{80} - %msg%n</pattern>
</encoder>
</appender>
<!--预期结果:
控制台只打印:【本项目】的debug级别以上日志+【非本项目】异常信息
日志:同上
错误日志:只打印error级别日志 -->
<logger name="com.fxkc.care" level="${customLevel}" additivity="false">
<appender-ref ref="MY_CONSOLE"/>
<appender-ref ref="TIMEROLL_FILE"/>
</logger>
<root level="INFO">
<appender-ref ref="MY_CONSOLE"/>
<appender-ref ref="TIMEROLL_FILE"/>
<appender-ref ref="TIMEROLL_ERROR_FILE"/>
</root>
</configuration>
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment