仓库初始化

This commit is contained in:
luozhun 2024-08-29 17:06:00 +08:00
commit a5f9731177
244 changed files with 22684 additions and 0 deletions

View File

@ -0,0 +1,11 @@
VITE_APP_NAME=超级后台
VITE_APP_ENV=development
VITE_APP_PORT=1000
VITE_DROP_CONSOLE=false
# axios
VITE_APP_BASE_API=/api
VITE_APP_PROXY_URL=http://localhost:8765
# rsa 公钥
VITE_APP_RSA_PUBLIC_KEY=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDJps/EXxxSpEM1Ix4R0NWIOBciHCr7P7coDT8tNKfelgR7txcJOqHCO/MIWe7T04aHQTcpQxqx9hMca7dbqz8TZpz9jvLzE/6ZonVKxHsoFnNlHMp1/CPAJ9f6D9wYicum2KltJkmQ0g//D9W2zPCYoGOmSRFcZx/KEBa4EM53jQIDAQAB

View File

@ -0,0 +1,8 @@
VITE_APP_NAME=超级后台
VITE_APP_ENV=production
VITE_APP_PORT=1001
VITE_DROP_CONSOLE=true
# axios
VITE_APP_BASE_API=/api
VITE_APP_PROXY_URL=https://172.10.10.238:8765

24
policeManagement/.gitignore vendored Normal file
View File

@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

View File

@ -0,0 +1,3 @@
{
"recommendations": ["Vue.volar"]
}

View File

@ -0,0 +1,5 @@
# Vue 3 + TypeScript + Vite
This template should help get you started developing with Vue 3 and TypeScript in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.
Learn more about the recommended Project Setup and IDE Support in the [Vue Docs TypeScript Guide](https://vuejs.org/guide/typescript/overview.html#project-setup).

View File

@ -0,0 +1,15 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<link rel="icon" type="image/svg+xml" href="/vite.svg"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<script type="module" src="/src/assets/iconfont/iconfont.js"></script>
<title>Vite + Vue + TS</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>

View File

@ -0,0 +1,31 @@
{
"name": "supermanagement",
"appName": "超级后台",
"private": true,
"version": "1.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vue-tsc -b && vite build",
"preview": "vite preview"
},
"dependencies": {
"ant-design-vue": "^4.2.3",
"axios": "^1.7.5",
"jsencrypt": "^3.3.2",
"pinia": "^2.2.2",
"pinia-plugin-persistedstate": "^3.2.0",
"sass": "^1.77.8",
"vue": "^3.4.37",
"vue-router": "4"
},
"devDependencies": {
"@types/node": "^22.5.1",
"@vitejs/plugin-vue": "^5.1.2",
"@vitejs/plugin-vue-jsx": "^4.0.1",
"typescript": "^5.5.3",
"unplugin-vue-components": "^0.27.4",
"vite": "^5.4.1",
"vue-tsc": "^2.0.29"
}
}

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -0,0 +1,22 @@
<template>
<a-config-provider :locale="zhCN">
<a-app>
<a-spin :spinning="false" style="z-index: 1001" :tip="''">
<router-view></router-view>
</a-spin>
</a-app>
</a-config-provider>
</template>
<script setup lang="ts">
import zhCN from 'ant-design-vue/es/locale/zh_CN';
import dayjs from 'dayjs';
import 'dayjs/locale/zh-cn';
import {App as AApp} from 'ant-design-vue'
dayjs.locale('zh-cn');
</script>
<style scoped>
</style>

View File

@ -0,0 +1,539 @@
/* Logo 字体 */
@font-face {
font-family: "iconfont logo";
src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834');
src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834#iefix') format('embedded-opentype'),
url('https://at.alicdn.com/t/font_985780_km7mi63cihi.woff?t=1545807318834') format('woff'),
url('https://at.alicdn.com/t/font_985780_km7mi63cihi.ttf?t=1545807318834') format('truetype'),
url('https://at.alicdn.com/t/font_985780_km7mi63cihi.svg?t=1545807318834#iconfont') format('svg');
}
.logo {
font-family: "iconfont logo";
font-size: 160px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
/* tabs */
.nav-tabs {
position: relative;
}
.nav-tabs .nav-more {
position: absolute;
right: 0;
bottom: 0;
height: 42px;
line-height: 42px;
color: #666;
}
#tabs {
border-bottom: 1px solid #eee;
}
#tabs li {
cursor: pointer;
width: 100px;
height: 40px;
line-height: 40px;
text-align: center;
font-size: 16px;
border-bottom: 2px solid transparent;
position: relative;
z-index: 1;
margin-bottom: -1px;
color: #666;
}
#tabs .active {
border-bottom-color: #f00;
color: #222;
}
.tab-container .content {
display: none;
}
/* 页面布局 */
.main {
padding: 30px 100px;
width: 960px;
margin: 0 auto;
}
.main .logo {
color: #333;
text-align: left;
margin-bottom: 30px;
line-height: 1;
height: 110px;
margin-top: -50px;
overflow: hidden;
*zoom: 1;
}
.main .logo a {
font-size: 160px;
color: #333;
}
.helps {
margin-top: 40px;
}
.helps pre {
padding: 20px;
margin: 10px 0;
border: solid 1px #e7e1cd;
background-color: #fffdef;
overflow: auto;
}
.icon_lists {
width: 100% !important;
overflow: hidden;
*zoom: 1;
}
.icon_lists li {
width: 100px;
margin-bottom: 10px;
margin-right: 20px;
text-align: center;
list-style: none !important;
cursor: default;
}
.icon_lists li .code-name {
line-height: 1.2;
}
.icon_lists .icon {
display: block;
height: 100px;
line-height: 100px;
font-size: 42px;
margin: 10px auto;
color: #333;
-webkit-transition: font-size 0.25s linear, width 0.25s linear;
-moz-transition: font-size 0.25s linear, width 0.25s linear;
transition: font-size 0.25s linear, width 0.25s linear;
}
.icon_lists .icon:hover {
font-size: 100px;
}
.icon_lists .svg-icon {
/* 通过设置 font-size 来改变图标大小 */
width: 1em;
/* 图标和文字相邻时,垂直对齐 */
vertical-align: -0.15em;
/* 通过设置 color 来改变 SVG 的颜色/fill */
fill: currentColor;
/* path stroke 溢出 viewBox 部分在 IE 下会显示
normalize.css 中也包含这行 */
overflow: hidden;
}
.icon_lists li .name,
.icon_lists li .code-name {
color: #666;
}
/* markdown 样式 */
.markdown {
color: #666;
font-size: 14px;
line-height: 1.8;
}
.highlight {
line-height: 1.5;
}
.markdown img {
vertical-align: middle;
max-width: 100%;
}
.markdown h1 {
color: #404040;
font-weight: 500;
line-height: 40px;
margin-bottom: 24px;
}
.markdown h2,
.markdown h3,
.markdown h4,
.markdown h5,
.markdown h6 {
color: #404040;
margin: 1.6em 0 0.6em 0;
font-weight: 500;
clear: both;
}
.markdown h1 {
font-size: 28px;
}
.markdown h2 {
font-size: 22px;
}
.markdown h3 {
font-size: 16px;
}
.markdown h4 {
font-size: 14px;
}
.markdown h5 {
font-size: 12px;
}
.markdown h6 {
font-size: 12px;
}
.markdown hr {
height: 1px;
border: 0;
background: #e9e9e9;
margin: 16px 0;
clear: both;
}
.markdown p {
margin: 1em 0;
}
.markdown>p,
.markdown>blockquote,
.markdown>.highlight,
.markdown>ol,
.markdown>ul {
width: 80%;
}
.markdown ul>li {
list-style: circle;
}
.markdown>ul li,
.markdown blockquote ul>li {
margin-left: 20px;
padding-left: 4px;
}
.markdown>ul li p,
.markdown>ol li p {
margin: 0.6em 0;
}
.markdown ol>li {
list-style: decimal;
}
.markdown>ol li,
.markdown blockquote ol>li {
margin-left: 20px;
padding-left: 4px;
}
.markdown code {
margin: 0 3px;
padding: 0 5px;
background: #eee;
border-radius: 3px;
}
.markdown strong,
.markdown b {
font-weight: 600;
}
.markdown>table {
border-collapse: collapse;
border-spacing: 0px;
empty-cells: show;
border: 1px solid #e9e9e9;
width: 95%;
margin-bottom: 24px;
}
.markdown>table th {
white-space: nowrap;
color: #333;
font-weight: 600;
}
.markdown>table th,
.markdown>table td {
border: 1px solid #e9e9e9;
padding: 8px 16px;
text-align: left;
}
.markdown>table th {
background: #F7F7F7;
}
.markdown blockquote {
font-size: 90%;
color: #999;
border-left: 4px solid #e9e9e9;
padding-left: 0.8em;
margin: 1em 0;
}
.markdown blockquote p {
margin: 0;
}
.markdown .anchor {
opacity: 0;
transition: opacity 0.3s ease;
margin-left: 8px;
}
.markdown .waiting {
color: #ccc;
}
.markdown h1:hover .anchor,
.markdown h2:hover .anchor,
.markdown h3:hover .anchor,
.markdown h4:hover .anchor,
.markdown h5:hover .anchor,
.markdown h6:hover .anchor {
opacity: 1;
display: inline-block;
}
.markdown>br,
.markdown>p>br {
clear: both;
}
.hljs {
display: block;
background: white;
padding: 0.5em;
color: #333333;
overflow-x: auto;
}
.hljs-comment,
.hljs-meta {
color: #969896;
}
.hljs-string,
.hljs-variable,
.hljs-template-variable,
.hljs-strong,
.hljs-emphasis,
.hljs-quote {
color: #df5000;
}
.hljs-keyword,
.hljs-selector-tag,
.hljs-type {
color: #a71d5d;
}
.hljs-literal,
.hljs-symbol,
.hljs-bullet,
.hljs-attribute {
color: #0086b3;
}
.hljs-section,
.hljs-name {
color: #63a35c;
}
.hljs-tag {
color: #333333;
}
.hljs-title,
.hljs-attr,
.hljs-selector-id,
.hljs-selector-class,
.hljs-selector-attr,
.hljs-selector-pseudo {
color: #795da3;
}
.hljs-addition {
color: #55a532;
background-color: #eaffea;
}
.hljs-deletion {
color: #bd2c00;
background-color: #ffecec;
}
.hljs-link {
text-decoration: underline;
}
/* 代码高亮 */
/* PrismJS 1.15.0
https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript */
/**
* prism.js default theme for JavaScript, CSS and HTML
* Based on dabblet (http://dabblet.com)
* @author Lea Verou
*/
code[class*="language-"],
pre[class*="language-"] {
color: black;
background: none;
text-shadow: 0 1px white;
font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
text-align: left;
white-space: pre;
word-spacing: normal;
word-break: normal;
word-wrap: normal;
line-height: 1.5;
-moz-tab-size: 4;
-o-tab-size: 4;
tab-size: 4;
-webkit-hyphens: none;
-moz-hyphens: none;
-ms-hyphens: none;
hyphens: none;
}
pre[class*="language-"]::-moz-selection,
pre[class*="language-"] ::-moz-selection,
code[class*="language-"]::-moz-selection,
code[class*="language-"] ::-moz-selection {
text-shadow: none;
background: #b3d4fc;
}
pre[class*="language-"]::selection,
pre[class*="language-"] ::selection,
code[class*="language-"]::selection,
code[class*="language-"] ::selection {
text-shadow: none;
background: #b3d4fc;
}
@media print {
code[class*="language-"],
pre[class*="language-"] {
text-shadow: none;
}
}
/* Code blocks */
pre[class*="language-"] {
padding: 1em;
margin: .5em 0;
overflow: auto;
}
:not(pre)>code[class*="language-"],
pre[class*="language-"] {
background: #f5f2f0;
}
/* Inline code */
:not(pre)>code[class*="language-"] {
padding: .1em;
border-radius: .3em;
white-space: normal;
}
.token.comment,
.token.prolog,
.token.doctype,
.token.cdata {
color: slategray;
}
.token.punctuation {
color: #999;
}
.namespace {
opacity: .7;
}
.token.property,
.token.tag,
.token.boolean,
.token.number,
.token.constant,
.token.symbol,
.token.deleted {
color: #905;
}
.token.selector,
.token.attr-name,
.token.string,
.token.char,
.token.builtin,
.token.inserted {
color: #690;
}
.token.operator,
.token.entity,
.token.url,
.language-css .token.string,
.style .token.string {
color: #9a6e3a;
background: hsla(0, 0%, 100%, .5);
}
.token.atrule,
.token.attr-value,
.token.keyword {
color: #07a;
}
.token.function,
.token.class-name {
color: #DD4A68;
}
.token.regex,
.token.important,
.token.variable {
color: #e90;
}
.token.important,
.token.bold {
font-weight: bold;
}
.token.italic {
font-style: italic;
}
.token.entity {
cursor: help;
}

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@ -0,0 +1,399 @@
@font-face {
font-family: "iconfont"; /* Project id 4036849 */
src: url('iconfont.woff2?t=1723194854588') format('woff2'),
url('iconfont.woff?t=1723194854588') format('woff'),
url('iconfont.ttf?t=1723194854588') format('truetype');
}
.iconfont {
font-family: "iconfont" !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.icon-daxiaochilun:before {
content: "\e6d0";
}
.icon-quanping:before {
content: "\e67d";
}
.icon-sousuo1:before {
content: "\e628";
}
.icon-xiaoxitongzhi:before {
content: "\eaf8";
}
.icon-tuichuquanping:before {
content: "\e6db";
}
.icon-tianjia:before {
content: "\e695";
}
.icon-bianji1:before {
content: "\10117";
}
.icon-grid:before {
content: "\e8e4";
}
.icon-ic_batch:before {
content: "\e739";
}
.icon-shanchu:before {
content: "\fcb6";
}
.icon-qita1:before {
content: "\e602";
}
.icon-quanbu:before {
content: "\e745";
}
.icon-yinpin:before {
content: "\e603";
}
.icon-wendang:before {
content: "\e60e";
}
.icon-tupian:before {
content: "\e606";
}
.icon-shipin:before {
content: "\fb3c";
}
.icon-a-041_wendang:before {
content: "\e6da";
}
.icon-guanlianbaoan:before {
content: "\e600";
}
.icon-baoanxiaofang:before {
content: "\e613";
}
.icon-youjiantou:before {
content: "\e60c";
}
.icon-zuojiantou:before {
content: "\e60d";
}
.icon-lingdang:before {
content: "\e649";
}
.icon-wenjianjia:before {
content: "\e662";
}
.icon-caidan1:before {
content: "\e626";
}
.icon-a-ziyuan4:before {
content: "\e669";
}
.icon-zichanguanli:before {
content: "\e62e";
}
.icon-fix:before {
content: "\e9b9";
}
.icon-wenzidaxiao2:before {
content: "\e854";
}
.icon-zhuanyi03:before {
content: "\ea34";
}
.icon-wxbpinpaibao:before {
content: "\e620";
}
.icon-chanpinku:before {
content: "\e65f";
}
.icon-gongyingshangzhifu:before {
content: "\e618";
}
.icon-kehu:before {
content: "\e6d2";
}
.icon-jinxiaocun:before {
content: "\e61f";
}
.icon-xiangmuguanli-:before {
content: "\e609";
}
.icon-exe:before {
content: "\e63a";
}
.icon-mp4:before {
content: "\e639";
}
.icon-zhutushipin:before {
content: "\e612";
}
.icon-weizhiwenjian:before {
content: "\e61a";
}
.icon-Jpg:before {
content: "\e731";
}
.icon-Pdf:before {
content: "\e733";
}
.icon-svgtubiao:before {
content: "\e650";
}
.icon-doc:before {
content: "\e735";
}
.icon-gif:before {
content: "\e6a4";
}
.icon-JPEG:before {
content: "\e66d";
}
.icon-DOCX:before {
content: "\e672";
}
.icon-XLS:before {
content: "\e673";
}
.icon-XLSX:before {
content: "\e674";
}
.icon-PNG:before {
content: "\e69f";
}
.icon-shangchuanwenjian:before {
content: "\e652";
}
.icon-24gl-folderPlus:before {
content: "\eabe";
}
.icon-019shanchuwenjian:before {
content: "\e7e5";
}
.icon-xiazaiwenjian:before {
content: "\e615";
}
.icon-3333:before {
content: "\e680";
}
.icon-shijianchaxun-yimidida-:before {
content: "\e64d";
}
.icon-wenjian:before {
content: "\e62b";
}
.icon-xitong1:before {
content: "\e67c";
}
.icon-danwei:before {
content: "\e611";
}
.icon-policeman-full:before {
content: "\e8f1";
}
.icon-mysql:before {
content: "\e667";
}
.icon-RabbitMQ:before {
content: "\e6a0";
}
.icon-duanluqi:before {
content: "\e60a";
}
.icon-baimingdan:before {
content: "\e643";
}
.icon-VPNwangguan:before {
content: "\e7da";
}
.icon-kaifazhezhongxin:before {
content: "\e70f";
}
.icon-kongzhitai:before {
content: "\e651";
}
.icon-baidu:before {
content: "\e8cb";
}
.icon-waibulianjie:before {
content: "\e858";
}
.icon-zidianguanli:before {
content: "\e625";
}
.icon-shujukaifajiaobenkaifa:before {
content: "\e65c";
}
.icon-chanpin:before {
content: "\e64f";
}
.icon-xiaoshou:before {
content: "\e624";
}
.icon-ceshi:before {
content: "\e853";
}
.icon-zhuanshujingli:before {
content: "\e883";
}
.icon-gongsi:before {
content: "\e679";
}
.icon-xitongquanxian:before {
content: "\e61e";
}
.icon-rizhi:before {
content: "\e647";
}
.icon-yonghuguanli_huaban:before {
content: "\e62d";
}
.icon-dingshirenwu:before {
content: "\e6a3";
}
.icon-dashboard:before {
content: "\e78b";
}
.icon-caidan:before {
content: "\e65d";
}
.icon-bumenguanli:before {
content: "\e61d";
}
.icon-jiaoseguanli:before {
content: "\e621";
}
.icon-xitong:before {
content: "\e601";
}
.icon-shouye:before {
content: "\e8b9";
}
.icon-guanyu:before {
content: "\e623";
}
.icon-DVLINK_daping:before {
content: "\e627";
}
.icon-weixin:before {
content: "\e656";
}
.icon-QQ:before {
content: "\e882";
}
.icon-contentright:before {
content: "\e67a";
}
.icon-zhuti:before {
content: "\e610";
}
.icon-sousuo:before {
content: "\e68a";
}
.icon-xiaoxi:before {
content: "\e8be";
}
.icon-zhongyingwen:before {
content: "\e605";
}
.icon-fangda:before {
content: "\e622";
}
.icon-suoxiao:before {
content: "\e62a";
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,681 @@
{
"id": "4036849",
"name": "luozhun",
"font_family": "iconfont",
"css_prefix_text": "icon-",
"description": "",
"glyphs": [
{
"icon_id": "8959587",
"name": "大小齿轮",
"font_class": "daxiaochilun",
"unicode": "e6d0",
"unicode_decimal": 59088
},
{
"icon_id": "23875177",
"name": "全屏",
"font_class": "quanping",
"unicode": "e67d",
"unicode_decimal": 59005
},
{
"icon_id": "25840027",
"name": "搜索",
"font_class": "sousuo1",
"unicode": "e628",
"unicode_decimal": 58920
},
{
"icon_id": "29312195",
"name": "消息通知",
"font_class": "xiaoxitongzhi",
"unicode": "eaf8",
"unicode_decimal": 60152
},
{
"icon_id": "37425403",
"name": "退出全屏",
"font_class": "tuichuquanping",
"unicode": "e6db",
"unicode_decimal": 59099
},
{
"icon_id": "12188081",
"name": "添加",
"font_class": "tianjia",
"unicode": "e695",
"unicode_decimal": 59029
},
{
"icon_id": "34882873",
"name": "编辑",
"font_class": "bianji1",
"unicode": "10117",
"unicode_decimal": 65815
},
{
"icon_id": "924384",
"name": "grid",
"font_class": "grid",
"unicode": "e8e4",
"unicode_decimal": 59620
},
{
"icon_id": "25944286",
"name": "ic_batch",
"font_class": "ic_batch",
"unicode": "e739",
"unicode_decimal": 59193
},
{
"icon_id": "24473117",
"name": "删除",
"font_class": "shanchu",
"unicode": "fcb6",
"unicode_decimal": 64694
},
{
"icon_id": "7632835",
"name": "其他",
"font_class": "qita1",
"unicode": "e602",
"unicode_decimal": 58882
},
{
"icon_id": "11412028",
"name": "全部",
"font_class": "quanbu",
"unicode": "e745",
"unicode_decimal": 59205
},
{
"icon_id": "12085836",
"name": "音频",
"font_class": "yinpin",
"unicode": "e603",
"unicode_decimal": 58883
},
{
"icon_id": "15173237",
"name": "文档",
"font_class": "wendang",
"unicode": "e60e",
"unicode_decimal": 58894
},
{
"icon_id": "24110222",
"name": "图片",
"font_class": "tupian",
"unicode": "e606",
"unicode_decimal": 58886
},
{
"icon_id": "24111668",
"name": "视频",
"font_class": "shipin",
"unicode": "fb3c",
"unicode_decimal": 64316
},
{
"icon_id": "38945085",
"name": "041_文档",
"font_class": "a-041_wendang",
"unicode": "e6da",
"unicode_decimal": 59098
},
{
"icon_id": "2667175",
"name": "关联保安",
"font_class": "guanlianbaoan",
"unicode": "e600",
"unicode_decimal": 58880
},
{
"icon_id": "12156012",
"name": "保安消防",
"font_class": "baoanxiaofang",
"unicode": "e613",
"unicode_decimal": 58899
},
{
"icon_id": "7587762",
"name": "右箭头",
"font_class": "youjiantou",
"unicode": "e60c",
"unicode_decimal": 58892
},
{
"icon_id": "7587767",
"name": "左箭头",
"font_class": "zuojiantou",
"unicode": "e60d",
"unicode_decimal": 58893
},
{
"icon_id": "2234525",
"name": "铃铛",
"font_class": "lingdang",
"unicode": "e649",
"unicode_decimal": 58953
},
{
"icon_id": "1301378",
"name": "文件夹",
"font_class": "wenjianjia",
"unicode": "e662",
"unicode_decimal": 58978
},
{
"icon_id": "10333707",
"name": "菜单",
"font_class": "caidan1",
"unicode": "e626",
"unicode_decimal": 58918
},
{
"icon_id": "27157318",
"name": "按钮",
"font_class": "a-ziyuan4",
"unicode": "e669",
"unicode_decimal": 58985
},
{
"icon_id": "6241686",
"name": "资产管理",
"font_class": "zichanguanli",
"unicode": "e62e",
"unicode_decimal": 58926
},
{
"icon_id": "18170419",
"name": "解除固定,图钉",
"font_class": "fix",
"unicode": "e9b9",
"unicode_decimal": 59833
},
{
"icon_id": "34198298",
"name": "文字大小2",
"font_class": "wenzidaxiao2",
"unicode": "e854",
"unicode_decimal": 59476
},
{
"icon_id": "35102495",
"name": "转移03",
"font_class": "zhuanyi03",
"unicode": "ea34",
"unicode_decimal": 59956
},
{
"icon_id": "591724",
"name": "wxb品牌宝",
"font_class": "wxbpinpaibao",
"unicode": "e620",
"unicode_decimal": 58912
},
{
"icon_id": "1680680",
"name": "产品库",
"font_class": "chanpinku",
"unicode": "e65f",
"unicode_decimal": 58975
},
{
"icon_id": "1964103",
"name": "供应商支付",
"font_class": "gongyingshangzhifu",
"unicode": "e618",
"unicode_decimal": 58904
},
{
"icon_id": "9874502",
"name": "KHCFDC_客户",
"font_class": "kehu",
"unicode": "e6d2",
"unicode_decimal": 59090
},
{
"icon_id": "31313005",
"name": "进销存",
"font_class": "jinxiaocun",
"unicode": "e61f",
"unicode_decimal": 58911
},
{
"icon_id": "4657688",
"name": "项目管理",
"font_class": "xiangmuguanli-",
"unicode": "e609",
"unicode_decimal": 58889
},
{
"icon_id": "31260962",
"name": "exe",
"font_class": "exe",
"unicode": "e63a",
"unicode_decimal": 58938
},
{
"icon_id": "31260975",
"name": "mp4",
"font_class": "mp4",
"unicode": "e639",
"unicode_decimal": 58937
},
{
"icon_id": "22325375",
"name": "主图视频",
"font_class": "zhutushipin",
"unicode": "e612",
"unicode_decimal": 58898
},
{
"icon_id": "19145266",
"name": "未知文件",
"font_class": "weizhiwenjian",
"unicode": "e61a",
"unicode_decimal": 58906
},
{
"icon_id": "6376407",
"name": "Jpg",
"font_class": "Jpg",
"unicode": "e731",
"unicode_decimal": 59185
},
{
"icon_id": "6376410",
"name": "Pdf",
"font_class": "Pdf",
"unicode": "e733",
"unicode_decimal": 59187
},
{
"icon_id": "6441713",
"name": "svg图标",
"font_class": "svgtubiao",
"unicode": "e650",
"unicode_decimal": 58960
},
{
"icon_id": "6472244",
"name": "doc",
"font_class": "doc",
"unicode": "e735",
"unicode_decimal": 59189
},
{
"icon_id": "17290933",
"name": "gif",
"font_class": "gif",
"unicode": "e6a4",
"unicode_decimal": 59044
},
{
"icon_id": "22007985",
"name": "JPEG",
"font_class": "JPEG",
"unicode": "e66d",
"unicode_decimal": 58989
},
{
"icon_id": "22008512",
"name": "DOCX",
"font_class": "DOCX",
"unicode": "e672",
"unicode_decimal": 58994
},
{
"icon_id": "22008667",
"name": "XLS",
"font_class": "XLS",
"unicode": "e673",
"unicode_decimal": 58995
},
{
"icon_id": "22008680",
"name": "XLSX",
"font_class": "XLSX",
"unicode": "e674",
"unicode_decimal": 58996
},
{
"icon_id": "23182868",
"name": "PNG",
"font_class": "PNG",
"unicode": "e69f",
"unicode_decimal": 59039
},
{
"icon_id": "567560",
"name": "上传文件",
"font_class": "shangchuanwenjian",
"unicode": "e652",
"unicode_decimal": 58962
},
{
"icon_id": "7594807",
"name": "24gl-folderPlus",
"font_class": "24gl-folderPlus",
"unicode": "eabe",
"unicode_decimal": 60094
},
{
"icon_id": "15816428",
"name": "019删除文件",
"font_class": "019shanchuwenjian",
"unicode": "e7e5",
"unicode_decimal": 59365
},
{
"icon_id": "27438098",
"name": "下载文件",
"font_class": "xiazaiwenjian",
"unicode": "e615",
"unicode_decimal": 58901
},
{
"icon_id": "406256",
"name": "私人文件夹",
"font_class": "3333",
"unicode": "e680",
"unicode_decimal": 59008
},
{
"icon_id": "11472722",
"name": "事件查询-壹米滴答-01",
"font_class": "shijianchaxun-yimidida-",
"unicode": "e64d",
"unicode_decimal": 58957
},
{
"icon_id": "21193294",
"name": "文件",
"font_class": "wenjian",
"unicode": "e62b",
"unicode_decimal": 58923
},
{
"icon_id": "1137788",
"name": "系统",
"font_class": "xitong1",
"unicode": "e67c",
"unicode_decimal": 59004
},
{
"icon_id": "5650859",
"name": "单位",
"font_class": "danwei",
"unicode": "e611",
"unicode_decimal": 58897
},
{
"icon_id": "18167099",
"name": "警察半身,公安",
"font_class": "policeman-full",
"unicode": "e8f1",
"unicode_decimal": 59633
},
{
"icon_id": "3876471",
"name": "mysql",
"font_class": "mysql",
"unicode": "e667",
"unicode_decimal": 58983
},
{
"icon_id": "3172487",
"name": "RabbitMQ",
"font_class": "RabbitMQ",
"unicode": "e6a0",
"unicode_decimal": 59040
},
{
"icon_id": "18337193",
"name": "断路器",
"font_class": "duanluqi",
"unicode": "e60a",
"unicode_decimal": 58890
},
{
"icon_id": "29328030",
"name": "白名单",
"font_class": "baimingdan",
"unicode": "e643",
"unicode_decimal": 58947
},
{
"icon_id": "10055646",
"name": "VPN网关",
"font_class": "VPNwangguan",
"unicode": "e7da",
"unicode_decimal": 59354
},
{
"icon_id": "1259944",
"name": "开发者中心",
"font_class": "kaifazhezhongxin",
"unicode": "e70f",
"unicode_decimal": 59151
},
{
"icon_id": "12975229",
"name": "控制台",
"font_class": "kongzhitai",
"unicode": "e651",
"unicode_decimal": 58961
},
{
"icon_id": "18166606",
"name": "百度",
"font_class": "baidu",
"unicode": "e8cb",
"unicode_decimal": 59595
},
{
"icon_id": "34201231",
"name": "外部链接 ",
"font_class": "waibulianjie",
"unicode": "e858",
"unicode_decimal": 59480
},
{
"icon_id": "5434087",
"name": "字典管理",
"font_class": "zidianguanli",
"unicode": "e625",
"unicode_decimal": 58917
},
{
"icon_id": "4773266",
"name": "数据开发—脚本开发",
"font_class": "shujukaifajiaobenkaifa",
"unicode": "e65c",
"unicode_decimal": 58972
},
{
"icon_id": "5121534",
"name": "产品",
"font_class": "chanpin",
"unicode": "e64f",
"unicode_decimal": 58959
},
{
"icon_id": "11641886",
"name": "销售",
"font_class": "xiaoshou",
"unicode": "e624",
"unicode_decimal": 58916
},
{
"icon_id": "16398952",
"name": "测试",
"font_class": "ceshi",
"unicode": "e853",
"unicode_decimal": 59475
},
{
"icon_id": "34453374",
"name": "专属经理",
"font_class": "zhuanshujingli",
"unicode": "e883",
"unicode_decimal": 59523
},
{
"icon_id": "9592764",
"name": "公司",
"font_class": "gongsi",
"unicode": "e679",
"unicode_decimal": 59001
},
{
"icon_id": "8225386",
"name": "系统权限",
"font_class": "xitongquanxian",
"unicode": "e61e",
"unicode_decimal": 58910
},
{
"icon_id": "6527123",
"name": "日志",
"font_class": "rizhi",
"unicode": "e647",
"unicode_decimal": 58951
},
{
"icon_id": "12753449",
"name": "用户管理",
"font_class": "yonghuguanli_huaban",
"unicode": "e62d",
"unicode_decimal": 58925
},
{
"icon_id": "20853327",
"name": "定时任务",
"font_class": "dingshirenwu",
"unicode": "e6a3",
"unicode_decimal": 59043
},
{
"icon_id": "4765881",
"name": "dashboard",
"font_class": "dashboard",
"unicode": "e78b",
"unicode_decimal": 59275
},
{
"icon_id": "5283349",
"name": "菜单",
"font_class": "caidan",
"unicode": "e65d",
"unicode_decimal": 58973
},
{
"icon_id": "6627737",
"name": "部门管理",
"font_class": "bumenguanli",
"unicode": "e61d",
"unicode_decimal": 58909
},
{
"icon_id": "7274106",
"name": "角色管理",
"font_class": "jiaoseguanli",
"unicode": "e621",
"unicode_decimal": 58913
},
{
"icon_id": "1119109",
"name": "系统",
"font_class": "xitong",
"unicode": "e601",
"unicode_decimal": 58881
},
{
"icon_id": "1727423",
"name": "204首页",
"font_class": "shouye",
"unicode": "e8b9",
"unicode_decimal": 59577
},
{
"icon_id": "11641882",
"name": "关于",
"font_class": "guanyu",
"unicode": "e623",
"unicode_decimal": 58915
},
{
"icon_id": "12769434",
"name": "DVLINK_大屏",
"font_class": "DVLINK_daping",
"unicode": "e627",
"unicode_decimal": 58919
},
{
"icon_id": "318438",
"name": "weixin",
"font_class": "weixin",
"unicode": "e656",
"unicode_decimal": 58966
},
{
"icon_id": "4936984",
"name": "QQ",
"font_class": "QQ",
"unicode": "e882",
"unicode_decimal": 59522
},
{
"icon_id": "128654",
"name": "content-right",
"font_class": "contentright",
"unicode": "e67a",
"unicode_decimal": 59002
},
{
"icon_id": "4608986",
"name": "主题",
"font_class": "zhuti",
"unicode": "e610",
"unicode_decimal": 58896
},
{
"icon_id": "10452247",
"name": "sousuo",
"font_class": "sousuo",
"unicode": "e68a",
"unicode_decimal": 59018
},
{
"icon_id": "11372726",
"name": "消息中心",
"font_class": "xiaoxi",
"unicode": "e8be",
"unicode_decimal": 59582
},
{
"icon_id": "7533292",
"name": "中英文",
"font_class": "zhongyingwen",
"unicode": "e605",
"unicode_decimal": 58885
},
{
"icon_id": "3278362",
"name": "放大",
"font_class": "fangda",
"unicode": "e622",
"unicode_decimal": 58914
},
{
"icon_id": "5698509",
"name": "全屏缩小",
"font_class": "suoxiao",
"unicode": "e62a",
"unicode_decimal": 58922
}
]
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 551 KiB

View File

@ -0,0 +1,493 @@
/* flex */
.flex-center {
display: flex;
align-items: center;
justify-content: center;
}
.flex-end {
display: flex;
align-items: center;
justify-content: flex-end;
}
.flex-justify-between {
display: flex;
align-items: center;
justify-content: space-between;
}
.flex-align-center {
display: flex;
align-items: center;
}
.flex-column-center {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.w-f {
width: 100%;
}
.h-f {
height: 100%;
}
.f-r {
float: right;
}
.f-l {
float: left;
}
/* clearfix */
.clearfix::after {
display: block;
height: 0;
overflow: hidden;
clear: both;
content: "";
}
/* 文字单行省略号 */
.sle {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* 文字多行省略号 */
.mle {
display: -webkit-box;
overflow: hidden;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
}
/* 文字多了自动換行 */
.break-word {
word-break: break-all;
word-wrap: break-word;
}
/* fade-transform */
.fade-transform-leave-active,
.fade-transform-enter-active {
transition: all 0.2s;
}
.fade-transform-enter-from {
opacity: 0;
transition: all 0.2s;
transform: translateX(-30px);
}
.fade-transform-leave-to {
opacity: 0;
transition: all 0.2s;
transform: translateX(30px);
}
/* breadcrumb-transform */
.breadcrumb-enter-active {
transition: all 0.2s;
}
.breadcrumb-enter-from,
.breadcrumb-leave-active {
opacity: 0;
transform: translateX(10px);
}
/* 外边距、内边距全局样式 */
@for $i from 0 through 40 {
.mt#{$i} {
margin-top: #{$i}px !important;
}
.mr#{$i} {
margin-right: #{$i}px !important;
}
.mb#{$i} {
margin-bottom: #{$i}px !important;
}
.ml#{$i} {
margin-left: #{$i}px !important;
}
.pt#{$i} {
padding-top: #{$i}px !important;
}
.pr#{$i} {
padding-right: #{$i}px !important;
}
.pb#{$i} {
padding-bottom: #{$i}px !important;
}
.pl#{$i} {
padding-left: #{$i}px !important;
}
}
/* -- 内外边距 -- */
.margin-0 {
margin: 0;
}
.margin-xs {
margin: 5px;
}
.margin-sm {
margin: 10px;
}
.margin {
margin: 15px;
}
.margin-lg {
margin: 20px;
}
.margin-xl {
margin: 25px;
}
.margin-top-xs {
margin-top: 5px;
}
.margin-top-sm {
margin-top: 10px;
}
.margin-top {
margin-top: 15px;
}
.margin-top-lg {
margin-top: 20px;
}
.margin-top-xl {
margin-top: 25px;
}
.margin-right-xs {
margin-right: 5px;
}
.margin-right-sm {
margin-right: 10px;
}
.margin-right {
margin-right: 15px;
}
.margin-right-lg {
margin-right: 20px;
}
.margin-right-xl {
margin-right: 25px;
}
.margin-bottom-xs {
margin-bottom: 5px;
}
.margin-bottom-sm {
margin-bottom: 10px;
}
.margin-bottom {
margin-bottom: 15px;
}
.margin-bottom-lg {
margin-bottom: 20px;
}
.margin-bottom-xl {
margin-bottom: 25px;
}
.margin-left-xs {
margin-left: 5px;
}
.margin-left-sm {
margin-left: 10px;
}
.margin-left {
margin-left: 15px;
}
.margin-left-lg {
margin-left: 20px;
}
.margin-left-xl {
margin-left: 25px;
}
.margin-lr-xs {
margin-left: 5px;
margin-right: 5px;
}
.margin-lr-sm {
margin-left: 10px;
margin-right: 10px;
}
.margin-lr {
margin-left: 15px;
margin-right: 15px;
}
.margin-lr-lg {
margin-left: 20px;
margin-right: 20px;
}
.margin-lr-xl {
margin-left: 25px;
margin-right: 25px;
}
.margin-tb-xs {
margin-top: 5px;
margin-bottom: 5px;
}
.margin-tb-sm {
margin-top: 10px;
margin-bottom: 10px;
}
.margin-tb {
margin-top: 15px;
margin-bottom: 15px;
}
.margin-tb-lg {
margin-top: 20px;
margin-bottom: 20px;
}
.margin-tb-xl {
margin-top: 25px;
margin-bottom: 25px;
}
.padding-0 {
padding: 0;
}
.padding-xs {
padding: 5px;
}
.padding-sm {
padding: 10px;
}
.padding {
padding: 15px;
}
.padding-lg {
padding: 20px;
}
.padding-xl {
padding: 25px;
}
.padding-top-xs {
padding-top: 5px;
}
.padding-top-sm {
padding-top: 10px;
}
.padding-top {
padding-top: 15px;
}
.padding-top-lg {
padding-top: 20px;
}
.padding-top-xl {
padding-top: 25px;
}
.padding-right-xs {
padding-right: 5px;
}
.padding-right-sm {
padding-right: 10px;
}
.padding-right {
padding-right: 15px;
}
.padding-right-lg {
padding-right: 20px;
}
.padding-right-xl {
padding-right: 25px;
}
.padding-bottom-xs {
padding-bottom: 5px;
}
.padding-bottom-sm {
padding-bottom: 10px;
}
.padding-bottom {
padding-bottom: 15px;
}
.padding-bottom-lg {
padding-bottom: 20px;
}
.padding-bottom-xl {
padding-bottom: 25px;
}
.padding-left-xs {
padding-left: 5px;
}
.padding-left-sm {
padding-left: 10px;
}
.padding-left {
padding-left: 15px;
}
.padding-left-lg {
padding-left: 20px;
}
.padding-left-xl {
padding-left: 25px;
}
.padding-lr-xs {
padding-left: 5px;
padding-right: 5px;
}
.padding-lr-sm {
padding-left: 10px;
padding-right: 10px;
}
.padding-lr {
padding-left: 15px;
padding-right: 15px;
}
.padding-lr-lg {
padding-left: 20px;
padding-right: 20px;
}
.padding-lr-xl {
padding-left: 25px;
padding-right: 25px;
}
.padding-tb-xs {
padding-top: 5px;
padding-bottom: 5px;
}
.padding-tb-sm {
padding-top: 10px;
padding-bottom: 10px;
}
.padding-tb {
padding-top: 15px;
padding-bottom: 15px;
}
.padding-tb-lg {
padding-top: 20px;
padding-bottom: 20px;
}
.padding-tb-xl {
padding-top: 25px;
padding-bottom: 25px;
}
/*高德地图去水印*/
.amap-logo {
display: none !important;
visibility: hidden !important;
}
.amap-copyright {
display: none !important;
visibility: hidden !important;
}
/*高德地图去水印 结束*/
::-webkit-scrollbar {
width: 8px;
height: 12px;
background-color: #fff;
}
/*浏览器滚动条样式*/
::-webkit-scrollbar-thumb {
display: block;
min-height: 12px;
min-width: 8px;
border-radius: 6px;
background-color: rgb(217, 217, 217);
}
::-webkit-scrollbar-thumb:hover {
display: block;
min-height: 12px;
min-width: 8px;
border-radius: 6px;
background-color: rgb(159, 159, 159);
}
/*浏览器滚动条样式 结束*/
/* 鼠标悬浮手指 */
.pointer {
cursor: pointer
}
.border-table {
thead, th, td {
border: 1px solid black;
padding: 5px;
}
}

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="37.07" height="36" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 198"><path fill="#41B883" d="M204.8 0H256L128 220.8L0 0h97.92L128 51.2L157.44 0h47.36Z"></path><path fill="#41B883" d="m0 0l128 220.8L256 0h-51.2L128 132.48L50.56 0H0Z"></path><path fill="#35495E" d="M50.56 0L128 133.12L204.8 0h-47.36L128 51.2L97.92 0H50.56Z"></path></svg>

After

Width:  |  Height:  |  Size: 496 B

View File

@ -0,0 +1,85 @@
import axios, {AxiosInstance, AxiosRequestConfig, InternalAxiosRequestConfig} from "axios";
import {message} from "ant-design-vue";
import {useUserStore} from "@/stores/modules/userStore.ts";
import router from "@/router";
export interface CustomAxiosRequestConfig extends AxiosRequestConfig {
//是否需要全屏禁用
loading?: boolean,
loadingMessage?: string
}
export interface CustomInternalAxiosRequestConfig extends InternalAxiosRequestConfig {
//是否需要全屏禁用
loading?: boolean,
loadingMessage?: string
}
const axiosConfig: AxiosRequestConfig = {
baseURL: __APP_ENV.VITE_APP_BASE_API,
timeout: __APP_ENV.VITE_APP_ENV === 'production' ? 10 * 1000 : 60 * 1000,
timeoutErrorMessage: "请求超时......"
};
class RequestHttp {
service: AxiosInstance;
constructor(config: AxiosRequestConfig) {
this.service = axios.create(config);
this.service.interceptors.request.use((cfg: CustomInternalAxiosRequestConfig) => {
const userStore = useUserStore()
cfg.headers.set(userStore.getTokenInfo?.name as string, userStore.getTokenInfo?.value as string)
return cfg;
}, async (error) => {
message.error(error.message)
return Promise.reject(error);
})
this.service.interceptors.response.use(async (resp): Promise<any> => {
const jsonResult: JsonResult<unknown> = resp.data;
if (jsonResult && jsonResult.code != 200) {
//一些特定的错误需要重新登录
if ([-1].includes(jsonResult.code)) {
//清除登录信息
await useUserStore().resetUserInfo();
//跳转登录页
await router.push({
path: '/login'
})
}
message.error(jsonResult.message)
return Promise.reject(jsonResult);
}
return Promise.resolve(jsonResult);
}, (error) => {
message.error(error.message)
return Promise.reject(error);
})
}
/**
*
*/
get<T>(url: string, params?: object, _object: CustomAxiosRequestConfig = {}): Promise<JsonResult<T>> {
return this.service.get(url, {params, ..._object});
}
post<T>(url: string, params?: object | object[], _object: CustomAxiosRequestConfig = {}): Promise<JsonResult<T>> {
return this.service.post(url, params, _object);
}
put<T>(url: string, params?: object | object[], _object: CustomAxiosRequestConfig = {}): Promise<JsonResult<T>> {
return this.service.put(url, params, _object);
}
delete<T>(url: string, params?: object, _object: CustomAxiosRequestConfig = {}): Promise<JsonResult<T>> {
return this.service.delete(url, {params, ..._object});
}
}
const api = new RequestHttp(axiosConfig);
export default api

View File

@ -0,0 +1,41 @@
<script setup lang="ts">
import { ref } from 'vue'
defineProps<{ msg: string }>()
const count = ref(0)
</script>
<template>
<h1>{{ msg }}</h1>
<div class="card">
<button type="button" @click="count++">count is {{ count }}</button>
<p>
Edit
<code>components/HelloWorld.vue</code> to test HMR
</p>
</div>
<p>
Check out
<a href="https://vuejs.org/guide/quick-start.html#local" target="_blank"
>create-vue</a
>, the official Vue + Vite starter
</p>
<p>
Learn more about IDE Support for Vue in the
<a
href="https://vuejs.org/guide/scaling-up/tooling.html#ide-support"
target="_blank"
>Vue Docs Scaling up Guide</a
>.
</p>
<p class="read-the-docs">Click on the Vite and Vue logos to learn more</p>
</template>
<style scoped>
.read-the-docs {
color: #888;
}
</style>

View File

@ -0,0 +1,36 @@
<template>
<div class="flex-justify-between h-f">
<div class="flex-center">
<menu-unfold-outlined
v-if="collapsed"
class="trigger"
@click="() => (collapsed = !collapsed)"
/>
<menu-fold-outlined v-else class="trigger" @click="() => (collapsed = !collapsed)"/>
</div>
<div class="margin-right flex-center">
<a-avatar/>
</div>
</div>
</template>
<script setup lang="ts">
import {MenuFoldOutlined, MenuUnfoldOutlined} from "@ant-design/icons-vue";
const collapsed = defineModel<boolean>('collapsed')
</script>
<style scoped lang="scss">
.trigger {
font-size: 18px;
line-height: 64px;
padding: 0 24px;
cursor: pointer;
transition: color 0.3s;
}
.trigger:hover {
color: #1890ff;
}
</style>

View File

@ -0,0 +1,90 @@
<template>
<a-layout class="main-content">
<a-layout-sider
:collapsed="collapsed"
theme="light"
:trigger="null"
collapsible
>
<div v-if="!collapsed" class="title flex-center">
<div>超级后台</div>
</div>
<div v-else class="logo flex-center">
<img src="@/assets/vue.svg" title="超级后台" alt="xx">
</div>
</a-layout-sider>
<a-layout>
<a-layout-header
class="layout-header"
>
<layout-header v-model:collapsed="collapsed"/>
</a-layout-header>
<a-layout-content
class="layout-content"
>
<router-view v-slot="{ Component, route }">
<transition appear name="fade-transform" mode="out-in">
<keep-alive :include="keepAliveNames">
<component :is="Component" :key="route.fullPath"/>
</keep-alive>
</transition>
</router-view>
</a-layout-content>
</a-layout>
</a-layout>
</template>
<script setup lang="ts">
import {ref} from "vue";
import LayoutHeader from "@/components/layout/header/LayoutHeader.vue";
const collapsed = ref<boolean>(false);
const keepAliveNames = ref<string[]>([])
</script>
<style scoped lang="scss">
.main-content {
width: 100vw;
height: 100vh;
.layout-header {
background: #ffffff;
padding: 0;
}
.layout-content {
margin: 10px;
padding: 4px;
background: #f5f7fd;
min-height: 280px;
border-radius: 5px;
overflow: auto;
}
}
.title {
height: 32px;
width: 168px;
transition: width 2ms linear 2ms;
margin: 16px;
color: black;
font-weight: bold;
font-size: 20px
}
.logo {
margin: 16px;
img {
width: 50px;
height: 50px
}
}
.site-layout .site-layout-background {
background: #ffffff;
}
</style>

View File

@ -0,0 +1,132 @@
<template>
<a-form
ref="formRef"
:model="loginParams"
:rules="loginParamsRule"
@finish="login"
layout="vertical"
size="large"
class="login-form"
>
<a-form-item name="telephone">
<a-input
v-model:value="loginParams.telephone"
placeholder="请输入账号/手机号"
:max-length="64"
allow-clear
/>
</a-form-item>
<a-form-item name="password">
<a-input-password
v-model:value="loginParams.password"
placeholder="请输入密码"
:max-length="32"
/>
</a-form-item>
<div class="remember-me">
<a-checkbox disabled>
记住我
</a-checkbox>
</div>
<a-button
class="btn"
type="primary"
html-type="submit"
>立即登录
</a-button>
</a-form>
</template>
<script lang="ts" setup>
import {ref} from 'vue'
import {FormInstance, message, notification} from "ant-design-vue";
import {Rule} from "ant-design-vue/es/form";
import {LoginParams} from "@/types/views/login.ts";
import api from "@/axios";
import {CLIENT_TYPE} from "@/config";
import rsaUtil from "@/utils/rsaUtil.ts";
import {TokenInfo} from "@/types/stores/userStore.ts";
import {useUserStore} from "@/stores/modules/userStore.ts";
import {useRouter} from "vue-router";
const userStore = useUserStore()
const router = useRouter()
const formRef = ref<FormInstance>(null!)
const loginParamsRule: Record<keyof LoginParams, Rule[]> = {
telephone: [
{required: true, message: '请输入手机号', trigger: 'change'},
{len: 11, message: "长度不够", trigger: 'blur'},
],
password: [
{required: true, message: '请输入密码', trigger: 'change'},
{min: 6, max: 20, message: '密码长度最小为6最长为20', trigger: 'blur'},
],
}
const loginParams = ref<LoginParams>({
telephone: __APP_ENV.VITE_APP_ENV === "development" ? '15576404472' : '',
password: __APP_ENV.VITE_APP_ENV === "development" ? '123456' : ''
});
/**
* 登录
*/
const login = async () => {
//
await formRef.value.validate()
//
const resp = await api.post<TokenInfo>('/login', {
clientType: CLIENT_TYPE,
loginParams: {
telephone: loginParams.value.telephone,
password: rsaUtil.encryptStr(loginParams.value.password)
}
})
//token
userStore.saveTokenInfo(resp.data as TokenInfo)
//
router.push("/index").then(() => {
notification.success({
message: '登录成功',
duration: 2,
description: '欢迎来到本系统!',
placement: 'topRight'
})
})
}
</script>
<style lang="scss" scoped>
.login-form {
box-sizing: border-box;
padding: 0 5px;
margin-top: 16px;
:deep(.ant-input-group-addon) {
padding: 0;
overflow: hidden;
}
.ant-form-item {
margin-bottom: 20px;
}
.remember-me {
display: flex;
justify-content: space-between;
}
.btn {
border-radius: 4px;
box-shadow: 0 0 0 1px #05f,
0 2px 1px rgba(0, 0, 0, 0.15);
font-size: 14px;
font-weight: 500;
height: 40px;
line-height: 22px;
margin: 20px 0 12px;
width: 100%;
}
}
</style>

View File

@ -0,0 +1,2 @@
export const CLIENT_TYPE = "MANAGEMENT_SUPER";
export const ROUTER_WHITE_LIST: string[] = ['/login', '/test'];

10
policeManagement/src/global.d.ts vendored Normal file
View File

@ -0,0 +1,10 @@
declare const __APP_ENV: ImportMetaEnv;
/**
*
*/
interface JsonResult<T> {
code: number;
message: string;
data?: T;
}

View File

@ -0,0 +1,19 @@
import {createApp} from 'vue'
import App from '@/App.vue'
import '@/reset.css'
// 公共样式
import '@/assets/scss/common.scss'
// iconfont css
import "@/assets/iconfont/iconfont.css";
// vue Router
import router from "@/router";
// pinia stores
import pinia from "@/stores";
const vueApp = createApp(App);
vueApp
.use(router)
.use(pinia)
.mount('#app')

View File

@ -0,0 +1,57 @@
/* http://meyerweb.com/eric/tools/css/reset/ */
/* v1.0 | 20080212 */
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, font, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td {
margin: 0;
padding: 0;
border: 0;
outline: 0;
font-size: 100%;
vertical-align: baseline;
background: transparent;
}
body {
line-height: 1;
}
ol, ul {
list-style: none;
}
blockquote, q {
quotes: none;
}
blockquote:before, blockquote:after,
q:before, q:after {
content: none;
}
/* remember to define focus styles! */
:focus {
outline: 0;
}
/* remember to highlight inserts somehow! */
ins {
text-decoration: none;
}
del {
text-decoration: line-through;
}
/* tables still need 'cellspacing="0"' in the markup */
table {
border-collapse: collapse;
border-spacing: 0;
}

View File

@ -0,0 +1,45 @@
import {createRouter, createWebHistory} from "vue-router";
import {staticRouter} from "@/router/staticRouters.ts";
import {message, Modal} from "ant-design-vue";
import {useUserStore} from "@/stores/modules/userStore.ts";
import {ROUTER_WHITE_LIST} from "@/config";
/**
* createWebHistory & createWebHashHistory
* createWebHistory: 路径不带#如nginx配置:try_files $uri $uri/ /index.html last;
* createWebHashHistory: 路径带# URL SEO
*/
const router = createRouter({
history: createWebHistory(),
routes: [...staticRouter],
strict: false,
scrollBehavior: () => ({left: 0, top: 0})
});
router.beforeEach(async (to, from, next) => {
Modal.destroyAll();
//判断访问的是不是登录页
const userStore = useUserStore();
if (to.path.toLocaleLowerCase() === '/login' && userStore.getTokenInfo?.value) {
//如果已登录 且访问login页面 直接返回当前页面
await message.warn('当前已登录,请先退出账号');
return next(from.fullPath)
}
//判断访问路径是不是白名单d
if (ROUTER_WHITE_LIST.includes(to.path)) {
return next();
}
//不在白名单内需要查看是否携带token 没有token需要返回登录页进行登录
if (!userStore.getTokenInfo?.value) {
await message.warn('未找到token请重新登陆!')
return next('/login');
}
//放行
return next();
})
router.onError(error => {
console.error("路由错误", error.message);
})
export default router

View File

@ -0,0 +1,33 @@
import {RouteRecordRaw} from "vue-router";
export const staticRouter: RouteRecordRaw[] = [
{
path: '/login',
name: 'login',
meta: {
title: '登录',
},
component: () => import("@/views/login.vue"),
}, {
path: "/",
redirect: '/index',
}, {
path: '/layout',
name: 'layout',
redirect: '/index',
component: () => import("@/components/layout/layout.vue"),
children: [
{
path: '/index',
name: 'index',
meta: {
title: '首页',
icon: 'icon-shouye',
fixed: true,
isKeepAlive: false
},
component: () => import('@/views/index.vue')
}
]
}
]

View File

@ -0,0 +1,8 @@
import {createPinia} from "pinia";
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
//创建store实例 持久化插件
const pinia = createPinia();
pinia.use(piniaPluginPersistedstate);
export default pinia;

View File

@ -0,0 +1,27 @@
import {defineStore} from "pinia";
import {TokenInfo, UserStore} from "@/types/stores/userStore.ts";
export const useUserStore = defineStore({
id: 'useUserStore',
state: (): UserStore => {
return {
tokenInfo: undefined
}
},
actions: {
saveTokenInfo(tokenInfo: TokenInfo) {
this.tokenInfo = tokenInfo
},
async resetUserInfo() {
this.tokenInfo = undefined;
}
},
getters: {
getTokenInfo: (state): TokenInfo => state.tokenInfo as TokenInfo,
},
persist: {
key: "useUserStore",
storage: window.localStorage,
paths: ["tokenInfo"],
}
})

View File

@ -0,0 +1,8 @@
export interface TokenInfo {
name: string;
value: string;
}
export interface UserStore {
tokenInfo?: TokenInfo;
}

View File

@ -0,0 +1,4 @@
export interface LoginParams {
telephone: string;
password: string;
}

View File

@ -0,0 +1,16 @@
import {JSEncrypt} from "jsencrypt";
const rsa = new JSEncrypt()
rsa.setPublicKey(__APP_ENV.VITE_APP_RSA_PUBLIC_KEY)
const encryptStr = (text: string): string => {
const r = rsa.encrypt(text);
if (!r) {
throw "加密失败";
}
return r;
}
export default {
encryptStr
}

View File

@ -0,0 +1,11 @@
<template>
index页面
</template>
<script setup lang="ts">
</script>
<style scoped lang="scss">
</style>

View File

@ -0,0 +1,236 @@
<template>
<div class="root">
<div class="header">
<img src="@/assets/vue.svg" alt="Logo" height="33" width="33"/>
<div class="logo-text">超级后台</div>
</div>
<div class="container">
<div class="left-banner"></div>
<div class="login-card">
<div class="title">
欢迎来到超级后台
</div>
<a-tabs class="account-tab" v-model:active-key="activeKey">
<a-tab-pane :key="0" tab="账号登录">
<TelephoneLogin/>
</a-tab-pane>
</a-tabs>
<div class="oauth">
<a-divider class="text" orientation="center">其他登录方式</a-divider>
<div class="idps">
<a-button class="app" type="link" shape="circle" disabled>
<QqOutlined/>
</a-button>
<a-button class="app" type="link" shape="circle" disabled>
<WechatOutlined/>
</a-button>
</div>
</div>
</div>
</div>
<div class="footer">
<div class="beian">
<div class="below text" v-html="'...'"></div>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import {QqOutlined, WechatOutlined} from '@ant-design/icons-vue'
import TelephoneLogin from '@/components/login/TelephoneLogin.vue';
import {ref} from "vue";
const activeKey = ref(0)
</script>
<style lang="scss" scoped>
.root {
background-image: url('@/assets/images/login/bg.jpg');
background-repeat: no-repeat;
background-size: cover;
min-height: 100vh;
a {
color: #3370ff;
cursor: pointer !important;
text-decoration: none;
}
a:hover {
color: #6694ff;
}
.header {
padding: 32px 40px 10px;
img {
vertical-align: middle;
}
.logo-text {
display: inline-block;
margin-right: 4px;
margin-left: 4px;
color: rgba(29, 33, 41);
font-size: 24px;
vertical-align: middle;
}
}
.container {
align-items: center;
box-sizing: border-box;
display: flex;
height: calc(100vh - 100px);
justify-content: center;
margin: 0 auto;
max-width: 1200px;
min-height: 650px;
.left-banner {
flex: 1 1;
height: 100%;
max-height: 700px;
position: relative;
img {
height: 100%;
left: 0;
max-height: 350px;
max-width: 500px;
object-fit: contain;
position: absolute;
top: 4.5%;
width: 100%;
}
}
.login-card {
display: flex;
background: #fff;
border-radius: 20px;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.05);
box-sizing: border-box;
min-height: 500px;
position: relative;
width: 450px;
min-width: 434px;
flex-direction: column;
margin-bottom: 53px;
padding: 48px 43px 32px;
.title {
color: #020814;
font-size: 24px;
font-weight: 500;
letter-spacing: 0.003em;
line-height: 32px;
padding: 0 5px;
}
.account-tab {
margin-top: 36px;
:deep(.ant-tabs-nav) {
margin-bottom: 0;
}
:deep(.ant-tabs-content) {
padding-top: 16px;
}
:deep(.ant-tabs-nav::before) {
display: none;
}
:deep(.ant-tabs-tab-btn) {
font-size: 16px;
font-weight: 500;
line-height: 22px;
display: inline-block;
padding: 1px 0;
position: relative;
}
:deep(.ant-tabs-tab-btn:before) {
display: none;
}
:deep(.ant-tabs-tab) {
margin: 0 30px 0 6px;
}
}
.oauth {
margin-top: 20px;
padding: 0 5px;
:deep(.ant-divider-inner-text) {
color: #80838a;
font-size: 12px;
font-weight: 400;
line-height: 20px;
}
:deep(.ant-divider) {
margin-bottom: 25px;
}
.idps {
align-items: center;
display: flex;
justify-content: center;
width: 100%;
.app {
margin-right: 12px;
align-items: center;
border: 1px solid #eaedf1;
border-radius: 32px;
box-sizing: border-box;
display: flex;
height: 32px;
justify-content: center;
width: 32px;
.icon {
width: 21px;
height: 20px;
}
}
.app:hover {
background: #f3f7ff;
border: 1px solid #97bcff;
}
}
}
}
}
.footer {
align-items: center;
box-sizing: border-box;
display: flex;
justify-content: center;
.beian {
.text {
color: #41464f;
font-size: 12px;
font-weight: 400;
letter-spacing: 0.2px;
line-height: 20px;
text-align: center;
}
.below {
align-items: center;
display: flex;
}
}
}
}
</style>

16
policeManagement/src/vite-env.d.ts vendored Normal file
View File

@ -0,0 +1,16 @@
/// <reference types="vite/client" />
interface ImportMetaEnv {
// 项目名称
readonly VITE_APP_NAME: string;
// 当前环境
readonly VITE_APP_ENV: 'development' | 'production';
// 启动端口
readonly VITE_APP_PORT: number;
// axios
readonly VITE_APP_BASE_API: string;
readonly VITE_APP_PROXY_URL: string;
// RSA公钥
readonly VITE_APP_RSA_PUBLIC_KEY: string;
}

View File

@ -0,0 +1,40 @@
{
"compilerOptions": {
"target": "ES2020",
"useDefineForClassFields": true,
"module": "ESNext",
"lib": [
"ES2020",
"DOM",
"DOM.Iterable"
],
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"isolatedModules": true,
"moduleDetection": "force",
"noEmit": true,
"jsx": "preserve",
"jsxImportSource": "vue",
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"baseUrl": "./",
/* */
"paths": {
"@/*": [
"src/*"
]
},
"allowSyntheticDefaultImports": true
},
"include": [
"src/**/*.ts",
"src/**/*.d.ts",
"src/**/*.tsx",
"src/**/*.vue"
]
}

View File

@ -0,0 +1,11 @@
{
"files": [],
"references": [
{
"path": "./tsconfig.app.json"
},
{
"path": "./tsconfig.node.json"
}
]
}

View File

@ -0,0 +1,24 @@
{
"compilerOptions": {
"target": "ES2022",
"lib": [
"ES2023"
],
"module": "ESNext",
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"isolatedModules": true,
"moduleDetection": "force",
"noEmit": true,
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true
},
"include": [
"vite.config.ts"
]
}

View File

@ -0,0 +1,83 @@
import {defineConfig, loadEnv} from 'vite'
import vue from '@vitejs/plugin-vue'
import Components from 'unplugin-vue-components/vite';
import {AntDesignVueResolver} from 'unplugin-vue-components/resolvers';
import * as path from "node:path";
import vueJsx from '@vitejs/plugin-vue-jsx'
const pathSrc = path.resolve(__dirname, 'src');
// https://vitejs.dev/config/
export default defineConfig(({mode}) => {
const env: Record<string, string> = loadEnv(mode, process.cwd(), '')
return {
define: {
__APP_ENV: JSON.stringify(env)
},
base: '/',
plugins: [
vue(),
vueJsx(),
Components({
resolvers: [
AntDesignVueResolver({importStyle: false})
]
})
],
resolve: {
alias: {
'@': pathSrc,
}
},
server: {
host: '0.0.0.0',
port: parseInt(env['VITE_APP_PORT']),
open: false,
proxy: {
[env["VITE_APP_BASE_API"]]: {
target: env["VITE_APP_PROXY_URL"],
changeOrigin: true,
secure: false,
rewrite: path => path.replace(RegExp(`^${env['VITE_APP_BASE_API']}`), '')
}
}
},
build: {
outDir: 'dist',
target: 'modules',
chunkSizeWarningLimit: 1500,
minify: 'terser',
terserOptions: {
compress: {
//生产环境时移除console
drop_console: env['VITE_DROP_CONSOLE'] as unknown as boolean,
drop_debugger: env['VITE_DROP_CONSOLE'] as unknown as boolean,
},
format: {
//删除注释
comments: false
}
},
rollupOptions: {
output: {
manualChunks(id) {
if (id.includes('node_modules')) {
return id
.toString()
.split('node_modules/')[1]
.split('/')[0]
.toString();
}
},
chunkFileNames(chunkInfo) {
const facadeModuleId = chunkInfo.facadeModuleId ? chunkInfo.facadeModuleId.split('/') : [];
const fileName =
facadeModuleId[facadeModuleId.length - 2] || '[name]';
return `js/${fileName}/[name].[hash].js`;
}
}
}
},
}
})

5
policeSecurityServer/.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
.idea
target
logs
temp
HELP.md

View File

@ -0,0 +1,313 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.changhu</groupId>
<artifactId>policeSecurityServer</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>policeSecurityServer</name>
<properties>
<java.version>17</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring.boot.version>3.2.2</spring.boot.version>
<hutool.version>5.8.27</hutool.version>
<knife4j.version>4.5.0</knife4j.version>
<zxing.version>3.3.3</zxing.version>
<pinyin4j.version>2.5.1</pinyin4j.version>
<fastjson2.version>2.0.50</fastjson2.version>
<easyexcel.version>3.3.4</easyexcel.version>
<mysql.driver.version>8.0.32</mysql.driver.version>
<mybatis.plus.version>3.5.7</mybatis.plus.version>
<druid.version>1.2.20</druid.version>
<minio.version>8.4.3</minio.version>
<okhttp.version>4.8.1</okhttp.version>
<sa.token.version>1.38.0</sa.token.version>
</properties>
<dependencies>
<!-- spring boot web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<!--过滤系统自带日志-->
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
<!-- 去掉Jackson依赖用fastjson -->
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-json</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 监控、追踪、审计、控制 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- 自动注解配置 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<!-- log4j2日志 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
<!-- 单元测试 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- spring boot websocket支持 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<!-- spring boot aop支持 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</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>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<!-- lombok工具 https://projectlombok.org/ -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!-- knife4j是为Java MVC框架集成Swagger生成Api文档的增强解决方案 https://github.com/xiaoymin/knife4j -->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
<version>${knife4j.version}</version>
</dependency>
<!-- fastjson2 对 SpringFramework 扩展 https://alibaba.github.io/fastjson2/ -->
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2-extension-spring6</artifactId>
<version>${fastjson2.version}</version>
</dependency>
<!-- easyexcel https://easyexcel.opensource.alibaba.com/-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>${easyexcel.version}</version>
</dependency>
<!-- hutool工具 https://doc.hutool.cn/pages/index/-->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>${hutool.version}</version>
</dependency>
<!-- hutool使用CaptchaUtil需要导入第三方包 -->
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>core</artifactId>
<version>${zxing.version}</version>
</dependency>
<!-- hutool使用PinyinUtil需要导入第三方包 -->
<dependency>
<groupId>com.belerweb</groupId>
<artifactId>pinyin4j</artifactId>
<version>${pinyin4j.version}</version>
</dependency>
<!-- Sa-Token 权限认证在线文档https://sa-token.cc -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot3-starter</artifactId>
<version>${sa.token.version}</version>
</dependency>
<!-- Sa-Token 整合 Redis -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-redis</artifactId>
<version>${sa.token.version}</version>
</dependency>
<!-- mysql 驱动 注:最新版本的MySQL Connector/J已经迁移到了符合反转DNS规范的Maven坐标 因此您需要按照新的坐标来引用该依赖。根据错误消息中提供的信息将mysql:mysql-connector-java替换为com.mysql:mysql-connector-j版本保持不变。-->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>${mysql.driver.version}</version>
</dependency>
<!-- druid数据库连接池 https://github.com/alibaba/druid/tree/master/druid-spring-boot-starter-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-3-starter</artifactId>
<version>${druid.version}</version>
</dependency>
<!-- mybatis plus https://baomidou.com/pages/24112f/-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
<version>${mybatis.plus.version}</version>
</dependency>
<!-- minio对象存储 https://www.minio.org.cn/ -->
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>${minio.version}</version>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>${okhttp.version}</version>
</dependency>
</dependencies>
<repositories>
<repository>
<id>osgeo</id>
<name>OSGeo Release Repository</name>
<url>https://repo.osgeo.org/repository/release/</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
<releases>
<enabled>true</enabled>
</releases>
</repository>
<repository>
<id>osgeo-snapshot</id>
<name>OSGeo Snapshot Repository</name>
<url>https://repo.osgeo.org/repository/snapshot/</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
<releases>
<enabled>false</enabled>
</releases>
</repository>
</repositories>
<dependencyManagement>
<dependencies>
<!-- spring boot版本依赖管理-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring.boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
<compilerArgs>
<arg>
-parameters
</arg>
</compilerArgs>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring.boot.version}</version>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
<!-- 定义资源目录 -->
<resources>
<!-- ${project.basedir}/src/main/resources/${profiles.environment} 下的文件输出到根目录下 -->
<resource>
<directory>${project.basedir}/src/main/resources/${profiles.environment}</directory>
</resource>
<!-- ${project.basedir}/src/main/resources/META-INF 下的文件输出到 META-INF 下 -->
<resource>
<directory>${project.basedir}/src/main/resources/META-INF</directory>
<targetPath>META-INF</targetPath>
</resource>
<!-- ${project.basedir}/src/main/resources/static 下的文件输出到 static 下 -->
<resource>
<directory>${project.basedir}/src/main/resources/static</directory>
<targetPath>static</targetPath>
</resource>
<!-- ${project.basedir}/src/main/resources/conf 下的文件输出到 conf 下 -->
<resource>
<directory>${project.basedir}/src/main/resources/conf</directory>
<targetPath>conf</targetPath>
</resource>
<!-- ${project.basedir}/src/main/resources/mapper 下的文件输出到 mapper 下 -->
<resource>
<directory>${project.basedir}/src/main/resources/mapper</directory>
<targetPath>mapper</targetPath>
</resource>
<resource>
<directory>${project.basedir}/src/main/resources/templates</directory>
<targetPath>templates</targetPath>
</resource>
</resources>
</build>
<profiles>
<!--开发环境-->
<profile>
<id>dev</id>
<properties>
<profiles.environment>env/dev</profiles.environment>
</properties>
<activation>
<!--默认激活本地环境-->
<activeByDefault>true</activeByDefault>
</activation>
</profile>
<!--本地环境-->
<profile>
<id>local</id>
<properties>
<profiles.environment>env/local</profiles.environment>
</properties>
</profile>
<!--线上环境-->
<profile>
<id>prod</id>
<properties>
<profiles.environment>env/prod</profiles.environment>
</properties>
</profile>
</profiles>
</project>

View File

@ -0,0 +1,47 @@
package com.changhu;
import cn.hutool.core.util.StrUtil;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.Environment;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import java.net.InetAddress;
/**
* Hello world!
*/
@Slf4j
@SpringBootApplication
@EnableTransactionManagement
@EnableScheduling
@EnableCaching
@EnableWebSocket
public class PoliceSecurityServerApp {
@SneakyThrows
public static void main(String[] args) {
SpringApplication app = new SpringApplication(PoliceSecurityServerApp.class);
ConfigurableApplicationContext application = app.run(args);
Environment env = application.getEnvironment();
log.info(StrUtil.format("""
\n----------------------------------------------------------
\tApplication '{}' is running! Access URLs:
\tLocal: \t\thttp://localhost:{}
\tExternal: \thttp://{}:{}
\tDoc: \t\thttp://{}:{}/doc.html
""",
env.getProperty("spring.application.name"),
env.getProperty("server.port"),
InetAddress.getLocalHost().getHostAddress(),
env.getProperty("server.port"),
InetAddress.getLocalHost().getHostAddress(),
env.getProperty("server.port")
));
}
}

View File

@ -0,0 +1,19 @@
package com.changhu.common.annotation;
import cn.hutool.core.util.DesensitizedUtil;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* author: luozhun
* desc: 脱敏注解
* createTime: 2023/9/13 16:45
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Desensitized {
DesensitizedUtil.DesensitizedType value();
}

View File

@ -0,0 +1,15 @@
package com.changhu.common.annotation;
import java.lang.annotation.*;
/**
* author: luozhun
* desc: 是拓展属性
* createTime: 2023/11/1 17:25
*/
@Documented
@Retention(value = RetentionPolicy.RUNTIME)
@Target(value = {ElementType.FIELD})
public @interface IsExtData {
boolean value() default true;
}

View File

@ -0,0 +1,18 @@
package com.changhu.common.annotation;
import org.springframework.web.bind.annotation.RestController;
import java.lang.annotation.*;
/**
* author: luozhun
* desc: JsonResult
* createTime: 2023/8/18 10:47
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@RestController
public @interface JsonBody {
boolean value() default true;
}

View File

@ -0,0 +1,14 @@
package com.changhu.common.annotation;
import java.lang.annotation.*;
/**
* @author 20252
* @createTime 2024/6/26 上午9:41
* @desc RealDelete...
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RealDelete {
}

View File

@ -0,0 +1,47 @@
package com.changhu.common.db;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.annotation.IEnum;
import com.changhu.common.exception.MessageException;
import java.io.Serializable;
/**
* author: luozhun
* desc: BaseEnum
* createTime: 2023/8/16 17:38
*/
public interface BaseEnum<V> {
/**
* 获取值
*
* @return V
*/
V getValue();
/**
* 获取文本
*
* @return string
*/
String getLabel();
@SuppressWarnings("unchecked")
static <P, T extends BaseEnum<P>> T valueOf(Class<? extends BaseEnum<P>> enumType, P code) {
BaseEnum<P>[] enumConstants = enumType.getEnumConstants();
for (BaseEnum<P> pBaseEnum : enumConstants) {
if (ObjectUtil.equals(code, pBaseEnum.getValue())) {
return (T) pBaseEnum;
}
}
throw new MessageException("不存在值为:{} 的【{}】对象!", code.toString(), enumType.componentType().getSimpleName());
}
default Object serializer() {
return Dict.of(
"value", this.getValue(),
"label", this.getLabel()
);
}
}

View File

@ -0,0 +1,22 @@
package com.changhu.common.db.enums;
import com.changhu.common.db.BaseEnum;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* @author 20252
* @createTime 2024/8/28 下午3:51
* @desc CheckStatus...
*/
@Getter
@AllArgsConstructor
public enum CheckStatus implements BaseEnum<Integer> {
checked(0, "已审核"),
unChecked(1, "未审核"),
;
private final Integer value;
private final String label;
}

View File

@ -0,0 +1,25 @@
package com.changhu.common.db.enums;
import cn.hutool.core.lang.Dict;
import com.changhu.common.db.BaseEnum;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* author: luozhun
* desc: DeleteFlag
* createTime: 2023/8/16 17:39
*/
@Getter
@AllArgsConstructor
public enum DeleteFlag implements BaseEnum<Integer> {
FALSE(0, "未删除"),
TRUE(1, "已删除"),
;
private final Integer value;
private final String label;
}

View File

@ -0,0 +1,27 @@
package com.changhu.common.db.enums;
import com.baomidou.mybatisplus.annotation.IEnum;
import com.changhu.common.annotation.IsExtData;
import com.changhu.common.db.BaseEnum;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* author: luozhun
* desc: IsEnable
* createTime: 2023/8/22 9:52
*/
@Getter
@AllArgsConstructor
public enum IsEnable implements BaseEnum<Integer>, IEnum<Integer> {
TRUE(0, "启用", "success"),
FALSE(1, "禁用", "error"),
;
private final Integer value;
private final String label;
@IsExtData
private final String color;
}

View File

@ -0,0 +1,22 @@
package com.changhu.common.db.enums;
import com.changhu.common.db.BaseEnum;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* author: luozhun
* desc: IsOrNot
* createTime: 2023/9/21 16:42
*/
@Getter
@AllArgsConstructor
public enum IsOrNot implements BaseEnum<Integer> {
IS(0, ""),
NOT(1, ""),
;
private final Integer value;
private final String label;
}

View File

@ -0,0 +1,23 @@
package com.changhu.common.db.enums;
import com.changhu.common.db.BaseEnum;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* fileName: Sex
* author: LuoZhun
* createTime: 2023/11/8 17:14
* description: 性别
*/
@Getter
@AllArgsConstructor
public enum Sex implements BaseEnum<Integer> {
MAN(0, ""),
WOMAN(1, ""),
UNKNOWN(2, "隐藏");
private final Integer value;
private final String label;
}

View File

@ -0,0 +1,21 @@
package com.changhu.common.enums;
import com.changhu.common.db.BaseEnum;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* author: luozhun
* desc: CaptchaType
* createTime: 2023/9/19 19:15
*/
@Getter
@AllArgsConstructor
public enum CaptchaType implements BaseEnum<String> {
LOGIN("login", "登录验证码"),
;
private final String value;
private final String label;
}

View File

@ -0,0 +1,62 @@
package com.changhu.common.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* author: luozhun
* desc: ResultCode
* createTime: 2023/8/16 17:30
*/
@Getter
@AllArgsConstructor
public enum ResultCode {
/**
* 常见状态码
*/
SUCCESS(200, "操作成功!"),
ERROR(500, "操作失败!"),
WARN(555, "出现警告!"),
/**
* 权限相关 -1错误需要重新返回登录页面 进行认证
*/
NOT_TOKEN(-1, "未能读取到有效 token"),
INVALID_TOKEN(-1, "token 无效"),
TOKEN_TIMEOUT(-1, "token 已过期"),
BE_REPLACED(-1, "token 已被顶下线"),
KICK_OUT(-1, "token 已被踢下线"),
TOKEN_FREEZE(-1, "token 已被冻结"),
NO_PREFIX(-1, "未按照指定前缀提交 token"),
OTHER_TOKEN_ERROR(-1, "当前会话未登录"),
/**
* 加密解密
*/
DECRYPT_ERROR(301, "解码失败"),
ENCIPHER_ERROR(302, "编码失败"),
/**
* 系统相关
*/
USER_NOT_FOUND(402, "用户不存在"),
PASSWORD_ERROR(403, "密码错误"),
NOT_FOUND_404(404, "找不到资源"),
USER_IS_DISABLE(405, "用户已禁用"),
ROLE_IS_DISABLE(405, "角色已禁用"),
CODE_ERROR(406, "验证码错误"),
DATA_NOT_FOUND(407, "数据不存在"),
/**
* 参数校验
*/
PARAM_ERROR(502, "参数错误"),
REQUIRED_PARAM_MISSING(503, "未找到必填参数"),
;
private final Integer code;
private final String message;
}

View File

@ -0,0 +1,67 @@
package com.changhu.common.exception;
import cn.hutool.core.util.StrUtil;
import com.changhu.common.enums.ResultCode;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.Arrays;
import java.util.Objects;
/**
* author: luozhun
* desc: MessageException
* createTime: 2023/8/16 17:27
*/
@Data
@EqualsAndHashCode(callSuper = false)
public class MessageException extends RuntimeException {
private Integer code = ResultCode.ERROR.getCode();
private String message = ResultCode.ERROR.getMessage();
private Object data;
public MessageException(String errorMessage) {
this.message = errorMessage;
}
public MessageException(Integer errorCode, String errorMessage) {
this.code = errorCode;
this.message = errorMessage;
}
public MessageException(Integer errorCode, String template, String... errorMessage) {
this.code = errorCode;
this.message = StrUtil.format(template, Arrays.stream(errorMessage).toArray());
}
public MessageException(String template, String... errorMessage) {
this.message = StrUtil.format(template, Arrays.stream(errorMessage).toArray());
}
public MessageException(ResultCode resultCode, String errorMessage) {
this.code = resultCode.getCode();
this.message = errorMessage;
}
public MessageException(ResultCode resultCode, Object data, String errorMessage) {
this.code = resultCode.getCode();
this.message = errorMessage;
this.data = data;
}
public MessageException(ResultCode resultCode, Object data) {
this.code = resultCode.getCode();
this.data = data;
}
public MessageException(ResultCode resultCode) {
this.code = resultCode.getCode();
this.message = resultCode.getMessage();
}
@Override
public String toString() {
return StrUtil.format("业务错误:错误代码:{},错误内容:{}", Objects.isNull(this.code) ? 500 : this.code, this.message);
}
}

View File

@ -0,0 +1,117 @@
package com.changhu.common.pojo.model;
import com.changhu.common.enums.ResultCode;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* author: luozhun
* desc: JsonResult
* createTime: 2023/8/16 17:29
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class JsonResult<T> implements java.io.Serializable {
/**
* 响应编码
*/
private Integer code;
/**
* 返回消息
*/
private String message;
/**
* 返回数据
*/
private T data;
public JsonResult(Integer code, String message) {
this.code = code;
this.message = message;
}
public JsonResult(ResultCode resultCode, T data) {
this.code = resultCode.getCode();
this.message = resultCode.getMessage();
this.data = data;
}
/**
* 成功信息
*/
public static <O> JsonResult<O> success() {
return new JsonResult<>(ResultCode.SUCCESS, null);
}
public static <O> JsonResult<O> successMsg(String message) {
return new JsonResult<>(ResultCode.SUCCESS.getCode(), message);
}
public static <O> JsonResult<O> success(O data) {
return new JsonResult<>(ResultCode.SUCCESS, data);
}
/**
* 操作失败
*/
public static <O> JsonResult<O> error() {
return new JsonResult<>(ResultCode.ERROR, null);
}
public static <O> JsonResult<O> errorMsg(String message) {
return new JsonResult<>(ResultCode.ERROR.getCode(), message);
}
public static <O> JsonResult<O> error(O data) {
return new JsonResult<>(ResultCode.ERROR, data);
}
/**
* 警告信息
*/
public static <O> JsonResult<O> warn() {
return new JsonResult<>(ResultCode.WARN, null);
}
public static <O> JsonResult<O> warnMsg(String message) {
return new JsonResult<>(ResultCode.WARN.getCode(), message);
}
public static <O> JsonResult<O> warn(O data) {
return new JsonResult<>(ResultCode.WARN, data);
}
/**
* 自定义信息
*/
public static <O> JsonResult<O> custom(Integer code, String message, O data) {
return new JsonResult<>(code, message, data);
}
public static <O> JsonResult<O> custom(Integer code, String message) {
return new JsonResult<>(code, message, null);
}
public static <O> JsonResult<O> custom(ResultCode resultCode) {
return new JsonResult<>(resultCode, null);
}
public static <O> JsonResult<O> custom(ResultCode resultCode, O data) {
return new JsonResult<>(resultCode, data);
}
public static <O> JsonResult<O> custom(Boolean result) {
return new JsonResult<>(result ? ResultCode.SUCCESS : ResultCode.ERROR, null);
}
public static <O> JsonResult<O> custom(Integer count) {
return new JsonResult<>(count > 0 ? ResultCode.SUCCESS : ResultCode.ERROR, null);
}
}

View File

@ -0,0 +1,40 @@
package com.changhu.common.pojo.vo;
import cn.hutool.core.lang.Dict;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.List;
/**
* author: luozhun
* desc: SelectNodeVo
* createTime: 2023/9/22 9:53
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class SelectGroupNodeVo<T> implements Serializable {
/**
* 选择的值
*/
private T value;
/**
* 显示文本
*/
private String label;
/**
* 分组选项
*/
private List<SelectGroupNodeVo<T>> options;
/**
* 拓展属性
*/
private Dict extData;
}

View File

@ -0,0 +1,33 @@
package com.changhu.common.pojo.vo;
import cn.hutool.core.lang.Dict;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/**
* author: luozhun
* desc: SelectNodeVo
* createTime: 2023/9/22 9:53
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class SelectNodeVo<T> implements Serializable {
/**
* 选择的值
*/
private T value;
/**
* 显示文本
*/
private String label;
/**
* 拓展属性
*/
private Dict extData;
}

View File

@ -0,0 +1,19 @@
package com.changhu.common.pojo.vo;
import lombok.Data;
/**
* @author 20252
* @createTime 2024/8/29 下午2:55
* @desc TokenInfo...
*/
@Data
public class TokenInfo {
private String name;
private String value;
public TokenInfo(String name, String value) {
this.name = name;
this.value = value;
}
}

View File

@ -0,0 +1,176 @@
package com.changhu.common.pojo.vo;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.lang.NonNull;
import java.io.Serializable;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* author: luozhun
* desc: TreeNodeVo
* createTime: 2023/9/4 16:38
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@Schema(description = "json树形数据")
public class TreeNodeVo<T> implements Serializable {
/**
*
*/
@Schema(description = "节点的唯一value")
private T value;
/**
* 父节点的值
*/
@Schema(description = "父节点的value")
private T parentValue;
/**
* 显示文本
*/
@Schema(description = "节点名称")
private String label;
/**
* 排序
*/
@Builder.Default
@Schema(description = "排序")
private Integer orderIndex = 0;
/**
* 拓展属性
*/
@Schema(description = "拓展属性")
private Dict extData;
/**
* 子节点
*/
@Schema(description = "子节点")
private List<TreeNodeVo<T>> children;
/**
* 树构建
*/
public static <T> List<TreeNodeVo<T>> buildTree(List<TreeNodeVo<T>> data, @NonNull T rootKey) {
//数据为空 直接返回空
if (CollUtil.isEmpty(data)) {
return new ArrayList<>();
}
//先将数据根据parentValue分组
Map<T, List<TreeNodeVo<T>>> groupData = data.stream().collect(Collectors.groupingBy(TreeNodeVo::getParentValue));
//组装树
return data.stream()
.filter(e -> ObjectUtil.equals(e.getParentValue(), rootKey))
.peek(d -> d.setChildren(children(groupData, d)))
.sorted((a, b) -> NumberUtil.compare(b.getOrderIndex(), a.getOrderIndex()))
.collect(Collectors.toList());
}
private static <T> List<TreeNodeVo<T>> children(Map<T, List<TreeNodeVo<T>>> groupData, TreeNodeVo<T> node) {
List<TreeNodeVo<T>> treeNodeVos = groupData.get(node.getValue());
if (CollUtil.isEmpty(treeNodeVos)) {
return null;
}
return treeNodeVos.stream()
.peek(p -> p.setChildren(children(groupData, p)))
.sorted((a, b) -> NumberUtil.compare(b.getOrderIndex(), a.getOrderIndex()))
.toList();
}
/**
* 自定义树构建
*/
public static <T> List<Map<String, Object>> buildTree(List<?> data, @NonNull String key, @NonNull String parentKey, @NonNull T rootKey) {
if (CollUtil.isEmpty(data)) {
return new ArrayList<>();
}
//先将数据转 对象为map
List<Map<String, Object>> mapListData = data.stream().map(BeanUtil::beanToMap).toList();
//将map集合根据parentKey分组
Map<Object, List<Map<String, Object>>> collect = mapListData.stream().collect(Collectors.groupingBy(e -> e.get(parentKey)));
return mapListData.stream()
.filter(e -> ObjectUtil.equals(e.get(parentKey), rootKey))
.peek(e -> e.put("children", children(collect, e, key)))
.collect(Collectors.toList());
}
private static List<Map<String, Object>> children(Map<Object, List<Map<String, Object>>> groupData, Map<String, Object> node, String key) {
List<Map<String, Object>> treeNodeVos = groupData.get(node.get(key));
if (CollUtil.isEmpty(treeNodeVos)) {
return null;
}
return treeNodeVos.stream()
.peek(p -> p.put("children", children(groupData, p, key)))
.toList();
}
/**
* 生成随机字符串
*
* @param len 长度
* @param fun 校验方法
*/
public static String generateCode(Integer len, Function<String, Boolean> fun) {
StringBuilder sb = new StringBuilder();
// 生成三位字符串
for (int i = 0; i < len; i++) {
// 生成随机索引范围是0到3526个字母加10个数字
int index = (int) (Math.random() * 36);
char ch;
if (index < 26) {
// 生成字母
ch = (char) ('a' + index); // 将索引转换为字母
} else {
// 生成数字
ch = (char) ('0' + (index - 26)); // 将索引转换为数字
}
sb.append(ch); // 将生成的字符添加到字符串中
}
String result = sb.toString();
boolean b = fun.apply(result);
if (b) {
result = generateCode(len, fun);
}
return result;
}
/**
* 生成随机字符串
*
* @param fun 校验方法
*/
public static String generateCode(Function<String, Boolean> fun) {
return generateCode(3, fun);
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
TreeNodeVo<?> that = (TreeNodeVo<?>) o;
return Objects.equals(value, that.value) && Objects.equals(label, that.label);
}
@Override
public int hashCode() {
return Objects.hash(value, label);
}
}

View File

@ -0,0 +1,17 @@
package com.changhu.common.properties;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* author: luozhun
* desc: some...
* createTime: 2023/12/15 10:43
*/
@Data
@Component
@ConfigurationProperties(prefix = "project.fastjson2")
public class Fastjson2Properties {
private String dateFormat;
}

View File

@ -0,0 +1,21 @@
package com.changhu.common.properties;
import com.changhu.common.db.BaseEnum;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* author: luozhun
* desc: some...
* createTime: 2023/11/29 15:34
*/
@Getter
@AllArgsConstructor
public enum ProjectEnv implements BaseEnum<String> {
DEV("开发环境", "dev"),
PROD("生产环境", "prod");
private final String label;
private final String value;
}

View File

@ -0,0 +1,33 @@
package com.changhu.common.properties;
import com.changhu.common.exception.MessageException;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
/**
* author: luozhun
* desc: some...
* createTime: 2023/11/29 15:42
*/
@Slf4j
@Data
@Component
@ConfigurationProperties(prefix = "project")
public class ProjectProperties {
/**
* 项目运行环境
*/
private ProjectEnv env;
@Bean
public ProjectEnv projectEnv() {
if (env == null) {
throw new MessageException("当前环境没有配置【projectEnv】请配置");
}
log.info("当前运行环境:【{} ({})】", env.getValue(), env.getLabel());
return env;
}
}

View File

@ -0,0 +1,36 @@
package com.changhu.common.properties;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* author: luozhun
* desc: some...
* createTime: 2023/12/15 10:46
*/
@Data
@Component
@ConfigurationProperties(prefix = "project.thread-pool")
public class ThreadProperties {
/**
* 线程名字前缀
*/
private String threadNamePrefix;
/**
* 核心线程数
*/
private Integer corePoolSize;
/**
* 最大线程数
*/
private Integer maxPoolSize;
/**
* 阻塞队列容量
*/
private Integer queueCapacity;
/**
* 空闲线程存活时间
*/
private Integer keepAliveSeconds;
}

View File

@ -0,0 +1,64 @@
package com.changhu.common.utils;
import cn.hutool.crypto.Mode;
import cn.hutool.crypto.Padding;
import cn.hutool.crypto.symmetric.AES;
import cn.hutool.setting.Setting;
import cn.hutool.setting.SettingUtil;
import com.changhu.common.enums.ResultCode;
import com.changhu.common.exception.MessageException;
import lombok.extern.slf4j.Slf4j;
import java.nio.charset.StandardCharsets;
/**
* author: luozhun
* desc: 加密解密工具
* createTime: 2023/8/19 12:21
*/
@Slf4j
public class AesUtil {
/**
* 配置文件
*/
private static final Setting setting = SettingUtil.get("aes");
/**
* AES实例
*/
private static final AES aes = new AES(
Mode.CBC,
Padding.PKCS5Padding,
setting.getStr("secretKey").getBytes(StandardCharsets.UTF_8),
setting.getStr("secretIv").getBytes(StandardCharsets.UTF_8)
);
/**
* 加密 明文 -> 密文
*
* @param text 加密字符串
* @return 加密的结果
*/
public static String encrypt(String text) {
try {
return aes.encryptHex(text);
} catch (Exception e) {
log.error("加密失败: {}", e.getMessage());
throw new MessageException(ResultCode.ENCIPHER_ERROR);
}
}
/**
* 解密: 密文 -> 明文
*/
public static String decrypt(String cipherString) {
try {
return aes.decryptStr(cipherString);
} catch (Exception e) {
log.error("解密失败: {}", e.getMessage());
throw new MessageException(ResultCode.DECRYPT_ERROR);
}
}
}

View File

@ -0,0 +1,52 @@
package com.changhu.common.utils;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import java.net.InetAddress;
import java.net.UnknownHostException;
/**
* author: luozhun
* desc: some...
* createTime: 2023/12/26 15:49
*/
@Slf4j
public class IpUtil {
public static String getIp(HttpServletRequest request) {
String ipAddress;
try {
ipAddress = request.getHeader("x-forwarded-for");
if (ipAddress == null || ipAddress.isEmpty() || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("Proxy-Client-IP");
}
if (ipAddress == null || ipAddress.isEmpty() || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("WL-Proxy-Client-IP");
}
if (ipAddress == null || ipAddress.isEmpty() || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getRemoteAddr();
if (ipAddress.equals("127.0.0.1") || ipAddress.equals("0:0:0:0:0:0:0:1")) {
try {
ipAddress = InetAddress.getLocalHost().getHostAddress();
} catch (UnknownHostException e) {
log.error("未知的host:{}", e.getMessage());
}
}
}
// 通过多个代理的情况第一个IP为客户端真实IP,多个IP按照','分割
if (ipAddress != null) {
if (ipAddress.contains(",")) {
return ipAddress.split(",")[0];
} else {
return ipAddress;
}
} else {
return "";
}
} catch (Exception e) {
log.error("ip获取失败:{}", e.getMessage());
return "";
}
}
}

View File

@ -0,0 +1,94 @@
package com.changhu.common.utils;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.Set;
import java.util.stream.Stream;
/**
* @author 20252
* @createTime 2024/8/5 下午4:11
* @desc JavaClassToTsUtils...
*/
public class JavaClassToTsUtil {
public static <T> String parse(Class<T> tClass) {
//类名字
StringBuilder tsInterface = new StringBuilder("interface " + tClass.getSimpleName() + " {");
//获取class所有属性
Field[] fields = tClass.getDeclaredFields();
for (Field field : fields) {
tsInterface
.append("\n\t")
.append("/** ")
.append(field.getAnnotation(Schema.class) != null ? field.getAnnotation(Schema.class).description() : field.getName())
.append(" **/")
.append("\n\t")
.append(field.getName())
.append(isMust(field) ? "" : "?")
.append(":")
.append(convertJavaTypeToTS(field.getType()))
.append(";");
}
return tsInterface
.append("\n")
.append("}")
.toString();
}
/**
* 将Java类型转化未ts类型
*
* @param type Java类型
* @return ts类型
*/
private static String convertJavaTypeToTS(Class<?> type) {
Set<Class<?>> stringList = Set.of(String.class);
Set<Class<?>> boolList = Set.of(Boolean.class, boolean.class);
Set<Class<?>> numberList = Set.of(
Integer.class, int.class,
Double.class, double.class,
Float.class, float.class,
Long.class, long.class,
Short.class, short.class,
BigDecimal.class,
Byte.class, byte.class
);
Set<Class<?>> dateList = Set.of(LocalDateTime.class, LocalDate.class, LocalTime.class);
if (stringList.contains(type)) {
return "string";
} else if (boolList.contains(type)) {
return "boolean";
} else if (numberList.contains(type)) {
return "number";
} else if (dateList.contains(type)) {
return "Dayjs";
} else {
return "unsupported";
}
}
/**
* 检查字段是否必须
*
* @param field 字段
* @return 是否必须
*/
private static boolean isMust(Field field) {
return Stream.of(
NotNull.class,
NotEmpty.class,
NotBlank.class
).anyMatch(field::isAnnotationPresent);
}
}

View File

@ -0,0 +1,67 @@
package com.changhu.common.utils;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.RSA;
import cn.hutool.setting.Setting;
import cn.hutool.setting.SettingUtil;
import com.changhu.common.enums.ResultCode;
import com.changhu.common.exception.MessageException;
import lombok.extern.slf4j.Slf4j;
/**
* @author 20252
* @createTime 2024/8/20 下午4:57
* @desc rsa加密工具类
*/
@Slf4j
public class RsaUtil {
/**
* rsa密钥配置文件
*/
private static final Setting setting = SettingUtil.get("rsa");
/**
* rsa实例
*/
private static final RSA rsa = new RSA(setting.getStr("privateKey"), setting.getStr("publicKey"));
/**
* 通过公钥进行加密
*
* @param text 需要加密的密码
* @return 加密的结果
*/
public static String encipher(String text) {
try {
return rsa.encryptHex(text, KeyType.PublicKey);
} catch (Exception e) {
log.error("加密失败: {}", e.getMessage());
throw new MessageException(ResultCode.ENCIPHER_ERROR);
}
}
/**
* 通过私钥解密
*
* @param encryptedString 加密后的字符串
* @return 解密后的结果
*/
public static String decrypt(String encryptedString) {
try {
return rsa.decryptStr(encryptedString, KeyType.PrivateKey);
} catch (Exception e) {
log.error("解码失败: {}", e.getMessage());
throw new MessageException(ResultCode.DECRYPT_ERROR);
}
}
public static void main(String[] args) {
RSA rs = new RSA();
System.out.println(rs.getPublicKeyBase64());
System.out.println(rs.getPrivateKeyBase64());
}
}

View File

@ -0,0 +1,37 @@
package com.changhu.common.utils;
import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.util.IdUtil;
import lombok.extern.slf4j.Slf4j;
/**
* author: luozhun
* desc: SnowFlakeIdUtil
* createTime: 2023/8/16 17:33
*/
@Slf4j
public class SnowFlakeIdUtil {
//todo 暂时先固定靶
private static final Snowflake snowflake = IdUtil.getSnowflake(1, 0);
/**
* 系统默认雪花id生成
*
* @return 雪花id
*/
public static synchronized long snowflakeId() {
return snowflake.nextId();
}
/**
* 指定生成雪花id
*
* @param workerId 机器id
* @param datacenterId 数据中心id
* @return 雪花id
*/
public static synchronized long snowflakeId(long workerId, long datacenterId) {
return IdUtil.getSnowflake(workerId, datacenterId).nextId();
}
}

View File

@ -0,0 +1,122 @@
package com.changhu.common.utils;
import cn.dev33.satoken.stp.SaTokenInfo;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import com.changhu.common.enums.ResultCode;
import com.changhu.common.exception.MessageException;
import lombok.extern.slf4j.Slf4j;
import java.util.Optional;
/**
* author: luozhun
* desc: some...
* createTime: 2023/12/15 11:30
*/
@Slf4j
public class UserUtil {
public static final String DEFAULT_PASSWORD = "123456";
public static final Long ERROR_USER_ID = -1L;
/**
* 用户登录 保存信息
*
* @param userId 用户id
*/
public static void login(Long userId) {
StpUtil.login(userId);
}
/**
* 登录并且返回SaTokenInfo
*
* @param userId 用户id
* @return SaTokenInfo
*/
public static SaTokenInfo loginAndTokenInfo(Long userId) {
login(userId);
return getTokenInfo();
}
/**
* 用户登出
*/
public static void logout() {
StpUtil.logout();
}
/**
* 获取用户id
*/
public static Long getUserId() {
return StpUtil.getLoginIdAsLong();
}
/**
* 获取当前用户token
*
* @return token
*/
public static String getToken() {
return StpUtil.getTokenValue();
}
/**
* 根据token获取id
*
* @param token token
* @return id
*/
public static Long getUserIdByToken(String token) {
return Long.parseLong(Optional.ofNullable(StpUtil.getLoginIdByToken(token))
.map(Object::toString)
.orElseThrow(() -> new MessageException(ResultCode.NOT_TOKEN)));
}
/**
* 获取token信息
*
* @return token信息
*/
public static SaTokenInfo getTokenInfo() {
return StpUtil.getTokenInfo();
}
/**
* 加密明文密码
*
* @param plainTextPassWord 明文密码
* @return + 加密后的密码
*/
public static String passWordEncrypt(String plainTextPassWord) {
//随机盐值
String salt = IdUtil.simpleUUID();
//生成密文密码
String ciphertextPassWord = SecureUtil.md5(salt + plainTextPassWord);
return salt + "$$" + ciphertextPassWord;
}
/**
* 解密密码 校验密码是否正确
*
* @param plainTextPassWord 明文密码
* @param salt
* @param ciphertextPassWord 密文密码
* @return 比对结果
*/
public static boolean verifyPassWord(String plainTextPassWord, String salt, String ciphertextPassWord) {
boolean result = false;
if (StrUtil.isAllNotEmpty(plainTextPassWord, salt, ciphertextPassWord)) {
// 使用同样的加密算法和随机盐值生成最终加密的密码
if (StrUtil.equals(SecureUtil.md5(salt + plainTextPassWord), ciphertextPassWord)) {
result = true;
}
}
return result;
}
}

View File

@ -0,0 +1,32 @@
package com.changhu.common.utils;
import cn.hutool.core.collection.CollUtil;
import com.changhu.common.exception.MessageException;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.Validation;
import jakarta.validation.Validator;
import jakarta.validation.ValidatorFactory;
import java.util.Set;
import java.util.stream.Collectors;
/**
* author: luozhun
* desc: ValidatorUtil
* createTime: 2023/8/22 9:39
*/
public class ValidatorUtil {
/**
* 手动校验
*/
public static <T> void manual(T params) {
try (ValidatorFactory vf = Validation.buildDefaultValidatorFactory()) {
Validator validator = vf.getValidator();
Set<ConstraintViolation<T>> validateResult = validator.validate(params);
if (CollUtil.isNotEmpty(validateResult)) {
String errMessage = validateResult.stream().map(ConstraintViolation::getMessage).collect(Collectors.joining(","));
throw new MessageException(errMessage);
}
}
}
}

View File

@ -0,0 +1,35 @@
package com.changhu.common.validator;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.StrUtil;
import com.changhu.common.validator.annotation.IdCard;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
/**
* @author 20252
* @createTime 2024/8/21 下午3:41
* @desc IdCardValidator...
*/
public class IdCardValidator implements ConstraintValidator<IdCard, String> {
private boolean required;
@Override
public void initialize(IdCard constraintAnnotation) {
required = constraintAnnotation.required();
}
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
if (required) {
return Validator.isCitizenId(value);
} else {
if (StrUtil.isBlank(value)) {
return true;
} else {
return Validator.isCitizenId(value);
}
}
}
}

View File

@ -0,0 +1,38 @@
package com.changhu.common.validator;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.StrUtil;
import com.changhu.common.utils.ValidatorUtil;
import com.changhu.common.validator.annotation.IsMobile;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
/**
* author: luozhun
* desc: IsMobileValidator
* createTime: 2023/8/22 9:38
*/
public class IsMobileValidator implements ConstraintValidator<IsMobile, String> {
private boolean required;
@Override
public void initialize(IsMobile constraintAnnotation) {
//初始化时先获取他是否为必填信息
required = constraintAnnotation.required();
}
@Override
public boolean isValid(String s, ConstraintValidatorContext constraintValidatorContext) {
if (required) {
//必填则校验手机号
return Validator.isMobile(s);
} else {
//非必填直接返回ture
if (StrUtil.isBlank(s)) {
return true;
} else {
return Validator.isMobile(s);
}
}
}
}

View File

@ -0,0 +1,57 @@
package com.changhu.common.validator;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.changhu.common.validator.annotation.OnlyInValue;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
/**
* author: luozhun
* desc: OnlyInValueValidator
* createTime: 2023/8/22 9:40
*/
public class OnlyInValueValidator implements ConstraintValidator<OnlyInValue, Object> {
private boolean required = false;
private String[] strValues;
private int[] intValues;
@Override
public void initialize(OnlyInValue constraintAnnotation) {
required = constraintAnnotation.required();
strValues = constraintAnnotation.strValues();
intValues = constraintAnnotation.intValues();
}
@Override
public boolean isValid(Object value, ConstraintValidatorContext context) {
if (required) {
return valid(value);
} else {
if (ObjectUtil.isNull(value)) {
return true;
} else {
return valid(value);
}
}
}
private boolean valid(Object value) {
if (value instanceof String) {
for (String str : strValues) {
if (StrUtil.equals(str, value + "")) {
return true;
}
}
} else if (value instanceof Integer) {
for (int num : intValues) {
if (NumberUtil.equals(num, (int) value)) {
return true;
}
}
}
return false;
}
}

View File

@ -0,0 +1,36 @@
package com.changhu.common.validator.annotation;
import com.changhu.common.validator.IdCardValidator;
import jakarta.validation.Constraint;
import jakarta.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.CONSTRUCTOR;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.ElementType.TYPE_USE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* @author 20252
* @createTime 2024/8/21 下午3:34
* @desc IdCard...
*/
@Documented
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
@Retention(RUNTIME)
@Constraint(validatedBy = {IdCardValidator.class})
public @interface IdCard {
boolean required() default true;
String message() default "身份证格式错误!";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}

View File

@ -0,0 +1,27 @@
package com.changhu.common.validator.annotation;
import com.changhu.common.validator.IsMobileValidator;
import jakarta.validation.Constraint;
import jakarta.validation.Payload;
import java.lang.annotation.*;
/**
* author: luozhun
* desc: IsMobile
* createTime: 2023/8/22 9:38
*/
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(validatedBy = {IsMobileValidator.class})
public @interface IsMobile {
boolean required() default true;
String message() default "手机号码格式错误!";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}

View File

@ -0,0 +1,34 @@
package com.changhu.common.validator.annotation;
import com.changhu.common.validator.OnlyInValueValidator;
import jakarta.validation.Constraint;
import jakarta.validation.Payload;
import java.lang.annotation.*;
/**
* author: luozhun
* desc: 只能为定义的值 比如[1,0]
* createTime: 2023/8/22 9:41
*/
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(
validatedBy = {
OnlyInValueValidator.class
}
)
public @interface OnlyInValue {
boolean required() default true;
String message() default "必须为指定值";
String[] strValues() default {};
int[] intValues() default {};
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}

View File

@ -0,0 +1,55 @@
package com.changhu.config;
import com.changhu.common.properties.ThreadProperties;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.ThreadPoolExecutor;
/**
* author: luozhun
* desc: 线程池配置
* createTime: 2023/8/23 18:12
*/
@Slf4j
@Configuration
@EnableAsync
public class ThreadPollConfig {
@Resource
private ThreadProperties threadProperties;
@Bean
public ThreadPoolTaskExecutor threadPoolTaskExecutor() {
log.info("已加载 【{}】 的配置 【{}】:", "线 程 池", threadProperties);
ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
// 配置核心线程数
threadPoolTaskExecutor.setCorePoolSize(threadProperties.getCorePoolSize());
// 配置最大线程数
threadPoolTaskExecutor.setMaxPoolSize(threadProperties.getMaxPoolSize());
// 配置队列大小
threadPoolTaskExecutor.setQueueCapacity(threadProperties.getQueueCapacity());
// 配置存活时间
threadPoolTaskExecutor.setKeepAliveSeconds(threadProperties.getKeepAliveSeconds());
// 配置线程池中的线程的名称前缀
threadPoolTaskExecutor.setThreadNamePrefix(threadProperties.getThreadNamePrefix());
/*
* rejection-policy当pool已经达到max size的时候如何处理新任务?
* 1.ThreadPoolExecutor.AbortPolicy 丢弃任务并抛出RejectedExecutionException异常(默认)
* 2.ThreadPoolExecutor.DiscardPolicy 丢弃任务但是不抛出异常
* 3.ThreadPoolExecutor.DiscardOldestPolicy 丢弃队列最前面的任务然后重新尝试执行任务
* 4.ThreadPoolExecutor.CallerRunsPolicy 由调用线程处理该任务
*/
threadPoolTaskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
// 执行初始化
threadPoolTaskExecutor.initialize();
return threadPoolTaskExecutor;
}
}

View File

@ -0,0 +1,64 @@
package com.changhu.config;
import cn.dev33.satoken.interceptor.SaInterceptor;
import cn.dev33.satoken.stp.StpUtil;
import com.changhu.support.interceptor.JsonBodyInterceptor;
import org.jetbrains.annotations.NotNull;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.ArrayList;
import java.util.List;
/**
* author: luozhun
* desc: WebConfig
* createTime: 2023/8/18 10:56
*/
@Configuration
public class WebConfig implements WebMvcConfigurer {
private final List<String> whiteList = new ArrayList<>();
public WebConfig() {
whiteList.add("/managementSuperUser/**");
whiteList.add("/common/**");
whiteList.add("/test/**");
whiteList.add("/login");
whiteList.add("/logout");
whiteList.add("/favicon.ico");
//druid console
whiteList.add("/druid/**");
//knife4j
whiteList.add("/doc.html/**");
whiteList.add("/static/**");
whiteList.add("/swagger-resources");
whiteList.add("/**webjars/**");
whiteList.add("/v3/**");
//平台信息
whiteList.add("/platformSetting/getPlatformInfo");
}
@Override
public void addInterceptors(@NotNull InterceptorRegistry registry) {
// 注册 Sa-Token 拦截器校验规则为 StpUtil.checkLogin() 登录校验
registry.addInterceptor(new SaInterceptor(handle -> StpUtil.checkLogin()))
.addPathPatterns("/**")
.excludePathPatterns(whiteList);
// 注册jsonBody 拦截器 用于标识是否需要JsonResult返回
registry.addInterceptor(new JsonBodyInterceptor());
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("doc.html")
.addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("swagger-ui.html")
.addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/");
}
}

View File

@ -0,0 +1,26 @@
package com.changhu.controller;
import com.changhu.common.annotation.JsonBody;
import com.changhu.common.pojo.vo.TokenInfo;
import com.changhu.pojo.params.LoginParams;
import com.changhu.service.LoginService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
/**
* @author 20252
* @createTime 2024/8/28 下午5:12
* @desc LoginController...
*/
@JsonBody
public class LoginController {
@Autowired
private LoginService loginService;
@PostMapping("/login")
public TokenInfo login(@RequestBody LoginParams loginParams) {
return loginService.login(loginParams);
}
}

View File

@ -0,0 +1,25 @@
package com.changhu.enums;
import com.changhu.enums.handler.AbstractLoginHandler;
import com.changhu.enums.handler.ManagementSuperLogin;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* @author 20252
* @createTime 2024/8/28 下午5:19
* @desc ClientType...
*/
@Getter
@AllArgsConstructor
public enum ClientType {
MANAGEMENT_SUPER("management_super", "超级后台", ManagementSuperLogin.instance),
MANAGEMENT_POLICE("management_police", "公安后台", ManagementSuperLogin.instance),
MANAGEMENT_SECURITY("management_security", "保安后台", ManagementSuperLogin.instance),
MINI_PROGRAM("mini_program", "微信小程序", ManagementSuperLogin.instance),
;
private final String value;
private final String remark;
private final AbstractLoginHandler loginHandler;
}

View File

@ -0,0 +1,23 @@
package com.changhu.enums.handler;
import com.alibaba.fastjson2.JSONObject;
import com.changhu.common.pojo.vo.TokenInfo;
import org.apache.poi.ss.formula.functions.T;
/**
* @author 20252
* @createTime 2024/8/28 下午5:24
* @desc AbstractLoginHandler...
*/
public abstract class AbstractLoginHandler {
/**
* 统一登录方法
*
* @param jsonObject 登录参数
* @return token
*/
public abstract TokenInfo login(JSONObject jsonObject);
}

View File

@ -0,0 +1,52 @@
package com.changhu.enums.handler;
import cn.dev33.satoken.stp.SaTokenInfo;
import cn.hutool.extra.spring.SpringUtil;
import com.alibaba.fastjson2.JSONObject;
import com.changhu.common.enums.ResultCode;
import com.changhu.common.exception.MessageException;
import com.changhu.common.pojo.vo.TokenInfo;
import com.changhu.common.utils.RsaUtil;
import com.changhu.common.utils.UserUtil;
import com.changhu.common.utils.ValidatorUtil;
import com.changhu.module.management.pojo.entity.ManagementSuperUser;
import com.changhu.module.management.service.ManagementSuperUserService;
import com.changhu.pojo.params.ManagementSuperLoginParams;
/**
* @author 20252
* @createTime 2024/8/28 下午5:25
* @desc ManagementSuperLogin...
*/
public class ManagementSuperLogin extends AbstractLoginHandler {
private static final ManagementSuperUserService managementSuperUserService = SpringUtil.getBean(ManagementSuperUserService.class);
public static final ManagementSuperLogin instance = new ManagementSuperLogin();
private ManagementSuperLogin() {
}
@Override
public TokenInfo login(JSONObject jsonObject) {
ManagementSuperLoginParams loginParams = jsonObject.to(ManagementSuperLoginParams.class);
ValidatorUtil.manual(loginParams);
String telephone = loginParams.getTelephone();
String password = RsaUtil.decrypt(loginParams.getPassword());
//用户是否存在
ManagementSuperUser user = managementSuperUserService.lambdaQuery()
.eq(ManagementSuperUser::getTelephone, telephone)
.oneOpt()
.orElseThrow(() -> new MessageException("用户不存在"));
//判断密码是否正确
if (!UserUtil.verifyPassWord(password, user.getSalt(), user.getPassword())) {
throw new MessageException(ResultCode.PASSWORD_ERROR);
}
//登录
SaTokenInfo saTokenInfo = UserUtil.loginAndTokenInfo(user.getSnowFlakeId());
//返回token
return new TokenInfo(saTokenInfo.getTokenName(), saTokenInfo.getTokenValue());
}
}

View File

@ -0,0 +1,32 @@
package com.changhu.module.management.controller;
import com.changhu.common.annotation.JsonBody;
import com.changhu.module.management.pojo.params.ManagementSuperUserSaveOrUpdateParams;
import com.changhu.module.management.service.ManagementSuperUserService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* @author 20252
* @createTime 2024/8/28 下午5:12
* @desc 超级后台用户控制器
*/
@Tag(name = "后台用户-超级后台")
@JsonBody
@RequestMapping("/managementSuperUser")
public class ManagementSuperUserController {
@Autowired
private ManagementSuperUserService managementSuperUserService;
@Operation(summary = "新增或保存")
@PostMapping("/saveOrUpdate")
public void saveOrUpdate(@RequestBody @Valid ManagementSuperUserSaveOrUpdateParams params) {
managementSuperUserService.saveOrUpdate(params);
}
}

View File

@ -0,0 +1,57 @@
package com.changhu.module.management.controller;
import com.changhu.common.db.enums.IsOrNot;
import com.changhu.common.db.enums.DeleteFlag;
import java.time.LocalDateTime;
import com.changhu.common.db.enums.IsEnable;
import com.changhu.common.db.enums.Sex;
import com.changhu.common.annotation.JsonBody;
import com.changhu.module.management.pojo.entity.ManagementPoliceUnitUser;
import lombok.Data;
import org.springframework.web.bind.annotation.*;
/**
* @author 20252
* @createTime 2024/8/28 下午4:02
* @desc TestController...
*/
@JsonBody
@RequestMapping("/test")
public class TestController {
@GetMapping("/get")
public ManagementPoliceUnitUser get() {
ManagementPoliceUnitUser managementPoliceUnitUser = new ManagementPoliceUnitUser();
managementPoliceUnitUser.setPoliceUnitId(0L);
managementPoliceUnitUser.setName("");
managementPoliceUnitUser.setSex(Sex.MAN);
managementPoliceUnitUser.setTelephone("");
managementPoliceUnitUser.setSalt("");
managementPoliceUnitUser.setPassword("");
managementPoliceUnitUser.setIsEnable(IsEnable.TRUE);
managementPoliceUnitUser.setIsAdmin(IsOrNot.IS);
managementPoliceUnitUser.setSnowFlakeId(0L);
managementPoliceUnitUser.setCreateBy(0L);
managementPoliceUnitUser.setCreateTime(LocalDateTime.now());
managementPoliceUnitUser.setUpdateBy(0L);
managementPoliceUnitUser.setUpdateTime(LocalDateTime.now());
managementPoliceUnitUser.setDeleteFlag(0);
return managementPoliceUnitUser;
}
@PostMapping("/de")
public void test(@RequestBody P p) {
System.out.println(p);
}
@Data
class P {
private String name;
private DeleteFlag deleteFlag;
}
}

View File

@ -0,0 +1,15 @@
package com.changhu.module.management.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.changhu.module.management.pojo.entity.ManagementPoliceUnitUser;
import org.apache.ibatis.annotations.Mapper;
/**
* management_police_user (后台-公安单位用户表) 固化类
* author: luozhun
* desc 由groovy脚本自动生成
*/
@Mapper
public interface ManagementPoliceUserMapper extends BaseMapper<ManagementPoliceUnitUser> {
}

View File

@ -0,0 +1,15 @@
package com.changhu.module.management.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.changhu.module.management.pojo.entity.ManagementSecurityUnitUser;
import org.apache.ibatis.annotations.Mapper;
/**
* management_security_unit_user (后台-保安单位用户表) 固化类
* author: luozhun
* desc 由groovy脚本自动生成
*/
@Mapper
public interface ManagementSecurityUnitUserMapper extends BaseMapper<ManagementSecurityUnitUser> {
}

View File

@ -0,0 +1,15 @@
package com.changhu.module.management.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.changhu.module.management.pojo.entity.ManagementSuperUser;
import org.apache.ibatis.annotations.Mapper;
/**
* management_super_user (后台-超级后台) 固化类
* author: luozhun
* desc 由groovy脚本自动生成
*/
@Mapper
public interface ManagementSuperUserMapper extends BaseMapper<ManagementSuperUser> {
}

View File

@ -0,0 +1,75 @@
package com.changhu.module.management.pojo.entity;
import java.io.Serial;
import java.io.Serializable;
import com.changhu.common.db.enums.IsEnable;
import com.changhu.common.db.enums.IsOrNot;
import com.changhu.common.db.enums.Sex;
import com.changhu.support.mybatisplus.pojo.entity.BaseEntity;
import lombok.Data;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
import lombok.EqualsAndHashCode;
import com.baomidou.mybatisplus.annotation.TableName;
/**
* 后台-公安单位用户表 实体类
* author: luozhun
* desc 由groovy脚本自动生成
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@SuperBuilder
@EqualsAndHashCode(callSuper = true)
@TableName(autoResultMap = true)
public class ManagementPoliceUnitUser extends BaseEntity implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 所属公安机关id
*/
private Long policeUnitId;
/**
* 名称
*/
private String name;
/**
* 性别
*/
private Sex sex;
/**
* 手机号
*/
private String telephone;
/**
* 盐值
*/
private String salt;
/**
* 密码
*/
private String password;
/**
* 是否启用
*/
private IsEnable isEnable;
/**
* 是否是超管
*/
private IsOrNot isAdmin;
}

View File

@ -0,0 +1,80 @@
package com.changhu.module.management.pojo.entity;
import java.io.Serial;
import java.io.Serializable;
import com.changhu.common.db.enums.IsEnable;
import com.changhu.common.db.enums.IsOrNot;
import com.changhu.common.db.enums.Sex;
import com.changhu.support.mybatisplus.pojo.entity.BaseEntity;
import lombok.Data;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
import lombok.EqualsAndHashCode;
import com.baomidou.mybatisplus.annotation.TableName;
/**
* 后台-保安单位用户表 实体类
* author: luozhun
* desc 由groovy脚本自动生成
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@SuperBuilder
@EqualsAndHashCode(callSuper = true)
@TableName(autoResultMap = true)
public class ManagementSecurityUnitUser extends BaseEntity implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 所属保安公司id
*/
private Long securityUnitId;
/**
* 名称
*/
private String name;
/**
* 性别
*/
private Sex sex;
/**
* 手机号
*/
private String telephone;
/**
* 盐值
*/
private String salt;
/**
* 密码
*/
private String password;
/**
* 是否启用
*/
private IsEnable isEnable;
/**
* 是否是超管
*/
private IsOrNot isAdmin;
/**
* 备注
*/
private String remark;
}

View File

@ -0,0 +1,58 @@
package com.changhu.module.management.pojo.entity;
import java.io.Serial;
import java.io.Serializable;
import com.changhu.common.db.enums.IsEnable;
import com.changhu.support.mybatisplus.pojo.entity.BaseEntity;
import lombok.Data;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
import lombok.EqualsAndHashCode;
import com.baomidou.mybatisplus.annotation.TableName;
/**
* 后台-超级后台 实体类
* author: luozhun
* desc 由groovy脚本自动生成
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@SuperBuilder
@EqualsAndHashCode(callSuper = true)
@TableName(autoResultMap = true)
public class ManagementSuperUser extends BaseEntity implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 名称
*/
private String name;
/**
* 手机号
*/
private String telephone;
/**
* 盐值
*/
private String salt;
/**
* 密码
*/
private String password;
/**
* 是否启用
*/
private IsEnable isEnable;
}

View File

@ -0,0 +1,26 @@
package com.changhu.module.management.pojo.params;
import com.changhu.common.validator.annotation.IsMobile;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
/**
* @author 20252
* @createTime 2024/8/28 下午5:47
* @desc ManagementSuperUserSaveOrUpdateParams...
*/
@Data
public class ManagementSuperUserSaveOrUpdateParams {
@Schema(description = "id")
private Long snowFlakeId;
@Schema(description = "名称")
@NotNull(message = "用户名不能为空")
private String name;
@Schema(description = "手机号")
@IsMobile
private String telephone;
}

View File

@ -0,0 +1,13 @@
package com.changhu.module.management.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.changhu.module.management.pojo.entity.ManagementPoliceUnitUser;
/**
* management_police_user (后台-公安单位用户表) 服务类
* author: luozhun
* desc 由groovy脚本自动生成
*/
public interface ManagementPoliceUserService extends IService<ManagementPoliceUnitUser> {
}

View File

@ -0,0 +1,13 @@
package com.changhu.module.management.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.changhu.module.management.pojo.entity.ManagementSecurityUnitUser;
/**
* management_security_unit_user (后台-保安单位用户表) 服务类
* author: luozhun
* desc 由groovy脚本自动生成
*/
public interface ManagementSecurityUnitUserService extends IService<ManagementSecurityUnitUser> {
}

View File

@ -0,0 +1,20 @@
package com.changhu.module.management.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.changhu.module.management.pojo.entity.ManagementSuperUser;
import com.changhu.module.management.pojo.params.ManagementSuperUserSaveOrUpdateParams;
/**
* management_super_user (后台-超级后台) 服务类
* author: luozhun
* desc 由groovy脚本自动生成
*/
public interface ManagementSuperUserService extends IService<ManagementSuperUser> {
/**
* 新增或保存
*
* @param params 参数
*/
void saveOrUpdate(ManagementSuperUserSaveOrUpdateParams params);
}

Some files were not shown because too many files have changed in this diff Show More