Compare commits
57 Commits
9dc9ea1454
..
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 2c37cd2c75 | |||
| aefb212285 | |||
| 2e18a8a419 | |||
| a6a5d42bea | |||
| eaf44e1b4f | |||
| 6da20bdac4 | |||
| 02010bd9ae | |||
| 660a8430f1 | |||
| da56185960 | |||
| 5d8816fb2f | |||
| b271f0aa87 | |||
| 50ac15a8f7 | |||
| 872be51082 | |||
| da78005ad7 | |||
| f6b3369dc0 | |||
| 155376ca6e | |||
| 865dfa31f5 | |||
| 25264daed8 | |||
| 37ba2a02ad | |||
| b87abe0e3e | |||
| 31f5a31b9b | |||
| ffecb1f6d8 | |||
| 160a5fecd9 | |||
| 1c6a5dc3be | |||
| afcf469488 | |||
| fe2608f3ea | |||
| e79df6fc1f | |||
| 3cba48728d | |||
| c32f06239e | |||
| f99f645cf7 | |||
| 0f069433bc | |||
| f9613d2e01 | |||
| 42a209400a | |||
| bc7ca98132 | |||
| 4edbe3438c | |||
| 8901d7401b | |||
| 1c1490271d | |||
| 2f001c3e9e | |||
| 3466810ae8 | |||
| 1c4a093b46 | |||
| 2bca8d96d3 | |||
| 8bcbb3c332 | |||
| 31d970a3ef | |||
| 88008a74fd | |||
| 4eb71c4d4d | |||
| 5c18560351 | |||
| 275e7dded9 | |||
| a836ae23bf | |||
| 4a9860a25c | |||
| f921bad9fc | |||
| 11b5f6fe65 | |||
| ebdcd3c78a | |||
| 149591eea8 | |||
| 3eddcf59ad | |||
| 26265bfd0e | |||
| f0ccb263d0 | |||
| ae8d28244b |
@@ -0,0 +1,36 @@
|
||||
name: Sync All Branches to GitHub
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ '*' ] # 监听所有分支的推送事件
|
||||
|
||||
jobs:
|
||||
sync:
|
||||
runs-on: sync
|
||||
steps:
|
||||
- name: Checkout code (Git CLI)
|
||||
run: |
|
||||
# 克隆 Gitea 仓库
|
||||
git clone ${{ github.server_url }}/${{ github.repository }}.git .
|
||||
# 切换到当前推送的分支
|
||||
git checkout ${{ github.ref }}
|
||||
|
||||
- name: Set up Git identity
|
||||
run: |
|
||||
# 配置 Git 用户信息
|
||||
git config --global user.name "ayi"
|
||||
git config --global user.email "2294931964@qq.com"
|
||||
|
||||
- name: Add GitHub remote repository
|
||||
run: |
|
||||
# 添加 GitHub 远程仓库,使用令牌进行身份验证
|
||||
git remote add github https://${{ secrets.GIT_HUB_TOKEN }}@github.com/xing-jiayi/xiaoyishu.git
|
||||
|
||||
- name: Push current branch to GitHub
|
||||
run: |
|
||||
# 获取当前推送的分支名(兼容 refs/heads/ 前缀)
|
||||
CURRENT_BRANCH="${GITHUB_REF#refs/heads/}"
|
||||
# 拉取 GitHub 对应分支的最新更改,允许无关历史(防止冲突)
|
||||
git pull github "${CURRENT_BRANCH}" --rebase --allow-unrelated-histories || true
|
||||
# 推送当前分支到 GitHub 对应分支
|
||||
git push github "${CURRENT_BRANCH}" --force-with-lease # 安全强制推送(避免覆盖他人提交)
|
||||
+5
-5
@@ -5,10 +5,7 @@ target/
|
||||
.kotlin
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea/modules.xml
|
||||
.idea/jarRepositories.xml
|
||||
.idea/compiler.xml
|
||||
.idea/libraries/
|
||||
.idea/
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
@@ -18,7 +15,6 @@ target/
|
||||
.classpath
|
||||
.factorypath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
.sts4-cache
|
||||
|
||||
@@ -35,3 +31,7 @@ build/
|
||||
|
||||
### Mac OS ###
|
||||
.DS_Store
|
||||
|
||||
### 日志 ###
|
||||
# 排除所有logs
|
||||
logs/
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
stages:
|
||||
- sync
|
||||
|
||||
sync-to-github:
|
||||
stage: sync
|
||||
tags:
|
||||
- sync
|
||||
image: ruby:3.3
|
||||
rules:
|
||||
- if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_TAG == null'
|
||||
when: on_success
|
||||
- when: never
|
||||
# 关闭浅克隆,拉取完整提交记录
|
||||
variables:
|
||||
GIT_DEPTH: 0
|
||||
script:
|
||||
# 1. 初始化Git配置,复用提交者信息
|
||||
- echo "当前提交者信息:$CI_COMMIT_AUTHOR"
|
||||
- GIT_AUTHOR_NAME=$(echo "$CI_COMMIT_AUTHOR" | cut -d'<' -f1 | xargs)
|
||||
- GIT_AUTHOR_EMAIL=$(echo "$CI_COMMIT_AUTHOR" | cut -d'<' -f2 | cut -d'>' -f1 | xargs)
|
||||
- git config --global user.name "$GIT_AUTHOR_NAME"
|
||||
- git config --global user.email "$GIT_AUTHOR_EMAIL"
|
||||
- git config --global http.sslVerify false
|
||||
# 核心新增:禁用HTTP2,强制使用HTTP/1.1(解决HTTP2 framing layer帧层错误)
|
||||
- git config --global http.version HTTP/1.1
|
||||
- git config --global http.proxy $PROXY_ADDR
|
||||
- git config --global https.proxy $PROXY_ADDR
|
||||
|
||||
|
||||
# 2. 构造带PAT的GitHub推送地址
|
||||
- GITHUB_PUSH_URL="https://$GITHUB_PAT@${GITHUB_REPO_URL#https://}"
|
||||
- echo "同步目标:GitHub仓库 $GITHUB_REPO_URL"
|
||||
|
||||
# 3. 安全处理GitHub远程仓库
|
||||
- git remote rm github || true
|
||||
- git remote add github "$GITHUB_PUSH_URL"
|
||||
|
||||
# 4. 核心修复:基于分离HEAD创建本地同名分支(解决refspec不匹配问题)
|
||||
- echo "基于分离HEAD创建本地分支 $CI_COMMIT_BRANCH"
|
||||
- git checkout -b $CI_COMMIT_BRANCH
|
||||
|
||||
# 5. 拉取GitHub远程最新代码(避免冲突,新分支拉取失败不影响)
|
||||
- git fetch github || true
|
||||
|
||||
# 6. 单分支精准推送(GitHub自动创建不存在的分支)
|
||||
- echo "开始同步GitLab分支 $CI_COMMIT_BRANCH 到GitHub..."
|
||||
- git push github $CI_COMMIT_BRANCH:$CI_COMMIT_BRANCH -f
|
||||
|
||||
retry:
|
||||
max: 2
|
||||
when:
|
||||
- runner_system_failure
|
||||
- stuck_or_timeout_failure
|
||||
- api_failure
|
||||
Vendored
+603
@@ -0,0 +1,603 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<profiles version="12">
|
||||
<profile kind="CodeFormatterProfile" name="P3C-CodeStyle" version="13">
|
||||
<!--可变参数的... Idea没有对应的配置项,强制insert-->
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_ellipsis" value="insert"/>
|
||||
<!--枚举值之间 Idea没有对应的配置项,强制insert-->
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations" value="insert"/>
|
||||
|
||||
<!--org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=Java:SPACE_BEFORE_COMMA-->
|
||||
<!--org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=Java:SPACE_BEFORE_COMMA
|
||||
由于IDEA只有一个SPACE_BEFORE_COMMA选项,所以统一设置 insert_space_before_comma 为 do not insert
|
||||
-->
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters"
|
||||
value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments"
|
||||
value="do not insert"/>
|
||||
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression"
|
||||
value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws"
|
||||
value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer"
|
||||
value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters"
|
||||
value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters"
|
||||
value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces"
|
||||
value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference"
|
||||
value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations"
|
||||
value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments"
|
||||
value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations"
|
||||
value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments"
|
||||
value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws"
|
||||
value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations"
|
||||
value="do not insert"/>
|
||||
<!--insert_space_before_comma end-->
|
||||
|
||||
<!--org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=Java:SPACE_AFTER_COMMA_IN_TYPE_ARGUMENTS-->
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments" value="insert"/>
|
||||
<!--IDEA只有一个配置项SPACE_AFTER_COMMA,insert_space_after_comma*统一设置成insert-->
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters"
|
||||
value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations"
|
||||
value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments"
|
||||
value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference"
|
||||
value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations"
|
||||
value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments"
|
||||
value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws"
|
||||
value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters"
|
||||
value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws"
|
||||
value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments"
|
||||
value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation" value="insert"/>
|
||||
<!--insert_space_after_comma end-->
|
||||
|
||||
|
||||
<!--org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=Java:SPACE_BEFORE_COLON-->
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for" value="insert"/>
|
||||
|
||||
<!--org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=Java:SPACE_AFTER_COLON-->
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement" value="insert"/>
|
||||
|
||||
<!--IDEA不支持配置,默认do not insert-->
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement"
|
||||
value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case" value="do not insert"/>
|
||||
<!--这个在Eclipse也没有找到配置的地方-->
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case" value="do not insert"/>
|
||||
|
||||
<!--org.eclipse.jdt.core.formatter.insert_space_before_semicolon=Java:SPACE_BEFORE_SEMICOLON
|
||||
程序导入的时候强制将SPACE_BEFORE_SEMICOLON设置为false
|
||||
-->
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources"
|
||||
value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for" value="do not insert"/>
|
||||
|
||||
<!--SPACE_AFTER_SEMICOLON=true-->
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources" value="insert"/>
|
||||
|
||||
<!--IDEA不支持配置,do not insert-->
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant"
|
||||
value="do not insert"/>
|
||||
<setting
|
||||
id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration"
|
||||
value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration"
|
||||
value="do not insert"/>
|
||||
|
||||
<!--IDEA不支持,使用默认-->
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator" value="do not insert"/>
|
||||
|
||||
<!--org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=Java:<Programmatic>-->
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_binary_operator" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_binary_operator" value="insert"/>
|
||||
|
||||
<!--IDEA不支持配置,使用如下值,两者对应-->
|
||||
<setting
|
||||
id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference"
|
||||
value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments"
|
||||
value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters"
|
||||
value="do not insert"/>
|
||||
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters"
|
||||
value="insert"/>
|
||||
<setting
|
||||
id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference"
|
||||
value="do not insert"/>
|
||||
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments"
|
||||
value="do not insert"/>
|
||||
<setting
|
||||
id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference"
|
||||
value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments"
|
||||
value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters"
|
||||
value="do not insert"/>
|
||||
|
||||
<!--Java:SPACE_BEFORE_OPENING_ANGLE_BRACKET_IN_TYPE_PARAMETER-->
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters"
|
||||
value="do not insert"/>
|
||||
<!--org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=Java:SPACE_AFTER_CLOSING_ANGLE_BRACKET_IN_TYPE_ARGUMENT-->
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments"
|
||||
value="do not insert"/>
|
||||
|
||||
<!--org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=Java:<Programmatic>-->
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block" value="insert"/>
|
||||
|
||||
<!--IDEA使用了对应的配置:Java:SPACE_WITHIN_ARRAY_INITIALIZER_BRACES,但感觉不太好,IDEA默认不插入,Eclipse也使用不插入-->
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer"
|
||||
value="do not insert"/>
|
||||
|
||||
<!--use default insert-->
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return"
|
||||
value="insert"/>
|
||||
|
||||
<!--use default do not insert -->
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration"
|
||||
value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference"
|
||||
value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression"
|
||||
value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration"
|
||||
value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation"
|
||||
value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation"
|
||||
value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference"
|
||||
value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized"
|
||||
value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference"
|
||||
value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized"
|
||||
value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression"
|
||||
value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation"
|
||||
value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration"
|
||||
value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation"
|
||||
value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer"
|
||||
value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation"
|
||||
value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch" value="do not insert"/>
|
||||
|
||||
|
||||
<!--use default insert-->
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration"
|
||||
value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration"
|
||||
value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration"
|
||||
value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration"
|
||||
value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw"
|
||||
value="insert"/>
|
||||
|
||||
|
||||
<!--org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=Java:SPACE_BEFORE_SWITCH_LBRACE-->
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch" value="insert"/>
|
||||
<!--org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=Java:SPACE_BEFORE_CLASS_LBRACE-->
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration"
|
||||
value="insert"/>
|
||||
<!--org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=Java:<Programmatic>-->
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block" value="insert"/>
|
||||
<!--org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=Java:SPACE_BEFORE_ARRAY_INITIALIZER_LBRACE-->
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer"
|
||||
value="insert"/>
|
||||
<!--org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=Java:SPACE_BEFORE_METHOD_LBRACE-->
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration"
|
||||
value="insert"/>
|
||||
<!--org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=Java:SPACE_AFTER_QUEST-->
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional" value="insert"/>
|
||||
<!--org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=Java:SPACE_BEFORE_QUEST-->
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional" value="insert"/>
|
||||
<!--org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=Java:SPACE_BEFORE_ANOTATION_PARAMETER_LIST-->
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation"
|
||||
value="do not insert"/>
|
||||
|
||||
<!--use default do not insert-->
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_ellipsis" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression"
|
||||
value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant"
|
||||
value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration"
|
||||
value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression"
|
||||
value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration"
|
||||
value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration"
|
||||
value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression"
|
||||
value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant"
|
||||
value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration"
|
||||
value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration"
|
||||
value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast" value="do not insert"/>
|
||||
<setting
|
||||
id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration"
|
||||
value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant"
|
||||
value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference"
|
||||
value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression"
|
||||
value="do not insert"/>
|
||||
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression"
|
||||
value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference"
|
||||
value="do not insert"/>
|
||||
|
||||
|
||||
<!--下面两个对应IDEA中的一个配置Java:SPACE_AROUND_ASSIGNMENT_OPERATORS,使用insert-->
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator" value="insert"/>
|
||||
|
||||
<!--org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=Java:SPACE_BEFORE_CATCH_PARENTHESES-->
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch" value="insert"/>
|
||||
<!--org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=Java:SPACE_BEFORE_METHOD_CALL_PARENTHESES-->
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation"
|
||||
value="do not insert"/>
|
||||
<!--org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try=Java:SPACE_BEFORE_TRY_PARENTHESES-->
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try" value="insert"/>
|
||||
|
||||
<!--下面两个对应IDEA中的一个配置Java:SPACE_AROUND_UNARY_OPERATOR,使用do not insert-->
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_unary_operator" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_unary_operator" value="do not insert"/>
|
||||
|
||||
<!--org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=Java:SPACE_BEFORE_IF_PARENTHESES-->
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if" value="insert"/>
|
||||
|
||||
<!--org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=Java:SPACE_BEFORE_WHILE_PARENTHESES-->
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while" value="insert"/>
|
||||
|
||||
<!--org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=Java:SPACE_AFTER_TYPE_CAST-->
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast" value="do not insert"/>
|
||||
|
||||
<!--org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=Java:SPACE_BEFORE_METHOD_PARENTHESES-->
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration"
|
||||
value="do not insert"/>
|
||||
|
||||
<!--org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=Java:SPACE_BEFORE_FOR_PARENTHESES-->
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for" value="insert"/>
|
||||
<!--org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=Java:SPACE_BEFORE_SYNCHRONIZED_PARENTHESES-->
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized" value="insert"/>
|
||||
|
||||
<!--org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=Java:SPACE_BEFORE_SWITCH_PARENTHESES-->
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch" value="insert"/>
|
||||
|
||||
<!--下面两个对应IDEA中的一个配置Java:SPACE_AROUND_LAMBDA_ARROW,使用insert-->
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_lambda_arrow" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow" value="insert"/>
|
||||
<!--SPACE_WITHIN_EMPTY_ARRAY_INITIALIZER_BRACES-->
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer"
|
||||
value="do not insert"/>
|
||||
|
||||
<!--Idea -> Wrapping And Braces -> Simple classes in one line -->
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration"
|
||||
value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration"
|
||||
value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration" value="do not insert"/>
|
||||
<!--Idea -> Wrapping And Braces -> Simple method in one line -->
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body" value="do not insert"/>
|
||||
<!--因为Idea不支持配置,所以设置为 Idea默认值-->
|
||||
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter"
|
||||
value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_enum_constant"
|
||||
value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_type_annotation" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_label" value="insert"/>
|
||||
<!--Idea可以通过Wrap Always实现 TODO-->
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type" value="insert"/>
|
||||
<!--Idea -> Wrapping And Braces -> Simple block in one line -> do not select -->
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block" value="insert"/>
|
||||
|
||||
<!--Idea -> Wrapping And Braces -> try statement -> catch.... (Java:CATCH_ON_NEW_LINE)-->
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement"
|
||||
value="do not insert"/>
|
||||
<!--org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=<Programmatic>-->
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing" value="do not insert"/>
|
||||
<!--org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=Java:ARRAY_INITIALIZER_RBRACE_ON_NEXT_LINE-->
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer"
|
||||
value="do not insert"/>
|
||||
<!--#org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=Java:ARRAY_INITIALIZER_LBRACE_ON_NEXT_LINE-->
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer"
|
||||
value="do not insert"/>
|
||||
<!--org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=Java:ELSE_ON_NEW_LINE-->
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement" value="do not insert"/>
|
||||
<!--org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=Java:WHILE_ON_NEW_LINE-->
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement"
|
||||
value="do not insert"/>
|
||||
<!--org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=Java:FINALLY_ON_NEW_LINE-->
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement"
|
||||
value="do not insert"/>
|
||||
|
||||
<!--comment start-->
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.line_length" value="120"/>
|
||||
<!--ENABLE_JAVADOC_FORMATTING-->
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.format_javadoc_comments" value="true"/>
|
||||
<!--org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=<Programmatic>-->
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment" value="false"/>
|
||||
<!--IDEA无对应设置,所以关闭对block comment的格式化 -->
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.format_block_comments" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries" value="true"/>
|
||||
|
||||
<!--org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=Java:KEEP_FIRST_COLUMN_COMMENT-->
|
||||
<setting id="org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column" value="false"/>
|
||||
<!--org.eclipse.jdt.core.formatter.use_on_off_tags=FORMATTER_TAGS_ENABLED-->
|
||||
<setting id="org.eclipse.jdt.core.formatter.use_on_off_tags" value="true"/>
|
||||
<!--org.eclipse.jdt.core.formatter.disabling_tag=FORMATTER_OFF_TAG-->
|
||||
<setting id="org.eclipse.jdt.core.formatter.disabling_tag" value="@formatter:off"/>
|
||||
<!--org.eclipse.jdt.core.formatter.enabling_tag=FORMATTER_ON_TAG-->
|
||||
<setting id="org.eclipse.jdt.core.formatter.enabling_tag" value="@formatter:on"/>
|
||||
|
||||
<!--下面的没有IDEA对应项,在代码里面对IDEA中使用默认值即可,LINE_COMMENT_AT_FIRST_COLUMN BLOCK_COMMENT_AT_FIRST_COLUMN设置为false-->
|
||||
<setting id="org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.indent_root_tags" value="true"/>
|
||||
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments"
|
||||
value="false"/>
|
||||
|
||||
|
||||
<setting id="org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.format_line_comments" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.format_header" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries" value="true"/>
|
||||
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.format_source_code" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.format_html" value="true"/>
|
||||
|
||||
<setting id="org.eclipse.jdt.core.formatter.join_lines_in_comments" value="true"/>
|
||||
<!--和IDEA保持一致,注释换行-->
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.indent_parameter_description" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter" value="do not insert"/>
|
||||
|
||||
|
||||
<!--comment end-->
|
||||
|
||||
<!--org.eclipse.jdt.core.formatter.blank_lines_after_imports=Java:BLANK_LINES_AFTER_IMPORTS-->
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_after_imports" value="1"/>
|
||||
<!--org.eclipse.jdt.core.formatter.blank_lines_before_imports=Java:BLANK_LINES_BEFORE_IMPORTS-->
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_imports" value="1"/>
|
||||
<!--org.eclipse.jdt.core.formatter.blank_lines_after_package=Java:BLANK_LINES_AFTER_PACKAGE-->
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_after_package" value="1"/>
|
||||
<!--org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=Java:BLANK_LINES_AROUND_CLASS-->
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations" value="1"/>
|
||||
<!--org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=Java:BLANK_LINES_BEFORE_METHOD_BODY-->
|
||||
<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body" value="0"/>
|
||||
<!--org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=Java:<Programmatic>-->
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration" value="0"/>
|
||||
<!--org.eclipse.jdt.core.formatter.blank_lines_before_field=Java:BLANK_LINES_AROUND_FIELD-->
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_field" value="0"/>
|
||||
<!--org.eclipse.jdt.core.formatter.blank_lines_before_method=Java:BLANK_LINES_AROUND_METHOD-->
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_method" value="1"/>
|
||||
<!--org.eclipse.jdt.core.formatter.blank_lines_before_package=Java:BLANK_LINES_BEFORE_PACKAGE-->
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_package" value="0"/>
|
||||
|
||||
<!--下面IDEA没有对应设置,使用对应值即可-->
|
||||
<setting id="org.eclipse.jdt.core.formatter.align_fields_grouping_blank_lines" value="2147483647"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk" value="1"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_member_type" value="1"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_between_import_groups" value="1"/>
|
||||
|
||||
<!--org.eclipse.jdt.core.formatter.indentation.size=Java:IndentOptions:INDENT_SIZE-->
|
||||
<setting id="org.eclipse.jdt.core.formatter.indentation.size" value="4"/>
|
||||
<!--org.eclipse.jdt.core.formatter.continuation_indentation=Java:IndentOptions:<Programmatic>-->
|
||||
<setting id="org.eclipse.jdt.core.formatter.continuation_indentation" value="1"/>
|
||||
<!--org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=Java:<Programmatic>-->
|
||||
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header" value="true"/>
|
||||
<!--org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=Java:IndentOptions:SMART_TABS-->
|
||||
<setting id="org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations" value="false"/>
|
||||
<!--org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=Java:INDENT_CASE_FROM_SWITCH-->
|
||||
<setting id="org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch" value="true"/>
|
||||
<!--KEEP_INDENTS_ON_EMPTY_LINES-->
|
||||
<setting id="org.eclipse.jdt.core.formatter.indent_empty_lines" value="false"/>
|
||||
<!--org.eclipse.jdt.core.formatter.tabulation.size=Java:IndentOptions:<Programmatic>-->
|
||||
<setting id="org.eclipse.jdt.core.formatter.tabulation.size" value="4"/>
|
||||
<!--Java:IndentOptions:<Programmatic>-->
|
||||
<setting id="org.eclipse.jdt.core.formatter.tabulation.char" value="space"/>
|
||||
|
||||
|
||||
<!--下面IDEA没有对应设置,使用对应值即可-->
|
||||
<setting id="org.eclipse.jdt.core.formatter.indent_statements_compare_to_block" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header"
|
||||
value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer" value="1"/>
|
||||
|
||||
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header"
|
||||
value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.indent_statements_compare_to_body" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header"
|
||||
value="true"/>
|
||||
|
||||
|
||||
<!--Java:<Programmatic>-->
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_assignment" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_binary_expression" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_resources_in_try" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_conditional_expression" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer" value="16"/>
|
||||
|
||||
<!--下面没有对应的IDEA设置,Eclipse先使用对应值-->
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_enum_constants" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_parameterized_type_references" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_type_parameters" value="16"/>
|
||||
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration" value="16"/>
|
||||
|
||||
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_method_declaration" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_compact_if" value="16"/>
|
||||
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_type_arguments" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression"
|
||||
value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_multiple_fields" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_expressions_in_for_loop_header" value="16"/>
|
||||
|
||||
<!--IDEA默认配置在同一行,Eclipse使用对应值即可-->
|
||||
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_for_statment" value="common_lines"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_method_invocation" value="common_lines"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_switch_statement" value="common_lines"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_enum_constant_declaration"
|
||||
value="common_lines"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_if_while_statement" value="common_lines"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_catch_clause" value="common_lines"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_annotation" value="common_lines"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_try_clause" value="common_lines"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_method_delcaration" value="common_lines"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_lambda_declaration" value="common_lines"/>
|
||||
|
||||
<!--Java:BINARY_OPERATION_SIGN_ON_NEXT_LINE-->
|
||||
<setting id="org.eclipse.jdt.core.formatter.wrap_before_binary_operator" value="true"/>
|
||||
|
||||
<!--ASSIGNMENT_WRAP 需要设置为 WRAP_AS_NEEDED WRAP_AS_NEEDED . Add in jdt.core-3.12,it's not work in previous version -->
|
||||
<setting id="org.eclipse.jdt.core.formatter.wrap_before_assignment_operator" value="false"/>
|
||||
|
||||
<!--IDEA无配置项,Eclipse使用对应值即可-->
|
||||
<setting id="org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.wrap_before_conditional_operator" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line" value="false"/>
|
||||
|
||||
<!--org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=Java:KEEP_CONTROL_STATEMENT_IN_ONE_LINE-->
|
||||
<setting id="org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line" value="false"/>
|
||||
<!--org.eclipse.jdt.core.formatter.compact_else_if=Java:SPECIAL_ELSE_IF_TREATMENT-->
|
||||
<setting id="org.eclipse.jdt.core.formatter.compact_else_if" value="true"/>
|
||||
<!--Java:ALIGN_GROUP_FIELD_DECLARATIONS-->
|
||||
<setting id="org.eclipse.jdt.core.formatter.align_type_members_on_columns" value="false"/>
|
||||
<!--Java:<Programmatic>-->
|
||||
<setting id="org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve" value="1"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.join_wrapped_lines" value="true"/>
|
||||
|
||||
<!--统一为end_of_lint,IDEA默认一致-->
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_method_declaration" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_block" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_lambda_body" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_block_in_case" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration"
|
||||
value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_switch" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_array_initializer" value="end_of_line"/>
|
||||
|
||||
|
||||
<!-- <setting id="org.eclipse.jdt.core.compiler.source" value="1.8"/>
|
||||
<setting id="org.eclipse.jdt.core.compiler.compliance" value="1.8"/>
|
||||
<setting id="org.eclipse.jdt.core.compiler.codegen.targetPlatform" value="1.8"/>
|
||||
<setting id="org.eclipse.jdt.core.compiler.problem.enumIdentifier" value="error"/>
|
||||
<setting id="org.eclipse.jdt.core.compiler.problem.assertIdentifier" value="error"/>
|
||||
<setting id="org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode" value="enabled"/>
|
||||
-->
|
||||
<!--Java:KEEP_SIMPLE_BLOCKS_IN_ONE_LINE-->
|
||||
<setting id="org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line" value="false"/>
|
||||
|
||||
<!--Java:CLASS_BRACE_STYLE,统一使用end_of_line TODO-->
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_enum_constant" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_type_declaration" value="end_of_line"/>
|
||||
|
||||
<!--org.eclipse.jdt.core.formatter.lineSplit=RIGHT_MARGIN-->
|
||||
<setting id="org.eclipse.jdt.core.formatter.lineSplit" value="120"/>
|
||||
</profile>
|
||||
</profiles>
|
||||
Vendored
+80
@@ -0,0 +1,80 @@
|
||||
{
|
||||
// Place your xiaoyishu 工作区 snippets here. Each snippet is defined under a snippet name and has a scope, prefix, body and
|
||||
// description. Add comma separated ids of the languages where the snippet is applicable in the scope field. If scope
|
||||
// is left empty or omitted, the snippet gets applied to all languages. The prefix is what is
|
||||
// used to trigger the snippet and the body will be expanded and inserted. Possible variables are:
|
||||
// $1, $2 for tab stops, $0 for the final cursor position, and ${1:label}, ${2:another} for placeholders.
|
||||
// Placeholders with the same ids are connected.
|
||||
// Example:
|
||||
// "Print to console": {
|
||||
// "scope": "javascript,typescript",
|
||||
// "prefix": "log",
|
||||
// "body": [
|
||||
// "console.log('$1');",
|
||||
// "$2"
|
||||
// ],
|
||||
// "description": "Log output to console"
|
||||
// }
|
||||
"类注释": {
|
||||
"prefix": "ccj",
|
||||
"body": [
|
||||
"/**",
|
||||
" *",
|
||||
" * @author ${1:}",
|
||||
" * @version V1.0",
|
||||
" * @title ${TM_FILENAME_BASE}",
|
||||
" * @date ${CURRENT_YEAR}/${CURRENT_MONTH}/${CURRENT_DATE} ${CURRENT_HOUR}:${CURRENT_MINUTE}",
|
||||
" * @description ${2:}",
|
||||
" */",
|
||||
""
|
||||
],
|
||||
"description": "生成类注释"
|
||||
},
|
||||
"接口注释": {
|
||||
"prefix": "icj",
|
||||
"body": [
|
||||
"/**",
|
||||
" *",
|
||||
" * @author ${1:}",
|
||||
" * @version V1.0",
|
||||
" * @title ${TM_FILENAME_BASE}",
|
||||
" * @date ${CURRENT_YEAR}/${CURRENT_MONTH}/${CURRENT_DATE} ${CURRENT_HOUR}:${CURRENT_MINUTE}",
|
||||
" * @description ${2:}",
|
||||
" */",
|
||||
""
|
||||
],
|
||||
"description": "生成接口注释"
|
||||
},
|
||||
"注解注释": {
|
||||
"prefix": "acj",
|
||||
"body": [
|
||||
"/**",
|
||||
" *",
|
||||
" * @author ${1:}",
|
||||
" * @version V1.0",
|
||||
" * @title ${TM_FILENAME_BASE}",
|
||||
" * @date ${CURRENT_YEAR}/${CURRENT_MONTH}/${CURRENT_DATE} ${CURRENT_HOUR}:${CURRENT_MINUTE}",
|
||||
" * @description ${2:}",
|
||||
" */",
|
||||
""
|
||||
],
|
||||
"description": "生成注解注释"
|
||||
},
|
||||
"字符串常量": {
|
||||
"prefix": "prfs",
|
||||
"body": ["private final static String ${1:NAME} = \"${1:NAME}\";"],
|
||||
"description": "生成字符串常量"
|
||||
},
|
||||
"Long常量": {
|
||||
"prefix": "prfl",
|
||||
"body": ["private final static Long ${1:NAME} = ${1:NAME}L;"],
|
||||
"description": "生成Long常量"
|
||||
},
|
||||
"Generate serialVersionUID": {
|
||||
"prefix": "serial", // 输入 "serial" 触发模板
|
||||
"body": [
|
||||
"private static final long serialVersionUID = ${:${generateSerialVersionUID}}L;"
|
||||
],
|
||||
"description": "生成 Serializable 类的 serialVersionUID"
|
||||
}
|
||||
}
|
||||
Vendored
+15
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"type": "java",
|
||||
"name": "Spring Boot-XiaoyiAuthApplication<xiaoyi-auth>",
|
||||
"request": "launch",
|
||||
"cwd": "${workspaceFolder}",
|
||||
"mainClass": "top.crushtj.xiaoyishu.auth.XiaoyiAuthApplication",
|
||||
"projectName": "xiaoyi-auth",
|
||||
"args": "",
|
||||
"vmArgs": "-Djasypt.encryptor.password=GhaU7VjZd2b3M4Hbx4SelEXZc",
|
||||
"envFile": "${workspaceFolder}/.env"
|
||||
}
|
||||
]
|
||||
}
|
||||
Vendored
+8
-8
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"editor.fontSize": 14,
|
||||
"files.autoSave": "onFocusChange",
|
||||
"maven.executable.options": "-s D:\\Programs\\Dev\\maven\\conf\\settings-aliyun.xml",
|
||||
"java.configuration.maven.userSettings": "D:\\Programs\\Dev\\maven\\conf\\settings-aliyun.xml",
|
||||
"java.configuration.maven.globalSettings": "D:\\Programs\\Dev\\maven\\conf\\settings-aliyun.xml",
|
||||
"editor.accessibilitySupport": "auto",
|
||||
"editor.fontFamily": "JetBrains Mono",
|
||||
"maven.executable.options": "-s D:\\Programs\\Dev\\maven\\conf\\settings-aliyun.xml -DskipTests",
|
||||
"java.format.settings.url": ".vscode/java-formatter.xml",
|
||||
"java.compile.nullAnalysis.mode": "automatic",
|
||||
"java.configuration.updateBuildConfiguration": "interactive"
|
||||
"java.configuration.updateBuildConfiguration": "automatic",
|
||||
"[java]": {
|
||||
"editor.tabSize": 4
|
||||
},
|
||||
"maven.executable.path": "D:\\Programs\\Dev\\maven\\bin\\mvn.cmd",
|
||||
"maven.view": "hierarchical"
|
||||
}
|
||||
@@ -1,136 +1,289 @@
|
||||
<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>
|
||||
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>top.crushtj</groupId>
|
||||
<artifactId>xiaoyishu</artifactId>
|
||||
<version>${revision}</version>
|
||||
<groupId>top.crushtj</groupId>
|
||||
<artifactId>xiaoyishu</artifactId>
|
||||
<version>${revision}</version>
|
||||
|
||||
<!-- 项目名称 -->
|
||||
<name>${project.artifactId}</name>
|
||||
<!-- 项目描述 -->
|
||||
<description>小壹书(仿小红书),基于 Spring Cloud Alibaba 微服务架构</description>
|
||||
<!-- 项目名称 -->
|
||||
<name>${project.artifactId}</name>
|
||||
<!-- 项目描述 -->
|
||||
<description>小壹书(仿小红书),基于 Spring Cloud Alibaba 微服务架构</description>
|
||||
|
||||
<!-- 多模块项目需要配置打包方式为 pom -->
|
||||
<packaging>pom</packaging>
|
||||
<!-- 多模块项目需要配置打包方式为 pom -->
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<!-- 子模块管理 -->
|
||||
<modules>
|
||||
<module>xiaoyi-auth</module>
|
||||
<module>xiaoyi-framework</module>
|
||||
</modules>
|
||||
<!-- 子模块管理 -->
|
||||
<modules>
|
||||
<module>xiaoyi-auth</module>
|
||||
<module>xiaoyi-framework</module>
|
||||
<module>xiaoyi-gateway</module>
|
||||
</modules>
|
||||
|
||||
<properties>
|
||||
<!-- 项目版本号 -->
|
||||
<revision>0.0.1-SNAPSHOT</revision>
|
||||
<!-- JDK 版本 -->
|
||||
<java.version>17</java.version>
|
||||
<maven.compiler.source>${java.version}</maven.compiler.source>
|
||||
<maven.compiler.target>${java.version}</maven.compiler.target>
|
||||
<!-- 项目编码 -->
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<!-- Maven 相关版本号 -->
|
||||
<maven-compiler-plugin.version>3.8.1</maven-compiler-plugin.version>
|
||||
<properties>
|
||||
<!-- 项目版本号 -->
|
||||
<revision>0.0.1-SNAPSHOT</revision>
|
||||
<!-- JDK 版本 -->
|
||||
<java.version>17</java.version>
|
||||
<maven.compiler.source>${java.version}</maven.compiler.source>
|
||||
<maven.compiler.target>${java.version}</maven.compiler.target>
|
||||
<!-- 项目编码 -->
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<!-- Maven 相关版本号 -->
|
||||
<maven-compiler-plugin.version>3.8.1</maven-compiler-plugin.version>
|
||||
|
||||
<!-- 依赖包版本 -->
|
||||
<lombok.version>1.18.30</lombok.version>
|
||||
<spring-boot.version>3.0.2</spring-boot.version>
|
||||
<spring-cloud-alibaba.version>2022.0.0.0</spring-cloud-alibaba.version>
|
||||
<spring-cloud.version>2022.0.0</spring-cloud.version>
|
||||
</properties>
|
||||
<!-- 依赖包版本 -->
|
||||
<lombok.version>1.18.30</lombok.version>
|
||||
<spring-boot.version>3.0.2</spring-boot.version>
|
||||
<spring-cloud-alibaba.version>2022.0.0.0</spring-cloud-alibaba.version>
|
||||
<spring-cloud.version>2022.0.0</spring-cloud.version>
|
||||
<jackson.version>2.16.1</jackson.version>
|
||||
<mysql-connector-java.version>8.0.29</mysql-connector-java.version>
|
||||
<mybatis-plus.version>3.5.5</mybatis-plus.version>
|
||||
<druid.version>1.2.27</druid.version>
|
||||
<flatten-maven-plugin.version>1.5.0</flatten-maven-plugin.version>
|
||||
<jansi.version>1.18</jansi.version>
|
||||
<sa-token.version>1.38.0</sa-token.version>
|
||||
<guava.version>33.0.0-jre</guava.version>
|
||||
<hutool.version>5.8.26</hutool.version>
|
||||
<commons-lang3.version>3.12.0</commons-lang3.version>
|
||||
<dypnsapi.version>2.0.0</dypnsapi.version>
|
||||
<jasypt-starter.version>3.0.5</jasypt-starter.version>
|
||||
<nacos-config.version>0.3.0-RC</nacos-config.version>
|
||||
</properties>
|
||||
|
||||
<!-- 统一依赖管理 -->
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<!-- Spring Boot 官方依赖管理 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-dependencies</artifactId>
|
||||
<version>${spring-boot.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
<!-- 统一依赖管理 -->
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>top.crushtj</groupId>
|
||||
<artifactId>xiaoyi-common</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
<!-- 业务接口日志组件 -->
|
||||
<dependency>
|
||||
<groupId>top.crushtj</groupId>
|
||||
<artifactId>xiaoyi-spring-boot-starter-biz-operationlog</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Spring Cloud Alibaba 官方依赖管理 -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
|
||||
<version>${spring-cloud-alibaba.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
<!-- 业务接口日志组件 -->
|
||||
<dependency>
|
||||
<groupId>top.crushtj</groupId>
|
||||
<artifactId>xiaoyi-spring-boot-starter-jackson</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
<!-- Spring Boot 官方依赖管理 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-dependencies</artifactId>
|
||||
<version>${spring-boot.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Spring Cloud 官方依赖管理 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-dependencies</artifactId>
|
||||
<version>${spring-cloud.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
<!-- Spring Cloud Alibaba 官方依赖管理 -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
|
||||
<version>${spring-cloud-alibaba.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- 避免编写那些冗余的 Java 样板式代码,如 get、set 等 -->
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>${lombok.version}</version>
|
||||
</dependency>
|
||||
<!-- Spring Cloud 官方依赖管理 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-dependencies</artifactId>
|
||||
<version>${spring-cloud.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
<build>
|
||||
<!-- 统一插件管理 -->
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<version>${spring-boot.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>repackage</id>
|
||||
<goals>
|
||||
<goal>repackage</goal> <!-- 将依赖的 Jar 一起打包 -->
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>${maven-compiler-plugin.version}</version>
|
||||
<configuration>
|
||||
<source>${java.version}</source>
|
||||
<target>${java.version}</target>
|
||||
<encoding>${project.build.sourceEncoding}</encoding>
|
||||
<annotationProcessorPaths>
|
||||
<path>
|
||||
<!-- 避免编写那些冗余的 Java 样板式代码,如 get、set 等 -->
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>${lombok.version}</version>
|
||||
</path>
|
||||
</annotationProcessorPaths>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
</dependency>
|
||||
|
||||
</build>
|
||||
<!-- Jackson -->
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
<version>${jackson.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 添加华为云、阿里云 maven 中央仓库,提升依赖下载速度 -->
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>huaweicloud</id>
|
||||
<name>huawei</name>
|
||||
<url>https://mirrors.huaweicloud.com/repository/maven/</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>aliyunmaven</id>
|
||||
<name>aliyun</name>
|
||||
<url>https://maven.aliyun.com/repository/public</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-core</artifactId>
|
||||
<version>${jackson.version}</version>
|
||||
</dependency>
|
||||
<!-- Mybatis-Plus -->
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-boot-starter</artifactId>
|
||||
<version>${mybatis-plus.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- MySQL 驱动 -->
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
<version>${mysql-connector-java.version}</version>
|
||||
</dependency>
|
||||
<!-- Druid 数据库连接池 -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>druid-spring-boot-3-starter</artifactId>
|
||||
<version>${druid.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- sa-token 认证组件 -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-spring-boot3-starter</artifactId>
|
||||
<version>${sa-token.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-reactor-spring-boot3-starter</artifactId>
|
||||
<version>${sa-token.version}</version>
|
||||
</dependency>
|
||||
<!-- Sa-Token 整合 Redis (使用 jackson 序列化方式) -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-redis-jackson</artifactId>
|
||||
<version>${sa-token.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.fusesource.jansi</groupId>
|
||||
<artifactId>jansi</artifactId>
|
||||
<version>${jansi.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 常用工具类 -->
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
<version>${guava.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-all</artifactId>
|
||||
<version>${hutool.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
<version>${commons-lang3.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!--阿里云短信认证服务-->
|
||||
<dependency>
|
||||
<groupId>com.aliyun</groupId>
|
||||
<artifactId>dypnsapi20170525</artifactId>
|
||||
<version>${dypnsapi.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- jasypt 加密工具 -->
|
||||
<dependency>
|
||||
<groupId>com.github.ulisesbocchio</groupId>
|
||||
<artifactId>jasypt-spring-boot-starter</artifactId>
|
||||
<version>${jasypt-starter.version}</version>
|
||||
</dependency>
|
||||
<!-- Nacos配置中心 -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.boot</groupId>
|
||||
<artifactId>nacos-config-spring-boot-starter</artifactId>
|
||||
<version>${nacos-config.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
<build>
|
||||
<!-- 统一插件管理 -->
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<version>${spring-boot.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>repackage</id>
|
||||
<goals>
|
||||
<goal>repackage</goal> <!-- 将依赖的 Jar 一起打包 -->
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>${maven-compiler-plugin.version}</version>
|
||||
<configuration>
|
||||
<source>${java.version}</source>
|
||||
<target>${java.version}</target>
|
||||
<encoding>${project.build.sourceEncoding}</encoding>
|
||||
<annotationProcessorPaths>
|
||||
<path>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>${lombok.version}</version>
|
||||
</path>
|
||||
</annotationProcessorPaths>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>flatten-maven-plugin</artifactId>
|
||||
<version>${flatten-maven-plugin.version}</version>
|
||||
<configuration>
|
||||
<flattenMode>resolveCiFriendliesOnly</flattenMode>
|
||||
<updatePomFile>true</updatePomFile>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>flatten</id>
|
||||
<phase>process-resources</phase>
|
||||
<goals>
|
||||
<goal>flatten</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>flatten.clean</id>
|
||||
<phase>clean</phase>
|
||||
<goals>
|
||||
<goal>clean</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
|
||||
</build>
|
||||
|
||||
<!-- 添加华为云、阿里云 maven 中央仓库,提升依赖下载速度 -->
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>huaweicloud</id>
|
||||
<name>huawei</name>
|
||||
<url>https://mirrors.huaweicloud.com/repository/maven/</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>aliyunmaven</id>
|
||||
<name>aliyun</name>
|
||||
<url>https://maven.aliyun.com/repository/public</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
</project>
|
||||
+133
-33
@@ -1,42 +1,142 @@
|
||||
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<!-- 指定父项目 -->
|
||||
<parent>
|
||||
<groupId>top.crushtj</groupId>
|
||||
<artifactId>xiaoyishu</artifactId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<!-- 指定父项目 -->
|
||||
<parent>
|
||||
<groupId>top.crushtj</groupId>
|
||||
<artifactId>xiaoyishu</artifactId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
|
||||
<!-- 指定打包方式 -->
|
||||
<packaging>jar</packaging>
|
||||
<!-- 指定打包方式 -->
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<artifactId>xiaoyi-auth</artifactId>
|
||||
<name>${project.artifactId}</name>
|
||||
<description>小壹书:认证服务(负责处理用户登录、注册、账号注销等)</description>
|
||||
<artifactId>xiaoyi-auth</artifactId>
|
||||
<name>${project.artifactId}</name>
|
||||
<description>小壹书:认证服务(负责处理用户登录、注册、账号注销等)</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>top.crushtj</groupId>
|
||||
<artifactId>xiaoyi-common</artifactId>
|
||||
</dependency>
|
||||
<!-- 业务接口日志组件 -->
|
||||
<dependency>
|
||||
<groupId>top.crushtj</groupId>
|
||||
<artifactId>xiaoyi-spring-boot-starter-biz-operationlog</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<!--Jackson-->
|
||||
<dependency>
|
||||
<groupId>top.crushtj</groupId>
|
||||
<artifactId>xiaoyi-spring-boot-starter-jackson</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<!-- Mybatis-Plus -->
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-boot-starter</artifactId>
|
||||
</dependency>
|
||||
<!-- MySQL 驱动 -->
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
</dependency>
|
||||
<!-- Druid 数据库连接池 -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>druid-spring-boot-3-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- sa-token 认证组件 -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-spring-boot3-starter</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-redis-jackson</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Redis -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Redis 连接池 -->
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-pool2</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 阿里云短信认证服务 -->
|
||||
<dependency>
|
||||
<groupId>com.aliyun</groupId>
|
||||
<artifactId>dypnsapi20170525</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- jasypt 加密工具 -->
|
||||
<dependency>
|
||||
<groupId>com.github.ulisesbocchio</groupId>
|
||||
<artifactId>jasypt-spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Nacos 配置中心 -->
|
||||
<!-- <dependency> -->
|
||||
<!-- <groupId>com.alibaba.boot</groupId> -->
|
||||
<!-- <artifactId>nacos-config-spring-boot-starter</artifactId> -->
|
||||
<!-- </dependency> -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-bootstrap</artifactId>
|
||||
</dependency>
|
||||
<!-- 服务注册发现 -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/java</directory>
|
||||
<includes>
|
||||
<include>**/*.xml</include>
|
||||
</includes>
|
||||
<filtering>false</filtering>
|
||||
</resource>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
</resource>
|
||||
</resources>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,21 @@
|
||||
package top.crushtj.xiaoyi.auth;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author ayi
|
||||
* @title XiaoyiAuthApplication
|
||||
* @description 启动类
|
||||
* @date 2025/11/20
|
||||
*/
|
||||
|
||||
@SpringBootApplication
|
||||
public class XiaoyiAuthApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(XiaoyiAuthApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package top.crushtj.xiaoyi.auth.alarm;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.cloud.context.config.annotation.RefreshScope;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import top.crushtj.xiaoyi.auth.alarm.impl.MailAlarmHelper;
|
||||
import top.crushtj.xiaoyi.auth.alarm.impl.SmsAlarmHelper;
|
||||
import top.crushtj.xiaoyi.auth.constant.XiaoyiAuthConstants;
|
||||
|
||||
/**
|
||||
* @author ayi
|
||||
* @version V1.0
|
||||
* @title AlarmConfig
|
||||
* @date 2026/2/2 15:37
|
||||
* @description 告警配置类
|
||||
*/
|
||||
|
||||
@Configuration
|
||||
@RefreshScope
|
||||
public class AlarmConfig {
|
||||
|
||||
@Value("${alarm.type}")
|
||||
private String alarmType;
|
||||
|
||||
@Bean
|
||||
@RefreshScope
|
||||
public AlarmInterface alarmHelper() {
|
||||
// 根据配置文件中的告警类型,初始化选择不同的告警实现类
|
||||
if (StringUtils.equals(XiaoyiAuthConstants.ALARM_TYPE_SMS, alarmType)) {
|
||||
return new SmsAlarmHelper();
|
||||
} else if (StringUtils.equals(XiaoyiAuthConstants.ALARM_TYPE_EMAIL, alarmType)) {
|
||||
return new MailAlarmHelper();
|
||||
} else {
|
||||
throw new IllegalArgumentException("错误的告警类型...");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package top.crushtj.xiaoyi.auth.alarm;
|
||||
|
||||
/**
|
||||
* @author ayi
|
||||
* @version V1.0
|
||||
* @title AlarmInterface
|
||||
* @date 2026/2/2 15:37
|
||||
* @description 告警接口
|
||||
*/
|
||||
|
||||
public interface AlarmInterface {
|
||||
|
||||
/**
|
||||
* 发送告警信息
|
||||
*
|
||||
* @param message 告警信息
|
||||
* @return 发送结果
|
||||
*/
|
||||
boolean send(String message);
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package top.crushtj.xiaoyi.auth.alarm.impl;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import top.crushtj.xiaoyi.auth.alarm.AlarmInterface;
|
||||
|
||||
/**
|
||||
* @author ayi
|
||||
* @version V1.0
|
||||
* @title MailAlarmHelper
|
||||
* @date 2026/2/2 15:38
|
||||
* @description 邮件告警通知类
|
||||
*/
|
||||
|
||||
@Slf4j
|
||||
public class MailAlarmHelper implements AlarmInterface {
|
||||
|
||||
/**
|
||||
* 发送告警信息
|
||||
*
|
||||
* @param message 告警信息
|
||||
* @return 发送结果
|
||||
*/
|
||||
@Override
|
||||
public boolean send(String message) {
|
||||
log.info("==> 【邮件告警】:{}", message);
|
||||
|
||||
// 业务逻辑...
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package top.crushtj.xiaoyi.auth.alarm.impl;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import top.crushtj.xiaoyi.auth.alarm.AlarmInterface;
|
||||
|
||||
/**
|
||||
* @author ayi
|
||||
* @version V1.0
|
||||
* @title SmsAlarmHelper
|
||||
* @date 2026/2/2 15:39
|
||||
* @description 短信告警通知类
|
||||
*/
|
||||
|
||||
@Slf4j
|
||||
public class SmsAlarmHelper implements AlarmInterface {
|
||||
|
||||
/**
|
||||
* 发送告警信息
|
||||
*
|
||||
* @param message 告警信息
|
||||
* @return 发送结果
|
||||
*/
|
||||
@Override
|
||||
public boolean send(String message) {
|
||||
log.info("==> 【短信告警】:{}", message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package top.crushtj.xiaoyi.auth.config;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
|
||||
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
|
||||
import org.mybatis.spring.annotation.MapperScan;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author ayi
|
||||
* @title MyBatisConfig
|
||||
* @date 2025/11/27
|
||||
* @description MybatisPlus配置类
|
||||
*/
|
||||
|
||||
@Configuration
|
||||
@MapperScan("top.crushtj.**.mappers")
|
||||
public class MybatisPlusConfig {
|
||||
/**
|
||||
* 分页插件
|
||||
*
|
||||
* @return MybatisPlusInterceptor
|
||||
*/
|
||||
@Bean
|
||||
public MybatisPlusInterceptor mybatisPlusInterceptor() {
|
||||
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
|
||||
interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
|
||||
return interceptor;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package top.crushtj.xiaoyi.auth.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
|
||||
import org.springframework.data.redis.serializer.StringRedisSerializer;
|
||||
import org.springframework.lang.NonNull;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author ayi
|
||||
* @version V1.0
|
||||
* @title RedisTemplateConfig
|
||||
* @description Redis 配置类
|
||||
* @date 2026/01/12 19:13
|
||||
*/
|
||||
|
||||
@Configuration
|
||||
public class RedisTemplateConfig {
|
||||
|
||||
@Bean
|
||||
public RedisTemplate<String, Object> redisTemplate(@NonNull RedisConnectionFactory connectionFactory) {
|
||||
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
|
||||
// 设置 RedisTemplate 的连接工厂
|
||||
redisTemplate.setConnectionFactory(connectionFactory);
|
||||
|
||||
// 使用 StringRedisSerializer 来序列化和反序列化 redis 的 key 值,确保 key 是可读的字符串
|
||||
redisTemplate.setKeySerializer(new StringRedisSerializer());
|
||||
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
|
||||
|
||||
// 使用 Jackson2JsonRedisSerializer 来序列化和反序列化 redis 的 value 值, 确保存储的是 JSON 格式
|
||||
Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class);
|
||||
redisTemplate.setValueSerializer(serializer);
|
||||
redisTemplate.setHashValueSerializer(serializer);
|
||||
|
||||
redisTemplate.afterPropertiesSet();
|
||||
return redisTemplate;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package top.crushtj.xiaoyi.auth.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
|
||||
import static top.crushtj.xiaoyi.auth.constant.ConfigConstants.*;
|
||||
|
||||
/**
|
||||
* @author ayi
|
||||
* @version V1.0
|
||||
* @title ThreadPoolConfig
|
||||
* @date 2026/01/16 22:26
|
||||
* @description 线程池配置类
|
||||
*/
|
||||
|
||||
@Configuration
|
||||
public class ThreadPoolConfig {
|
||||
|
||||
@Bean(name = "taskExecutor")
|
||||
public Executor taskExecutor() {
|
||||
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
|
||||
// 核心线程数
|
||||
executor.setCorePoolSize(CORE_POOL_SIZE);
|
||||
// 最大线程数
|
||||
executor.setMaxPoolSize(MAX_POOL_SIZE);
|
||||
// 队列容量
|
||||
executor.setQueueCapacity(QUEUE_CAPACITY);
|
||||
// 线程活跃时间(秒)
|
||||
executor.setKeepAliveSeconds(KEEP_ALIVE_SECONDS);
|
||||
// 线程名前缀
|
||||
executor.setThreadNamePrefix(THREAD_NAME_PREFIX);
|
||||
|
||||
// 拒绝策略:由调用线程处理(一般为主线程)
|
||||
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
|
||||
|
||||
// 等待所有任务结束后再关闭线程池
|
||||
executor.setWaitForTasksToCompleteOnShutdown(true);
|
||||
// 设置等待时间,如果超过这个时间还没有销毁就强制销毁,以确保应用最后能够被关闭,而不是被没有完成的任务阻塞
|
||||
executor.setAwaitTerminationSeconds(AWAIT_TERMINATION_SECONDS);
|
||||
|
||||
executor.initialize();
|
||||
return executor;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package top.crushtj.xiaoyi.auth.constant;
|
||||
|
||||
/**
|
||||
* @author ayi
|
||||
* @version V1.0
|
||||
* @title ConfigConstants
|
||||
* @date 2026/01/16
|
||||
* @description 配置常量类
|
||||
*/
|
||||
|
||||
public class ConfigConstants {
|
||||
|
||||
/**
|
||||
* 核心线程数
|
||||
*/
|
||||
public static final int CORE_POOL_SIZE = 10;
|
||||
/**
|
||||
* 最大线程数
|
||||
*/
|
||||
public static final int MAX_POOL_SIZE = 50;
|
||||
/**
|
||||
* 队列容量
|
||||
*/
|
||||
public static final int QUEUE_CAPACITY = 200;
|
||||
/**
|
||||
* 线程活跃时间(秒)
|
||||
*/
|
||||
public static final int KEEP_ALIVE_SECONDS = 30;
|
||||
/**
|
||||
* 线程名前缀
|
||||
*/
|
||||
public static final String THREAD_NAME_PREFIX = "AuthExecutor-";
|
||||
|
||||
public static final int AWAIT_TERMINATION_SECONDS = 60;
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package top.crushtj.xiaoyi.auth.constant;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author ayi
|
||||
* @version V1.0
|
||||
* @title RedisKeyConstants
|
||||
* @description Redis key 常量
|
||||
* @date 2026/01/15 18:45
|
||||
*/
|
||||
|
||||
public class RedisKeyConstants {
|
||||
|
||||
/**
|
||||
* 验证码 KEY 前缀
|
||||
*/
|
||||
private static final String VERIFICATION_CODE_KEY_PREFIX = "verification_code:";
|
||||
/**
|
||||
* 验证码 KEY 过期时间 (分钟)
|
||||
*/
|
||||
public static final long VERIFICATION_CODE_EXPIRE_TIME = 3;
|
||||
|
||||
/**
|
||||
* 用户角色数据 KEY 前缀
|
||||
*/
|
||||
private static final String USER_ROLES_KEY_PREFIX = "user:roles:";
|
||||
|
||||
/**
|
||||
* 小壹书全局 ID 生成器 KEY
|
||||
*/
|
||||
public static final String XIAOYI_ID_GENERATOR_KEY = "xiaoyishu.id.generator";
|
||||
|
||||
/**
|
||||
* 角色权限数据 KEY 前缀
|
||||
*/
|
||||
public static final String ROLE_PERMISSIONS_KEY_PREFIX = "role:permissions:";
|
||||
|
||||
/**
|
||||
* 构建验证码 KEY
|
||||
*
|
||||
* @param phone 手机号
|
||||
* @return 验证码 KEY
|
||||
*/
|
||||
public static String buildVerificationCodeKey(String phone) {
|
||||
return VERIFICATION_CODE_KEY_PREFIX + phone;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建用户角色数据 KEY
|
||||
*
|
||||
* @param userId 用户手机号
|
||||
* @return 用户角色数据 KEY
|
||||
*/
|
||||
public static String buildUserRolesKey(Long userId) {
|
||||
return USER_ROLES_KEY_PREFIX + userId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建角色权限数据 KEY
|
||||
*
|
||||
* @param roleKey 角色 ID
|
||||
* @return 角色权限数据 KEY
|
||||
*/
|
||||
public static String buildRolePermissionsKey(String roleKey) {
|
||||
return ROLE_PERMISSIONS_KEY_PREFIX + roleKey;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package top.crushtj.xiaoyi.auth.constant;
|
||||
|
||||
/**
|
||||
* @author ayi
|
||||
* @version V1.0
|
||||
* @title RoleConstants
|
||||
* @date 2026/01/18 21:24
|
||||
* @description 角色常量
|
||||
*/
|
||||
|
||||
public class RoleConstants {
|
||||
/**
|
||||
* 普通用户的角色 ID
|
||||
*/
|
||||
public static final Long COMMON_USER_ROLE_ID = 1L;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package top.crushtj.xiaoyi.auth.constant;
|
||||
|
||||
/**
|
||||
* @author ayi
|
||||
* @version V1.0
|
||||
* @title XiaoyiAuthConstants
|
||||
* @date 2026/01/19 19:40
|
||||
* @description 小一书常量
|
||||
*/
|
||||
|
||||
public class XiaoyiAuthConstants {
|
||||
public static final String NICK_NAME_PREFIX = "咿呀_";
|
||||
public static final Long XIAOYI_ID_INITIAL_VALUE = 1000000L;
|
||||
|
||||
public static final String ALARM_TYPE_SMS = "sms";
|
||||
public static final String ALARM_TYPE_EMAIL = "email";
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package top.crushtj.xiaoyi.auth.controller;
|
||||
|
||||
import com.alibaba.nacos.api.config.annotation.NacosValue;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import top.crushtj.xiaoyi.auth.alarm.AlarmInterface;
|
||||
|
||||
@Slf4j
|
||||
@RestController
|
||||
public class TestController {
|
||||
|
||||
@NacosValue(value = "${rate-limit.api.limit}",autoRefreshed = true)
|
||||
private Integer limit;
|
||||
|
||||
@Resource
|
||||
private AlarmInterface alarm;
|
||||
|
||||
@GetMapping("/test")
|
||||
public String test() {
|
||||
return "当前限流阈值为: " + limit;
|
||||
}
|
||||
|
||||
@GetMapping("/alarm")
|
||||
public String sendAlarm() {
|
||||
alarm.send("系统出错啦,犬小哈这个月绩效没了,速度上线解决问题!");
|
||||
return "alarm success";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package top.crushtj.xiaoyi.auth.controller;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import top.crushtj.framework.biz.operationlog.aspect.ApiOperationLog;
|
||||
import top.crushtj.framework.common.response.Response;
|
||||
import top.crushtj.xiaoyi.auth.model.vo.user.UserLoginReqVO;
|
||||
import top.crushtj.xiaoyi.auth.service.UserService;
|
||||
|
||||
/**
|
||||
* @author ayi
|
||||
* @version V1.0
|
||||
* @title UserController
|
||||
* @date 2026-01-18 20:40:21
|
||||
* @description 用户表(User)表控制层
|
||||
*/
|
||||
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/user")
|
||||
public class UserController {
|
||||
|
||||
@Resource
|
||||
private UserService userService;
|
||||
|
||||
@PostMapping("/login")
|
||||
@ApiOperationLog(description = "用户登录或注册")
|
||||
public Response<String> loginOrRegister(@RequestBody @Validated UserLoginReqVO userLoginReqVO) {
|
||||
return userService.loginOrRegister(userLoginReqVO);
|
||||
}
|
||||
|
||||
@PostMapping("/logout")
|
||||
@ApiOperationLog(description = "用户登出")
|
||||
public Response<Void> logout(@RequestHeader("userId") String userId) {
|
||||
// todo 实现用户登出逻辑
|
||||
return Response.success();
|
||||
}
|
||||
}
|
||||
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
package top.crushtj.xiaoyi.auth.controller;
|
||||
|
||||
import top.crushtj.framework.biz.operationlog.aspect.ApiOperationLog;
|
||||
import top.crushtj.framework.common.response.Response;
|
||||
import top.crushtj.xiaoyi.auth.model.vo.verificationcode.SendVerificationCodeReqVO;
|
||||
import top.crushtj.xiaoyi.auth.service.VerificationCodeService;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* @author ayi
|
||||
* @version V1.0
|
||||
* @title VerificationCodeController
|
||||
* @date 2026/01/15
|
||||
* @description 验证码控制器
|
||||
*/
|
||||
|
||||
@Slf4j
|
||||
@RestController
|
||||
public class VerificationCodeController {
|
||||
@Resource
|
||||
private VerificationCodeService verificationCodeService;
|
||||
|
||||
@PostMapping("/verification/code/send")
|
||||
@ApiOperationLog(description = "发送短信验证码")
|
||||
public Response<?> send(@Validated @RequestBody SendVerificationCodeReqVO sendVerificationCodeReqVO) {
|
||||
return verificationCodeService.send(sendVerificationCodeReqVO);
|
||||
}
|
||||
}
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
package top.crushtj.xiaoyi.auth.domain.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* @author ayi
|
||||
* @version V1.0
|
||||
* @title PermissionEntity
|
||||
* @date 2026-01-19 19:47:27
|
||||
* @description 权限表(t_permission)表实体类
|
||||
*/
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@TableName("t_permission")
|
||||
public class PermissionEntity implements Serializable {
|
||||
@Serial
|
||||
private static final long serialVersionUID = -30618877661010662L;
|
||||
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
@TableId("ID")
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 父ID
|
||||
*/
|
||||
@TableField("PARENT_ID")
|
||||
private Long parentId;
|
||||
|
||||
/**
|
||||
* 权限名称
|
||||
*/
|
||||
@TableField("NAME")
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 类型(1:目录 2:菜单 3:按钮)
|
||||
*/
|
||||
@TableField("TYPE")
|
||||
private Integer type;
|
||||
|
||||
/**
|
||||
* 菜单路由
|
||||
*/
|
||||
@TableField("MENU_URL")
|
||||
private String menuUrl;
|
||||
|
||||
/**
|
||||
* 菜单图标
|
||||
*/
|
||||
@TableField("MENU_ICON")
|
||||
private String menuIcon;
|
||||
|
||||
/**
|
||||
* 管理系统中的显示顺序
|
||||
*/
|
||||
@TableField("SORT")
|
||||
private Integer sort;
|
||||
|
||||
/**
|
||||
* 权限标识
|
||||
*/
|
||||
@TableField("PERMISSION_KEY")
|
||||
private String permissionKey;
|
||||
|
||||
/**
|
||||
* 状态(1:启用;0:禁用)
|
||||
*/
|
||||
@TableField("STATUS")
|
||||
private Integer status;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@TableField("CREATE_TIME")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
@TableField("UPDATE_TIME")
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
/**
|
||||
* 逻辑删除(0:未删除 1:已删除)
|
||||
*/
|
||||
@TableField("IS_DELETED")
|
||||
private Boolean isDeleted;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
package top.crushtj.xiaoyi.auth.domain.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* @author ayi
|
||||
* @version V1.0
|
||||
* @title RoleEntity
|
||||
* @date 2026-01-19 19:48:23
|
||||
* @description 角色表(t_role)表实体类
|
||||
*/
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@TableName("t_role")
|
||||
public class RoleEntity implements Serializable {
|
||||
@Serial
|
||||
private static final long serialVersionUID = -77681371692201000L;
|
||||
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
@TableId("ID")
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 角色名
|
||||
*/
|
||||
@TableField("ROLE_NAME")
|
||||
private String roleName;
|
||||
|
||||
/**
|
||||
* 角色唯一标识
|
||||
*/
|
||||
@TableField("ROLE_KEY")
|
||||
private String roleKey;
|
||||
|
||||
/**
|
||||
* 状态(1:启用 0:禁用)
|
||||
*/
|
||||
@TableField("STATUS")
|
||||
private Integer status;
|
||||
|
||||
/**
|
||||
* 管理系统中的显示顺序
|
||||
*/
|
||||
@TableField("SORT")
|
||||
private Integer sort;
|
||||
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
@TableField("REMARK")
|
||||
private String remark;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@TableField("CREATE_TIME")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 最后一次更新时间
|
||||
*/
|
||||
@TableField("UPDATE_TIME")
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
/**
|
||||
* 逻辑删除(0:未删除 1:已删除)
|
||||
*/
|
||||
@TableField("IS_DELETED")
|
||||
private Boolean isDeleted;
|
||||
|
||||
}
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
package top.crushtj.xiaoyi.auth.domain.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* @author ayi
|
||||
* @version V1.0
|
||||
* @title RolePermissionRelEntity
|
||||
* @date 2026-01-19 19:48:31
|
||||
* @description 用户权限表(t_role_permission_rel)表实体类
|
||||
*/
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@TableName("t_role_permission_rel")
|
||||
public class RolePermissionRelEntity implements Serializable {
|
||||
@Serial
|
||||
private static final long serialVersionUID = 345004944667469434L;
|
||||
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
@TableId("ID")
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 角色ID
|
||||
*/
|
||||
@TableField("ROLE_ID")
|
||||
private Long roleId;
|
||||
|
||||
/**
|
||||
* 权限ID
|
||||
*/
|
||||
@TableField("PERMISSION_ID")
|
||||
private Long permissionId;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@TableField("CREATE_TIME")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
@TableField("UPDATE_TIME")
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
/**
|
||||
* 逻辑删除(0:未删除 1:已删除)
|
||||
*/
|
||||
@TableField("IS_DELETED")
|
||||
private Boolean isDeleted;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
package top.crushtj.xiaoyi.auth.domain.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* @author ayi
|
||||
* @version V1.0
|
||||
* @title UserEntity
|
||||
* @date 2026-01-19 19:49:50
|
||||
* @description 用户表(t_user)表实体类
|
||||
*/
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@TableName("t_user")
|
||||
public class UserEntity implements Serializable {
|
||||
@Serial
|
||||
private static final long serialVersionUID = -47473970233354078L;
|
||||
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
@TableId("ID")
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 小壹书号(唯一凭证)
|
||||
*/
|
||||
@TableField("XIAOYISHU_ID")
|
||||
private String xiaoyishuId;
|
||||
|
||||
/**
|
||||
* 密码
|
||||
*/
|
||||
@TableField("PASSWORD")
|
||||
private String password;
|
||||
|
||||
/**
|
||||
* 昵称
|
||||
*/
|
||||
@TableField("NICKNAME")
|
||||
private String nickname;
|
||||
|
||||
/**
|
||||
* 头像
|
||||
*/
|
||||
@TableField("AVATAR")
|
||||
private String avatar;
|
||||
|
||||
/**
|
||||
* 生日
|
||||
*/
|
||||
@TableField("BIRTHDAY")
|
||||
private LocalDate birthday;
|
||||
|
||||
/**
|
||||
* 背景图
|
||||
*/
|
||||
@TableField("BACKGROUND_IMG")
|
||||
private String backgroundImg;
|
||||
|
||||
/**
|
||||
* 手机号
|
||||
*/
|
||||
@TableField("PHONE")
|
||||
private String phone;
|
||||
|
||||
/**
|
||||
* 性别(1: 男 2: 女 3: 未知)
|
||||
*/
|
||||
@TableField("SEX")
|
||||
private Integer sex;
|
||||
|
||||
/**
|
||||
* 状态(1:启用 0:禁用)
|
||||
*/
|
||||
@TableField("STATUS")
|
||||
private Integer status;
|
||||
|
||||
/**
|
||||
* 个人简介
|
||||
*/
|
||||
@TableField("INTRODUCTION")
|
||||
private String introduction;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@TableField("CREATE_TIME")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
@TableField("UPDATE_TIME")
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
/**
|
||||
* 逻辑删除(0:未删除 1:已删除)
|
||||
*/
|
||||
@TableField("IS_DELETED")
|
||||
private Boolean isDeleted;
|
||||
|
||||
}
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
package top.crushtj.xiaoyi.auth.domain.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* @author ayi
|
||||
* @version V1.0
|
||||
* @title UserRoleRelEntity
|
||||
* @date 2026-01-19 19:49:59
|
||||
* @description 用户角色表(t_user_role_rel)表实体类
|
||||
*/
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@TableName("t_user_role_rel")
|
||||
public class UserRoleRelEntity implements Serializable {
|
||||
@Serial
|
||||
private static final long serialVersionUID = -77498437697772085L;
|
||||
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
@TableId("ID")
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 用户ID
|
||||
*/
|
||||
@TableField("USER_ID")
|
||||
private Long userId;
|
||||
|
||||
/**
|
||||
* 角色ID
|
||||
*/
|
||||
@TableField("ROLE_ID")
|
||||
private Long roleId;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@TableField("CREATE_TIME")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
@TableField("UPDATE_TIME")
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
/**
|
||||
* 逻辑删除(0:未删除 1:已删除)
|
||||
*/
|
||||
@TableField("IS_DELETED")
|
||||
private Boolean isDeleted;
|
||||
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
package top.crushtj.xiaoyi.auth.domain.mappers;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import top.crushtj.xiaoyi.auth.domain.entity.PermissionEntity;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author ayi
|
||||
* @version V1.0
|
||||
* @title PermissionMapper
|
||||
* @date 2026-01-19 19:47:28
|
||||
* @description 权限表(t_permission)表数据库访问层
|
||||
*/
|
||||
public interface PermissionMapper extends BaseMapper<PermissionEntity> {
|
||||
|
||||
/**
|
||||
* 查询所有已启用的权限列表
|
||||
*
|
||||
* @return 已启用的权限列表
|
||||
*/
|
||||
List<PermissionEntity> selectAppEnabledList();
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package top.crushtj.xiaoyi.auth.domain.mappers;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import top.crushtj.xiaoyi.auth.domain.entity.RoleEntity;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author ayi
|
||||
* @version V1.0
|
||||
* @title RoleMapper
|
||||
* @date 2026-01-19 19:48:24
|
||||
* @description 角色表(t_role)表数据库访问层
|
||||
*/
|
||||
public interface RoleMapper extends BaseMapper<RoleEntity> {
|
||||
|
||||
/**
|
||||
* 查询所有启用的角色列表
|
||||
*
|
||||
* @return 角色列表
|
||||
*/
|
||||
List<RoleEntity> selectEnabledRoleList();
|
||||
|
||||
List<String> selectRoleKeyByUserId(Long userId);
|
||||
|
||||
}
|
||||
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
package top.crushtj.xiaoyi.auth.domain.mappers;
|
||||
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import top.crushtj.xiaoyi.auth.domain.entity.RolePermissionRelEntity;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author ayi
|
||||
* @version V1.0
|
||||
* @title RolePermissionRelMapper
|
||||
* @date 2026-01-19 19:48:32
|
||||
* @description 用户权限表(t_role_permission_rel)表数据库访问层
|
||||
*/
|
||||
public interface RolePermissionRelMapper extends BaseMapper<RolePermissionRelEntity> {
|
||||
|
||||
/**
|
||||
* 根据角色ID查询权限
|
||||
*
|
||||
* @param roleIds 角色ID列表
|
||||
* @return 权限列表
|
||||
*/
|
||||
List<RolePermissionRelEntity> selectByRoleIds(@Param("roleIds") List<Long> roleIds);
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
package top.crushtj.xiaoyi.auth.domain.mappers;
|
||||
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import top.crushtj.xiaoyi.auth.domain.entity.UserEntity;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
|
||||
/**
|
||||
* @author ayi
|
||||
* @version V1.0
|
||||
* @title UserMapper
|
||||
* @date 2026-01-19 19:49:51
|
||||
* @description 用户表(t_user)表数据库访问层
|
||||
*/
|
||||
public interface UserMapper extends BaseMapper<UserEntity> {
|
||||
|
||||
/**
|
||||
* 根据手机号查询用户
|
||||
* @param phone 手机号
|
||||
* @return 用户信息
|
||||
*/
|
||||
UserEntity selectByPhone(@Param("phone") String phone);
|
||||
}
|
||||
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
package top.crushtj.xiaoyi.auth.domain.mappers;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import top.crushtj.xiaoyi.auth.domain.entity.UserRoleRelEntity;
|
||||
|
||||
/**
|
||||
* @author ayi
|
||||
* @version V1.0
|
||||
* @title UserRoleRelMapper
|
||||
* @date 2026-01-19 19:50:00
|
||||
* @description 用户角色表(t_user_role_rel)表数据库访问层
|
||||
*/
|
||||
public interface UserRoleRelMapper extends BaseMapper<UserRoleRelEntity> {
|
||||
|
||||
}
|
||||
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="top.crushtj.xiaoyi.auth.domain.mappers.PermissionMapper">
|
||||
|
||||
<resultMap type="top.crushtj.xiaoyi.auth.domain.entity.PermissionEntity" id="PermissionMap">
|
||||
<result property="id" column="id" jdbcType="INTEGER"/>
|
||||
<result property="parentId" column="parent_id" jdbcType="INTEGER"/>
|
||||
<result property="name" column="name" jdbcType="VARCHAR"/>
|
||||
<result property="type" column="type" jdbcType="INTEGER"/>
|
||||
<result property="menuUrl" column="menu_url" jdbcType="VARCHAR"/>
|
||||
<result property="menuIcon" column="menu_icon" jdbcType="VARCHAR"/>
|
||||
<result property="sort" column="sort" jdbcType="INTEGER"/>
|
||||
<result property="permissionKey" column="permission_key" jdbcType="VARCHAR"/>
|
||||
<result property="status" column="status" jdbcType="INTEGER"/>
|
||||
<result property="createTime" column="create_time" jdbcType="TIMESTAMP"/>
|
||||
<result property="updateTime" column="update_time" jdbcType="TIMESTAMP"/>
|
||||
<result property="isDeleted" column="is_deleted" jdbcType="BOOLEAN"/>
|
||||
</resultMap>
|
||||
<select id="selectAppEnabledList" resultType="top.crushtj.xiaoyi.auth.domain.entity.PermissionEntity">
|
||||
SELECT id, name, permission_key
|
||||
FROM t_permission p
|
||||
WHERE p.status = 1
|
||||
AND p.is_deleted = 0
|
||||
AND type = 3
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
@@ -0,0 +1,29 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="top.crushtj.xiaoyi.auth.domain.mappers.RoleMapper">
|
||||
|
||||
<resultMap type="top.crushtj.xiaoyi.auth.domain.entity.RoleEntity" id="RoleMap">
|
||||
<result property="id" column="id" jdbcType="INTEGER"/>
|
||||
<result property="roleName" column="role_name" jdbcType="VARCHAR"/>
|
||||
<result property="roleKey" column="role_key" jdbcType="VARCHAR"/>
|
||||
<result property="status" column="status" jdbcType="INTEGER"/>
|
||||
<result property="sort" column="sort" jdbcType="INTEGER"/>
|
||||
<result property="remark" column="remark" jdbcType="VARCHAR"/>
|
||||
<result property="createTime" column="create_time" jdbcType="TIMESTAMP"/>
|
||||
<result property="updateTime" column="update_time" jdbcType="TIMESTAMP"/>
|
||||
<result property="isDeleted" column="is_deleted" jdbcType="BOOLEAN"/>
|
||||
</resultMap>
|
||||
|
||||
<select id="selectEnabledRoleList" resultType="top.crushtj.xiaoyi.auth.domain.entity.RoleEntity">
|
||||
SELECT id, role_name, role_key
|
||||
FROM t_role
|
||||
WHERE status = 1
|
||||
AND is_deleted = 0
|
||||
</select>
|
||||
<select id="selectRoleKeyByUserId" resultType="java.lang.String">
|
||||
SELECT r.role_key
|
||||
FROM t_role r
|
||||
LEFT JOIN t_user_role_rel urr ON r.id = urr.role_id
|
||||
WHERE urr.user_id = #{userId}
|
||||
</select>
|
||||
</mapper>
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="top.crushtj.xiaoyi.auth.domain.mappers.RolePermissionRelMapper">
|
||||
|
||||
<resultMap type="top.crushtj.xiaoyi.auth.domain.entity.RolePermissionRelEntity" id="RolePermissionRelMap">
|
||||
<result property="id" column="id" jdbcType="INTEGER"/>
|
||||
<result property="roleId" column="role_id" jdbcType="INTEGER"/>
|
||||
<result property="permissionId" column="permission_id" jdbcType="INTEGER"/>
|
||||
<result property="createTime" column="create_time" jdbcType="TIMESTAMP"/>
|
||||
<result property="updateTime" column="update_time" jdbcType="TIMESTAMP"/>
|
||||
<result property="isDeleted" column="is_deleted" jdbcType="BOOLEAN"/>
|
||||
</resultMap>
|
||||
<select id="selectByRoleIds" resultType="top.crushtj.xiaoyi.auth.domain.entity.RolePermissionRelEntity">
|
||||
SELECT role_id, permission_id FROM t_role_permission_rel
|
||||
<where>
|
||||
<if test="roleIds!=null and !roleIds.empty">
|
||||
AND role_id IN
|
||||
<foreach collection="roleIds" item="roleId" open="(" separator="," close=")">
|
||||
#{roleId}
|
||||
</foreach>
|
||||
</if>
|
||||
</where>
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="top.crushtj.xiaoyi.auth.domain.mappers.UserMapper">
|
||||
|
||||
<resultMap type="top.crushtj.xiaoyi.auth.domain.entity.UserEntity" id="UserMap">
|
||||
<result property="id" column="id" jdbcType="INTEGER"/>
|
||||
<result property="xiaoyishuId" column="xiaoyishu_id" jdbcType="VARCHAR"/>
|
||||
<result property="password" column="password" jdbcType="VARCHAR"/>
|
||||
<result property="nickname" column="nickname" jdbcType="VARCHAR"/>
|
||||
<result property="avatar" column="avatar" jdbcType="VARCHAR"/>
|
||||
<result property="birthday" column="birthday" jdbcType="TIMESTAMP"/>
|
||||
<result property="backgroundImg" column="background_img" jdbcType="VARCHAR"/>
|
||||
<result property="phone" column="phone" jdbcType="VARCHAR"/>
|
||||
<result property="sex" column="sex" jdbcType="INTEGER"/>
|
||||
<result property="status" column="status" jdbcType="INTEGER"/>
|
||||
<result property="introduction" column="introduction" jdbcType="VARCHAR"/>
|
||||
<result property="createTime" column="create_time" jdbcType="TIMESTAMP"/>
|
||||
<result property="updateTime" column="update_time" jdbcType="TIMESTAMP"/>
|
||||
<result property="isDeleted" column="is_deleted" jdbcType="BOOLEAN"/>
|
||||
</resultMap>
|
||||
<select id = "selectByPhone" resultType = "top.crushtj.xiaoyi.auth.domain.entity.UserEntity">
|
||||
SELECT id, password, xiaoyishu_id
|
||||
FROM t_user
|
||||
WHERE phone = #{phone}
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="top.crushtj.xiaoyi.auth.domain.mappers.UserRoleRelMapper">
|
||||
|
||||
<resultMap type="top.crushtj.xiaoyi.auth.domain.entity.UserRoleRelEntity" id="UserRoleRelMap">
|
||||
<result property="id" column="id" jdbcType="INTEGER"/>
|
||||
<result property="userId" column="user_id" jdbcType="INTEGER"/>
|
||||
<result property="roleId" column="role_id" jdbcType="INTEGER"/>
|
||||
<result property="createTime" column="create_time" jdbcType="TIMESTAMP"/>
|
||||
<result property="updateTime" column="update_time" jdbcType="TIMESTAMP"/>
|
||||
<result property="isDeleted" column="is_deleted" jdbcType="BOOLEAN"/>
|
||||
</resultMap>
|
||||
|
||||
</mapper>
|
||||
@@ -0,0 +1,34 @@
|
||||
package top.crushtj.xiaoyi.auth.enums;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @author ayi
|
||||
* @version V1.0
|
||||
* @title LoginTypeEnum
|
||||
* @date 2026/01/18 20:15
|
||||
* @description 登录类型枚举
|
||||
*/
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum LoginTypeEnum {
|
||||
// 验证码
|
||||
VERIFICATION_CODE(1),
|
||||
// 密码
|
||||
PASSWORD(2);
|
||||
|
||||
private final Integer value;
|
||||
|
||||
public static LoginTypeEnum valueOf(Integer code) {
|
||||
for (LoginTypeEnum loginTypeEnum : LoginTypeEnum.values()) {
|
||||
if (Objects.equals(code, loginTypeEnum.getValue())) {
|
||||
return loginTypeEnum;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package top.crushtj.xiaoyi.auth.enums;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import top.crushtj.framework.common.exception.BaseExceptionInterface;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author ayi
|
||||
* @version V1.0
|
||||
* @title ResponseCodeEnum
|
||||
* @description 响应码枚举类
|
||||
* @date 2026/01/15 14:37
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum ResponseCodeEnum implements BaseExceptionInterface {
|
||||
// -------- 通用异常状态码 --------
|
||||
SYSTEM_ERROR("AUTH-10000", "系统错误"),
|
||||
PARAM_NOT_VALID("AUTH-10001", "参数错误"), // -------- 验证码异常状态码 --------
|
||||
VERIFICATION_CODE_SEND_FREQUENTLY("AUTH-20000", "请求太频繁,请3分钟后再试"),
|
||||
SMS_SEND_FAILED("AUTH-20001", "短信发送失败,请稍后再试"),
|
||||
SMS_SEND_TIMEOUT("AUTH-20002", "短信发送超时,请稍后再试"),
|
||||
VERIFICATION_CODE_ERROR("AUTH-20003", "验证码错误"),
|
||||
;
|
||||
|
||||
/**
|
||||
* 错误码
|
||||
*/
|
||||
private final String errorCode;
|
||||
/**
|
||||
* 错误信息
|
||||
*/
|
||||
private final String errorMessage;
|
||||
}
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
package top.crushtj.xiaoyi.auth.exception;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.web.bind.MethodArgumentNotValidException;
|
||||
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
import top.crushtj.framework.common.exception.BizException;
|
||||
import top.crushtj.framework.common.response.Response;
|
||||
import top.crushtj.xiaoyi.auth.enums.ResponseCodeEnum;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author ayi
|
||||
* @version V1.0
|
||||
* @title GlobalExceptionHandler
|
||||
* @description 全局异常处理器
|
||||
* @date 2026/01/15 18:40
|
||||
*/
|
||||
|
||||
@Slf4j
|
||||
@ControllerAdvice
|
||||
public class GlobalExceptionHandler {
|
||||
|
||||
/**
|
||||
* 捕获自定义业务异常
|
||||
*
|
||||
* @param request 请求
|
||||
* @param e 异常
|
||||
* @return 响应结果
|
||||
*/
|
||||
@ExceptionHandler({BizException.class})
|
||||
@ResponseBody
|
||||
public Response<Object> handleBizException(HttpServletRequest request, BizException e) {
|
||||
log.warn("{} request failure, errorCode: {}, errorMessage: {}", request.getRequestURI(), e.getErrorCode(),
|
||||
e.getErrorMessage());
|
||||
return Response.failure(e);
|
||||
}
|
||||
|
||||
/**
|
||||
* 捕获参数校验异常
|
||||
*
|
||||
* @param request 请求
|
||||
* @param e 异常
|
||||
* @return 响应结果
|
||||
*/
|
||||
@ExceptionHandler({MethodArgumentNotValidException.class})
|
||||
@ResponseBody
|
||||
public Response<Object> handleMethodArgumentNotValidException(HttpServletRequest request,
|
||||
MethodArgumentNotValidException e) {
|
||||
// 参数错误异常码
|
||||
String errorCode = ResponseCodeEnum.PARAM_NOT_VALID.getErrorCode();
|
||||
|
||||
// 获取 BindingResult
|
||||
BindingResult bindingResult = e.getBindingResult();
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
// 获取校验不通过的字段,并组合错误信息,格式为: email 邮箱格式不正确, 当前值: '123124qq.com';
|
||||
Optional.of(bindingResult.getFieldErrors()).ifPresent(errors -> {
|
||||
errors.forEach(error -> sb.append(error.getField()).append(" ").append(error.getDefaultMessage())
|
||||
.append(", 当前值: '").append(error.getRejectedValue()).append("'; ")
|
||||
|
||||
);
|
||||
});
|
||||
|
||||
// 错误信息
|
||||
String errorMessage = sb.toString();
|
||||
|
||||
log.warn("{} request error, errorCode: {}, errorMessage: {}", request.getRequestURI(), errorCode, errorMessage);
|
||||
|
||||
return Response.failure(errorCode, errorMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
* 其他类型异常
|
||||
*
|
||||
* @param request 请求
|
||||
* @param e 异常
|
||||
* @return 响应结果
|
||||
*/
|
||||
@ExceptionHandler({Exception.class})
|
||||
@ResponseBody
|
||||
public Response<Object> handleOtherException(HttpServletRequest request, Exception e) {
|
||||
log.error("{} request error, ", request.getRequestURI(), e);
|
||||
return Response.failure(ResponseCodeEnum.SYSTEM_ERROR);
|
||||
}
|
||||
|
||||
@ExceptionHandler({IllegalArgumentException.class})
|
||||
@ResponseBody
|
||||
public Response<Object> handleIllegalArgumentException(HttpServletRequest request, IllegalArgumentException e) {
|
||||
log.error("{} request error, ", request.getRequestURI(), e);
|
||||
String errorCode = ResponseCodeEnum.PARAM_NOT_VALID.getErrorCode();
|
||||
String errorMessage = e.getMessage();
|
||||
return Response.failure(errorCode,errorMessage);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package top.crushtj.xiaoyi.auth.model.vo.user;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import top.crushtj.framework.common.validator.PhoneNumber;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* @author ayi
|
||||
* @version V1.0
|
||||
* @title UserVo
|
||||
* @date 2026/01/18 20:13
|
||||
* @description 用户登录参数
|
||||
*/
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class UserLoginReqVO implements Serializable {
|
||||
@Serial
|
||||
private static final long serialVersionUID = -2349922278492431614L;
|
||||
/**
|
||||
* 手机号
|
||||
*/
|
||||
@NotBlank(message = "手机号不能为空")
|
||||
@PhoneNumber
|
||||
private String phone;
|
||||
|
||||
/**
|
||||
* 验证码
|
||||
*/
|
||||
private String code;
|
||||
|
||||
/**
|
||||
* 密码
|
||||
*/
|
||||
private String password;
|
||||
|
||||
/**
|
||||
* 登录类型:手机号验证码,或者是账号密码
|
||||
*/
|
||||
@NotNull(message = "登录类型不能为空")
|
||||
private Integer type;
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
package top.crushtj.xiaoyi.auth.model.vo.verificationcode;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import top.crushtj.framework.common.validator.PhoneNumber;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author ayi
|
||||
* @version V1.0
|
||||
* @title SendVerificationCodeReqVO
|
||||
* @description 发送验证码请求参数
|
||||
* @date 2026/01/15 18:39
|
||||
*/
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class SendVerificationCodeReqVO {
|
||||
|
||||
/**
|
||||
* 手机号
|
||||
*/
|
||||
@NotBlank(message = "手机号不能为空")
|
||||
@PhoneNumber
|
||||
private String phoneNumber;
|
||||
|
||||
}
|
||||
+123
@@ -0,0 +1,123 @@
|
||||
package top.crushtj.xiaoyi.auth.runner;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import com.google.common.collect.Lists;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.ApplicationArguments;
|
||||
import org.springframework.boot.ApplicationRunner;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
import top.crushtj.framework.common.utils.JsonUtils;
|
||||
import top.crushtj.xiaoyi.auth.constant.RedisKeyConstants;
|
||||
import top.crushtj.xiaoyi.auth.domain.entity.PermissionEntity;
|
||||
import top.crushtj.xiaoyi.auth.domain.entity.RoleEntity;
|
||||
import top.crushtj.xiaoyi.auth.domain.entity.RolePermissionRelEntity;
|
||||
import top.crushtj.xiaoyi.auth.domain.mappers.PermissionMapper;
|
||||
import top.crushtj.xiaoyi.auth.domain.mappers.RoleMapper;
|
||||
import top.crushtj.xiaoyi.auth.domain.mappers.RolePermissionRelMapper;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author ayi
|
||||
* @version V1.0
|
||||
* @title PushRolePermissions2RedisRunner
|
||||
* @date 2026/01/23 16:51
|
||||
* @description 推送角色权限到Redis
|
||||
*/
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
public class PushRolePermissions2RedisRunner implements ApplicationRunner {
|
||||
|
||||
@Resource
|
||||
private RedisTemplate<String, Object> redisTemplate;
|
||||
|
||||
@Resource
|
||||
private RoleMapper roleMapper;
|
||||
|
||||
@Resource
|
||||
private RolePermissionRelMapper rolePermissionRelMapper;
|
||||
|
||||
@Resource
|
||||
private PermissionMapper permissionMapper;
|
||||
|
||||
public static final String PUSH_PERMISSION_FLAG = "push.permission.flag";
|
||||
|
||||
@Override
|
||||
public void run(ApplicationArguments args) throws Exception {
|
||||
log.info("==> 开始同步角色权限数据到 Redis 中...");
|
||||
|
||||
try {
|
||||
// 是否能够同步数据: 原子操作,只有在键 PUSH_PERMISSION_FLAG 不存在时,才会设置该键的值为 "1",并设置过期时间为 1 天
|
||||
boolean canPushed = Boolean.TRUE.equals(redisTemplate.opsForValue()
|
||||
.setIfAbsent(PUSH_PERMISSION_FLAG, "1", 1, TimeUnit.DAYS));
|
||||
// 如果无法同步权限数据
|
||||
if (!canPushed) {
|
||||
log.warn("==> 角色权限数据已经同步至 Redis 中,不再同步...");
|
||||
return;
|
||||
}
|
||||
// 查询所有已启用角色
|
||||
List<RoleEntity> roleList = roleMapper.selectEnabledRoleList();
|
||||
if (CollUtil.isNotEmpty(roleList)) {
|
||||
// 获取角色ID集合
|
||||
List<Long> roleIds = roleList.stream()
|
||||
.map(RoleEntity::getId)
|
||||
.toList();
|
||||
|
||||
//查询角色对应的权限
|
||||
List<RolePermissionRelEntity> rolePermissionList = rolePermissionRelMapper.selectByRoleIds(roleIds);
|
||||
|
||||
// 按角色 ID 分组, 每个角色 ID 对应多个权限 ID
|
||||
Map<Long, List<Long>> roleIdPermissionIdsMap = rolePermissionList.stream()
|
||||
.collect(Collectors.groupingBy(RolePermissionRelEntity::getRoleId,
|
||||
Collectors.mapping(RolePermissionRelEntity::getPermissionId, Collectors.toList())));
|
||||
|
||||
// 查询所有启用的权限
|
||||
List<PermissionEntity> permissionList = permissionMapper.selectAppEnabledList();
|
||||
|
||||
// 构建权限 ID 和权限 DO 对象的映射关系
|
||||
Map<Long, PermissionEntity> permissionIdEntityMap = permissionList.stream()
|
||||
.collect(Collectors.toMap(PermissionEntity::getId, permissionEntity -> permissionEntity));
|
||||
|
||||
// 构建角色 ID 和权限 DO 列表的映射关系
|
||||
Map<String, List<String>> roleIdPermissionMap = new HashMap<>();
|
||||
roleList.forEach(role -> {
|
||||
Long roleId = role.getId();
|
||||
//获取角色对应的权限id列表
|
||||
List<Long> permissionIds = roleIdPermissionIdsMap.get(roleId);
|
||||
if (CollUtil.isNotEmpty(permissionIds)) {
|
||||
List<String> permissionKeys = Lists.newArrayList();
|
||||
permissionIds.forEach(permissionId -> {
|
||||
// 根据权限 ID 获取具体的权限 DO 对象
|
||||
PermissionEntity permissionDO = permissionIdEntityMap.get(permissionId);
|
||||
if (Objects.nonNull(permissionDO)) {
|
||||
permissionKeys.add(permissionDO.getPermissionKey());
|
||||
}
|
||||
});
|
||||
roleIdPermissionMap.put(role.getRoleKey(), permissionKeys);
|
||||
}
|
||||
});
|
||||
|
||||
// 将角色 ID 和权限 DO 列表的映射关系写入 Redis
|
||||
roleIdPermissionMap.forEach((roleKey, permission) -> {
|
||||
String key = RedisKeyConstants.buildRolePermissionsKey(roleKey);
|
||||
redisTemplate.opsForValue()
|
||||
.set(key, JsonUtils.toJsonString(permission));
|
||||
});
|
||||
}
|
||||
log.info("==> 角色权限数据同步到 Redis 完成...");
|
||||
} catch (Exception e) {
|
||||
log.error("==> 角色权限数据同步到 Redis 失败...", e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
package top.crushtj.xiaoyi.auth.runner.cache;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
import top.crushtj.xiaoyi.auth.domain.entity.UserEntity;
|
||||
import top.crushtj.xiaoyi.auth.domain.mappers.UserMapper;
|
||||
|
||||
import static top.crushtj.xiaoyi.auth.constant.RedisKeyConstants.XIAOYI_ID_GENERATOR_KEY;
|
||||
import static top.crushtj.xiaoyi.auth.constant.XiaoyiAuthConstants.XIAOYI_ID_INITIAL_VALUE;
|
||||
|
||||
/**
|
||||
* @author ayi
|
||||
* @version V1.0
|
||||
* @title CacheLoader
|
||||
* @date 2026/01/20 18:25
|
||||
* @description 初始化缓存
|
||||
*/
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
public class CacheLoader {
|
||||
@Resource
|
||||
private RedisTemplate<String, Object> redisTemplate;
|
||||
|
||||
@Resource
|
||||
private UserMapper userMapper;
|
||||
|
||||
/**
|
||||
* 加载用户自增ID缓存
|
||||
*/
|
||||
@PostConstruct
|
||||
public void loadUserCache() {
|
||||
log.info("==> 加载用户自增ID缓存开始...");
|
||||
LambdaQueryWrapper<UserEntity> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.orderByDesc(UserEntity::getXiaoyishuId);
|
||||
queryWrapper.last("limit 1");
|
||||
UserEntity user = userMapper.selectOne(queryWrapper);
|
||||
if (user != null) {
|
||||
redisTemplate.opsForValue()
|
||||
.set(XIAOYI_ID_GENERATOR_KEY, Long.valueOf(user.getXiaoyishuId()));
|
||||
} else {
|
||||
redisTemplate.opsForValue()
|
||||
.set(XIAOYI_ID_GENERATOR_KEY, XIAOYI_ID_INITIAL_VALUE);
|
||||
}
|
||||
log.info("==> 加载用户自增ID缓存结束...");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package top.crushtj.xiaoyi.auth.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import top.crushtj.framework.common.response.Response;
|
||||
import top.crushtj.xiaoyi.auth.domain.entity.UserEntity;
|
||||
import top.crushtj.xiaoyi.auth.model.vo.user.UserLoginReqVO;
|
||||
|
||||
/**
|
||||
* @author ayi
|
||||
* @version V1.0
|
||||
* @title UserService
|
||||
* @date 2026-01-18 20:09:57
|
||||
* @description 用户表(t_user)表服务接口
|
||||
*/
|
||||
|
||||
public interface UserService extends IService<UserEntity> {
|
||||
|
||||
/**
|
||||
* 登录或注册
|
||||
*
|
||||
* @param userLoginReqVO 登录或注册参数
|
||||
* @return 登录结果
|
||||
*/
|
||||
Response<String> loginOrRegister(UserLoginReqVO userLoginReqVO);
|
||||
}
|
||||
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
package top.crushtj.xiaoyi.auth.service;
|
||||
|
||||
import top.crushtj.framework.common.response.Response;
|
||||
import top.crushtj.xiaoyi.auth.model.vo.verificationcode.SendVerificationCodeReqVO;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author ayi
|
||||
* @version V1.0
|
||||
* @title VerificationCodeService
|
||||
* @description 验证码服务
|
||||
* @date 2026/01/15 18:48
|
||||
*/
|
||||
|
||||
public interface VerificationCodeService {
|
||||
|
||||
/**
|
||||
* 发送验证码
|
||||
*
|
||||
* @param sendVerificationCodeReqVO 发送验证码请求参数
|
||||
* @return 响应结果
|
||||
*/
|
||||
Response<?> send(SendVerificationCodeReqVO sendVerificationCodeReqVO);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,203 @@
|
||||
package top.crushtj.xiaoyi.auth.service.impl;
|
||||
|
||||
import cn.dev33.satoken.stp.SaTokenInfo;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.google.common.base.Preconditions;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.support.TransactionTemplate;
|
||||
import top.crushtj.framework.common.enums.DeleteEnum;
|
||||
import top.crushtj.framework.common.enums.StatusEnum;
|
||||
import top.crushtj.framework.common.exception.BizException;
|
||||
import top.crushtj.framework.common.response.Response;
|
||||
import top.crushtj.framework.common.utils.IdGenerator;
|
||||
import top.crushtj.framework.common.utils.JsonUtils;
|
||||
import top.crushtj.framework.common.utils.MaskUtils;
|
||||
import top.crushtj.xiaoyi.auth.constant.RedisKeyConstants;
|
||||
import top.crushtj.xiaoyi.auth.domain.entity.RoleEntity;
|
||||
import top.crushtj.xiaoyi.auth.domain.entity.UserEntity;
|
||||
import top.crushtj.xiaoyi.auth.domain.entity.UserRoleRelEntity;
|
||||
import top.crushtj.xiaoyi.auth.domain.mappers.RoleMapper;
|
||||
import top.crushtj.xiaoyi.auth.domain.mappers.UserMapper;
|
||||
import top.crushtj.xiaoyi.auth.domain.mappers.UserRoleRelMapper;
|
||||
import top.crushtj.xiaoyi.auth.enums.LoginTypeEnum;
|
||||
import top.crushtj.xiaoyi.auth.enums.ResponseCodeEnum;
|
||||
import top.crushtj.xiaoyi.auth.model.vo.user.UserLoginReqVO;
|
||||
import top.crushtj.xiaoyi.auth.service.UserService;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import static top.crushtj.xiaoyi.auth.constant.RedisKeyConstants.XIAOYI_ID_GENERATOR_KEY;
|
||||
import static top.crushtj.xiaoyi.auth.constant.RoleConstants.COMMON_USER_ROLE_ID;
|
||||
import static top.crushtj.xiaoyi.auth.constant.XiaoyiAuthConstants.NICK_NAME_PREFIX;
|
||||
import static top.crushtj.xiaoyi.auth.constant.XiaoyiAuthConstants.XIAOYI_ID_INITIAL_VALUE;
|
||||
|
||||
/**
|
||||
* @author ayi
|
||||
* @version V1.0
|
||||
* @title UserServiceImpl
|
||||
* @date 2026-01-18 20:09:58
|
||||
* @description 用户表(t_user)表服务实现类
|
||||
*/
|
||||
|
||||
@Slf4j
|
||||
@Service("userService")
|
||||
public class UserServiceImpl extends ServiceImpl<UserMapper, UserEntity> implements UserService {
|
||||
@Resource
|
||||
private UserMapper userMapper;
|
||||
@Resource
|
||||
private RedisTemplate<String, Object> redisTemplate;
|
||||
@Resource
|
||||
private UserRoleRelMapper userRoleRelMapper;
|
||||
|
||||
@Resource
|
||||
private RoleMapper roleMapper;
|
||||
|
||||
@Resource
|
||||
private TransactionTemplate transactionTemplate;
|
||||
|
||||
@Override
|
||||
public Response<String> loginOrRegister(UserLoginReqVO userLoginReqVO) {
|
||||
String phone = userLoginReqVO.getPhone();
|
||||
Integer type = userLoginReqVO.getType();
|
||||
Long userId = null;
|
||||
LoginTypeEnum loginTypeEnum = LoginTypeEnum.valueOf(type);
|
||||
switch (loginTypeEnum) {
|
||||
case PASSWORD -> {
|
||||
String password = userLoginReqVO.getPassword();
|
||||
// 密码登录
|
||||
|
||||
}
|
||||
case VERIFICATION_CODE -> {
|
||||
String verificationCode = userLoginReqVO.getCode();
|
||||
// 验证码登录
|
||||
// 校验入参验证码是否为空
|
||||
Preconditions.checkArgument(StringUtils.isNotBlank(verificationCode), "验证码不能为空");
|
||||
|
||||
// 构建验证码 Redis Key
|
||||
String key = RedisKeyConstants.buildVerificationCodeKey(phone);
|
||||
// 查询存储在 Redis 中该用户的登录验证码
|
||||
String sentCode = (String)redisTemplate.opsForValue()
|
||||
.get(key);
|
||||
|
||||
// 判断用户提交的验证码,与 Redis 中的验证码是否一致
|
||||
if (!StringUtils.equals(verificationCode, sentCode)) {
|
||||
throw new BizException(ResponseCodeEnum.VERIFICATION_CODE_ERROR);
|
||||
}
|
||||
|
||||
// 通过手机号查询记录
|
||||
UserEntity userEntity = userMapper.selectByPhone(phone);
|
||||
|
||||
log.info("==> 用户是否注册, phone: {}, userId: {}", MaskUtils.maskMobile(phone),
|
||||
userEntity == null ? "未注册" : userEntity.getXiaoyishuId());
|
||||
|
||||
// 判断是否注册
|
||||
if (Objects.isNull(userEntity)) {
|
||||
// 若此用户还没有注册,系统自动注册该用户
|
||||
// todo 自动注册用户逻辑
|
||||
userId = registerUser(phone);
|
||||
|
||||
} else {
|
||||
// 已注册,则获取其用户 ID
|
||||
userId = userEntity.getId();
|
||||
this.loadUserRole(userId);
|
||||
}
|
||||
}
|
||||
}
|
||||
StpUtil.login(userId);
|
||||
|
||||
// 获取 Token 令牌
|
||||
SaTokenInfo tokenInfo = StpUtil.getTokenInfo();
|
||||
|
||||
// 返回 Token 令牌
|
||||
return Response.success(tokenInfo.tokenValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户注册
|
||||
*
|
||||
* @param phone 手机号
|
||||
* @return 用户ID
|
||||
*/
|
||||
private Long registerUser(String phone) {
|
||||
return transactionTemplate.execute(status -> {
|
||||
try {
|
||||
long userId = IdGenerator.getInstance()
|
||||
.nextId();
|
||||
// 小壹书用户ID,非数据库主键ID
|
||||
Long xiaoyishuId = redisTemplate.opsForValue()
|
||||
.increment(XIAOYI_ID_GENERATOR_KEY);
|
||||
UserEntity userEntity = UserEntity.builder()
|
||||
.id(userId)
|
||||
.phone(phone)
|
||||
.xiaoyishuId(String.valueOf(xiaoyishuId))
|
||||
.nickname(NICK_NAME_PREFIX + xiaoyishuId) // 自动注册的用户昵称默认为"咿呀_" + 用户ID
|
||||
.status(StatusEnum.ENABLED.getValue())
|
||||
.createTime(LocalDateTime.now())
|
||||
.updateTime(LocalDateTime.now())
|
||||
.isDeleted(DeleteEnum.NO.getValue())
|
||||
.build();
|
||||
save(userEntity);
|
||||
|
||||
UserRoleRelEntity userRoleRel = UserRoleRelEntity.builder()
|
||||
.id(IdGenerator.getInstance()
|
||||
.nextId())
|
||||
.userId(userId)
|
||||
.roleId(COMMON_USER_ROLE_ID) // 自动注册的用户角色为普通用户
|
||||
.createTime(LocalDateTime.now())
|
||||
.updateTime(LocalDateTime.now())
|
||||
.isDeleted(DeleteEnum.NO.getValue())
|
||||
.build();
|
||||
int i = 1 / 0;
|
||||
userRoleRelMapper.insert(userRoleRel);
|
||||
|
||||
// 将用户角色信息存储到 Redis 中
|
||||
RoleEntity role = roleMapper.selectById(COMMON_USER_ROLE_ID);
|
||||
List<String> roles = new ArrayList<>(1);
|
||||
roles.add(role.getRoleKey());
|
||||
String userRolesKey = RedisKeyConstants.buildUserRolesKey(userId);
|
||||
redisTemplate.opsForValue()
|
||||
.set(userRolesKey, JsonUtils.toJsonString(roles));
|
||||
return userId;
|
||||
} catch (Exception e) {
|
||||
// 回滚事务
|
||||
log.error("==> 用户注册异常,开始回滚事务: ", e);
|
||||
status.setRollbackOnly();
|
||||
// 回滚 redis 自增ID
|
||||
Long decrement = Long.valueOf(Objects.requireNonNull(redisTemplate.opsForValue()
|
||||
.get(XIAOYI_ID_GENERATOR_KEY))
|
||||
.toString()
|
||||
.trim());
|
||||
if (decrement.compareTo(XIAOYI_ID_INITIAL_VALUE) > 0) {
|
||||
log.error("==> 用户注册异常,回滚redis自增ID: {}", decrement);
|
||||
redisTemplate.opsForValue()
|
||||
.decrement(XIAOYI_ID_GENERATOR_KEY);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载用户角色信息到缓存
|
||||
*
|
||||
* @param userId 用户ID
|
||||
*/
|
||||
private void loadUserRole(Long userId) {
|
||||
String userRolesKey = RedisKeyConstants.buildUserRolesKey(userId);
|
||||
Boolean hasKey = redisTemplate.hasKey(userRolesKey);
|
||||
if (!hasKey) {
|
||||
List<String> roles = roleMapper.selectRoleKeyByUserId(userId);
|
||||
redisTemplate.opsForValue()
|
||||
.set(userRolesKey, JsonUtils.toJsonString(roles));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
package top.crushtj.xiaoyi.auth.service.impl;
|
||||
|
||||
import cn.hutool.core.util.RandomUtil;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import top.crushtj.framework.common.exception.BizException;
|
||||
import top.crushtj.framework.common.response.Response;
|
||||
import top.crushtj.framework.common.utils.MaskUtils;
|
||||
import top.crushtj.xiaoyi.auth.constant.RedisKeyConstants;
|
||||
import top.crushtj.xiaoyi.auth.enums.ResponseCodeEnum;
|
||||
import top.crushtj.xiaoyi.auth.model.vo.verificationcode.SendVerificationCodeReqVO;
|
||||
import top.crushtj.xiaoyi.auth.service.VerificationCodeService;
|
||||
import top.crushtj.xiaoyi.auth.sms.AliyunSmsHelper;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static top.crushtj.xiaoyi.auth.constant.RedisKeyConstants.VERIFICATION_CODE_EXPIRE_TIME;
|
||||
|
||||
/**
|
||||
* 验证码服务实现类
|
||||
* 修复点:异步异常捕获、Redis过期时间、验证码日志脱敏、手机号校验、线程任务跟踪
|
||||
*/
|
||||
@Service
|
||||
@Slf4j
|
||||
public class VerificationCodeServiceImpl implements VerificationCodeService {
|
||||
// 短信发送超时时间(秒)
|
||||
private static final int SMS_SEND_TIMEOUT_SECONDS = 5;
|
||||
|
||||
@Resource
|
||||
private RedisTemplate<String, Object> redisTemplate;
|
||||
|
||||
@Resource(name = "taskExecutor")
|
||||
private ThreadPoolTaskExecutor taskExecutor;
|
||||
|
||||
@Resource
|
||||
private AliyunSmsHelper aliyunSmsHelper;
|
||||
|
||||
@Override
|
||||
public Response<?> send(SendVerificationCodeReqVO sendVerificationCodeReqVO) {
|
||||
// 1. 获取并校验手机号格式
|
||||
String phoneNumber = sendVerificationCodeReqVO.getPhoneNumber();
|
||||
|
||||
// 2. 构建Redis Key并检查发送频率
|
||||
String key = RedisKeyConstants.buildVerificationCodeKey(phoneNumber);
|
||||
boolean isSent = redisTemplate.hasKey(key);
|
||||
if (isSent) {
|
||||
throw new BizException(ResponseCodeEnum.VERIFICATION_CODE_SEND_FREQUENTLY);
|
||||
}
|
||||
|
||||
// 3. 生成6位随机验证码
|
||||
String verificationCode = RandomUtil.randomNumbers(6);
|
||||
|
||||
//=============== 开发环境不实际调用短信发送接口 ===============
|
||||
// 4. 异步发送短信(用CompletableFuture跟踪任务状态,捕获异常)
|
||||
//CompletableFuture<Boolean> smsSendFuture = CompletableFuture.supplyAsync(() -> {
|
||||
// // 设置线程名称,便于日志排查
|
||||
// Thread.currentThread().setName("sms-send-" + MaskUtils.maskMobile(phoneNumber));
|
||||
// String signName = "速通互联验证码";
|
||||
// String templateCode = "100001";
|
||||
// String templateParam = String.format("{\"code\":\"%s\",\"min\":\"%d\"}", verificationCode, VERIFICATION_CODE_EXPIRE_TIME);
|
||||
// try {
|
||||
// return aliyunSmsHelper.sendMessage(signName, templateCode, phoneNumber, templateParam);
|
||||
// } catch (Exception e) {
|
||||
// log.error("==> 手机号: {}, 短信发送接口调用异常", MaskUtils.maskMobile(phoneNumber), e);
|
||||
// return false;
|
||||
// }
|
||||
//}, taskExecutor);
|
||||
//
|
||||
// //5. 同步等待短信发送结果(超时控制,避免主线程阻塞过久)
|
||||
//boolean smsSendSuccess;
|
||||
//try {
|
||||
// smsSendSuccess = smsSendFuture.get(SMS_SEND_TIMEOUT_SECONDS, TimeUnit.SECONDS);
|
||||
//} catch (InterruptedException e) {
|
||||
// log.error("==> 手机号: {}, 短信发送任务被中断", MaskUtils.maskMobile(phoneNumber), e);
|
||||
// Thread.currentThread().interrupt(); // 恢复中断状态
|
||||
// throw new BizException(ResponseCodeEnum.SMS_SEND_FAILED);
|
||||
//} catch (ExecutionException e) {
|
||||
// log.error("==> 手机号: {}, 短信发送任务执行异常", MaskUtils.maskMobile(phoneNumber), e);
|
||||
// throw new BizException(ResponseCodeEnum.SMS_SEND_FAILED);
|
||||
//} catch (TimeoutException e) {
|
||||
// log.error("==> 手机号: {}, 短信发送任务超时({}秒)", MaskUtils.maskMobile(phoneNumber), SMS_SEND_TIMEOUT_SECONDS, e);
|
||||
// throw new BizException(ResponseCodeEnum.SMS_SEND_TIMEOUT);
|
||||
//}
|
||||
//
|
||||
//// 6. 短信发送失败则直接抛异常,不存储Redis
|
||||
//if (!smsSendSuccess) {
|
||||
// log.error("==> 手机号: {}, 发送验证码失败(第三方接口返回失败)", MaskUtils.maskMobile(phoneNumber));
|
||||
// throw new BizException(ResponseCodeEnum.SMS_SEND_FAILED);
|
||||
//}
|
||||
//=============== 开发环境不实际调用短信发送接口 ===============
|
||||
|
||||
// 7. 短信发送成功后,记录日志(验证码脱敏,仅保留后2位)+ 存储Redis
|
||||
log.info("==> 手机号: {}, 已发送验证码:【****{}】", MaskUtils.maskMobile(phoneNumber), verificationCode.substring(4));
|
||||
redisTemplate.opsForValue().set(key, verificationCode, VERIFICATION_CODE_EXPIRE_TIME, TimeUnit.MINUTES);
|
||||
|
||||
return Response.success(verificationCode);
|
||||
//return Response.success();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package top.crushtj.xiaoyi.auth.sms;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* @author ayi
|
||||
* @version V1.0
|
||||
* @title AliyunAccessKeyProperties
|
||||
* @date 2026/01/16 23:31
|
||||
* @description 阿里云访问密钥配置
|
||||
*/
|
||||
|
||||
@ConfigurationProperties(prefix = "aliyun")
|
||||
@Component
|
||||
@Data
|
||||
public class AliyunAccessKeyProperties {
|
||||
private String accessKeyId;
|
||||
private String accessKeySecret;
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package top.crushtj.xiaoyi.auth.sms;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* @author ayi
|
||||
* @version V1.0
|
||||
* @title AliyunSmsClientConfig
|
||||
* @date 2026/01/16 23:48
|
||||
* @description 阿里云短信配置
|
||||
*/
|
||||
|
||||
@Slf4j
|
||||
@Configuration
|
||||
public class AliyunSmsClientConfig {
|
||||
@Resource
|
||||
private AliyunAccessKeyProperties aliyunAccessKeyProperties;
|
||||
|
||||
@Bean
|
||||
public com.aliyun.dypnsapi20170525.Client smsClient() {
|
||||
try {
|
||||
com.aliyun.credentials.Client credential = new com.aliyun.credentials.Client();
|
||||
|
||||
com.aliyun.teaopenapi.models.Config config = new com.aliyun.teaopenapi.models.Config()
|
||||
.setCredential(credential);
|
||||
|
||||
// Endpoint 请参考 https://api.aliyun.com/product/Dypnsapi
|
||||
config.endpoint = "dypnsapi.aliyuncs.com";
|
||||
|
||||
config.accessKeyId = aliyunAccessKeyProperties.getAccessKeyId();
|
||||
config.accessKeySecret = aliyunAccessKeyProperties.getAccessKeySecret();
|
||||
|
||||
return new com.aliyun.dypnsapi20170525.Client(config);
|
||||
} catch (Exception e) {
|
||||
log.error("初始化阿里云短信发送客户端错误: ", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package top.crushtj.xiaoyi.auth.sms;
|
||||
|
||||
import com.aliyun.dypnsapi20170525.models.SendSmsVerifyCodeResponse;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
import top.crushtj.framework.common.utils.JsonUtils;
|
||||
import top.crushtj.framework.common.utils.MaskUtils;
|
||||
|
||||
/**
|
||||
* @author ayi
|
||||
* @version V1.0
|
||||
* @title AliyunSmsHelper
|
||||
* @date 2026/01/16 23:36
|
||||
* @description 阿里云短信工具
|
||||
*/
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
public class AliyunSmsHelper {
|
||||
|
||||
@Resource
|
||||
private com.aliyun.dypnsapi20170525.Client client;
|
||||
|
||||
public boolean sendMessage(String signName, String templateCode, String phoneNumber, String templateParam) {
|
||||
com.aliyun.dypnsapi20170525.models.SendSmsVerifyCodeRequest sendSmsVerifyCodeRequest = new com.aliyun.dypnsapi20170525.models.SendSmsVerifyCodeRequest()
|
||||
.setSignName(signName)
|
||||
.setTemplateCode(templateCode)
|
||||
.setPhoneNumber(phoneNumber)
|
||||
.setTemplateParam(templateParam);
|
||||
|
||||
com.aliyun.teautil.models.RuntimeOptions runtime = new com.aliyun.teautil.models.RuntimeOptions();
|
||||
|
||||
try {
|
||||
log.info("==> 开始短信发送, phone: {}, signName: {}, templateCode: {}, templateParam: {}", MaskUtils.maskMobile(phoneNumber), signName, templateCode, templateParam);
|
||||
|
||||
// 发送短信
|
||||
SendSmsVerifyCodeResponse response = client.sendSmsVerifyCodeWithOptions(sendSmsVerifyCodeRequest, runtime);
|
||||
|
||||
boolean success = response.getBody().success;
|
||||
log.info("==> 短信发送状态: {}, response: {}", success, JsonUtils.toJsonString(response));
|
||||
return success;
|
||||
} catch (Exception error) {
|
||||
log.error("==> 短信发送错误: ", error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
package top.crushtj.xiaoyishu.auth;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class XiaoyishuAuthApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(XiaoyishuAuthApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
spring:
|
||||
application:
|
||||
name: xiaoyishu-auth
|
||||
output:
|
||||
ansi:
|
||||
enabled: always
|
||||
@@ -0,0 +1,18 @@
|
||||
/$$ /$$ /$$ /$$ /$$
|
||||
| $$ / $$|__/ |__/ | $$
|
||||
| $$/ $$/ /$$ /$$$$$$ /$$$$$$ /$$ /$$ /$$ /$$$$$$$| $$$$$$$ /$$ /$$
|
||||
\ $$$$/ | $$ |____ $$ /$$__ $$| $$ | $$| $$ /$$_____/| $$__ $$| $$ | $$
|
||||
>$$ $$ | $$ /$$$$$$$| $$ \ $$| $$ | $$| $$| $$$$$$ | $$ \ $$| $$ | $$
|
||||
/$$/\ $$| $$ /$$__ $$| $$ | $$| $$ | $$| $$ \____ $$| $$ | $$| $$ | $$
|
||||
| $$ \ $$| $$| $$$$$$$| $$$$$$/| $$$$$$$| $$ /$$$$$$$/| $$ | $$| $$$$$$/
|
||||
|__/ |__/|__/ \_______/ \______/ \____ $$|__/|_______/ |__/ |__/ \______/
|
||||
/$$ | $$
|
||||
| $$$$$$/
|
||||
\______/
|
||||
|
||||
One-stop focus on the whole network hotspot.
|
||||
:::::::::::::::::::::::::::::::::::::::::::::::::::
|
||||
author: 刑加一
|
||||
email: 2294931964@qq.com
|
||||
about me: https://blog.crushtj.top
|
||||
:::::::::::::::::::::::::::::::::::::::::::::::::::
|
||||
@@ -0,0 +1,97 @@
|
||||
spring:
|
||||
datasource:
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
# 数据库连接信息
|
||||
url: ENC(oyIjmfAXMQ/z3uHl88N68RYJ96HJhdLkXMBBpE1wq9kSbYuTlxwZV/F7Qxa6tDAu+MD6I9swGqmYl2ormW/bzxPQdk+rrQANSQVA+ekyPGd97I5RGPfLLGQCw6FExrbhh9uzKGvekSmsIC8h91dTL2sMD8dYHbuRpW9J5knEyHSCZ90Bep1ZOlu2mWNQfdbaqmbafs6Bhjj9Iu4m9SblAMomAbDVT1oREPzjtMX640ro+Ou/YGVYPuLj3LlwVooauGCoswmHK0cA2TCD0RqaIg==)
|
||||
username: ENC(Hf/tJoMig8FWFHopB7CMlQFuBhu61JbpA64JQWvqrmL+fTtGi6QNuI+sHBNY43nY)
|
||||
password: ENC(l2k707lyMR9ACwqEHl/d3VOojwmvIyRsH7leB9fa9ppOtbKBae8YnVVuEMcRhEqMTTvcqZ4Otd/TotqZMerL/w==)
|
||||
druid: # Druid 连接池
|
||||
initial-size: 5 # 初始化连接池大小
|
||||
min-idle: 5 # 最小连接池数量
|
||||
max-active: 100 # 最大连接池数量
|
||||
max-wait: 60000 # 连接时最大等待时间(单位:毫秒)
|
||||
test-while-idle: true
|
||||
time-between-eviction-runs-millis: 60000 # 配置多久进行一次检测,检测需要关闭的连接(单位:毫秒)
|
||||
min-evictable-idle-time-millis: 300000 # 配置一个连接在连接池中最小生存的时间(单位:毫秒)
|
||||
max-evictable-idle-time-millis: 900000 # 配置一个连接在连接池中最大生存的时间(单位:毫秒)
|
||||
validation-query: SELECT 1 FROM DUAL # 配置测试连接是否可用的查询 sql
|
||||
test-on-borrow: false
|
||||
test-on-return: false
|
||||
pool-prepared-statements: false
|
||||
web-stat-filter:
|
||||
enabled: true
|
||||
stat-view-servlet:
|
||||
enabled: true
|
||||
url-pattern: /druid/* # 配置监控后台访问路径
|
||||
login-username: admin # 配置监控后台登录的用户名、密码
|
||||
login-password: admin
|
||||
filter:
|
||||
config:
|
||||
enabled: true
|
||||
stat:
|
||||
enabled: true
|
||||
log-slow-sql: true # 开启慢 sql 记录
|
||||
slow-sql-millis: 2000 # 若执行耗时大于 2s,则视为慢 sql
|
||||
merge-sql: true
|
||||
wall: # 防火墙
|
||||
config:
|
||||
multi-statement-allow: true
|
||||
data:
|
||||
redis:
|
||||
database: 0 # Redis 数据库索引(默认为 0)
|
||||
host: ENC(C1TWXF+/HzWQBF25uXCdy/0fHoRDXdCW72+NKCIJURg4l3IDnJzl278KmFhfsusX) # Redis 服务器地址
|
||||
port: 6379 # Redis 服务器连接端口
|
||||
password: ENC(iK/k0IGPflACqYMUwX4N/sGvCVuysYywLcAO+Ikeqk326V8hCr8dgEGzkiEIwWOo) # Redis 服务器连接密码(默认为空)
|
||||
timeout: 5s # 读超时时间
|
||||
connect-timeout: 5s # 链接超时时间
|
||||
lettuce:
|
||||
pool:
|
||||
max-active: 200 # 连接池最大连接数
|
||||
max-wait: -1ms # 连接池最大阻塞等待时间(使用负值表示没有限制)
|
||||
min-idle: 0 # 连接池中的最小空闲连接
|
||||
max-idle: 10 # 连接池中的最大空闲连接
|
||||
output:
|
||||
ansi:
|
||||
enabled: always
|
||||
application:
|
||||
name: xiaoyishu-auth
|
||||
|
||||
mybatis-plus:
|
||||
mapper-locations:
|
||||
- classpath*:top/crushtj/**/*.xml # 匹配所有模块中的 Mapper XML 文件
|
||||
type-aliases-package: top.crushtj.xiaoyishu.auth.domain.entity # 实体类包路径
|
||||
configuration:
|
||||
map-underscore-to-camel-case: true # 开启驼峰命名转换
|
||||
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 日志输出(调试用)
|
||||
|
||||
logging:
|
||||
level:
|
||||
top.crushtj.xiaoyishu.auth.domain.mappers: debug
|
||||
|
||||
aliyun:
|
||||
accessKeyId: ENC(0GWRL+sq6nY4HvFY8tGqAJz/21NQm4Ga3Qbek+yiR3dwgk4TZXLpcoflD1pyXI/nrkzgcbZsiXXBd6FXW00GJA==)
|
||||
accessKeySecret: ENC(VUngnZ/ERJDRHmimA0CB7tla6LKIXB7K6jzRJjOg2kqc3KwtXwin+E3cnpJSe92daR5yLCcMa2kPYwy9h6lPqg==)
|
||||
jasypt:
|
||||
encryptor:
|
||||
password:
|
||||
algorithm: PBEWithHMACSHA512AndAES_256
|
||||
key-obtention-iterations: 1000
|
||||
string-output-type: base64
|
||||
provider-name: SunJCE
|
||||
iv-generator-classname: org.jasypt.iv.RandomIvGenerator
|
||||
#nacos:
|
||||
# config: # Nacos 配置中心
|
||||
# access-key: # 身份验证
|
||||
# secret-key: # 身份验证
|
||||
# data-id: xiaoyishu-auth # 指定要加载的配置数据的 Data Id
|
||||
# group: DEFAULT_GROUP # 指定配置数据所属的组
|
||||
# type: yaml # 指定配置数据的格式
|
||||
# server-addr: http://127.0.0.1:8848/ # 指定 Nacos 配置中心的服务器地址
|
||||
# auto-refresh: true # 是否自动刷新配置
|
||||
# remote-first: true # 是否优先使用远程配置
|
||||
# bootstrap:
|
||||
# enable: true # 启动时,预热配置
|
||||
|
||||
rate-limit:
|
||||
api:
|
||||
limit: 100 # 接口限流阈值
|
||||
@@ -0,0 +1,60 @@
|
||||
server:
|
||||
servlet:
|
||||
context-path: /xiaoyishu-auth
|
||||
|
||||
spring:
|
||||
datasource:
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
# 数据库连接信息
|
||||
url: jdbc:mysql://127.0.0.1:3306/xiaoyishu?useUnicode=true&characterEncoding=utf-8&autoReconnect=true&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
|
||||
username: ayi
|
||||
password: Os0TpcErSh26nT4Nqqjgo2vwi3IaEglzj+brT2b7q0P4Dlhnv3OEQVUNpG/dYqvJZUCR2/IyfxQ4LnQIB7FcfQ==
|
||||
druid: # Druid 连接池
|
||||
initial-size: 5 # 初始化连接池大小
|
||||
min-idle: 5 # 最小连接池数量
|
||||
max-active: 100 # 最大连接池数量
|
||||
max-wait: 60000 # 连接时最大等待时间(单位:毫秒)
|
||||
test-while-idle: true
|
||||
time-between-eviction-runs-millis: 60000 # 配置多久进行一次检测,检测需要关闭的连接(单位:毫秒)
|
||||
min-evictable-idle-time-millis: 300000 # 配置一个连接在连接池中最小生存的时间(单位:毫秒)
|
||||
max-evictable-idle-time-millis: 900000 # 配置一个连接在连接池中最大生存的时间(单位:毫秒)
|
||||
validation-query: SELECT 1 FROM DUAL # 配置测试连接是否可用的查询 sql
|
||||
test-on-borrow: false
|
||||
test-on-return: false
|
||||
pool-prepared-statements: false
|
||||
web-stat-filter:
|
||||
enabled: true
|
||||
stat-view-servlet:
|
||||
enabled: true
|
||||
url-pattern: /druid/* # 配置监控后台访问路径
|
||||
login-username: admin # 配置监控后台登录的用户名、密码
|
||||
login-password: admin
|
||||
filter:
|
||||
config:
|
||||
enabled: true
|
||||
stat:
|
||||
enabled: true
|
||||
log-slow-sql: true # 开启慢 sql 记录
|
||||
slow-sql-millis: 2000 # 若执行耗时大于 2s,则视为慢 sql
|
||||
merge-sql: true
|
||||
wall: # 防火墙
|
||||
config:
|
||||
multi-statement-allow: true
|
||||
connection-properties: config.decrypt=true;config.decrypt.key=MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAK6C4nQHNuYSebx/5vOdvDqP/o8AH+p73s1LWCFs915RiwVHvtEd+ropmXkCO3Agc9Zuo8pyMvccIgPL9F0I8YkCAwEAAQ==
|
||||
output:
|
||||
ansi:
|
||||
enabled: always
|
||||
application:
|
||||
name: xiaoyishu-auth
|
||||
|
||||
mybatis-plus:
|
||||
mapper-locations:
|
||||
- classpath*:top/crushtj/**/*.xml # 匹配所有模块中的 Mapper XML 文件
|
||||
type-aliases-package: top.crushtj.xiaoyishu.auth.domain.entity # 实体类包路径
|
||||
configuration:
|
||||
map-underscore-to-camel-case: true # 开启驼峰命名转换
|
||||
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 日志输出(调试用)
|
||||
|
||||
logging:
|
||||
level:
|
||||
top.crushtj.xiaoyishu.auth.domain.mappers: debug
|
||||
@@ -0,0 +1,30 @@
|
||||
spring:
|
||||
profiles:
|
||||
active: dev
|
||||
server:
|
||||
port: 18881
|
||||
|
||||
logging:
|
||||
config: classpath:config/logback-spring.xml
|
||||
|
||||
############## Sa-Token 配置 ##############
|
||||
sa-token:
|
||||
# token 名称(同时也是 cookie 名称)
|
||||
token-name: Authorization
|
||||
token-prefix: Bearer
|
||||
# token 有效期(单位:秒) 默认30天,-1 代表永久有效
|
||||
timeout: 2592000
|
||||
# timeout: 60
|
||||
# token 最低活跃频率(单位:秒),如果 token 超过此时间没有访问系统就会被冻结,默认-1 代表不限制,永不冻结
|
||||
active-timeout: -1
|
||||
# 是否允许同一账号多地同时登录 (为 true 时允许一起登录, 为 false 时新登录挤掉旧登录)
|
||||
is-concurrent: true
|
||||
# 在多人登录同一账号时,是否共用一个 token (为 true 时所有登录共用一个 token, 为 false 时每次登录新建一个 token)
|
||||
is-share: true
|
||||
# token 风格(默认可取值:uuid、simple-uuid、random-32、random-64、random-128、tik)
|
||||
token-style: random-128
|
||||
# 是否输出操作日志
|
||||
is-log: true
|
||||
|
||||
alarm:
|
||||
type: sms # 告警方式,sms-短信,email-邮件
|
||||
@@ -0,0 +1,19 @@
|
||||
spring:
|
||||
application:
|
||||
name: xiaoyishu-auth # 应用名称
|
||||
profiles:
|
||||
active: dev # 默认激活 dev 本地开发环境
|
||||
cloud:
|
||||
nacos:
|
||||
config:
|
||||
server-addr: http://127.0.0.1:8848 # 指定 Nacos 配置中心的服务器地址
|
||||
prefix: ${spring.application.name} # 配置 Data Id 前缀,这里使用应用名称作为前缀
|
||||
group: DEFAULT_GROUP # 所属组
|
||||
namespace: xiaoyishu # 命名空间
|
||||
file-extension: yaml # 配置文件格式
|
||||
refresh-enabled: true # 是否开启动态刷新
|
||||
discovery:
|
||||
enabled: true # 启用服务发现
|
||||
group: DEFAULT_GROUP # 所属组
|
||||
namespace: xiaoyishu # 命名空间
|
||||
server-addr: 127.0.0.1:8848 # 指定 Nacos 配置中心的服务器地址
|
||||
@@ -0,0 +1,60 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration>
|
||||
<jmxConfigurator/>
|
||||
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
|
||||
|
||||
<!-- 应用名称 -->
|
||||
<property scope="context" name="appName" value="xiaoyi-auth"/>
|
||||
<property name="LOG_FILE" value="./logs/${appName}.%d{yyyy-MM-dd}"/>
|
||||
<property name="CONSOLE_LOG_PATTERN"
|
||||
value="[%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){cyan}] [%thread] [%clr(%-5level){highlight}] %clr(%logger{50}){magenta} - %msg%n"/>
|
||||
<property name="FILE_LOG_PATTERN" value="[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%thread] [%-5level] %logger{50} - %msg%n"/>
|
||||
<!-- 文件输出 -->
|
||||
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
|
||||
<!-- 日志文件的命名格式 -->
|
||||
<fileNamePattern>${LOG_FILE}-%i.log</fileNamePattern>
|
||||
<!-- 保留 30 天的日志文件 -->
|
||||
<maxHistory>30</maxHistory>
|
||||
<!-- 单个日志文件最大大小 -->
|
||||
<maxFileSize>10MB</maxFileSize>
|
||||
<!-- 日志文件的总大小,0 表示不限制 -->
|
||||
<totalSizeCap>0</totalSizeCap>
|
||||
<!-- 重启服务时,是否清除历史日志,不推荐清理 -->
|
||||
<cleanHistoryOnStart>false</cleanHistoryOnStart>
|
||||
</rollingPolicy>
|
||||
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
|
||||
<pattern>${FILE_LOG_PATTERN}</pattern>
|
||||
<charset>UTF-8</charset>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<!-- 异步写入日志,提升性能 -->
|
||||
<appender name="ASYNC_FILE" class="ch.qos.logback.classic.AsyncAppender">
|
||||
<!-- 是否丢弃日志, 0 表示不丢弃。默认情况下,如果队列满 80%, 会丢弃 TRACE、DEBUG、INFO 级别的日志 -->
|
||||
<discardingThreshold>0</discardingThreshold>
|
||||
<!-- 队列大小。默认值为 256 -->
|
||||
<queueSize>256</queueSize>
|
||||
<appender-ref ref="FILE"/>
|
||||
</appender>
|
||||
|
||||
<springProfile name="dev">
|
||||
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<withJansi>true</withJansi>
|
||||
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
|
||||
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
<root level="INFO">
|
||||
<appender-ref ref="CONSOLE"/>
|
||||
<appender-ref ref="ASYNC_FILE"/>
|
||||
</root>
|
||||
</springProfile>
|
||||
|
||||
<springProfile name="prod">
|
||||
<include resource="org/springframework/boot/logging/logback/console-appender.xml"/>
|
||||
<root level="INFO">
|
||||
<appender-ref ref="ASYNC_FILE"/> <!-- 生产环境下,仅打印日志到文件中 -->
|
||||
</root>
|
||||
</springProfile>
|
||||
</configuration>
|
||||
@@ -0,0 +1,85 @@
|
||||
DROP TABLE IF EXISTS `t_user`;
|
||||
CREATE TABLE IF NOT EXISTS `t_user` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`xiaoyishu_id` varchar(15) COLLATE UTF8MB4_UNICODE_CI NOT NULL COMMENT '小壹书号(唯一凭证)',
|
||||
`password` varchar(64) COLLATE UTF8MB4_UNICODE_CI DEFAULT NULL COMMENT '密码',
|
||||
`nickname` varchar(24) COLLATE UTF8MB4_UNICODE_CI NOT NULL COMMENT '昵称',
|
||||
`avatar` varchar(120) COLLATE UTF8MB4_UNICODE_CI DEFAULT NULL COMMENT '头像',
|
||||
`birthday` date DEFAULT NULL COMMENT '生日',
|
||||
`background_img` varchar(120) COLLATE UTF8MB4_UNICODE_CI DEFAULT NULL COMMENT '背景图',
|
||||
`phone` varchar(11) COLLATE UTF8MB4_UNICODE_CI NOT NULL COMMENT '手机号',
|
||||
`sex` tinyint DEFAULT '3' COMMENT '性别(1: 男 2: 女 3: 未知)',
|
||||
`status` tinyint NOT NULL DEFAULT '1' COMMENT '状态(1:启用 0:禁用)',
|
||||
`introduction` varchar(100) COLLATE UTF8MB4_UNICODE_CI DEFAULT NULL COMMENT '个人简介',
|
||||
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`is_deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '逻辑删除(0:未删除 1:已删除)',
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
UNIQUE KEY `uk_xiaoyishu_id` (`xiaoyishu_id`),
|
||||
UNIQUE KEY `uk_phone` (`phone`)
|
||||
) ENGINE = InnoDB
|
||||
DEFAULT CHARSET = UTF8MB4
|
||||
COLLATE = UTF8MB4_UNICODE_CI COMMENT = '用户表';
|
||||
|
||||
DROP TABLE IF EXISTS `t_role`;
|
||||
CREATE TABLE IF NOT EXISTS `t_role` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`role_name` varchar(32) COLLATE UTF8MB4_UNICODE_CI NOT NULL COMMENT '角色名',
|
||||
`role_key` varchar(32) COLLATE UTF8MB4_UNICODE_CI NOT NULL COMMENT '角色唯一标识',
|
||||
`status` tinyint unsigned NOT NULL DEFAULT '1' COMMENT '状态(1:启用 0:禁用)',
|
||||
`sort` int unsigned NOT NULL DEFAULT 0 COMMENT '管理系统中的显示顺序',
|
||||
`remark` varchar(255) COLLATE UTF8MB4_UNICODE_CI DEFAULT NULL COMMENT '备注',
|
||||
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '最后一次更新时间',
|
||||
`is_deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '逻辑删除(0:未删除 1:已删除)',
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
UNIQUE KEY `uk_role_key` (`role_key`)
|
||||
) ENGINE = InnoDB
|
||||
DEFAULT CHARSET = UTF8MB4
|
||||
COLLATE = UTF8MB4_UNICODE_CI COMMENT ='角色表';
|
||||
|
||||
DROP TABLE IF EXISTS `t_permission`;
|
||||
CREATE TABLE IF NOT EXISTS `t_permission` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`parent_id` bigint NOT NULL DEFAULT '0' COMMENT '父ID',
|
||||
`name` varchar(16) COLLATE UTF8MB4_UNICODE_CI NOT NULL COMMENT '权限名称',
|
||||
`type` tinyint unsigned NOT NULL COMMENT '类型(1:目录 2:菜单 3:按钮)',
|
||||
`menu_url` varchar(32) COLLATE UTF8MB4_UNICODE_CI NOT NULL DEFAULT '' COMMENT '菜单路由',
|
||||
`menu_icon` varchar(255) COLLATE UTF8MB4_UNICODE_CI NOT NULL DEFAULT '' COMMENT '菜单图标',
|
||||
`sort` int unsigned NOT NULL DEFAULT 0 COMMENT '管理系统中的显示顺序',
|
||||
`permission_key` varchar(64) COLLATE UTF8MB4_UNICODE_CI NOT NULL COMMENT '权限标识',
|
||||
`status` tinyint unsigned NOT NULL DEFAULT '1' COMMENT '状态(1:启用;0:禁用)',
|
||||
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`is_deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '逻辑删除(0:未删除 1:已删除)',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE = InnoDB
|
||||
DEFAULT CHARSET = UTF8MB4
|
||||
COLLATE = UTF8MB4_UNICODE_CI COMMENT ='权限表';
|
||||
|
||||
DROP TABLE IF EXISTS `t_user_role_rel`;
|
||||
CREATE TABLE IF NOT EXISTS `t_user_role_rel` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`user_id` bigint NOT NULL COMMENT '用户ID',
|
||||
`role_id` bigint NOT NULL COMMENT '角色ID',
|
||||
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`is_deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '逻辑删除(0:未删除 1:已删除)',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE = InnoDB
|
||||
DEFAULT CHARSET = UTF8MB4
|
||||
COLLATE = UTF8MB4_UNICODE_CI COMMENT ='用户角色表';
|
||||
|
||||
DROP TABLE IF EXISTS `t_role_permission_rel`;
|
||||
CREATE TABLE IF NOT EXISTS `t_role_permission_rel` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`role_id` bigint NOT NULL COMMENT '角色ID',
|
||||
`permission_id` bigint NOT NULL COMMENT '权限ID',
|
||||
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`is_deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '逻辑删除(0:未删除 1:已删除)',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE = InnoDB
|
||||
DEFAULT CHARSET = UTF8MB4
|
||||
COLLATE = UTF8MB4_UNICODE_CI COMMENT ='用户权限表';
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
-- Description: 初始化权限数据-权限管理未开发,用于测试
|
||||
INSERT INTO `t_permission` (`id`, `parent_id`, `name`, `type`, `menu_url`, `menu_icon`, `sort`,
|
||||
`permission_key`, `status`, `create_time`, `update_time`, `is_deleted`)
|
||||
VALUES (1, 0, '发布笔记', 3, '', '', 1, 'app:note:publish', 1, NOW(), NOW(), b'0');
|
||||
INSERT INTO `t_permission` (`id`, `parent_id`, `name`, `type`, `menu_url`, `menu_icon`, `sort`,
|
||||
`permission_key`, `status`, `create_time`, `update_time`, `is_deleted`)
|
||||
VALUES (2, 0, '发布评论', 3, '', '', 2, 'app:comment:publish', 1, NOW(), NOW(), b'0');
|
||||
|
||||
INSERT INTO `t_role` (`id`, `role_name`, `role_key`, `status`, `sort`, `remark`, `create_time`, `update_time`,
|
||||
`is_deleted`)
|
||||
VALUES (1, '普通用户', 'common_user', 1, 1, '', NOW(), NOW(), b'0');
|
||||
|
||||
INSERT INTO `t_role_permission_rel` (`id`, `role_id`, `permission_id`, `create_time`, `update_time`, `is_deleted`)
|
||||
VALUES (1, 1, 1, NOW(), NOW(), b'0');
|
||||
INSERT INTO `t_role_permission_rel` (`id`, `role_id`, `permission_id`, `create_time`, `update_time`, `is_deleted`)
|
||||
VALUES (2, 1, 2, NOW(), NOW(), b'0');
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
package top.crushtj.xiaoyi.auth;
|
||||
|
||||
import com.alibaba.druid.filter.config.ConfigTools;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jasypt.encryption.pbe.StandardPBEStringEncryptor;
|
||||
import org.jasypt.iv.RandomIvGenerator;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.test.context.TestPropertySource;
|
||||
|
||||
/**
|
||||
* @author ayi
|
||||
* @version V1.0
|
||||
* @title DruidTest
|
||||
* @description
|
||||
* @date 2025/12/01
|
||||
*/
|
||||
|
||||
@SpringBootTest
|
||||
@Slf4j
|
||||
@TestPropertySource(properties = {"jasypt.encryptor.password="})
|
||||
public class EncryptTest {
|
||||
//@Autowired
|
||||
//private StringEncryptor defaultLazyEncryptor;
|
||||
//private StringEncryptor pooledPbeStringEncryptor;
|
||||
@Value("${jasypt.encryptor.password}")
|
||||
private String encryptorPassword;
|
||||
|
||||
/**
|
||||
* Druid 密码加密
|
||||
*/
|
||||
@Test
|
||||
@SneakyThrows
|
||||
void testEncodePassword() {
|
||||
// 你的密码
|
||||
String password = "";//密码
|
||||
String[] arr = ConfigTools.genKeyPair(512);
|
||||
|
||||
// 私钥
|
||||
log.info("privateKey: {}", arr[0]);
|
||||
// 公钥
|
||||
log.info("publicKey: {}", arr[1]);
|
||||
|
||||
// 通过私钥加密密码
|
||||
String encodePassword = ConfigTools.encrypt(arr[0], password);
|
||||
log.info("password: {}", encodePassword);
|
||||
}
|
||||
|
||||
@Test
|
||||
void encrypt() {
|
||||
StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor();
|
||||
|
||||
// JDK 17适配的核心配置(关键参数必须完整)
|
||||
String password = ""; // 加密密码
|
||||
encryptor.setPassword(password); // AES-256要求密钥至少32位
|
||||
encryptor.setAlgorithm("PBEWithHMACSHA512AndAES_256"); // JDK17原生支持的算法
|
||||
encryptor.setKeyObtentionIterations(1000); // 迭代次数(固定值)
|
||||
encryptor.setStringOutputType("base64"); // 输出格式(固定)
|
||||
encryptor.setProviderName("SunJCE"); // 加密提供者(JDK17默认)
|
||||
encryptor.setIvGenerator(new RandomIvGenerator()); // AES必须的IV生成器
|
||||
|
||||
// 待加密的原始值
|
||||
String encry1 = ""; // 待加密的原始值1
|
||||
String encry2 = ""; // 待加密的原始值2
|
||||
String encry3 = ""; // 待加密的原始值3
|
||||
|
||||
try {
|
||||
String cipherText1 = encryptor.encrypt(encry1);
|
||||
System.out.println("字段1加密成功,密文:" + cipherText1);
|
||||
String cipherText2 = encryptor.encrypt(encry2);
|
||||
System.out.println("字段2加密成功,密文:" + cipherText2);
|
||||
String cipherText3 = encryptor.encrypt(encry3);
|
||||
System.out.println("字段2加密成功,密文:" + cipherText3);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
System.out.println("加密失败原因:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dencrypt(){
|
||||
StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor();
|
||||
String cipherText1 = "";//密文
|
||||
String cipherText2 = "";//密文
|
||||
String cipherText3 = "";//密文
|
||||
String password = "";//加密密码
|
||||
encryptor.setPassword(password); // AES-256要求密钥至少32位
|
||||
encryptor.setAlgorithm("PBEWithHMACSHA512AndAES_256"); // JDK17原生支持的算法
|
||||
encryptor.setKeyObtentionIterations(1000); // 迭代次数(固定值)
|
||||
encryptor.setStringOutputType("base64"); // 输出格式(固定)
|
||||
encryptor.setProviderName("SunJCE"); // 加密提供者(JDK17默认)
|
||||
encryptor.setIvGenerator(new RandomIvGenerator());
|
||||
try {
|
||||
String text1 = encryptor.decrypt(cipherText1);
|
||||
System.out.println("字段1解密成功,明文:" + text1);
|
||||
String text2 = encryptor.decrypt(cipherText2);
|
||||
System.out.println("字段2解密成功,明文:" + text2);
|
||||
String text3 = encryptor.decrypt(cipherText3);
|
||||
System.out.println("字段2解密成功,明文:" + text3);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
System.out.println("加密失败原因:" + e.getMessage());
|
||||
}// AES必须的IV生成器
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package top.crushtj.xiaoyi.auth;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author ayi
|
||||
* @version V1.0
|
||||
* @title RedisTests
|
||||
* @description Redis 测试类
|
||||
* @date 2026/01/12 19:15
|
||||
*/
|
||||
|
||||
@SpringBootTest
|
||||
@Slf4j
|
||||
public class RedisTests {
|
||||
@Resource
|
||||
private RedisTemplate<String, Object> redisTemplate;
|
||||
|
||||
/**
|
||||
* set key value
|
||||
*/
|
||||
@Test
|
||||
void testSetKeyValue() {
|
||||
// 添加一个 key 为 name, value 值为 刑加一
|
||||
redisTemplate.opsForValue()
|
||||
.set("name", "刑加一");
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断某个 key 是否存在
|
||||
*/
|
||||
@Test
|
||||
void testHasKey() {
|
||||
log.info("key 是否存在:{}", Boolean.TRUE.equals(redisTemplate.hasKey("name")));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取某个 key 的 value
|
||||
*/
|
||||
@Test
|
||||
void testGetValue() {
|
||||
log.info("value 值:{}", redisTemplate.opsForValue()
|
||||
.get("name"));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除某个 key
|
||||
*/
|
||||
@Test
|
||||
void testDelete() {
|
||||
redisTemplate.delete("name");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package top.crushtj.xiaoyi.auth;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
import org.springframework.test.context.TestPropertySource;
|
||||
|
||||
/**
|
||||
* @author ayi
|
||||
* @version V1.0
|
||||
* @title ThreadPoolTaskExecutorTests
|
||||
* @date 2026/01/16 22:34
|
||||
* @description 线程池测试类
|
||||
*/
|
||||
|
||||
@Slf4j
|
||||
@SpringBootTest
|
||||
@TestPropertySource(properties = {"jasypt.encryptor.password="})
|
||||
public class ThreadPoolTaskExecutorTests {
|
||||
@Resource(name = "taskExecutor")
|
||||
private ThreadPoolTaskExecutor taskExecutor;
|
||||
|
||||
/**
|
||||
* 测试线程池
|
||||
*/
|
||||
@Test
|
||||
void testSubmit() {
|
||||
int count = 300;
|
||||
while (count-- > 0) {
|
||||
int finalCount = count;
|
||||
taskExecutor.submit(() -> log.info("异步线程: 这是{}号线程", finalCount));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
package top.crushtj.xiaoyishu.auth;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
@SpringBootTest
|
||||
class XiaoyishuAuthApplicationTests {
|
||||
|
||||
@Test
|
||||
void contextLoads() {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -4,9 +4,9 @@
|
||||
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>
|
||||
<parent>
|
||||
<groupId>top.crushtj</groupId>
|
||||
<artifactId>xiaoyishu</artifactId>
|
||||
<version>${revision}</version>
|
||||
<groupId>top.crushtj</groupId>
|
||||
<artifactId>xiaoyishu</artifactId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
|
||||
<packaging>pom</packaging>
|
||||
@@ -15,8 +15,13 @@
|
||||
<name>${project.artifactId}</name>
|
||||
<description>平台基础设施层:封装一些常用功能,供各个业务线拿来即用</description>
|
||||
|
||||
<modules>
|
||||
<modules>
|
||||
<!--通用-->
|
||||
<module>xiaoyi-common</module>
|
||||
<!--日志切面-->
|
||||
<module>xiaoyi-spring-boot-starter-biz-operationlog</module>
|
||||
<!--Jackson-->
|
||||
<module>xiaoyi-spring-boot-starter-jackson</module>
|
||||
</modules>
|
||||
|
||||
</project>
|
||||
@@ -20,6 +20,59 @@
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</dependency>
|
||||
<!-- Jackson -->
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-core</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 解决 Jackson Java 8 新日期 API 的序列化问题 -->
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.datatype</groupId>
|
||||
<artifactId>jackson-datatype-jsr310</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- AOP 切面 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-aop</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- jansi -->
|
||||
<dependency>
|
||||
<groupId>org.fusesource.jansi</groupId>
|
||||
<artifactId>jansi</artifactId>
|
||||
</dependency>
|
||||
<!-- 参数校验 -->
|
||||
<dependency>
|
||||
<groupId>jakarta.validation</groupId>
|
||||
<artifactId>jakarta.validation-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hibernate.validator</groupId>
|
||||
<artifactId>hibernate-validator</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 工具类 -->
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-all</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
package top.crushtj.framework.common.constant;
|
||||
|
||||
import java.time.format.DateTimeFormatter;
|
||||
|
||||
/**
|
||||
* @author ayi
|
||||
* @title DateConstants
|
||||
* @description 日期常量
|
||||
* @date 2025/11/26
|
||||
*/
|
||||
public interface DateConstants {
|
||||
/**
|
||||
* DateTimeFormatter:年-月-日 时:分:秒
|
||||
*/
|
||||
DateTimeFormatter DATE_FORMAT_Y_M_D_H_M_S = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
/**
|
||||
* DateTimeFormatter:年-月-日
|
||||
*/
|
||||
DateTimeFormatter DATE_FORMAT_Y_M_D = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
||||
|
||||
/**
|
||||
* DateTimeFormatter:时:分:秒
|
||||
*/
|
||||
DateTimeFormatter DATE_FORMAT_H_M_S = DateTimeFormatter.ofPattern("HH:mm:ss");
|
||||
|
||||
/**
|
||||
* DateTimeFormatter:年-月
|
||||
*/
|
||||
DateTimeFormatter DATE_FORMAT_Y_M = DateTimeFormatter.ofPattern("yyyy-MM");
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
package top.crushtj.framework.common.enums;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* @author ayi
|
||||
* @version V1.0
|
||||
* @title DeleteEnum
|
||||
* @date 2026/01/18 21:15
|
||||
* @description 删除标记
|
||||
*/
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum DeleteEnum {
|
||||
YES(true),
|
||||
NO(false);
|
||||
|
||||
public final Boolean value;
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
package top.crushtj.framework.common.enums;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* @author ayi
|
||||
* @version V1.0
|
||||
* @title StatusEnum
|
||||
* @date 2026/01/18
|
||||
* @description 状态
|
||||
*/
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum StatusEnum {
|
||||
ENABLED(1),
|
||||
DISABLED(0);
|
||||
|
||||
public final Integer value;
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
package top.crushtj.framework.common.exception;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author ayi
|
||||
* @title BaseExceptionInterface
|
||||
* @description 基础异常接口
|
||||
* @date 2025/11/20
|
||||
*/
|
||||
|
||||
public interface BaseExceptionInterface {
|
||||
/**
|
||||
* 获取异常码
|
||||
*
|
||||
* @return 异常码
|
||||
*/
|
||||
String getErrorCode();
|
||||
|
||||
/**
|
||||
* 获取异常信息
|
||||
*
|
||||
* @return 异常信息
|
||||
*/
|
||||
String getErrorMessage();
|
||||
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
package top.crushtj.framework.common.exception;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author ayi
|
||||
* @title BusiException
|
||||
* @description 业务异常
|
||||
* @date 2025/11/20
|
||||
*/
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public class BizException extends RuntimeException {
|
||||
|
||||
/**
|
||||
* 异常码
|
||||
*/
|
||||
private String errorCode;
|
||||
|
||||
/**
|
||||
* 异常信息
|
||||
*/
|
||||
private String errorMessage;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
*
|
||||
* @param baseExceptionInterface 基础异常接口
|
||||
*/
|
||||
public BizException(BaseExceptionInterface baseExceptionInterface) {
|
||||
this.errorCode = baseExceptionInterface.getErrorCode();
|
||||
this.errorMessage = baseExceptionInterface.getErrorMessage();
|
||||
}
|
||||
|
||||
}
|
||||
+138
@@ -0,0 +1,138 @@
|
||||
package top.crushtj.framework.common.response;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import lombok.Data;
|
||||
import top.crushtj.framework.common.exception.BaseExceptionInterface;
|
||||
import top.crushtj.framework.common.exception.BizException;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author ayi
|
||||
* @title Response
|
||||
* @description 响应体
|
||||
* @date 2025/11/20
|
||||
*/
|
||||
|
||||
@Data
|
||||
public class Response<T> implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = -6624218097474846897L;
|
||||
|
||||
/**
|
||||
* 响应码
|
||||
*/
|
||||
private String code;
|
||||
|
||||
/**
|
||||
* 响应信息
|
||||
*/
|
||||
private String message;
|
||||
|
||||
/**
|
||||
* 响应时间
|
||||
*/
|
||||
private LocalDateTime time = LocalDateTime.now();
|
||||
|
||||
/**
|
||||
* 是否成功
|
||||
*/
|
||||
private boolean success = true;
|
||||
|
||||
/**
|
||||
* 响应数据
|
||||
*/
|
||||
private T data;
|
||||
|
||||
/**
|
||||
* 成功响应
|
||||
*
|
||||
* @return 成功响应
|
||||
*/
|
||||
public static <T> Response<T> success() {
|
||||
return new Response<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* 成功响应
|
||||
*
|
||||
* @param data 响应数据
|
||||
* @return 成功响应
|
||||
*/
|
||||
public static <T> Response<T> success(T data) {
|
||||
Response<T> response = new Response<>();
|
||||
response.setData(data);
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* 失败响应
|
||||
*
|
||||
* @return 失败响应
|
||||
*/
|
||||
public static <T> Response<T> failure() {
|
||||
Response<T> response = new Response<>();
|
||||
response.setSuccess(false);
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* 失败响应
|
||||
*
|
||||
* @param message 异常信息
|
||||
* @return 失败响应
|
||||
*/
|
||||
public static <T> Response<T> failure(String message) {
|
||||
Response<T> response = new Response<>();
|
||||
response.setSuccess(false);
|
||||
response.setMessage(message);
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* 失败响应
|
||||
*
|
||||
* @param code 异常码
|
||||
* @param message 异常信息
|
||||
* @return 失败响应
|
||||
*/
|
||||
public static <T> Response<T> failure(String code, String message) {
|
||||
Response<T> response = new Response<>();
|
||||
response.setSuccess(false);
|
||||
response.setCode(code);
|
||||
response.setMessage(message);
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* 失败响应
|
||||
*
|
||||
* @param bizException 业务异常
|
||||
* @return 失败响应
|
||||
*/
|
||||
public static <T> Response<T> failure(BizException bizException) {
|
||||
Response<T> response = new Response<>();
|
||||
response.setSuccess(false);
|
||||
response.setCode(bizException.getErrorCode());
|
||||
response.setMessage(bizException.getErrorMessage());
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* 失败响应
|
||||
*
|
||||
* @param <T> 响应数据类型
|
||||
* @param baseExceptionInterface 基础异常
|
||||
* @return 失败响应
|
||||
*/
|
||||
public static <T> Response<T> failure(BaseExceptionInterface baseExceptionInterface) {
|
||||
Response<T> response = new Response<>();
|
||||
response.setSuccess(false);
|
||||
response.setCode(baseExceptionInterface.getErrorCode());
|
||||
response.setMessage(baseExceptionInterface.getErrorMessage());
|
||||
return response;
|
||||
}
|
||||
}
|
||||
+180
@@ -0,0 +1,180 @@
|
||||
package top.crushtj.framework.common.utils;
|
||||
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.net.InetAddress;
|
||||
import java.net.NetworkInterface;
|
||||
|
||||
public class IdGenerator {
|
||||
// ====================== 配置参数 ======================
|
||||
/** 开始时间戳 (2024-01-01 00:00:00),可自定义,减少ID长度 */
|
||||
private static final long START_TIMESTAMP = 1767196800000L;
|
||||
|
||||
/** 机器ID所占的位数 (最多10位,支持1024个节点) */
|
||||
private static final long WORKER_ID_BITS = 10L;
|
||||
|
||||
/** 序列号所占的位数 (最多12位,支持4096个/毫秒) */
|
||||
private static final long SEQUENCE_BITS = 12L;
|
||||
|
||||
// ====================== 位移计算 ======================
|
||||
/** 机器ID左移位数 (12位) */
|
||||
private static final long WORKER_ID_SHIFT = SEQUENCE_BITS;
|
||||
|
||||
/** 时间戳左移位数 (10+12=22位) */
|
||||
private static final long TIMESTAMP_LEFT_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS;
|
||||
|
||||
/** 序列号的最大值 (4095) */
|
||||
private static final long SEQUENCE_MASK = ~(-1L << SEQUENCE_BITS);
|
||||
|
||||
// ====================== 全局变量 ======================
|
||||
/** 机器ID (0-1023) */
|
||||
private final long workerId;
|
||||
|
||||
/** 序列号 (0-4095) */
|
||||
private long sequence = 0L;
|
||||
|
||||
/** 上一次生成ID的时间戳 */
|
||||
private long lastTimestamp = -1L;
|
||||
|
||||
// ====================== 单例实例 ======================
|
||||
private static volatile IdGenerator INSTANCE;
|
||||
|
||||
/**
|
||||
* 私有构造器,初始化机器ID(自动计算,也可手动指定)
|
||||
*
|
||||
* @param workerId 机器ID (0-1023)
|
||||
* @throws IllegalArgumentException 机器ID超出范围时抛出
|
||||
*/
|
||||
private IdGenerator(long workerId) {
|
||||
// 校验机器ID范围
|
||||
long maxWorkerId = ~(-1L << WORKER_ID_BITS);
|
||||
if (workerId < 0 || workerId > maxWorkerId) {
|
||||
throw new IllegalArgumentException(String.format("Worker ID 必须在 0 到 %d 之间", maxWorkerId));
|
||||
}
|
||||
this.workerId = workerId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取单例实例(自动计算机器ID)
|
||||
*
|
||||
* @return 雪花算法生成器实例
|
||||
*/
|
||||
public static IdGenerator getInstance() {
|
||||
if (INSTANCE == null) {
|
||||
synchronized (IdGenerator.class) {
|
||||
if (INSTANCE == null) {
|
||||
INSTANCE = new IdGenerator(calculateWorkerId());
|
||||
}
|
||||
}
|
||||
}
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
/**
|
||||
* 手动指定机器ID获取实例(适合分布式部署时手动分配节点ID)
|
||||
*
|
||||
* @param workerId 机器ID (0-1023)
|
||||
* @return 雪花算法生成器实例
|
||||
*/
|
||||
public static IdGenerator getInstance(long workerId) {
|
||||
if (INSTANCE == null) {
|
||||
synchronized (IdGenerator.class) {
|
||||
if (INSTANCE == null) {
|
||||
INSTANCE = new IdGenerator(workerId);
|
||||
}
|
||||
}
|
||||
}
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成下一个ID(核心方法,线程安全)
|
||||
*
|
||||
* @return 有序、唯一的Long类型ID
|
||||
* @throws RuntimeException 时钟回拨时抛出(避免ID重复)
|
||||
*/
|
||||
public synchronized long nextId() {
|
||||
long currentTimestamp = System.currentTimeMillis();
|
||||
|
||||
// 检查时钟回拨(关键:避免ID重复)
|
||||
if (currentTimestamp < lastTimestamp) {
|
||||
throw new RuntimeException(
|
||||
String.format("时钟回拨检测!拒绝生成ID,上一次时间:%d,当前时间:%d", lastTimestamp, currentTimestamp));
|
||||
}
|
||||
|
||||
// 同一毫秒内,序列号递增
|
||||
if (currentTimestamp == lastTimestamp) {
|
||||
sequence = (sequence + 1) & SEQUENCE_MASK;
|
||||
// 同一毫秒内序列号用尽,等待下一毫秒
|
||||
if (sequence == 0) {
|
||||
currentTimestamp = waitNextMillis(lastTimestamp);
|
||||
}
|
||||
} else {
|
||||
// 不同毫秒,序列号重置为0
|
||||
sequence = 0L;
|
||||
}
|
||||
|
||||
// 更新最后生成ID的时间戳
|
||||
lastTimestamp = currentTimestamp;
|
||||
|
||||
// 组合ID:时间戳 + 机器ID + 序列号
|
||||
return ((currentTimestamp - START_TIMESTAMP) << TIMESTAMP_LEFT_SHIFT) | (workerId << WORKER_ID_SHIFT)
|
||||
| sequence;
|
||||
}
|
||||
|
||||
/**
|
||||
* 等待下一毫秒,直到获取新的时间戳
|
||||
*
|
||||
* @param lastTimestamp 上一次生成ID的时间戳
|
||||
* @return 新的时间戳
|
||||
*/
|
||||
private long waitNextMillis(long lastTimestamp) {
|
||||
long timestamp = System.currentTimeMillis();
|
||||
while (timestamp <= lastTimestamp) {
|
||||
timestamp = System.currentTimeMillis();
|
||||
}
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* 自动计算机器ID(基于网卡+进程ID,避免手动配置)
|
||||
*
|
||||
* @return 0-1023之间的机器ID
|
||||
*/
|
||||
private static long calculateWorkerId() {
|
||||
try {
|
||||
// 第一步:获取网卡MAC地址的哈希值
|
||||
InetAddress address = InetAddress.getLocalHost();
|
||||
long macHash = NetworkInterface.getByInetAddress(address).getHardwareAddress()[0] & 0xFF;
|
||||
|
||||
// 第二步:获取进程ID(避免同一机器多进程冲突)
|
||||
String processName = ManagementFactory.getRuntimeMXBean().getName();
|
||||
long pid = Long.parseLong(processName.split("@")[0]);
|
||||
|
||||
// 组合并取模,确保在0-1023之间
|
||||
return (macHash + pid) % (~(-1L << WORKER_ID_BITS));
|
||||
} catch (Exception e) {
|
||||
// 异常时返回随机数(生产环境建议手动指定机器ID)
|
||||
return (long)(Math.random() * (~(-1L << WORKER_ID_BITS)));
|
||||
}
|
||||
}
|
||||
|
||||
// ====================== 测试用例 ======================
|
||||
public static void main(String[] args) {
|
||||
// 测试生成10个ID,验证有序性和唯一性
|
||||
IdGenerator generator = IdGenerator.getInstance();
|
||||
for (int i = 0; i < 10; i++) {
|
||||
long id = generator.nextId();
|
||||
System.out.println("生成的ID:" + id);
|
||||
}
|
||||
|
||||
// 多线程测试(验证线程安全)
|
||||
Runnable task = () -> {
|
||||
for (int i = 0; i < 5; i++) {
|
||||
System.out.println(Thread.currentThread().getName() + " -> " + generator.nextId());
|
||||
}
|
||||
};
|
||||
new Thread(task, "线程1").start();
|
||||
new Thread(task, "线程2").start();
|
||||
}
|
||||
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
package top.crushtj.framework.common.utils;
|
||||
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author ayi
|
||||
* @title JsonUtils
|
||||
* @description JSON 工具类
|
||||
* @date 2025/11/21
|
||||
*/
|
||||
public class JsonUtils {
|
||||
private static ObjectMapper OBJECT_MAPPER = new ObjectMapper();
|
||||
|
||||
static {
|
||||
OBJECT_MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||
OBJECT_MAPPER.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
|
||||
OBJECT_MAPPER.registerModules(new JavaTimeModule());
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化:统一使用 Spring Boot 个性化配置的 ObjectMapper
|
||||
*
|
||||
* @param objectMapper
|
||||
*/
|
||||
public static void init(ObjectMapper objectMapper) {
|
||||
OBJECT_MAPPER = objectMapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将对象转换为 JSON 字符串
|
||||
*
|
||||
* @param obj 要转换的对象
|
||||
* @return JSON 字符串
|
||||
*/
|
||||
@SneakyThrows
|
||||
public static String toJsonString(Object obj) {
|
||||
return OBJECT_MAPPER.writeValueAsString(obj);
|
||||
}
|
||||
}
|
||||
+143
@@ -0,0 +1,143 @@
|
||||
package top.crushtj.framework.common.utils;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
/**
|
||||
* @author ayi
|
||||
* @version V1.0
|
||||
* @title MaskUtils
|
||||
* @date 2026/01/17 13:22
|
||||
* @description 数据脱敏工具类
|
||||
*/
|
||||
|
||||
public class MaskUtils {
|
||||
|
||||
// ===================== 通用脱敏方法(基础) =====================
|
||||
/**
|
||||
* 通用脱敏方法:保留前prefixLen位,后suffixLen位,中间用*填充
|
||||
* @param content 原始字符串
|
||||
* @param prefixLen 保留前缀长度
|
||||
* @param suffixLen 保留后缀长度
|
||||
* @return 脱敏后的字符串
|
||||
*/
|
||||
public static String maskGeneral(String content, int prefixLen, int suffixLen) {
|
||||
// 空值处理:null/空串直接返回,避免NPE
|
||||
if (StringUtils.isBlank(content)) {
|
||||
return content;
|
||||
}
|
||||
int contentLen = content.length();
|
||||
// 若长度小于等于前缀+后缀,直接返回原字符串(避免过度脱敏)
|
||||
if (contentLen <= prefixLen + suffixLen) {
|
||||
return content;
|
||||
}
|
||||
// 拼接前缀 + 中间* + 后缀
|
||||
StringBuilder sb = new StringBuilder();
|
||||
// 添加前缀
|
||||
sb.append(content.substring(0, prefixLen));
|
||||
// 添加中间掩码(长度=总长度-前缀-后缀)
|
||||
sb.append("*".repeat(Math.max(0, contentLen - prefixLen - suffixLen)));
|
||||
// 添加后缀
|
||||
sb.append(content.substring(contentLen - suffixLen));
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
// ===================== 常用敏感字段脱敏方法(业务封装) =====================
|
||||
|
||||
/**
|
||||
* 手机号脱敏:保留前3位,后4位,中间4位掩码(如:138****1234)
|
||||
* @param mobile 手机号(支持11位常规手机号)
|
||||
* @return 脱敏后的手机号
|
||||
*/
|
||||
public static String maskMobile(String mobile) {
|
||||
// 先校验手机号格式(简单校验,可根据业务调整)
|
||||
if (StringUtils.isBlank(mobile) || !mobile.matches("^1[3-9]\\d{9}$")) {
|
||||
return mobile;
|
||||
}
|
||||
return maskGeneral(mobile, 3, 4);
|
||||
}
|
||||
|
||||
/**
|
||||
* 邮箱脱敏:保留前缀前3位,@及域名完整,中间掩码(如:123****@qq.com)
|
||||
* @param email 邮箱地址
|
||||
* @return 脱敏后的邮箱
|
||||
*/
|
||||
public static String maskEmail(String email) {
|
||||
if (StringUtils.isBlank(email) || !email.contains("@")) {
|
||||
return email;
|
||||
}
|
||||
String[] parts = email.split("@");
|
||||
String prefix = parts[0];
|
||||
String domain = parts[1];
|
||||
// 前缀长度<=3时不脱敏,否则保留前3位
|
||||
String maskedPrefix = prefix.length() <= 3 ? prefix : maskGeneral(prefix, 3, 0);
|
||||
return maskedPrefix + "@" + domain;
|
||||
}
|
||||
|
||||
/**
|
||||
* 姓名脱敏:
|
||||
* - 单字名:直接返回(如:李 → 李)
|
||||
* - 两字名:保留姓,名掩码(如:李白 → 李*)
|
||||
* - 三字及以上:保留姓和最后一个字,中间掩码(如:李世民 → 李*民)
|
||||
* @param name 姓名
|
||||
* @return 脱敏后的姓名
|
||||
*/
|
||||
public static String maskName(String name) {
|
||||
if (StringUtils.isBlank(name)) {
|
||||
return name;
|
||||
}
|
||||
int nameLen = name.length();
|
||||
if (nameLen == 1) {
|
||||
return name;
|
||||
} else if (nameLen == 2) {
|
||||
return name.substring(0, 1) + "*";
|
||||
} else {
|
||||
// 姓 + 中间* + 最后一个字
|
||||
return name.substring(0, 1) +
|
||||
"*".repeat(nameLen - 2) +
|
||||
name.substring(nameLen - 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 身份证号脱敏:保留前3位,后4位,中间掩码(如:110***********1234)
|
||||
* @param idCard 身份证号(支持15位/18位)
|
||||
* @return 脱敏后的身份证号
|
||||
*/
|
||||
public static String maskIdCard(String idCard) {
|
||||
if (StringUtils.isBlank(idCard) || (idCard.length() != 15 && idCard.length() != 18)) {
|
||||
return idCard;
|
||||
}
|
||||
return maskGeneral(idCard, 3, 4);
|
||||
}
|
||||
|
||||
/**
|
||||
* 银行卡号脱敏:保留前6位,后4位,中间掩码(如:622260**********1234)
|
||||
* @param bankCard 银行卡号(常规16/19位)
|
||||
* @return 脱敏后的银行卡号
|
||||
*/
|
||||
public static String maskBankCard(String bankCard) {
|
||||
if (StringUtils.isBlank(bankCard) || bankCard.length() < 10) {
|
||||
return bankCard;
|
||||
}
|
||||
return maskGeneral(bankCard, 6, 4);
|
||||
}
|
||||
|
||||
// ===================== 测试用例(可直接运行验证) =====================
|
||||
public static void main(String[] args) {
|
||||
// 测试手机号脱敏
|
||||
System.out.println("手机号脱敏:" + maskMobile("13812345678")); // 输出:138****5678
|
||||
// 测试邮箱脱敏
|
||||
System.out.println("邮箱脱敏:" + maskEmail("1234567890@qq.com")); // 输出:123****@qq.com
|
||||
// 测试姓名脱敏
|
||||
System.out.println("单字名:" + maskName("李")); // 输出:李
|
||||
System.out.println("两字名:" + maskName("李白")); // 输出:李*
|
||||
System.out.println("三字名:" + maskName("李世民")); // 输出:李*民
|
||||
// 测试身份证脱敏
|
||||
System.out.println("身份证脱敏:" + maskIdCard("110101199001011234")); // 输出:110***********1234
|
||||
// 测试银行卡脱敏
|
||||
System.out.println("银行卡脱敏:" + maskBankCard("6222600000000001234")); // 输出:622260**********1234
|
||||
// 测试空值/异常值
|
||||
System.out.println("空手机号:" + maskMobile(null)); // 输出:null
|
||||
System.out.println("无效邮箱:" + maskEmail("123456")); // 输出:123456
|
||||
}
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
package top.crushtj.framework.common.validator;
|
||||
|
||||
import jakarta.validation.Constraint;
|
||||
import jakarta.validation.Payload;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* @author ayi
|
||||
* @version V1.0
|
||||
* @title PhoneNumber
|
||||
* @date 2026/01/18
|
||||
* @description 手机号验证
|
||||
*/
|
||||
|
||||
@Target({ ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.PARAMETER })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Constraint(validatedBy = PhoneNumberValidator.class)
|
||||
public @interface PhoneNumber {
|
||||
String message() default "手机号格式不正确, 需为 11 位数字";
|
||||
|
||||
Class<?>[] groups() default {};
|
||||
|
||||
Class<? extends Payload>[] payload() default {};
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
package top.crushtj.framework.common.validator;
|
||||
|
||||
import jakarta.validation.ConstraintValidator;
|
||||
import jakarta.validation.ConstraintValidatorContext;
|
||||
|
||||
/**
|
||||
* @author ayi
|
||||
* @version V1.0
|
||||
* @title PhoneNumberValidator
|
||||
* @date 2026/01/18 13:17
|
||||
* @description 手机号验证器
|
||||
*/
|
||||
|
||||
public class PhoneNumberValidator implements ConstraintValidator<PhoneNumber,String> {
|
||||
@Override
|
||||
public boolean isValid(String phoneNumber, ConstraintValidatorContext context) {
|
||||
// 校验逻辑:正则表达式判断手机号是否为 11 位数字
|
||||
return phoneNumber != null && phoneNumber.matches("\\d{11}");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(PhoneNumber constraintAnnotation) {
|
||||
ConstraintValidator.super.initialize(constraintAnnotation);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.DateTimeParseException;
|
||||
|
||||
/**
|
||||
* 时间戳工具类 - 用于获取指定时间的时间戳(毫秒级)
|
||||
* 基于Java 8+ java.time包实现,线程安全、支持时区、异常友好
|
||||
*/
|
||||
public class TimestampUtils {
|
||||
|
||||
// ====================== 常量定义 ======================
|
||||
/** 默认时间格式:yyyy-MM-dd HH:mm:ss */
|
||||
public static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
|
||||
|
||||
/** 系统默认时区(可根据需求修改,如ZoneId.of("UTC")、ZoneId.of("Asia/Shanghai")) */
|
||||
public static final ZoneId DEFAULT_ZONE_ID = ZoneId.systemDefault();
|
||||
|
||||
// ====================== 方法1:通过年月日时分秒获取时间戳 ======================
|
||||
/**
|
||||
* 获取指定年月日时分秒的时间戳(毫秒级),使用系统默认时区
|
||||
* @param year 年(如2024)
|
||||
* @param month 月(1-12)
|
||||
* @param day 日(1-31)
|
||||
* @param hour 时(0-23)
|
||||
* @param minute 分(0-59)
|
||||
* @param second 秒(0-59)
|
||||
* @return 对应时间的毫秒级时间戳
|
||||
* @throws IllegalArgumentException 时间参数不合法时抛出
|
||||
*/
|
||||
public static long getTimestamp(int year, int month, int day, int hour, int minute, int second) {
|
||||
return getTimestamp(year, month, day, hour, minute, second, DEFAULT_ZONE_ID);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定年月日时分秒的时间戳(毫秒级),支持指定时区
|
||||
* @param year 年(如2024)
|
||||
* @param month 月(1-12)
|
||||
* @param day 日(1-31)
|
||||
* @param hour 时(0-23)
|
||||
* @param minute 分(0-59)
|
||||
* @param second 秒(0-59)
|
||||
* @param zoneId 时区(如ZoneId.of("Asia/Shanghai"))
|
||||
* @return 对应时间的毫秒级时间戳
|
||||
* @throws IllegalArgumentException 时间参数不合法时抛出
|
||||
*/
|
||||
public static long getTimestamp(int year, int month, int day, int hour, int minute, int second, ZoneId zoneId) {
|
||||
try {
|
||||
// 构建指定时间的LocalDateTime
|
||||
LocalDateTime localDateTime = LocalDateTime.of(year, month, day, hour, minute, second);
|
||||
// 转换为指定时区的ZonedDateTime,再获取时间戳
|
||||
ZonedDateTime zonedDateTime = localDateTime.atZone(zoneId);
|
||||
return zonedDateTime.toInstant().toEpochMilli();
|
||||
} catch (Exception e) {
|
||||
throw new IllegalArgumentException("时间参数不合法:" + year + "-" + month + "-" + day + " " + hour + ":" + minute + ":" + second, e);
|
||||
}
|
||||
}
|
||||
|
||||
// ====================== 方法2:解析时间字符串获取时间戳 ======================
|
||||
/**
|
||||
* 解析指定格式的时间字符串为时间戳(毫秒级),使用系统默认时区
|
||||
* @param timeStr 时间字符串(如"2024-01-01 12:00:00")
|
||||
* @param formatter 时间格式化器(如DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))
|
||||
* @return 对应时间的毫秒级时间戳
|
||||
* @throws DateTimeParseException 时间字符串格式不匹配时抛出
|
||||
*/
|
||||
public static long getTimestamp(String timeStr, DateTimeFormatter formatter) {
|
||||
return getTimestamp(timeStr, formatter, DEFAULT_ZONE_ID);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析默认格式(yyyy-MM-dd HH:mm:ss)的时间字符串为时间戳(毫秒级)
|
||||
* @param timeStr 时间字符串(如"2024-01-01 12:00:00")
|
||||
* @return 对应时间的毫秒级时间戳
|
||||
* @throws DateTimeParseException 时间字符串格式不匹配时抛出
|
||||
*/
|
||||
public static long getTimestamp(String timeStr) {
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT);
|
||||
return getTimestamp(timeStr, formatter, DEFAULT_ZONE_ID);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析指定格式的时间字符串为时间戳(毫秒级),支持指定时区
|
||||
* @param timeStr 时间字符串(如"2024-01-01 12:00:00")
|
||||
* @param formatter 时间格式化器
|
||||
* @param zoneId 时区
|
||||
* @return 对应时间的毫秒级时间戳
|
||||
* @throws DateTimeParseException 时间字符串格式不匹配时抛出
|
||||
*/
|
||||
public static long getTimestamp(String timeStr, DateTimeFormatter formatter, ZoneId zoneId) {
|
||||
try {
|
||||
// 解析时间字符串为LocalDateTime
|
||||
LocalDateTime localDateTime = LocalDateTime.parse(timeStr, formatter);
|
||||
// 转换为指定时区的ZonedDateTime,再获取时间戳
|
||||
ZonedDateTime zonedDateTime = localDateTime.atZone(zoneId);
|
||||
return zonedDateTime.toInstant().toEpochMilli();
|
||||
} catch (DateTimeParseException e) {
|
||||
throw new DateTimeParseException("时间字符串格式解析失败:" + timeStr + ",期望格式:" + formatter.toString(),
|
||||
e.getParsedString(), e.getErrorIndex(), e);
|
||||
}
|
||||
}
|
||||
|
||||
// ====================== 测试用例 ======================
|
||||
public static void main(String[] args) {
|
||||
// 测试方法1:指定年月日时分秒获取时间戳
|
||||
long timestamp1 = TimestampUtils.getTimestamp(2024, 1, 1, 0, 0, 0);
|
||||
System.out.println("2024-01-01 00:00:00 的时间戳:" + timestamp1); // 输出:1704067200000
|
||||
|
||||
// 测试方法2:指定时区(UTC)获取时间戳
|
||||
long timestamp2 = TimestampUtils.getTimestamp(2024, 1, 1, 0, 0, 0, ZoneId.of("UTC"));
|
||||
System.out.println("2024-01-01 00:00:00 (UTC) 的时间戳:" + timestamp2); // 输出:1704038400000
|
||||
|
||||
// 测试方法3:解析默认格式的时间字符串
|
||||
long timestamp3 = TimestampUtils.getTimestamp("2026-01-01 00:00:00");
|
||||
System.out.println("2026-01-01 00:00:00 的时间戳:" + timestamp3);
|
||||
|
||||
// 测试方法4:解析自定义格式的时间字符串
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm");
|
||||
long timestamp4 = TimestampUtils.getTimestamp("2024/06/01 12:30", formatter);
|
||||
System.out.println("2024/06/01 12:30 的时间戳:" + timestamp4);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<?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>
|
||||
<parent>
|
||||
<groupId>top.crushtj</groupId>
|
||||
<artifactId>xiaoyi-framework</artifactId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<artifactId>xiaoyi-spring-boot-starter-biz-operationlog</artifactId>
|
||||
<name>${project.artifactId}</name>
|
||||
<description>平台基础设施层:操作日志记录器</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>top.crushtj</groupId>
|
||||
<artifactId>xiaoyi-common</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
package top.crushtj.framework.biz.operationlog;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.annotation.Around;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.aspectj.lang.annotation.Pointcut;
|
||||
import org.aspectj.lang.reflect.MethodSignature;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import top.crushtj.framework.biz.operationlog.aspect.ApiOperationLog;
|
||||
import top.crushtj.framework.common.utils.JsonUtils;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author ayi
|
||||
* @title ApiOperationLog
|
||||
* @description 自定义注解,用于标记需要记录操作日志的方法
|
||||
* @date 2025/11/21
|
||||
*/
|
||||
|
||||
@Slf4j
|
||||
@Aspect
|
||||
public class ApiOperationLogAspect {
|
||||
/** 以自定义 @ApiOperationLog 注解为切点,凡是添加 @ApiOperationLog 的方法,都会执行环绕中的代码 */
|
||||
@Pointcut("@annotation(top.crushtj.framework.biz.operationlog.aspect.ApiOperationLog)")
|
||||
public void apiOperationLog() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 环绕
|
||||
*
|
||||
* @param joinPoint 连接点
|
||||
* @return 方法执行结果
|
||||
* @throws Throwable 方法执行过程中抛出的异常
|
||||
*/
|
||||
@Around("apiOperationLog()")
|
||||
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
|
||||
// 请求开始时间
|
||||
long startTime = System.currentTimeMillis();
|
||||
|
||||
// 获取被请求的类和方法
|
||||
String className = joinPoint.getTarget().getClass().getSimpleName();
|
||||
String methodName = joinPoint.getSignature().getName();
|
||||
|
||||
// 请求入参
|
||||
Object[] args = joinPoint.getArgs();
|
||||
// 入参转 JSON 字符串
|
||||
String argsJsonStr = Arrays.stream(args).map(toJsonStr()).collect(Collectors.joining(", "));
|
||||
|
||||
// 功能描述信息
|
||||
String description = getApiOperationLogDescription(joinPoint);
|
||||
|
||||
// 打印请求相关参数
|
||||
log.info("\n\n请求开始: [{}], 请求参数: {}, 请求类: {}, 请求方法: {}\n", description, argsJsonStr, className,
|
||||
methodName);
|
||||
|
||||
// 执行切点方法
|
||||
Object result = joinPoint.proceed();
|
||||
|
||||
// 执行耗时
|
||||
long executionTime = System.currentTimeMillis() - startTime;
|
||||
|
||||
// 打印出参等相关信息
|
||||
log.info("\n\n请求结束: [{}], 耗时: {}ms, 响应参数: {}\n", description, executionTime,
|
||||
JsonUtils.toJsonString(result));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取注解的描述信息
|
||||
*
|
||||
* @param joinPoint 连接点
|
||||
* @return 注解的描述信息
|
||||
*/
|
||||
private String getApiOperationLogDescription(ProceedingJoinPoint joinPoint) {
|
||||
// 1. 从 ProceedingJoinPoint 获取 MethodSignature
|
||||
MethodSignature signature = (MethodSignature)joinPoint.getSignature();
|
||||
|
||||
// 2. 使用 MethodSignature 获取当前被注解的 Method
|
||||
Method method = signature.getMethod();
|
||||
|
||||
// 3. 从 Method 中提取 LogExecution 注解
|
||||
ApiOperationLog apiOperationLog = method.getAnnotation(ApiOperationLog.class);
|
||||
|
||||
// 4. 从 LogExecution 注解中获取 description 属性
|
||||
return apiOperationLog.description();
|
||||
}
|
||||
|
||||
/**
|
||||
* 转 JSON 字符串
|
||||
*
|
||||
* @return 入参的 JSON 字符串
|
||||
*/
|
||||
private Function<Object, String> toJsonStr() {
|
||||
return JsonUtils::toJsonString;
|
||||
}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
package top.crushtj.framework.biz.operationlog.aspect;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author ayi
|
||||
* @title ApiOperationLog
|
||||
* @description 自定义注解,用于标记需要记录操作日志的方法
|
||||
* @date 2025/11/21
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.METHOD})
|
||||
@Documented
|
||||
public @interface ApiOperationLog {
|
||||
/**
|
||||
* API 功能描述
|
||||
*
|
||||
* @return 功能描述
|
||||
*/
|
||||
String description() default "";
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
package top.crushtj.framework.biz.operationlog.config;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
import top.crushtj.framework.biz.operationlog.ApiOperationLogAspect;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author ayi
|
||||
* @title ApiOperationLog
|
||||
* @description 自定义注解,用于标记需要记录操作日志的方法
|
||||
* @date 2025/11/21
|
||||
*/
|
||||
|
||||
@AutoConfiguration
|
||||
public class ApiOperationLogAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
public ApiOperationLogAspect apiOperationLogAspect() {
|
||||
return new ApiOperationLogAspect();
|
||||
}
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
top.crushtj.framework.biz.operationlog.config.ApiOperationLogAutoConfiguration
|
||||
@@ -0,0 +1,28 @@
|
||||
<?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>
|
||||
<parent>
|
||||
<groupId>top.crushtj</groupId>
|
||||
<artifactId>xiaoyi-framework</artifactId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
|
||||
<packaging>jar</packaging>
|
||||
<artifactId>xiaoyi-spring-boot-starter-jackson</artifactId>
|
||||
<name>${project.artifactId}</name>
|
||||
<description>平台基础设施层:Jackson 配置</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>top.crushtj</groupId>
|
||||
<artifactId>xiaoyi-common</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-autoconfigure</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
package top.crushtj.framework.jackson;
|
||||
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
||||
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
|
||||
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
|
||||
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
|
||||
import com.fasterxml.jackson.datatype.jsr310.deser.YearMonthDeserializer;
|
||||
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
|
||||
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
|
||||
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
|
||||
import com.fasterxml.jackson.datatype.jsr310.ser.YearMonthSerializer;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import top.crushtj.framework.common.constant.DateConstants;
|
||||
import top.crushtj.framework.common.utils.JsonUtils;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.time.YearMonth;
|
||||
import java.util.TimeZone;
|
||||
|
||||
/**
|
||||
* @author ayi
|
||||
* @version V1.0
|
||||
* @title JacksonConfig
|
||||
* @description jackson 配置类
|
||||
* @date 2026/01/06
|
||||
*/
|
||||
|
||||
@AutoConfiguration
|
||||
@AutoConfigureBefore(org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration.class)
|
||||
public class JacksonAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
public ObjectMapper objectMapper() {
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
// 忽略未知属性
|
||||
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||
objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
|
||||
|
||||
// 设置凡是为 null 的字段,返参中均不返回,请根据项目组约定是否开启
|
||||
// objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
|
||||
|
||||
// 设置时区
|
||||
objectMapper.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai"));
|
||||
|
||||
// JavaTimeModule 用于指定序列化和反序列化规则
|
||||
JavaTimeModule javaTimeModule = new JavaTimeModule();
|
||||
|
||||
// 支持 LocalDateTime、LocalDate、LocalTime
|
||||
// 支持 LocalDateTime、LocalDate、LocalTime
|
||||
javaTimeModule.addSerializer(LocalDateTime.class,
|
||||
new LocalDateTimeSerializer(DateConstants.DATE_FORMAT_Y_M_D_H_M_S));
|
||||
javaTimeModule.addDeserializer(LocalDateTime.class,
|
||||
new LocalDateTimeDeserializer(DateConstants.DATE_FORMAT_Y_M_D_H_M_S));
|
||||
javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateConstants.DATE_FORMAT_Y_M_D));
|
||||
javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateConstants.DATE_FORMAT_Y_M_D));
|
||||
javaTimeModule.addSerializer(LocalTime.class, new LocalTimeSerializer(DateConstants.DATE_FORMAT_H_M_S));
|
||||
javaTimeModule.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateConstants.DATE_FORMAT_H_M_S));
|
||||
// 支持 YearMonth
|
||||
javaTimeModule.addSerializer(YearMonth.class, new YearMonthSerializer(DateConstants.DATE_FORMAT_Y_M));
|
||||
javaTimeModule.addDeserializer(YearMonth.class, new YearMonthDeserializer(DateConstants.DATE_FORMAT_Y_M));
|
||||
|
||||
objectMapper.registerModule(javaTimeModule);
|
||||
|
||||
JsonUtils.init(objectMapper);
|
||||
|
||||
return objectMapper;
|
||||
}
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
top.crushtj.framework.jackson.JacksonAutoConfiguration
|
||||
@@ -0,0 +1,93 @@
|
||||
<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>
|
||||
<parent>
|
||||
<groupId>top.crushtj</groupId>
|
||||
<artifactId>xiaoyishu</artifactId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>xiaoyi-gateway</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>${project.artifactId}</name>
|
||||
<description>网关服务</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>top.crushtj</groupId>
|
||||
<artifactId>xiaoyi-common</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-bootstrap</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 服务发现 -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 网关 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-gateway</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 负载均衡 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Sa-Token 权限认证,在线文档:https://sa-token.cc -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-reactor-spring-boot3-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Sa-Token 整合 Redis (使用 jackson 序列化方式) -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-redis-jackson</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 提供Redis连接池 -->
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-pool2</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- jasypt 加密工具 -->
|
||||
<dependency>
|
||||
<groupId>com.github.ulisesbocchio</groupId>
|
||||
<artifactId>jasypt-spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Redis -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Jackson 组件 -->
|
||||
<dependency>
|
||||
<groupId>top.crushtj</groupId>
|
||||
<artifactId>xiaoyi-spring-boot-starter-jackson</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -0,0 +1,20 @@
|
||||
package top.crushtj.xiaoyi.gateway;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
/**
|
||||
* @author ayi
|
||||
* @version V1.0
|
||||
* @title XiaoyiGatewayApplication
|
||||
* @date 2026/2/2 16:46
|
||||
* @description 网关启动类
|
||||
*/
|
||||
|
||||
@SpringBootApplication
|
||||
public class XiaoyiGatewayApplication {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(XiaoyiGatewayApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package top.crushtj.xiaoyi.gateway.auth;
|
||||
|
||||
import cn.dev33.satoken.exception.NotLoginException;
|
||||
import cn.dev33.satoken.exception.NotPermissionException;
|
||||
import cn.dev33.satoken.exception.NotRoleException;
|
||||
import cn.dev33.satoken.reactor.filter.SaReactorFilter;
|
||||
import cn.dev33.satoken.router.SaRouter;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* @author ayi
|
||||
* @version V1.0
|
||||
* @title SaTokenConfigure
|
||||
* @date 2026/2/2 17:16
|
||||
* @description SaToken配置类
|
||||
*/
|
||||
|
||||
@Configuration
|
||||
public class SaTokenConfigure {
|
||||
// 注册 Sa-Token全局过滤器
|
||||
@Bean
|
||||
public SaReactorFilter getSaReactorFilter() {
|
||||
return new SaReactorFilter()
|
||||
// 拦截地址
|
||||
.addInclude("/**") /* 拦截全部path */
|
||||
// 鉴权方法:每次访问进入
|
||||
.setAuth(obj -> {
|
||||
// 登录校验
|
||||
SaRouter.match("/**") // 拦截所有路由
|
||||
.notMatch("/auth/user/login") // 排除登录接口
|
||||
.notMatch("/auth/verification/code/send") // 排除验证码发送接口
|
||||
.check(r -> StpUtil.checkLogin()) // 校验是否登录
|
||||
;
|
||||
// SaRouter.match("/auth/user/logout", r -> StpUtil.checkPermission("user"));
|
||||
// 权限认证 -- 不同模块, 校验不同权限
|
||||
// SaRouter.match("/user/**", r -> StpUtil.checkPermission("user"));
|
||||
// SaRouter.match("/admin/**", r -> StpUtil.checkPermission("admin"));
|
||||
// SaRouter.match("/goods/**", r -> StpUtil.checkPermission("goods"));
|
||||
// SaRouter.match("/orders/**", r -> StpUtil.checkPermission("orders"));
|
||||
|
||||
// 更多匹配 ... */
|
||||
})
|
||||
.setError(e -> {
|
||||
if (e instanceof NotLoginException) { // 未登录异常
|
||||
throw new NotLoginException(e.getMessage(), null, null);
|
||||
} else if (e instanceof NotPermissionException || e instanceof NotRoleException) { // 权限不足,或不具备角色,统一抛出权限不足异常
|
||||
throw new NotPermissionException(e.getMessage());
|
||||
} else { // 其他异常,则抛出一个运行时异常
|
||||
throw new RuntimeException(e.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
package top.crushtj.xiaoyi.gateway.auth;
|
||||
|
||||
import cn.dev33.satoken.stp.StpInterface;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.google.common.collect.Lists;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
import top.crushtj.xiaoyi.gateway.constant.RedisKeyConstants;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @author ayi
|
||||
* @version V1.0
|
||||
* @title StpInterfaceImpl
|
||||
* @date 2026/2/2 17:14
|
||||
* @description 自定义权限验证接口
|
||||
*/
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
public class StpInterfaceImpl implements StpInterface {
|
||||
|
||||
@Resource
|
||||
private RedisTemplate<String, String> redisTemplate;
|
||||
@Resource
|
||||
private ObjectMapper objectMapper;
|
||||
|
||||
@SneakyThrows
|
||||
@Override
|
||||
public List<String> getPermissionList(Object loginId, String loginType) {
|
||||
log.info("## 获取用户权限列表, loginId: {}", loginId);
|
||||
// 构建 用户-角色 Redis Key
|
||||
String userRolesKey = RedisKeyConstants.buildUserRoleKey(Long.valueOf(loginId.toString()));
|
||||
|
||||
// 根据用户 ID ,从 Redis 中获取该用户的角色集合
|
||||
String useRolesValue = redisTemplate.opsForValue()
|
||||
.get(userRolesKey);
|
||||
|
||||
if (StringUtils.isBlank(useRolesValue)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 将 JSON 字符串转换为 List<String> 角色集合
|
||||
List<String> userRoleKeys = objectMapper.readValue(useRolesValue, new TypeReference<>() {
|
||||
});
|
||||
|
||||
if (CollUtil.isNotEmpty(userRoleKeys)) {
|
||||
// 查询这些角色对应的权限
|
||||
// 构建 角色-权限 Redis Key 集合
|
||||
List<String> rolePermissionsKeys = userRoleKeys.stream()
|
||||
.map(RedisKeyConstants::buildRolePermissionsKey)
|
||||
.toList();
|
||||
|
||||
// 通过 key 集合批量查询权限,提升查询性能。
|
||||
List<String> rolePermissionsValues = Objects.requireNonNull(redisTemplate.opsForValue()
|
||||
.multiGet(rolePermissionsKeys))
|
||||
.stream()
|
||||
.filter(Objects::nonNull)
|
||||
.toList();
|
||||
|
||||
if (CollUtil.isNotEmpty(rolePermissionsValues)) {
|
||||
List<String> permissions = Lists.newArrayList();
|
||||
|
||||
// 遍历所有角色的权限集合,统一添加到 permissions 集合中
|
||||
rolePermissionsValues.forEach(jsonValue -> {
|
||||
try {
|
||||
// 将 JSON 字符串转换为 List<String> 权限集合
|
||||
List<String> rolePermissions = objectMapper.readValue(jsonValue, new TypeReference<>() {
|
||||
});
|
||||
permissions.addAll(rolePermissions);
|
||||
} catch (JsonProcessingException e) {
|
||||
log.error("==> JSON 解析错误: ", e);
|
||||
}
|
||||
});
|
||||
|
||||
// 返回此用户所拥有的权限
|
||||
return permissions;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
@Override
|
||||
public List<String> getRoleList(Object loginId, String loginType) {
|
||||
log.info("## 获取用户角色列表, loginId: {}", loginId);
|
||||
String userRolesKey = RedisKeyConstants.buildUserRoleKey(Long.valueOf(loginId.toString()));
|
||||
String useRolesValue = redisTemplate.opsForValue()
|
||||
.get(userRolesKey);
|
||||
if (StringUtils.isBlank(useRolesValue)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 将 JSON 字符串转换为 List<String> 集合
|
||||
return objectMapper.readValue(useRolesValue, new TypeReference<>() {
|
||||
});
|
||||
}
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
package top.crushtj.xiaoyi.gateway.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
|
||||
import org.springframework.data.redis.serializer.StringRedisSerializer;
|
||||
import org.springframework.lang.NonNull;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author ayi
|
||||
* @version V1.0
|
||||
* @title RedisTemplateConfig
|
||||
* @description Redis 配置类
|
||||
* @date 2026/01/12 19:13
|
||||
*/
|
||||
|
||||
@Configuration
|
||||
public class RedisTemplateConfig {
|
||||
|
||||
@Bean
|
||||
public RedisTemplate<String, Object> redisTemplate(@NonNull RedisConnectionFactory connectionFactory) {
|
||||
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
|
||||
// 设置 RedisTemplate 的连接工厂
|
||||
redisTemplate.setConnectionFactory(connectionFactory);
|
||||
|
||||
// 使用 StringRedisSerializer 来序列化和反序列化 redis 的 key 值,确保 key 是可读的字符串
|
||||
redisTemplate.setKeySerializer(new StringRedisSerializer());
|
||||
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
|
||||
|
||||
// 使用 Jackson2JsonRedisSerializer 来序列化和反序列化 redis 的 value 值, 确保存储的是 JSON 格式
|
||||
Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class);
|
||||
redisTemplate.setValueSerializer(serializer);
|
||||
redisTemplate.setHashValueSerializer(serializer);
|
||||
|
||||
redisTemplate.afterPropertiesSet();
|
||||
return redisTemplate;
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
package top.crushtj.xiaoyi.gateway.constant;
|
||||
|
||||
/**
|
||||
* @author ayi
|
||||
* @version V1.0
|
||||
* @title RedisKeyConstants
|
||||
* @date 2026/2/3 15:24
|
||||
* @description Redis key 常量
|
||||
*/
|
||||
|
||||
public class RedisKeyConstants {
|
||||
|
||||
/**
|
||||
* 用户对应角色集合 KEY 前缀
|
||||
*/
|
||||
private static final String USER_ROLES_KEY_PREFIX = "user:roles:";
|
||||
|
||||
/**
|
||||
* 角色对应的权限集合 KEY 前缀
|
||||
*/
|
||||
private static final String ROLE_PERMISSIONS_KEY_PREFIX = "role:permissions:";
|
||||
|
||||
/**
|
||||
* 构建角色对应的权限集合 KEY
|
||||
*
|
||||
* @param roleKey 角色 key
|
||||
* @return 角色对应的权限集合 KEY
|
||||
*/
|
||||
public static String buildRolePermissionsKey(String roleKey) {
|
||||
return ROLE_PERMISSIONS_KEY_PREFIX + roleKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建用户-角色 KEY
|
||||
*
|
||||
* @param userId 用户 ID
|
||||
* @return 用户-角色 KEY
|
||||
*/
|
||||
public static String buildUserRoleKey(Long userId) {
|
||||
return USER_ROLES_KEY_PREFIX + userId;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package top.crushtj.xiaoyi.gateway.enums;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import top.crushtj.framework.common.exception.BaseExceptionInterface;
|
||||
|
||||
/**
|
||||
* @author ayi
|
||||
* @version V1.0
|
||||
* @title ResponseCodeEnum
|
||||
* @date 2026/2/3 16:15
|
||||
* @description 异常状态吗枚举
|
||||
*/
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum ResponseCodeEnum implements BaseExceptionInterface {
|
||||
// ----------- 通用异常状态码 -----------
|
||||
SYSTEM_ERROR("500", "系统繁忙,请稍后再试"),
|
||||
UNAUTHORIZED("401", "没有权限"),
|
||||
|
||||
// ----------- 业务异常状态码 -----------
|
||||
;
|
||||
|
||||
// 异常码
|
||||
private final String errorCode;
|
||||
// 错误信息
|
||||
private final String errorMessage;
|
||||
}
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
package top.crushtj.xiaoyi.gateway.exception;
|
||||
|
||||
import cn.dev33.satoken.exception.NotLoginException;
|
||||
import cn.dev33.satoken.exception.NotPermissionException;
|
||||
import cn.dev33.satoken.exception.SaTokenException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler;
|
||||
import org.springframework.core.io.buffer.DataBufferFactory;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.server.reactive.ServerHttpResponse;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import reactor.core.publisher.Mono;
|
||||
import top.crushtj.framework.common.response.Response;
|
||||
import top.crushtj.xiaoyi.gateway.enums.ResponseCodeEnum;
|
||||
|
||||
/**
|
||||
* @author ayi
|
||||
* @version V1.0
|
||||
* @title GlobalExceptionHandler
|
||||
* @date 2026/2/3 16:17
|
||||
* @description 全局异常处理
|
||||
*/
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
public class GlobalExceptionHandler implements ErrorWebExceptionHandler {
|
||||
|
||||
@Resource
|
||||
private ObjectMapper objectMapper;
|
||||
|
||||
@Override
|
||||
public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
|
||||
// 获取响应对象
|
||||
ServerHttpResponse response = exchange.getResponse();
|
||||
|
||||
log.error("==> 全局异常捕获: ", ex);
|
||||
|
||||
// 响参
|
||||
Response<?> result;
|
||||
// 根据捕获的异常类型,设置不同的响应状态码和响应消息
|
||||
if (ex instanceof SaTokenException) { // Sa-Token 异常
|
||||
// 权限认证失败时,设置 401 状态码
|
||||
response.setStatusCode(HttpStatus.UNAUTHORIZED);
|
||||
if (ex instanceof NotLoginException){
|
||||
// 构建响应结果
|
||||
result = Response.failure(ResponseCodeEnum.UNAUTHORIZED.getErrorCode(),
|
||||
ex.getMessage());
|
||||
}else if (ex instanceof NotPermissionException) {
|
||||
result = Response.failure(ResponseCodeEnum.UNAUTHORIZED.getErrorCode(),
|
||||
ResponseCodeEnum.UNAUTHORIZED.getErrorMessage());
|
||||
} else {
|
||||
result = Response.failure(ResponseCodeEnum.SYSTEM_ERROR);
|
||||
}
|
||||
|
||||
} else { // 其他异常,则统一提示 “系统繁忙” 错误
|
||||
result = Response.failure(ResponseCodeEnum.SYSTEM_ERROR);
|
||||
}
|
||||
|
||||
// 设置响应头的内容类型为 application/json;charset=UTF-8,表示响应体为 JSON 格式
|
||||
response.getHeaders()
|
||||
.setContentType(MediaType.APPLICATION_JSON_UTF8);
|
||||
// 设置 body 响应体
|
||||
return response.writeWith(Mono.fromSupplier(() -> { // 使用 Mono.fromSupplier 创建响应体
|
||||
DataBufferFactory bufferFactory = response.bufferFactory();
|
||||
try {
|
||||
// 使用 ObjectMapper 将 result 对象转换为 JSON 字节数组
|
||||
return bufferFactory.wrap(objectMapper.writeValueAsBytes(result));
|
||||
} catch (Exception e) {
|
||||
// 如果转换过程中出现异常,则返回空字节数组
|
||||
return bufferFactory.wrap(new byte[0]);
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
package top.crushtj.xiaoyi.gateway.filter;
|
||||
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
|
||||
import org.springframework.cloud.gateway.filter.GlobalFilter;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* @author ayi
|
||||
* @version V1.0
|
||||
* @title AddUserId2HeaderFilter
|
||||
* @date 2026/2/4 15:28
|
||||
* @description 添加用户ID到请求头过滤器
|
||||
*/
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
public class AddUserId2HeaderFilter implements GlobalFilter {
|
||||
|
||||
private static final String HEADER_USER_ID = "userId";
|
||||
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
|
||||
Long userId = null;
|
||||
try {
|
||||
userId = StpUtil.getLoginIdAsLong();
|
||||
} catch (Exception e) {
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
Long finalUserId = userId;
|
||||
ServerWebExchange newExchange = exchange.mutate()
|
||||
.request(builder -> builder.header(HEADER_USER_ID, String.valueOf(finalUserId)))
|
||||
.build();
|
||||
|
||||
return chain.filter(newExchange);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
server:
|
||||
port: 8000 # 指定启动端口
|
||||
spring:
|
||||
cloud:
|
||||
gateway:
|
||||
routes:
|
||||
- id: auth
|
||||
uri: lb://xiaoyishu-auth
|
||||
predicates:
|
||||
- Path=/auth/**
|
||||
filters:
|
||||
- StripPrefix=1
|
||||
data:
|
||||
redis:
|
||||
database: 0 # Redis 数据库索引(默认为 0)
|
||||
host: ENC(C1TWXF+/HzWQBF25uXCdy/0fHoRDXdCW72+NKCIJURg4l3IDnJzl278KmFhfsusX) # Redis 服务器地址
|
||||
port: 6379 # Redis 服务器连接端口
|
||||
password: ENC(iK/k0IGPflACqYMUwX4N/sGvCVuysYywLcAO+Ikeqk326V8hCr8dgEGzkiEIwWOo) # Redis 服务器连接密码(默认为空)
|
||||
timeout: 5s # 读超时时间
|
||||
connect-timeout: 5s # 链接超时时间
|
||||
lettuce:
|
||||
pool:
|
||||
max-active: 200 # 连接池最大连接数
|
||||
max-wait: -1ms # 连接池最大阻塞等待时间(使用负值表示没有限制)
|
||||
min-idle: 0 # 连接池中的最小空闲连接
|
||||
max-idle: 10 # 连接池中的最大空闲连接
|
||||
|
||||
############## Sa-Token 配置 ##############
|
||||
sa-token:
|
||||
# token 名称(同时也是 cookie 名称)
|
||||
token-name: Authorization
|
||||
token-prefix: Bearer
|
||||
# token 有效期(单位:秒) 默认30天,-1 代表永久有效
|
||||
timeout: 2592000
|
||||
# timeout: 60
|
||||
# token 最低活跃频率(单位:秒),如果 token 超过此时间没有访问系统就会被冻结,默认-1 代表不限制,永不冻结
|
||||
active-timeout: -1
|
||||
# 是否允许同一账号多地同时登录 (为 true 时允许一起登录, 为 false 时新登录挤掉旧登录)
|
||||
is-concurrent: true
|
||||
# 在多人登录同一账号时,是否共用一个 token (为 true 时所有登录共用一个 token, 为 false 时每次登录新建一个 token)
|
||||
is-share: true
|
||||
# token 风格(默认可取值:uuid、simple-uuid、random-32、random-64、random-128、tik)
|
||||
token-style: random-128
|
||||
# 是否输出操作日志
|
||||
is-log: true
|
||||
|
||||
jasypt:
|
||||
encryptor:
|
||||
password:
|
||||
algorithm: PBEWithHMACSHA512AndAES_256
|
||||
key-obtention-iterations: 1000
|
||||
string-output-type: base64
|
||||
provider-name: SunJCE
|
||||
iv-generator-classname: org.jasypt.iv.RandomIvGenerator
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user