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
|
.kotlin
|
||||||
|
|
||||||
### IntelliJ IDEA ###
|
### IntelliJ IDEA ###
|
||||||
.idea/modules.xml
|
.idea/
|
||||||
.idea/jarRepositories.xml
|
|
||||||
.idea/compiler.xml
|
|
||||||
.idea/libraries/
|
|
||||||
*.iws
|
*.iws
|
||||||
*.iml
|
*.iml
|
||||||
*.ipr
|
*.ipr
|
||||||
@@ -18,7 +15,6 @@ target/
|
|||||||
.classpath
|
.classpath
|
||||||
.factorypath
|
.factorypath
|
||||||
.project
|
.project
|
||||||
.settings
|
|
||||||
.springBeans
|
.springBeans
|
||||||
.sts4-cache
|
.sts4-cache
|
||||||
|
|
||||||
@@ -35,3 +31,7 @@ build/
|
|||||||
|
|
||||||
### Mac OS ###
|
### Mac OS ###
|
||||||
.DS_Store
|
.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,
|
"maven.executable.options": "-s D:\\Programs\\Dev\\maven\\conf\\settings-aliyun.xml -DskipTests",
|
||||||
"files.autoSave": "onFocusChange",
|
"java.format.settings.url": ".vscode/java-formatter.xml",
|
||||||
"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",
|
|
||||||
"java.compile.nullAnalysis.mode": "automatic",
|
"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"
|
||||||
}
|
}
|
||||||
@@ -19,6 +19,7 @@
|
|||||||
<modules>
|
<modules>
|
||||||
<module>xiaoyi-auth</module>
|
<module>xiaoyi-auth</module>
|
||||||
<module>xiaoyi-framework</module>
|
<module>xiaoyi-framework</module>
|
||||||
|
<module>xiaoyi-gateway</module>
|
||||||
</modules>
|
</modules>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
@@ -38,11 +39,42 @@
|
|||||||
<spring-boot.version>3.0.2</spring-boot.version>
|
<spring-boot.version>3.0.2</spring-boot.version>
|
||||||
<spring-cloud-alibaba.version>2022.0.0.0</spring-cloud-alibaba.version>
|
<spring-cloud-alibaba.version>2022.0.0.0</spring-cloud-alibaba.version>
|
||||||
<spring-cloud.version>2022.0.0</spring-cloud.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>
|
</properties>
|
||||||
|
|
||||||
<!-- 统一依赖管理 -->
|
<!-- 统一依赖管理 -->
|
||||||
<dependencyManagement>
|
<dependencyManagement>
|
||||||
<dependencies>
|
<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>
|
||||||
|
|
||||||
|
<!-- 业务接口日志组件 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>top.crushtj</groupId>
|
||||||
|
<artifactId>xiaoyi-spring-boot-starter-jackson</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</dependency>
|
||||||
<!-- Spring Boot 官方依赖管理 -->
|
<!-- Spring Boot 官方依赖管理 -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
@@ -77,6 +109,100 @@
|
|||||||
<version>${lombok.version}</version>
|
<version>${lombok.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Jackson -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.core</groupId>
|
||||||
|
<artifactId>jackson-databind</artifactId>
|
||||||
|
<version>${jackson.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<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>
|
</dependencies>
|
||||||
</dependencyManagement>
|
</dependencyManagement>
|
||||||
|
|
||||||
@@ -116,6 +242,33 @@
|
|||||||
</plugin>
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
</pluginManagement>
|
</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>
|
</build>
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,21 @@
|
|||||||
<description>小壹书:认证服务(负责处理用户登录、注册、账号注销等)</description>
|
<description>小壹书:认证服务(负责处理用户登录、注册、账号注销等)</description>
|
||||||
|
|
||||||
<dependencies>
|
<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>
|
||||||
|
|
||||||
|
<!--Jackson-->
|
||||||
|
<dependency>
|
||||||
|
<groupId>top.crushtj</groupId>
|
||||||
|
<artifactId>xiaoyi-spring-boot-starter-jackson</artifactId>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-web</artifactId>
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
@@ -28,6 +43,79 @@
|
|||||||
<artifactId>spring-boot-starter-test</artifactId>
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</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>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
@@ -37,6 +125,18 @@
|
|||||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||||
</plugin>
|
</plugin>
|
||||||
</plugins>
|
</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>
|
</build>
|
||||||
|
|
||||||
</project>
|
</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() {
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -16,7 +16,12 @@
|
|||||||
<description>平台基础设施层:封装一些常用功能,供各个业务线拿来即用</description>
|
<description>平台基础设施层:封装一些常用功能,供各个业务线拿来即用</description>
|
||||||
|
|
||||||
<modules>
|
<modules>
|
||||||
|
<!--通用-->
|
||||||
<module>xiaoyi-common</module>
|
<module>xiaoyi-common</module>
|
||||||
|
<!--日志切面-->
|
||||||
|
<module>xiaoyi-spring-boot-starter-biz-operationlog</module>
|
||||||
|
<!--Jackson-->
|
||||||
|
<module>xiaoyi-spring-boot-starter-jackson</module>
|
||||||
</modules>
|
</modules>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
@@ -20,6 +20,59 @@
|
|||||||
<groupId>org.projectlombok</groupId>
|
<groupId>org.projectlombok</groupId>
|
||||||
<artifactId>lombok</artifactId>
|
<artifactId>lombok</artifactId>
|
||||||
</dependency>
|
</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>
|
</dependencies>
|
||||||
|
|
||||||
</project>
|
</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