├── .gitattributes
├── .github
└── logo.png
├── .gitignore
├── .mvn
└── wrapper
│ └── maven-wrapper.properties
├── Dockerfile
├── LICENSE
├── README.md
├── deploy
└── compose
│ ├── .env
│ ├── docker-compose.yml
│ └── https
│ ├── .env
│ ├── docker-compose.yml
│ └── nginx.conf
├── mvnw
├── mvnw.cmd
├── pom.xml
└── src
├── main
├── java
│ └── com
│ │ └── cershy
│ │ └── linyuminiserver
│ │ ├── LinyuMiniServerApplication.java
│ │ ├── annotation
│ │ ├── CommandInfo.java
│ │ ├── UrlFree.java
│ │ ├── UrlLimit.java
│ │ ├── UrlResource.java
│ │ ├── UserIp.java
│ │ └── Userid.java
│ │ ├── aop
│ │ └── UrlLimitAspect.java
│ │ ├── configs
│ │ ├── AsyncConfig.java
│ │ ├── DatabaseInitializer.java
│ │ ├── LinyuConfig.java
│ │ ├── MybatisHandler.java
│ │ ├── SensitiveWordConfig.java
│ │ ├── UserInfoArgumentResolver.java
│ │ └── WebMvcConfig.java
│ │ ├── constant
│ │ ├── BadgeType.java
│ │ ├── ChatListType.java
│ │ ├── LimitKeyType.java
│ │ ├── MessageSource.java
│ │ ├── MessageType.java
│ │ ├── NotifyType.java
│ │ ├── TextContentType.java
│ │ ├── UserType.java
│ │ └── WsContentType.java
│ │ ├── controller
│ │ ├── ChatListController.java
│ │ ├── FileController.java
│ │ ├── LoginController.java
│ │ ├── MessageController.java
│ │ ├── UserController.java
│ │ └── VideoController.java
│ │ ├── dto
│ │ ├── NotifyDto.java
│ │ ├── UrlLimitStats.java
│ │ └── UserDto.java
│ │ ├── entity
│ │ ├── ChatList.java
│ │ ├── Group.java
│ │ ├── Message.java
│ │ └── User.java
│ │ ├── exception
│ │ ├── GlobalExceptionHandler.java
│ │ └── LinyuException.java
│ │ ├── filter
│ │ └── AuthenticationTokenFilter.java
│ │ ├── mapper
│ │ ├── ChatListMapper.java
│ │ ├── GroupMapper.java
│ │ ├── MessageMapper.java
│ │ └── UserMapper.java
│ │ ├── runner
│ │ └── UrlPassRunner.java
│ │ ├── schedule
│ │ └── ExpiredClearTask.java
│ │ ├── service
│ │ ├── AiChatService.java
│ │ ├── ChatListService.java
│ │ ├── DeepSeekAiService.java
│ │ ├── DoubaoAiService.java
│ │ ├── FileService.java
│ │ ├── GroupService.java
│ │ ├── LoginService.java
│ │ ├── MessageService.java
│ │ ├── SshServerService.java
│ │ ├── UserService.java
│ │ ├── VideoService.java
│ │ ├── WebSocketService.java
│ │ └── impl
│ │ │ ├── ChatListServiceImpl.java
│ │ │ ├── GroupServiceImpl.java
│ │ │ ├── MessageServiceImpl.java
│ │ │ └── UserServiceImpl.java
│ │ ├── ssh
│ │ ├── CommandManager.java
│ │ ├── CustomCommand.java
│ │ ├── InteractionConnect.java
│ │ └── commands
│ │ │ ├── LinyuHelpCommand.java
│ │ │ ├── LinyuMsgCommand.java
│ │ │ └── MessageCommand.java
│ │ ├── utils
│ │ ├── CacheUtil.java
│ │ ├── IpUtil.java
│ │ ├── JwtUtil.java
│ │ ├── ResultUtil.java
│ │ ├── SecurityUtil.java
│ │ └── UrlPermitUtil.java
│ │ ├── vo
│ │ ├── chatList
│ │ │ ├── CreateVo.java
│ │ │ ├── DeleteVo.java
│ │ │ └── ReadVo.java
│ │ ├── file
│ │ │ ├── AcceptVo.java
│ │ │ ├── AnswerVo.java
│ │ │ ├── CancelVo.java
│ │ │ ├── CandidateVo.java
│ │ │ ├── InviteVo.java
│ │ │ └── OfferVo.java
│ │ ├── login
│ │ │ ├── LoginVo.java
│ │ │ └── VerifyVo.java
│ │ ├── message
│ │ │ ├── RecallVo.java
│ │ │ ├── RecordVo.java
│ │ │ ├── SendMessageVo.java
│ │ │ └── TextMessageContent.java
│ │ ├── user
│ │ │ ├── CreateUserVo.java
│ │ │ └── UpdateUserVo.java
│ │ └── video
│ │ │ ├── AcceptVo.java
│ │ │ ├── AnswerVo.java
│ │ │ ├── CandidateVo.java
│ │ │ ├── HangupVo.java
│ │ │ ├── InviteVo.java
│ │ │ └── OfferVo.java
│ │ └── websocket
│ │ ├── HttpHeadersHandler.java
│ │ ├── NettyUtil.java
│ │ ├── NettyWebSocketServer.java
│ │ └── NettyWebSocketServerHandler.java
└── resources
│ ├── application-docker.yml
│ ├── application.yml
│ ├── ip2region.xdb
│ ├── linyu-mini-mysql.sql
│ ├── linyu-mini-sqlite.sql
│ └── mapper
│ ├── ChatListMapper.xml
│ ├── GroupMapper.xml
│ ├── MessageMapper.xml
│ └── UserMapper.xml
└── test
└── java
└── com
└── cershy
└── linyuminiserver
└── LinyuMiniServerApplicationTests.java
/.gitattributes:
--------------------------------------------------------------------------------
1 | /mvnw text eol=lf
2 | *.cmd text eol=crlf
3 |
--------------------------------------------------------------------------------
/.github/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/linyu-im/linyu-mini-server/5fdc2df4f912017ab93a8428d526c72a57ffd7d3/.github/logo.png
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | HELP.md
2 | target/
3 | !.mvn/wrapper/maven-wrapper.jar
4 | !**/src/main/**/target/
5 | !**/src/test/**/target/
6 |
7 | ### STS ###
8 | .apt_generated
9 | .classpath
10 | .factorypath
11 | .project
12 | .settings
13 | .springBeans
14 | .sts4-cache
15 |
16 | ### IntelliJ IDEA ###
17 | .idea
18 | *.iws
19 | *.iml
20 | *.ipr
21 |
22 | ### NetBeans ###
23 | /nbproject/private/
24 | /nbbuild/
25 | /dist/
26 | /nbdist/
27 | /.nb-gradle/
28 | build/
29 | !**/src/main/**/build/
30 | !**/src/test/**/build/
31 |
32 | ### VS Code ###
33 | .vscode/
34 |
--------------------------------------------------------------------------------
/.mvn/wrapper/maven-wrapper.properties:
--------------------------------------------------------------------------------
1 | # Licensed to the Apache Software Foundation (ASF) under one
2 | # or more contributor license agreements. See the NOTICE file
3 | # distributed with this work for additional information
4 | # regarding copyright ownership. The ASF licenses this file
5 | # to you under the Apache License, Version 2.0 (the
6 | # "License"); you may not use this file except in compliance
7 | # with the License. You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing,
12 | # software distributed under the License is distributed on an
13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14 | # KIND, either express or implied. See the License for the
15 | # specific language governing permissions and limitations
16 | # under the License.
17 | wrapperVersion=3.3.2
18 | distributionType=only-script
19 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip
20 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM openjdk:8-jdk-alpine
2 |
3 | WORKDIR /app
4 | COPY ./target/*.jar /app/app.jar
5 |
6 | EXPOSE 9100
7 | EXPOSE 9200
8 |
9 | CMD ["java", "-jar", "-Dspring.profiles.active=docker", "app.jar"]
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
Linyu-Mini
6 |
一个轻量级的在线聊天室系统,支持实时消息交流,适合多场景使用。系统采用轻量级架构,具备快速响应能力,同时提供多种实用功能,如用户登录、消息记录、群组聊天等,确保良好的用户体验和高效的沟通效果
7 |

8 |

9 |

10 |

11 |

12 |

13 |
14 |
15 | ## 介绍
16 |
17 | 林语Mini(Linyu-mini)是一款基于 Springboot 和 Netty
18 | 构建的高性能即时通讯在线聊天系统。系统以轻量化设计为核心,具备快速部署和便捷扩展的特点,适用于企业内部协作、团队沟通以及小型社交平台等多种场景。
19 |
20 | ## 相关环境
21 |
22 | - java版本:8
23 | - springboot版本:2.6.7
24 | - netty版本:4.1.108.Final
25 |
26 | ## 技术栈
27 |
28 | - Java:一种广泛使用的高级编程语言,具备跨平台特性和高效的性能,广泛应用于企业级应用开发。Java 提供了丰富的类库和框架,支持高并发和分布式系统的构建。
29 |
30 | - Spring Boot:一个基于 Spring 框架的快速开发框架,简化了企业级应用的构建和配置,能够让开发者专注于业务逻辑的实现。它提供了一整套用于构建和部署生产级应用的开箱即用的功能。
31 |
32 | - Netty:一个高性能、异步事件驱动的网络通信框架,适用于构建高效的网络应用。Netty
33 | 支持多种协议,能够提供低延迟和高吞吐量的网络服务,广泛应用于即时通讯、游戏服务器等高并发场景。
34 |
35 | - MySQL:一种流行的关系型数据库管理系统,具备高效的查询能力和事务处理能力,支持大规模数据存储和高并发访问,是很多企业应用的首选数据库解决方案。
36 |
37 | - Caffeine:一个高效的 Java 缓存库,提供了先进的缓存机制和过期策略,能够优化系统性能并减少数据库负载。
38 |
39 | ## 项目效果
40 |
41 |
42 |
43 | 浅色 |
44 | 深色 |
45 |
46 |
47 |  |
48 |  |
49 |
50 |
51 |  |
52 |  |
53 |
54 |
55 |  |
56 |  |
57 |
58 |
59 |
60 | ## 免责声明
61 |
62 | ### 1. 基本声明
63 |
64 | 本软件作为开源项目提供,在法律允许的最大范围内,开发者不对软件的功能性、安全性或适用性作出任何形式的保证,无论是明示的还是暗示的。
65 |
66 | ### 2. 使用风险声明
67 |
68 | 2.1 本软件按"现状"提供,使用者需自行承担使用本软件的全部风险。
69 | 2.2 开发者不对软件的运行可靠性、适用性或与特定需求的兼容性提供任何保证。
70 | 2.3 使用者应在充分评估风险的基础上决定是否使用本软件。
71 |
72 | ### 3. 责任限制与豁免
73 |
74 | 在任何情况下,开发者及其关联方均不对因使用或无法使用本软件而导致的任何损失或损害承担责任,包括但不限于:
75 |
76 | - 数据丢失或泄露
77 | - 利润损失
78 | - 系统中断
79 | - 商业机会损失
80 | - 其他直接、间接或衍生性损失
81 |
82 | ### 4. 用户义务与责任
83 |
84 | 4.1 使用者应确保其对本软件的使用符合所有适用的法律法规要求。
85 | 4.2 对本软件进行修改、分发或二次开发的使用者,需自行承担由此产生的全部责任,包括但不限于:
86 |
87 | - 法律风险
88 | - 知识产权风险
89 | - 安全风险
90 | - 数据保护责任
91 |
92 | ### 5. 开发者权利
93 |
94 | 5.1 开发者保留对本软件进行更新、修改、调整或停止维护的权利。
95 | 5.2 开发者可能在不事先通知的情况下修改本软件或相关服务。
96 | 5.3 开发者保留对本免责声明进行修改的权利。
97 |
98 | ### 6. 开源贡献
99 |
100 | 6.1 本软件欢迎社区贡献,但贡献者需遵守相关开源协议。
101 | 6.2 开发者不对第三方贡献的代码质量和安全性负责。
102 |
103 | ### 7. 其他条款
104 |
105 | 7.1 本免责声明的任何部分被认定为无效或不可执行时,其余部分仍然有效。
106 | 7.2 本免责声明的最终解释权归开发者所有。
--------------------------------------------------------------------------------
/deploy/compose/.env:
--------------------------------------------------------------------------------
1 | #服务地址(域名)
2 | SERVER_NAME=localhost
3 |
4 | #sqlite文件地址
5 | SQLITE_FILE_PATH=/linyu/data/sqlite.db
6 | #日志文件地址
7 | LINYU_LOG_PATH=/linyu/logs/linyu-mini.log
8 | #群聊密码
9 | LINYU_PASSWORD=sun55@kong
10 | #限制用户数量
11 | LINYU_LIMIT=100
12 | #聊天室名称
13 | LINYU_NAME=Linyu在线聊天室
14 | #数据过期时间(天)
15 | LINYU_EXPIRES=7
16 |
17 | # MySQL配置
18 | MYSQL_ROOT_PASSWORD=@zhu88jie
19 | MYSQL_DATABASE=linyu-mini
20 | MYSQL_USER=linyu
21 | MYSQL_PASSWORD=@zhu88jie
22 |
23 | #豆包配置
24 | LINYU_DOUBAO_API_KEY=apikey
25 | #次数限制,0-不限制
26 | LINYU_DOUBAO_COUNT_LIMIT=5
27 | #内容长度限制,0-不限制
28 | LINYU_DOUBAO_LENGTH_LIMIT=50
29 | #使用的模型
30 | LINYU_DOUBAO_MODEL=model
31 |
32 | #deepseek配置
33 | LINYU_DEEPSEEK_API_KEY=apikey
34 | #次数限制,0-不限制
35 | LINYU_DEEPSEEK_COUNT_LIMIT=2
36 | #内容长度限制,0-不限制
37 | LINYU_DEEPSEEK_LENGTH_LIMIT=50
38 | #使用的模型
39 | LINYU_DEEPSEEK_MODEL=model
40 |
--------------------------------------------------------------------------------
/deploy/compose/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '3.8'
2 | services:
3 | mysql:
4 | image: mysql:8.0
5 | container_name: mysql
6 | environment:
7 | MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
8 | MYSQL_DATABASE: ${MYSQL_DATABASE}
9 | MYSQL_USER: ${MYSQL_USER}
10 | MYSQL_PASSWORD: ${MYSQL_PASSWORD}
11 | ports:
12 | - "3306:3306"
13 | volumes:
14 | - /linyu/mysql:/var/lib/mysql
15 |
16 | linyu-mini-web:
17 | image: henam/linyu-mini-web:latest
18 | container_name: linyu-mini-web
19 | ports:
20 | - "80:80"
21 | depends_on:
22 | - linyu-mini-server
23 | environment:
24 | SERVER_NAME: ${SERVER_NAME}
25 | SERVER_HTTP_URL: http://${SERVER_NAME}
26 | SERVER_WS_URL: ws://${SERVER_NAME}
27 |
28 | linyu-mini-server:
29 | image: henam/linyu-mini-server:latest
30 | container_name: linyu-mini-server
31 | volumes:
32 | - /linyu/data:/linyu/data
33 | - /linyu/logs:/linyu/logs
34 | ports:
35 | - "9100:9100"
36 | - "9200:9200"
37 | depends_on:
38 | - mysql
39 | environment:
40 | SQLITE_FILE_PATH: ${SQLITE_FILE_PATH}
41 | LINYU_LOG_PATH: ${LINYU_LOG_PATH}
42 | LINYU_PASSWORD: ${LINYU_PASSWORD}
43 | LINYU_LIMIT: ${LINYU_LIMIT}
44 | LINYU_NAME: ${LINYU_NAME}
45 | LINYU_EXPIRES: ${LINYU_EXPIRES}
46 | MYSQL_DATABASE: ${MYSQL_DATABASE}
47 | MYSQL_USER: ${MYSQL_USER}
48 | MYSQL_PASSWORD: ${MYSQL_PASSWORD}
49 | LINYU_DOUBAO_API_KEY: ${LINYU_DOUBAO_API_KEY}
50 | LINYU_DOUBAO_COUNT_LIMIT: ${LINYU_DOUBAO_COUNT_LIMIT}
51 | LINYU_DOUBAO_LENGTH_LIMIT: ${LINYU_DOUBAO_LENGTH_LIMIT}
52 | LINYU_DOUBAO_MODEL: ${LINYU_DOUBAO_MODEL}
53 | LINYU_DEEPSEEK_API_KEY: ${LINYU_DEEPSEEK_API_KEY}
54 | LINYU_DEEPSEEK_COUNT_LIMIT: ${LINYU_DEEPSEEK_COUNT_LIMIT}
55 | LINYU_DEEPSEEK_LENGTH_LIMIT: ${LINYU_DEEPSEEK_LENGTH_LIMIT}
56 | LINYU_DEEPSEEK_MODEL: ${LINYU_DEEPSEEK_MODEL}
--------------------------------------------------------------------------------
/deploy/compose/https/.env:
--------------------------------------------------------------------------------
1 | #服务地址(域名)
2 | SERVER_NAME=localhost
3 |
4 | #sqlite文件地址
5 | SQLITE_FILE_PATH=/linyu/data/sqlite.db
6 | #日志文件地址
7 | LINYU_LOG_PATH=/linyu/logs/linyu-mini.log
8 | #群聊密码
9 | LINYU_PASSWORD=sun55@kong
10 | #限制用户数量
11 | LINYU_LIMIT=100
12 | #聊天室名称
13 | LINYU_NAME=Linyu在线聊天室
14 | #数据过期时间(天)
15 | LINYU_EXPIRES=7
16 |
17 | # MySQL配置
18 | MYSQL_ROOT_PASSWORD=@zhu88jie
19 | MYSQL_DATABASE=linyu-mini
20 | MYSQL_USER=linyu
21 | MYSQL_PASSWORD=@zhu88jie
22 |
23 | #豆包配置
24 | LINYU_DOUBAO_API_KEY=apikey
25 | #次数限制,0-不限制
26 | LINYU_DOUBAO_COUNT_LIMIT=5
27 | #内容长度限制,0-不限制
28 | LINYU_DOUBAO_LENGTH_LIMIT=50
29 | #使用的模型
30 | LINYU_DOUBAO_MODEL=model
31 |
32 | #deepseek配置
33 | LINYU_DEEPSEEK_API_KEY=apikey
34 | #次数限制,0-不限制
35 | LINYU_DEEPSEEK_COUNT_LIMIT=2
36 | #内容长度限制,0-不限制
37 | LINYU_DEEPSEEK_LENGTH_LIMIT=50
38 | #使用的模型
39 | LINYU_DEEPSEEK_MODEL=model
--------------------------------------------------------------------------------
/deploy/compose/https/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '3.8'
2 | services:
3 | mysql:
4 | image: mysql:8.0
5 | container_name: mysql
6 | environment:
7 | MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
8 | MYSQL_DATABASE: ${MYSQL_DATABASE}
9 | MYSQL_USER: ${MYSQL_USER}
10 | MYSQL_PASSWORD: ${MYSQL_PASSWORD}
11 | ports:
12 | - "3306:3306"
13 | volumes:
14 | - /linyu/mysql:/var/lib/mysql
15 |
16 | linyu-mini-web:
17 | image: henam/linyu-mini-web:latest
18 | container_name: linyu-mini-web
19 | ports:
20 | - "80:80"
21 | - "443:443"
22 | volumes:
23 | - /linyu/nginx/ssl:/etc/nginx/ssl
24 | - /linyu/nginx/nginx.conf:/etc/nginx/nginx.conf
25 | depends_on:
26 | - linyu-mini-server
27 | environment:
28 | SERVER_NAME: ${SERVER_NAME}
29 | SERVER_HTTP_URL: https://${SERVER_NAME}
30 | SERVER_WS_URL: wss://${SERVER_NAME}
31 |
32 | linyu-mini-server:
33 | image: henam/linyu-mini-server:latest
34 | container_name: linyu-mini-server
35 | volumes:
36 | - /linyu/data:/linyu/data
37 | - /linyu/logs:/linyu/logs
38 | ports:
39 | - "9100:9100"
40 | - "9200:9200"
41 | depends_on:
42 | - mysql
43 | environment:
44 | SQLITE_FILE_PATH: ${SQLITE_FILE_PATH}
45 | LINYU_LOG_PATH: ${LINYU_LOG_PATH}
46 | LINYU_PASSWORD: ${LINYU_PASSWORD}
47 | LINYU_LIMIT: ${LINYU_LIMIT}
48 | LINYU_NAME: ${LINYU_NAME}
49 | LINYU_EXPIRES: ${LINYU_EXPIRES}
50 | MYSQL_DATABASE: ${MYSQL_DATABASE}
51 | MYSQL_USER: ${MYSQL_USER}
52 | MYSQL_PASSWORD: ${MYSQL_PASSWORD}
53 | LINYU_DOUBAO_API_KEY: ${LINYU_DOUBAO_API_KEY}
54 | LINYU_DOUBAO_COUNT_LIMIT: ${LINYU_DOUBAO_COUNT_LIMIT}
55 | LINYU_DOUBAO_LENGTH_LIMIT: ${LINYU_DOUBAO_LENGTH_LIMIT}
56 | LINYU_DOUBAO_MODEL: ${LINYU_DOUBAO_MODEL}
57 | LINYU_DEEPSEEK_API_KEY: ${LINYU_DEEPSEEK_API_KEY}
58 | LINYU_DEEPSEEK_COUNT_LIMIT: ${LINYU_DEEPSEEK_COUNT_LIMIT}
59 | LINYU_DEEPSEEK_LENGTH_LIMIT: ${LINYU_DEEPSEEK_LENGTH_LIMIT}
60 | LINYU_DEEPSEEK_MODEL: ${LINYU_DEEPSEEK_MODEL}
--------------------------------------------------------------------------------
/deploy/compose/https/nginx.conf:
--------------------------------------------------------------------------------
1 | user nginx;
2 | worker_processes auto;
3 |
4 | error_log /var/log/nginx/error.log notice;
5 | pid /var/run/nginx.pid;
6 |
7 | events {
8 | worker_connections 1024;
9 | }
10 |
11 | http {
12 | include /etc/nginx/mime.types;
13 | default_type application/octet-stream;
14 |
15 | log_format main '$remote_addr - $remote_user [$time_local] "$request" '
16 | '$status $body_bytes_sent "$http_referer" '
17 | '"$http_user_agent" "$http_x_forwarded_for"';
18 |
19 | access_log /var/log/nginx/access.log main;
20 |
21 | sendfile on;
22 | keepalive_timeout 65;
23 |
24 | server {
25 | listen 80;
26 | # 对应服务
27 | server_name $SERVER_NAME;
28 | return 301 https://$host$request_uri;
29 | }
30 |
31 | server {
32 | listen 443 ssl;
33 | # 对应服务
34 | server_name $SERVER_NAME;
35 |
36 | ssl_certificate /etc/nginx/ssl/cert.pem;
37 | ssl_certificate_key /etc/nginx/ssl/cert.key;
38 |
39 | # 前端服务
40 | location / {
41 | root /usr/share/nginx/html;
42 | index index.html;
43 | try_files $uri $uri/ /index.html;
44 | }
45 |
46 | # 后端api
47 | location /api/ {
48 | proxy_pass http://linyu-mini-server:9200;
49 | proxy_set_header Host $host;
50 | proxy_set_header X-Real-IP $remote_addr;
51 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
52 | }
53 |
54 | # 后端websocket
55 | location /ws {
56 | proxy_pass http://linyu-mini-server:9100;
57 | proxy_http_version 1.1;
58 | proxy_set_header Upgrade $http_upgrade;
59 | proxy_set_header Connection 'upgrade';
60 | proxy_set_header Host $host;
61 | proxy_set_header X-Real-IP $remote_addr;
62 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
63 | }
64 |
65 | error_page 404 /index.html;
66 | }
67 | }
--------------------------------------------------------------------------------
/mvnw:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | # ----------------------------------------------------------------------------
3 | # Licensed to the Apache Software Foundation (ASF) under one
4 | # or more contributor license agreements. See the NOTICE file
5 | # distributed with this work for additional information
6 | # regarding copyright ownership. The ASF licenses this file
7 | # to you under the Apache License, Version 2.0 (the
8 | # "License"); you may not use this file except in compliance
9 | # with the License. You may obtain a copy of the License at
10 | #
11 | # http://www.apache.org/licenses/LICENSE-2.0
12 | #
13 | # Unless required by applicable law or agreed to in writing,
14 | # software distributed under the License is distributed on an
15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 | # KIND, either express or implied. See the License for the
17 | # specific language governing permissions and limitations
18 | # under the License.
19 | # ----------------------------------------------------------------------------
20 |
21 | # ----------------------------------------------------------------------------
22 | # Apache Maven Wrapper startup batch script, version 3.3.2
23 | #
24 | # Optional ENV vars
25 | # -----------------
26 | # JAVA_HOME - location of a JDK home dir, required when download maven via java source
27 | # MVNW_REPOURL - repo url base for downloading maven distribution
28 | # MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven
29 | # MVNW_VERBOSE - true: enable verbose log; debug: trace the mvnw script; others: silence the output
30 | # ----------------------------------------------------------------------------
31 |
32 | set -euf
33 | [ "${MVNW_VERBOSE-}" != debug ] || set -x
34 |
35 | # OS specific support.
36 | native_path() { printf %s\\n "$1"; }
37 | case "$(uname)" in
38 | CYGWIN* | MINGW*)
39 | [ -z "${JAVA_HOME-}" ] || JAVA_HOME="$(cygpath --unix "$JAVA_HOME")"
40 | native_path() { cygpath --path --windows "$1"; }
41 | ;;
42 | esac
43 |
44 | # set JAVACMD and JAVACCMD
45 | set_java_home() {
46 | # For Cygwin and MinGW, ensure paths are in Unix format before anything is touched
47 | if [ -n "${JAVA_HOME-}" ]; then
48 | if [ -x "$JAVA_HOME/jre/sh/java" ]; then
49 | # IBM's JDK on AIX uses strange locations for the executables
50 | JAVACMD="$JAVA_HOME/jre/sh/java"
51 | JAVACCMD="$JAVA_HOME/jre/sh/javac"
52 | else
53 | JAVACMD="$JAVA_HOME/bin/java"
54 | JAVACCMD="$JAVA_HOME/bin/javac"
55 |
56 | if [ ! -x "$JAVACMD" ] || [ ! -x "$JAVACCMD" ]; then
57 | echo "The JAVA_HOME environment variable is not defined correctly, so mvnw cannot run." >&2
58 | echo "JAVA_HOME is set to \"$JAVA_HOME\", but \"\$JAVA_HOME/bin/java\" or \"\$JAVA_HOME/bin/javac\" does not exist." >&2
59 | return 1
60 | fi
61 | fi
62 | else
63 | JAVACMD="$(
64 | 'set' +e
65 | 'unset' -f command 2>/dev/null
66 | 'command' -v java
67 | )" || :
68 | JAVACCMD="$(
69 | 'set' +e
70 | 'unset' -f command 2>/dev/null
71 | 'command' -v javac
72 | )" || :
73 |
74 | if [ ! -x "${JAVACMD-}" ] || [ ! -x "${JAVACCMD-}" ]; then
75 | echo "The java/javac command does not exist in PATH nor is JAVA_HOME set, so mvnw cannot run." >&2
76 | return 1
77 | fi
78 | fi
79 | }
80 |
81 | # hash string like Java String::hashCode
82 | hash_string() {
83 | str="${1:-}" h=0
84 | while [ -n "$str" ]; do
85 | char="${str%"${str#?}"}"
86 | h=$(((h * 31 + $(LC_CTYPE=C printf %d "'$char")) % 4294967296))
87 | str="${str#?}"
88 | done
89 | printf %x\\n $h
90 | }
91 |
92 | verbose() { :; }
93 | [ "${MVNW_VERBOSE-}" != true ] || verbose() { printf %s\\n "${1-}"; }
94 |
95 | die() {
96 | printf %s\\n "$1" >&2
97 | exit 1
98 | }
99 |
100 | trim() {
101 | # MWRAPPER-139:
102 | # Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds.
103 | # Needed for removing poorly interpreted newline sequences when running in more
104 | # exotic environments such as mingw bash on Windows.
105 | printf "%s" "${1}" | tr -d '[:space:]'
106 | }
107 |
108 | # parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties
109 | while IFS="=" read -r key value; do
110 | case "${key-}" in
111 | distributionUrl) distributionUrl=$(trim "${value-}") ;;
112 | distributionSha256Sum) distributionSha256Sum=$(trim "${value-}") ;;
113 | esac
114 | done <"${0%/*}/.mvn/wrapper/maven-wrapper.properties"
115 | [ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in ${0%/*}/.mvn/wrapper/maven-wrapper.properties"
116 |
117 | case "${distributionUrl##*/}" in
118 | maven-mvnd-*bin.*)
119 | MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/
120 | case "${PROCESSOR_ARCHITECTURE-}${PROCESSOR_ARCHITEW6432-}:$(uname -a)" in
121 | *AMD64:CYGWIN* | *AMD64:MINGW*) distributionPlatform=windows-amd64 ;;
122 | :Darwin*x86_64) distributionPlatform=darwin-amd64 ;;
123 | :Darwin*arm64) distributionPlatform=darwin-aarch64 ;;
124 | :Linux*x86_64*) distributionPlatform=linux-amd64 ;;
125 | *)
126 | echo "Cannot detect native platform for mvnd on $(uname)-$(uname -m), use pure java version" >&2
127 | distributionPlatform=linux-amd64
128 | ;;
129 | esac
130 | distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip"
131 | ;;
132 | maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;;
133 | *) MVN_CMD="mvn${0##*/mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;;
134 | esac
135 |
136 | # apply MVNW_REPOURL and calculate MAVEN_HOME
137 | # maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/
138 | [ -z "${MVNW_REPOURL-}" ] || distributionUrl="$MVNW_REPOURL$_MVNW_REPO_PATTERN${distributionUrl#*"$_MVNW_REPO_PATTERN"}"
139 | distributionUrlName="${distributionUrl##*/}"
140 | distributionUrlNameMain="${distributionUrlName%.*}"
141 | distributionUrlNameMain="${distributionUrlNameMain%-bin}"
142 | MAVEN_USER_HOME="${MAVEN_USER_HOME:-${HOME}/.m2}"
143 | MAVEN_HOME="${MAVEN_USER_HOME}/wrapper/dists/${distributionUrlNameMain-}/$(hash_string "$distributionUrl")"
144 |
145 | exec_maven() {
146 | unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || :
147 | exec "$MAVEN_HOME/bin/$MVN_CMD" "$@" || die "cannot exec $MAVEN_HOME/bin/$MVN_CMD"
148 | }
149 |
150 | if [ -d "$MAVEN_HOME" ]; then
151 | verbose "found existing MAVEN_HOME at $MAVEN_HOME"
152 | exec_maven "$@"
153 | fi
154 |
155 | case "${distributionUrl-}" in
156 | *?-bin.zip | *?maven-mvnd-?*-?*.zip) ;;
157 | *) die "distributionUrl is not valid, must match *-bin.zip or maven-mvnd-*.zip, but found '${distributionUrl-}'" ;;
158 | esac
159 |
160 | # prepare tmp dir
161 | if TMP_DOWNLOAD_DIR="$(mktemp -d)" && [ -d "$TMP_DOWNLOAD_DIR" ]; then
162 | clean() { rm -rf -- "$TMP_DOWNLOAD_DIR"; }
163 | trap clean HUP INT TERM EXIT
164 | else
165 | die "cannot create temp dir"
166 | fi
167 |
168 | mkdir -p -- "${MAVEN_HOME%/*}"
169 |
170 | # Download and Install Apache Maven
171 | verbose "Couldn't find MAVEN_HOME, downloading and installing it ..."
172 | verbose "Downloading from: $distributionUrl"
173 | verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName"
174 |
175 | # select .zip or .tar.gz
176 | if ! command -v unzip >/dev/null; then
177 | distributionUrl="${distributionUrl%.zip}.tar.gz"
178 | distributionUrlName="${distributionUrl##*/}"
179 | fi
180 |
181 | # verbose opt
182 | __MVNW_QUIET_WGET=--quiet __MVNW_QUIET_CURL=--silent __MVNW_QUIET_UNZIP=-q __MVNW_QUIET_TAR=''
183 | [ "${MVNW_VERBOSE-}" != true ] || __MVNW_QUIET_WGET='' __MVNW_QUIET_CURL='' __MVNW_QUIET_UNZIP='' __MVNW_QUIET_TAR=v
184 |
185 | # normalize http auth
186 | case "${MVNW_PASSWORD:+has-password}" in
187 | '') MVNW_USERNAME='' MVNW_PASSWORD='' ;;
188 | has-password) [ -n "${MVNW_USERNAME-}" ] || MVNW_USERNAME='' MVNW_PASSWORD='' ;;
189 | esac
190 |
191 | if [ -z "${MVNW_USERNAME-}" ] && command -v wget >/dev/null; then
192 | verbose "Found wget ... using wget"
193 | wget ${__MVNW_QUIET_WGET:+"$__MVNW_QUIET_WGET"} "$distributionUrl" -O "$TMP_DOWNLOAD_DIR/$distributionUrlName" || die "wget: Failed to fetch $distributionUrl"
194 | elif [ -z "${MVNW_USERNAME-}" ] && command -v curl >/dev/null; then
195 | verbose "Found curl ... using curl"
196 | curl ${__MVNW_QUIET_CURL:+"$__MVNW_QUIET_CURL"} -f -L -o "$TMP_DOWNLOAD_DIR/$distributionUrlName" "$distributionUrl" || die "curl: Failed to fetch $distributionUrl"
197 | elif set_java_home; then
198 | verbose "Falling back to use Java to download"
199 | javaSource="$TMP_DOWNLOAD_DIR/Downloader.java"
200 | targetZip="$TMP_DOWNLOAD_DIR/$distributionUrlName"
201 | cat >"$javaSource" <<-END
202 | public class Downloader extends java.net.Authenticator
203 | {
204 | protected java.net.PasswordAuthentication getPasswordAuthentication()
205 | {
206 | return new java.net.PasswordAuthentication( System.getenv( "MVNW_USERNAME" ), System.getenv( "MVNW_PASSWORD" ).toCharArray() );
207 | }
208 | public static void main( String[] args ) throws Exception
209 | {
210 | setDefault( new Downloader() );
211 | java.nio.file.Files.copy( java.net.URI.create( args[0] ).toURL().openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() );
212 | }
213 | }
214 | END
215 | # For Cygwin/MinGW, switch paths to Windows format before running javac and java
216 | verbose " - Compiling Downloader.java ..."
217 | "$(native_path "$JAVACCMD")" "$(native_path "$javaSource")" || die "Failed to compile Downloader.java"
218 | verbose " - Running Downloader.java ..."
219 | "$(native_path "$JAVACMD")" -cp "$(native_path "$TMP_DOWNLOAD_DIR")" Downloader "$distributionUrl" "$(native_path "$targetZip")"
220 | fi
221 |
222 | # If specified, validate the SHA-256 sum of the Maven distribution zip file
223 | if [ -n "${distributionSha256Sum-}" ]; then
224 | distributionSha256Result=false
225 | if [ "$MVN_CMD" = mvnd.sh ]; then
226 | echo "Checksum validation is not supported for maven-mvnd." >&2
227 | echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2
228 | exit 1
229 | elif command -v sha256sum >/dev/null; then
230 | if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c >/dev/null 2>&1; then
231 | distributionSha256Result=true
232 | fi
233 | elif command -v shasum >/dev/null; then
234 | if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | shasum -a 256 -c >/dev/null 2>&1; then
235 | distributionSha256Result=true
236 | fi
237 | else
238 | echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2
239 | echo "Please install either command, or disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2
240 | exit 1
241 | fi
242 | if [ $distributionSha256Result = false ]; then
243 | echo "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised." >&2
244 | echo "If you updated your Maven version, you need to update the specified distributionSha256Sum property." >&2
245 | exit 1
246 | fi
247 | fi
248 |
249 | # unzip and move
250 | if command -v unzip >/dev/null; then
251 | unzip ${__MVNW_QUIET_UNZIP:+"$__MVNW_QUIET_UNZIP"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -d "$TMP_DOWNLOAD_DIR" || die "failed to unzip"
252 | else
253 | tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar"
254 | fi
255 | printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/mvnw.url"
256 | mv -- "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME"
257 |
258 | clean || :
259 | exec_maven "$@"
260 |
--------------------------------------------------------------------------------
/mvnw.cmd:
--------------------------------------------------------------------------------
1 | <# : batch portion
2 | @REM ----------------------------------------------------------------------------
3 | @REM Licensed to the Apache Software Foundation (ASF) under one
4 | @REM or more contributor license agreements. See the NOTICE file
5 | @REM distributed with this work for additional information
6 | @REM regarding copyright ownership. The ASF licenses this file
7 | @REM to you under the Apache License, Version 2.0 (the
8 | @REM "License"); you may not use this file except in compliance
9 | @REM with the License. You may obtain a copy of the License at
10 | @REM
11 | @REM http://www.apache.org/licenses/LICENSE-2.0
12 | @REM
13 | @REM Unless required by applicable law or agreed to in writing,
14 | @REM software distributed under the License is distributed on an
15 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 | @REM KIND, either express or implied. See the License for the
17 | @REM specific language governing permissions and limitations
18 | @REM under the License.
19 | @REM ----------------------------------------------------------------------------
20 |
21 | @REM ----------------------------------------------------------------------------
22 | @REM Apache Maven Wrapper startup batch script, version 3.3.2
23 | @REM
24 | @REM Optional ENV vars
25 | @REM MVNW_REPOURL - repo url base for downloading maven distribution
26 | @REM MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven
27 | @REM MVNW_VERBOSE - true: enable verbose log; others: silence the output
28 | @REM ----------------------------------------------------------------------------
29 |
30 | @IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0)
31 | @SET __MVNW_CMD__=
32 | @SET __MVNW_ERROR__=
33 | @SET __MVNW_PSMODULEP_SAVE=%PSModulePath%
34 | @SET PSModulePath=
35 | @FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @(
36 | IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B)
37 | )
38 | @SET PSModulePath=%__MVNW_PSMODULEP_SAVE%
39 | @SET __MVNW_PSMODULEP_SAVE=
40 | @SET __MVNW_ARG0_NAME__=
41 | @SET MVNW_USERNAME=
42 | @SET MVNW_PASSWORD=
43 | @IF NOT "%__MVNW_CMD__%"=="" (%__MVNW_CMD__% %*)
44 | @echo Cannot start maven from wrapper >&2 && exit /b 1
45 | @GOTO :EOF
46 | : end batch / begin powershell #>
47 |
48 | $ErrorActionPreference = "Stop"
49 | if ($env:MVNW_VERBOSE -eq "true") {
50 | $VerbosePreference = "Continue"
51 | }
52 |
53 | # calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties
54 | $distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl
55 | if (!$distributionUrl) {
56 | Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties"
57 | }
58 |
59 | switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) {
60 | "maven-mvnd-*" {
61 | $USE_MVND = $true
62 | $distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip"
63 | $MVN_CMD = "mvnd.cmd"
64 | break
65 | }
66 | default {
67 | $USE_MVND = $false
68 | $MVN_CMD = $script -replace '^mvnw','mvn'
69 | break
70 | }
71 | }
72 |
73 | # apply MVNW_REPOURL and calculate MAVEN_HOME
74 | # maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/
75 | if ($env:MVNW_REPOURL) {
76 | $MVNW_REPO_PATTERN = if ($USE_MVND) { "/org/apache/maven/" } else { "/maven/mvnd/" }
77 | $distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace '^.*'+$MVNW_REPO_PATTERN,'')"
78 | }
79 | $distributionUrlName = $distributionUrl -replace '^.*/',''
80 | $distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$',''
81 | $MAVEN_HOME_PARENT = "$HOME/.m2/wrapper/dists/$distributionUrlNameMain"
82 | if ($env:MAVEN_USER_HOME) {
83 | $MAVEN_HOME_PARENT = "$env:MAVEN_USER_HOME/wrapper/dists/$distributionUrlNameMain"
84 | }
85 | $MAVEN_HOME_NAME = ([System.Security.Cryptography.MD5]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join ''
86 | $MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME"
87 |
88 | if (Test-Path -Path "$MAVEN_HOME" -PathType Container) {
89 | Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME"
90 | Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD"
91 | exit $?
92 | }
93 |
94 | if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) {
95 | Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl"
96 | }
97 |
98 | # prepare tmp dir
99 | $TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile
100 | $TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir"
101 | $TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null
102 | trap {
103 | if ($TMP_DOWNLOAD_DIR.Exists) {
104 | try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }
105 | catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" }
106 | }
107 | }
108 |
109 | New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null
110 |
111 | # Download and Install Apache Maven
112 | Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..."
113 | Write-Verbose "Downloading from: $distributionUrl"
114 | Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName"
115 |
116 | $webclient = New-Object System.Net.WebClient
117 | if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) {
118 | $webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD)
119 | }
120 | [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
121 | $webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null
122 |
123 | # If specified, validate the SHA-256 sum of the Maven distribution zip file
124 | $distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum
125 | if ($distributionSha256Sum) {
126 | if ($USE_MVND) {
127 | Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties."
128 | }
129 | Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash
130 | if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) {
131 | Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property."
132 | }
133 | }
134 |
135 | # unzip and move
136 | Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null
137 | Rename-Item -Path "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" -NewName $MAVEN_HOME_NAME | Out-Null
138 | try {
139 | Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null
140 | } catch {
141 | if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) {
142 | Write-Error "fail to move MAVEN_HOME"
143 | }
144 | } finally {
145 | try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }
146 | catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" }
147 | }
148 |
149 | Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD"
150 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 | org.springframework.boot
7 | spring-boot-starter-parent
8 | 2.6.7
9 |
10 |
11 | com.cershy
12 | linyu-mini-server
13 | 0.0.1-SNAPSHOT
14 | linyu-mini-server
15 | linyu-mini-server
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 | 1.8
31 |
32 |
33 |
34 | org.springframework.boot
35 | spring-boot-starter
36 |
37 |
38 |
39 | org.springframework.boot
40 | spring-boot-starter-test
41 | test
42 |
43 |
44 |
45 | org.springframework.boot
46 | spring-boot-starter-web
47 |
48 |
49 |
50 | org.projectlombok
51 | lombok
52 | true
53 |
54 |
55 |
56 | io.netty
57 | netty-all
58 | 4.1.108.Final
59 |
60 |
61 |
62 | org.apache.sshd
63 | sshd-core
64 | 2.14.0
65 |
66 |
67 |
68 | org.reflections
69 | reflections
70 | 0.10.2
71 |
72 |
73 |
74 |
75 | com.baomidou
76 | mybatis-plus-boot-starter
77 | 3.5.3
78 |
79 |
80 |
81 |
82 | org.xerial
83 | sqlite-jdbc
84 | 3.46.1.1
85 |
86 |
87 |
88 |
89 | org.springframework.boot
90 | spring-boot-starter-validation
91 |
92 |
93 |
94 |
95 | cn.hutool
96 | hutool-all
97 | 5.8.18
98 |
99 |
100 |
101 | org.springframework.security
102 | spring-security-crypto
103 |
104 |
105 |
106 |
107 | io.jsonwebtoken
108 | jjwt
109 | 0.9.0
110 |
111 |
112 |
113 |
114 | mysql
115 | mysql-connector-java
116 |
117 |
118 |
119 | org.lionsoul
120 | ip2region
121 | 2.7.0
122 |
123 |
124 |
125 | org.springframework.boot
126 | spring-boot-starter-aop
127 |
128 |
129 |
130 | com.github.ben-manes.caffeine
131 | caffeine
132 |
133 |
134 |
135 | com.github.houbb
136 | sensitive-word
137 | 0.24.0
138 |
139 |
140 |
141 | org.springframework.boot
142 | spring-boot-configuration-processor
143 | true
144 |
145 |
146 |
147 | com.volcengine
148 | volcengine-java-sdk-ark-runtime
149 | LATEST
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 | org.springframework.boot
158 | spring-boot-maven-plugin
159 |
160 |
161 |
162 |
163 |
164 |
--------------------------------------------------------------------------------
/src/main/java/com/cershy/linyuminiserver/LinyuMiniServerApplication.java:
--------------------------------------------------------------------------------
1 | package com.cershy.linyuminiserver;
2 |
3 | import org.mybatis.spring.annotation.MapperScan;
4 | import org.springframework.boot.SpringApplication;
5 | import org.springframework.boot.autoconfigure.SpringBootApplication;
6 | import org.springframework.scheduling.annotation.EnableAsync;
7 | import org.springframework.scheduling.annotation.EnableScheduling;
8 |
9 | @MapperScan("com.cershy.linyuminiserver.mapper")
10 | @SpringBootApplication
11 | @EnableScheduling
12 | @EnableAsync
13 | public class LinyuMiniServerApplication {
14 |
15 | public static void main(String[] args) {
16 | SpringApplication.run(LinyuMiniServerApplication.class, args);
17 | }
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/src/main/java/com/cershy/linyuminiserver/annotation/CommandInfo.java:
--------------------------------------------------------------------------------
1 | package com.cershy.linyuminiserver.annotation;
2 |
3 | import java.lang.annotation.*;
4 |
5 | @Target(ElementType.TYPE)
6 | @Retention(RetentionPolicy.RUNTIME)
7 | @Documented
8 | public @interface CommandInfo {
9 |
10 | String description();
11 |
12 | String name();
13 | }
14 |
--------------------------------------------------------------------------------
/src/main/java/com/cershy/linyuminiserver/annotation/UrlFree.java:
--------------------------------------------------------------------------------
1 | package com.cershy.linyuminiserver.annotation;
2 |
3 | import java.lang.annotation.*;
4 |
5 | @Documented
6 | @Retention(RetentionPolicy.RUNTIME)
7 | @Target({ElementType.METHOD, ElementType.TYPE})
8 | public @interface UrlFree {
9 | String value() default "";
10 | }
11 |
--------------------------------------------------------------------------------
/src/main/java/com/cershy/linyuminiserver/annotation/UrlLimit.java:
--------------------------------------------------------------------------------
1 | package com.cershy.linyuminiserver.annotation;
2 |
3 | import com.cershy.linyuminiserver.constant.LimitKeyType;
4 |
5 | import java.lang.annotation.ElementType;
6 | import java.lang.annotation.Retention;
7 | import java.lang.annotation.RetentionPolicy;
8 | import java.lang.annotation.Target;
9 |
10 | @Target({ElementType.METHOD})
11 | @Retention(RetentionPolicy.RUNTIME)
12 | public @interface UrlLimit {
13 | LimitKeyType keyType() default LimitKeyType.ID; //限制类型,ip或者id
14 |
15 | int maxRequests() default 60; //每分钟最大请求次数
16 | }
17 |
--------------------------------------------------------------------------------
/src/main/java/com/cershy/linyuminiserver/annotation/UrlResource.java:
--------------------------------------------------------------------------------
1 | package com.cershy.linyuminiserver.annotation;
2 |
3 | import java.lang.annotation.*;
4 |
5 | @Documented
6 | @Retention(RetentionPolicy.RUNTIME)
7 | @Target({ElementType.METHOD, ElementType.TYPE})
8 | public @interface UrlResource {
9 | String value() default "";
10 | }
11 |
--------------------------------------------------------------------------------
/src/main/java/com/cershy/linyuminiserver/annotation/UserIp.java:
--------------------------------------------------------------------------------
1 | package com.cershy.linyuminiserver.annotation;
2 |
3 | import java.lang.annotation.*;
4 |
5 | @Target(ElementType.PARAMETER)
6 | @Retention(RetentionPolicy.RUNTIME)
7 | @Documented
8 | public @interface UserIp {
9 | }
10 |
--------------------------------------------------------------------------------
/src/main/java/com/cershy/linyuminiserver/annotation/Userid.java:
--------------------------------------------------------------------------------
1 | package com.cershy.linyuminiserver.annotation;
2 |
3 | import java.lang.annotation.*;
4 |
5 | @Target(ElementType.PARAMETER)
6 | @Retention(RetentionPolicy.RUNTIME)
7 | @Documented
8 | public @interface Userid {
9 | }
10 |
--------------------------------------------------------------------------------
/src/main/java/com/cershy/linyuminiserver/aop/UrlLimitAspect.java:
--------------------------------------------------------------------------------
1 | package com.cershy.linyuminiserver.aop;
2 |
3 | import com.cershy.linyuminiserver.annotation.UrlLimit;
4 | import com.cershy.linyuminiserver.constant.LimitKeyType;
5 | import com.cershy.linyuminiserver.dto.UrlLimitStats;
6 | import com.cershy.linyuminiserver.exception.LinyuException;
7 | import com.cershy.linyuminiserver.utils.IpUtil;
8 | import com.github.benmanes.caffeine.cache.Cache;
9 | import com.github.benmanes.caffeine.cache.Caffeine;
10 | import lombok.extern.slf4j.Slf4j;
11 | import org.aspectj.lang.ProceedingJoinPoint;
12 | import org.aspectj.lang.annotation.Around;
13 | import org.aspectj.lang.annotation.Aspect;
14 | import org.aspectj.lang.annotation.Pointcut;
15 | import org.springframework.stereotype.Component;
16 | import org.springframework.web.context.request.RequestContextHolder;
17 | import org.springframework.web.context.request.ServletRequestAttributes;
18 |
19 | import javax.servlet.http.HttpServletRequest;
20 | import java.time.LocalDateTime;
21 | import java.util.Map;
22 | import java.util.concurrent.TimeUnit;
23 | import java.util.concurrent.atomic.AtomicInteger;
24 |
25 | @Aspect
26 | @Component
27 | @Slf4j
28 | public class UrlLimitAspect {
29 | private final Cache requestCountCache;
30 | private final Cache statsCache;
31 |
32 | public UrlLimitAspect() {
33 | // 创建请求计数缓存
34 | this.requestCountCache = Caffeine.newBuilder()
35 | .expireAfterWrite(1, TimeUnit.MINUTES)
36 | .build();
37 | // 创建统计信息缓存
38 | this.statsCache = Caffeine.newBuilder()
39 | .expireAfterWrite(1, TimeUnit.HOURS)
40 | .build();
41 | }
42 |
43 | @Pointcut("@annotation(com.cershy.linyuminiserver.annotation.UrlLimit)")
44 | public void rateLimitPointcut() {
45 | }
46 |
47 | @Around("rateLimitPointcut() && @annotation(urlLimit)")
48 | public Object around(ProceedingJoinPoint joinPoint, UrlLimit urlLimit) throws Throwable {
49 | HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder
50 | .getRequestAttributes()).getRequest();
51 | String key = "";
52 | // 获取key的类型
53 | if (urlLimit.keyType() == LimitKeyType.ID) {
54 | Map userinfo = (Map) request.getAttribute("userinfo");
55 | key = userinfo.get("userId").toString();
56 | } else {
57 | key = IpUtil.getIpAddr(request);
58 | }
59 | String path = request.getRequestURI();
60 | key = key + ":" + path;
61 | // 检查是否被封禁
62 | UrlLimitStats stats = statsCache.get(key, k -> new UrlLimitStats());
63 | if (stats.isBlocked()) {
64 | throw new LinyuException("访问过于频繁,您已被封禁~");
65 | }
66 | // 获取并增加计数
67 | AtomicInteger count = requestCountCache.get(key, k -> new AtomicInteger(0));
68 | int currentCount = count.incrementAndGet();
69 | if (currentCount > urlLimit.maxRequests()) {
70 | // 记录违规
71 | stats.setViolationCount(stats.getViolationCount() + 1);
72 | stats.setLastViolationTime(LocalDateTime.now());
73 | // 检查是否需要封禁
74 | if (stats.getViolationCount() >= urlLimit.maxRequests() + 100) {
75 | stats.setBlocked(true);
76 | throw new LinyuException("访问过于频繁,您已被封禁~");
77 | }
78 | statsCache.put(key, stats);
79 | throw new LinyuException("访问过快,请稍后再试~");
80 | }
81 | return joinPoint.proceed();
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/src/main/java/com/cershy/linyuminiserver/configs/AsyncConfig.java:
--------------------------------------------------------------------------------
1 | package com.cershy.linyuminiserver.configs;
2 |
3 | import org.springframework.context.annotation.Bean;
4 | import org.springframework.context.annotation.Configuration;
5 | import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
6 |
7 | @Configuration
8 | public class AsyncConfig {
9 |
10 | @Bean(name = "taskExecutor")
11 | public ThreadPoolTaskExecutor taskExecutor() {
12 | ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
13 | executor.setCorePoolSize(10); // 核心线程数
14 | executor.setMaxPoolSize(20); // 最大线程数
15 | executor.setQueueCapacity(100); // 队列容量
16 | executor.setThreadNamePrefix("async-task-");
17 | return executor;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/main/java/com/cershy/linyuminiserver/configs/DatabaseInitializer.java:
--------------------------------------------------------------------------------
1 | package com.cershy.linyuminiserver.configs;
2 |
3 | import com.cershy.linyuminiserver.service.GroupService;
4 | import com.cershy.linyuminiserver.service.UserService;
5 | import org.springframework.beans.factory.annotation.Value;
6 | import org.springframework.jdbc.core.JdbcTemplate;
7 | import org.springframework.stereotype.Component;
8 |
9 | import javax.annotation.PostConstruct;
10 | import javax.annotation.Resource;
11 | import java.io.BufferedReader;
12 | import java.io.InputStreamReader;
13 | import java.nio.charset.StandardCharsets;
14 | import java.nio.file.Files;
15 | import java.nio.file.Path;
16 | import java.nio.file.Paths;
17 |
18 | @Component
19 | public class DatabaseInitializer {
20 |
21 | @Resource
22 | private JdbcTemplate jdbcTemplate;
23 |
24 | @Value("${spring.datasource.url}")
25 | private String datasourceUrl;
26 |
27 | @Resource
28 | private GroupService groupService;
29 |
30 | @Resource
31 | private UserService userService;
32 |
33 | @PostConstruct
34 | public void init() {
35 | if (datasourceUrl.startsWith("jdbc:mysql:")) {
36 | executeSqlFromFile("linyu-mini-mysql.sql");
37 | } else {
38 | sqliteCreateDatabase();
39 | executeSqlFromFile("linyu-mini-sqlite.sql");
40 | }
41 | }
42 |
43 | public void sqliteCreateDatabase() {
44 | try {
45 | String dbFilePath;
46 | if (datasourceUrl.startsWith("jdbc:sqlite:")) {
47 | dbFilePath = datasourceUrl.substring("jdbc:sqlite:".length());
48 | if (dbFilePath.contains("?")) {
49 | dbFilePath = dbFilePath.substring(0, dbFilePath.indexOf("?"));
50 | }
51 | } else {
52 | throw new IllegalArgumentException("Invalid SQLite URL: " + datasourceUrl);
53 | }
54 | Path dbPath = Paths.get(dbFilePath);
55 | // 判断数据库文件是否存在
56 | if (Files.notExists(dbPath)) {
57 | System.out.println("Database file does not exist. Creating database...");
58 | // 创建空的数据库文件
59 | Files.createFile(dbPath);
60 | } else {
61 | System.out.println("Database file already exists. Skipping initialization.");
62 | }
63 | System.out.println("Database initialized successfully.");
64 | } catch (Exception e) {
65 | throw new RuntimeException("Failed to initialize database", e);
66 | }
67 | }
68 |
69 | /**
70 | * 执行 SQL 文件
71 | *
72 | * @param resourcePath 资源文件路径
73 | */
74 | private void executeSqlFromFile(String resourcePath) {
75 | try (BufferedReader reader = new BufferedReader(new InputStreamReader(
76 | getClass().getClassLoader().getResourceAsStream(resourcePath), StandardCharsets.UTF_8))) {
77 | StringBuilder sqlBuilder = new StringBuilder();
78 | String line;
79 | while ((line = reader.readLine()) != null) {
80 | sqlBuilder.append(line).append("\n");
81 | }
82 | String[] sqlStatements = sqlBuilder.toString().split(";");
83 | for (String sql : sqlStatements) {
84 | if (!sql.trim().isEmpty()) {
85 | jdbcTemplate.execute(sql.trim());
86 | }
87 | }
88 | //更新默认群组
89 | groupService.updateDefaultGroup();
90 | //创建机器人
91 | userService.initBotUser();
92 | } catch (Exception e) {
93 | throw new RuntimeException("Failed to execute SQL file: " + resourcePath, e);
94 | }
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/src/main/java/com/cershy/linyuminiserver/configs/LinyuConfig.java:
--------------------------------------------------------------------------------
1 | package com.cershy.linyuminiserver.configs;
2 |
3 | import lombok.Data;
4 | import org.springframework.boot.context.properties.ConfigurationProperties;
5 | import org.springframework.context.annotation.Configuration;
6 |
7 | @Data
8 | @Configuration
9 | @ConfigurationProperties(prefix = "linyu")
10 | public class LinyuConfig {
11 |
12 | private String password;
13 | private int limit;
14 | private String name;
15 | private int expires;
16 | private AiConfig doubao;
17 | private AiConfig deepSeek;
18 |
19 | @Data
20 | public static class AiConfig {
21 | private String apiKey;
22 | private int countLimit;
23 | private int lengthLimit;
24 | private String model;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/main/java/com/cershy/linyuminiserver/configs/MybatisHandler.java:
--------------------------------------------------------------------------------
1 | package com.cershy.linyuminiserver.configs;
2 |
3 | import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
4 | import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
5 | import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
6 | import org.apache.ibatis.reflection.MetaObject;
7 | import org.springframework.context.annotation.Bean;
8 | import org.springframework.stereotype.Component;
9 |
10 | import java.util.Date;
11 |
12 |
13 | @Component
14 | public class MybatisHandler implements MetaObjectHandler {
15 |
16 | @Override
17 | public void insertFill(MetaObject metaObject) {
18 | this.setFieldValByName("createTime", new Date(), metaObject);
19 | this.setFieldValByName("updateTime", new Date(), metaObject);
20 | }
21 |
22 | @Override
23 | public void updateFill(MetaObject metaObject) {
24 | this.setFieldValByName("updateTime", new Date(), metaObject);
25 | }
26 |
27 | @Bean
28 | public MybatisPlusInterceptor mybatisPlusInterceptor() {
29 | MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
30 | interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
31 | return interceptor;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/main/java/com/cershy/linyuminiserver/configs/SensitiveWordConfig.java:
--------------------------------------------------------------------------------
1 | package com.cershy.linyuminiserver.configs;
2 |
3 | import com.github.houbb.sensitive.word.bs.SensitiveWordBs;
4 | import com.github.houbb.sensitive.word.support.ignore.SensitiveWordCharIgnores;
5 | import com.github.houbb.sensitive.word.support.resultcondition.WordResultConditions;
6 | import com.github.houbb.sensitive.word.support.tag.WordTags;
7 | import org.springframework.context.annotation.Bean;
8 | import org.springframework.context.annotation.Configuration;
9 |
10 | @Configuration
11 | public class SensitiveWordConfig {
12 |
13 | @Bean
14 | public SensitiveWordBs sensitiveWordBs() {
15 | SensitiveWordBs wordBs = SensitiveWordBs.newInstance()
16 | .ignoreCase(true)
17 | .ignoreWidth(true)
18 | .ignoreNumStyle(true)
19 | .ignoreChineseStyle(true)
20 | .ignoreEnglishStyle(true)
21 | .ignoreRepeat(false)
22 | .enableNumCheck(false)
23 | .enableEmailCheck(false)
24 | .enableUrlCheck(false)
25 | .enableIpv4Check(false)
26 | .enableWordCheck(true)
27 | .numCheckLen(8)
28 | .wordTag(WordTags.none())
29 | .charIgnore(SensitiveWordCharIgnores.defaults())
30 | .wordResultCondition(WordResultConditions.alwaysTrue())
31 | .init();
32 | return wordBs;
33 | }
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/src/main/java/com/cershy/linyuminiserver/configs/UserInfoArgumentResolver.java:
--------------------------------------------------------------------------------
1 | package com.cershy.linyuminiserver.configs;
2 |
3 |
4 | import com.cershy.linyuminiserver.annotation.UserIp;
5 | import com.cershy.linyuminiserver.annotation.Userid;
6 | import com.cershy.linyuminiserver.utils.IpUtil;
7 | import org.springframework.core.MethodParameter;
8 | import org.springframework.lang.Nullable;
9 | import org.springframework.web.bind.support.WebDataBinderFactory;
10 | import org.springframework.web.context.request.NativeWebRequest;
11 | import org.springframework.web.method.support.HandlerMethodArgumentResolver;
12 | import org.springframework.web.method.support.ModelAndViewContainer;
13 |
14 | import javax.servlet.http.HttpServletRequest;
15 | import java.util.Map;
16 |
17 | /**
18 | * @author: dwh
19 | **/
20 | public class UserInfoArgumentResolver implements HandlerMethodArgumentResolver {
21 |
22 | @Override
23 | public boolean supportsParameter(MethodParameter parameter) {
24 | return parameter.hasParameterAnnotation(Userid.class) ||
25 | parameter.hasParameterAnnotation(UserIp.class);
26 | }
27 |
28 | @Override
29 | public Object resolveArgument(
30 | MethodParameter parameter,
31 | @Nullable ModelAndViewContainer mavContainer,
32 | NativeWebRequest webRequest,
33 | @Nullable WebDataBinderFactory binderFactory) {
34 |
35 | HttpServletRequest request = (HttpServletRequest) webRequest.getNativeRequest();
36 |
37 | if (parameter.hasParameterAnnotation(Userid.class)) {
38 | Map userinfo = (Map) request.getAttribute("userinfo");
39 | if (userinfo != null) {
40 | return userinfo.get("userId");
41 | }
42 | }
43 | if (parameter.hasParameterAnnotation(UserIp.class)) {
44 | String ipAddr = IpUtil.getIpAddr(request);
45 | if (ipAddr != null) {
46 | return ipAddr;
47 | }
48 | }
49 | return null;
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/main/java/com/cershy/linyuminiserver/configs/WebMvcConfig.java:
--------------------------------------------------------------------------------
1 | package com.cershy.linyuminiserver.configs;
2 |
3 | import org.springframework.boot.web.servlet.FilterRegistrationBean;
4 | import org.springframework.context.annotation.Bean;
5 | import org.springframework.context.annotation.Configuration;
6 | import org.springframework.web.cors.CorsConfiguration;
7 | import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
8 | import org.springframework.web.filter.CorsFilter;
9 | import org.springframework.web.method.support.HandlerMethodArgumentResolver;
10 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
11 |
12 | import java.util.List;
13 |
14 | /**
15 | * @author: dwh
16 | **/
17 | @Configuration
18 | public class WebMvcConfig implements WebMvcConfigurer {
19 |
20 | @Override
21 | public void addArgumentResolvers(List resolvers) {
22 | resolvers.add(new UserInfoArgumentResolver());
23 | }
24 |
25 | @Bean
26 | public FilterRegistrationBean corsFilter() {
27 | UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
28 | CorsConfiguration config = new CorsConfiguration();
29 | config.setAllowCredentials(true);
30 | config.addAllowedOriginPattern("*");
31 | config.addAllowedHeader("*");
32 | config.addAllowedMethod("*");
33 | source.registerCorsConfiguration("/**", config);
34 | FilterRegistrationBean bean = new FilterRegistrationBean<>(new CorsFilter(source));
35 | bean.setOrder(0);
36 | return bean;
37 | }
38 |
39 | }
40 |
--------------------------------------------------------------------------------
/src/main/java/com/cershy/linyuminiserver/constant/BadgeType.java:
--------------------------------------------------------------------------------
1 | package com.cershy.linyuminiserver.constant;
2 |
3 | public class BadgeType {
4 | //皇冠
5 | public static final String Crown = "crown";
6 | //四叶草
7 | public static final String Clover = "clover";
8 | //钻石
9 | public static final String Diamond = "diamond";
10 | }
11 |
--------------------------------------------------------------------------------
/src/main/java/com/cershy/linyuminiserver/constant/ChatListType.java:
--------------------------------------------------------------------------------
1 | package com.cershy.linyuminiserver.constant;
2 |
3 | public class ChatListType {
4 | //私聊
5 | public static final String User = "user";
6 | //群聊
7 | public static final String Group = "group";
8 | }
9 |
--------------------------------------------------------------------------------
/src/main/java/com/cershy/linyuminiserver/constant/LimitKeyType.java:
--------------------------------------------------------------------------------
1 | package com.cershy.linyuminiserver.constant;
2 |
3 | public enum LimitKeyType {
4 | ID,
5 | IP;
6 |
7 | private LimitKeyType() {
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/main/java/com/cershy/linyuminiserver/constant/MessageSource.java:
--------------------------------------------------------------------------------
1 | package com.cershy.linyuminiserver.constant;
2 |
3 | public class MessageSource {
4 | //用户
5 | public static final String User = "user";
6 | //群
7 | public static final String Group = "group";
8 | }
9 |
--------------------------------------------------------------------------------
/src/main/java/com/cershy/linyuminiserver/constant/MessageType.java:
--------------------------------------------------------------------------------
1 | package com.cershy.linyuminiserver.constant;
2 |
3 | public class MessageType {
4 | //文本
5 | public static final String Text = "text";
6 | //撤回
7 | public static final String Recall = "recall";
8 | //表情
9 | public static final String Emoji = "emoji";
10 | }
11 |
--------------------------------------------------------------------------------
/src/main/java/com/cershy/linyuminiserver/constant/NotifyType.java:
--------------------------------------------------------------------------------
1 | package com.cershy.linyuminiserver.constant;
2 |
3 | public class NotifyType {
4 | //web用户上线
5 | public static final String Web_Online = "web-online";
6 | //web用户下线
7 | public static final String Web_Offline = "web-offline";
8 | }
9 |
--------------------------------------------------------------------------------
/src/main/java/com/cershy/linyuminiserver/constant/TextContentType.java:
--------------------------------------------------------------------------------
1 | package com.cershy.linyuminiserver.constant;
2 |
3 | public class TextContentType {
4 | //文本
5 | public static final String Text = "text";
6 | //at
7 | public static final String At = "at";
8 | }
9 |
--------------------------------------------------------------------------------
/src/main/java/com/cershy/linyuminiserver/constant/UserType.java:
--------------------------------------------------------------------------------
1 | package com.cershy.linyuminiserver.constant;
2 |
3 | public class UserType {
4 | //普通用户
5 | public static final String User = "user";
6 | //机器人
7 | public static final String Bot = "bot";
8 | }
9 |
--------------------------------------------------------------------------------
/src/main/java/com/cershy/linyuminiserver/constant/WsContentType.java:
--------------------------------------------------------------------------------
1 | package com.cershy.linyuminiserver.constant;
2 |
3 | import lombok.Data;
4 |
5 | @Data
6 | public class WsContentType {
7 | //消息
8 | public static final String Msg = "msg";
9 | //通知
10 | public static final String Notify = "notify";
11 | //视频/音频
12 | public static String Video = "video";
13 | //文件
14 | public static String File = "file";
15 | }
16 |
--------------------------------------------------------------------------------
/src/main/java/com/cershy/linyuminiserver/controller/ChatListController.java:
--------------------------------------------------------------------------------
1 | package com.cershy.linyuminiserver.controller;
2 |
3 | import com.cershy.linyuminiserver.annotation.UrlLimit;
4 | import com.cershy.linyuminiserver.annotation.Userid;
5 | import com.cershy.linyuminiserver.entity.ChatList;
6 | import com.cershy.linyuminiserver.service.ChatListService;
7 | import com.cershy.linyuminiserver.utils.ResultUtil;
8 | import com.cershy.linyuminiserver.vo.chatList.CreateVo;
9 | import com.cershy.linyuminiserver.vo.chatList.DeleteVo;
10 | import com.cershy.linyuminiserver.vo.chatList.ReadVo;
11 | import org.springframework.web.bind.annotation.*;
12 |
13 | import javax.annotation.Resource;
14 | import javax.validation.Valid;
15 | import java.util.List;
16 |
17 | @RestController
18 | @RequestMapping("/api/v1/chat-list")
19 | public class ChatListController {
20 |
21 | @Resource
22 | ChatListService chatListService;
23 |
24 | @UrlLimit
25 | @GetMapping("/list/private")
26 | public Object privateList(@Userid String userId) {
27 | List result = chatListService.privateList(userId);
28 | return ResultUtil.Succeed(result);
29 | }
30 |
31 | @UrlLimit
32 | @GetMapping("/group")
33 | public Object group(@Userid String userId) {
34 | ChatList result = chatListService.getGroup(userId);
35 | return ResultUtil.Succeed(result);
36 | }
37 |
38 | @UrlLimit
39 | @PostMapping("/create")
40 | public Object create(@Userid String userId, @RequestBody @Valid CreateVo createVo) {
41 | ChatList result = chatListService.create(userId, createVo.getTargetId());
42 | return ResultUtil.Succeed(result);
43 | }
44 |
45 | @UrlLimit
46 | @PostMapping("/read")
47 | public Object read(@Userid String userId, @RequestBody @Valid ReadVo readVo) {
48 | boolean result = chatListService.read(userId, readVo.getTargetId());
49 | return ResultUtil.Succeed(result);
50 | }
51 |
52 | @UrlLimit
53 | @PostMapping("/delete")
54 | public Object delete(@Userid String userId, @RequestBody @Valid DeleteVo deleteVo) {
55 | boolean result = chatListService.delete(userId, deleteVo.getChatListId());
56 | return ResultUtil.Succeed(result);
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/main/java/com/cershy/linyuminiserver/controller/FileController.java:
--------------------------------------------------------------------------------
1 | package com.cershy.linyuminiserver.controller;
2 |
3 | import cn.hutool.json.JSONObject;
4 | import com.cershy.linyuminiserver.annotation.UrlLimit;
5 | import com.cershy.linyuminiserver.annotation.Userid;
6 | import com.cershy.linyuminiserver.service.FileService;
7 | import com.cershy.linyuminiserver.utils.ResultUtil;
8 | import com.cershy.linyuminiserver.vo.file.*;
9 | import lombok.extern.slf4j.Slf4j;
10 | import org.springframework.web.bind.annotation.PostMapping;
11 | import org.springframework.web.bind.annotation.RequestBody;
12 | import org.springframework.web.bind.annotation.RequestMapping;
13 | import org.springframework.web.bind.annotation.RestController;
14 |
15 | import javax.annotation.Resource;
16 |
17 | @RestController
18 | @RequestMapping("/api/v1/file")
19 | @Slf4j
20 | public class FileController {
21 |
22 | @Resource
23 | FileService fileService;
24 |
25 | /**
26 | * 发送offer
27 | */
28 | @UrlLimit
29 | @PostMapping("/offer")
30 | public JSONObject offer(@Userid String userId, @RequestBody OfferVo offerVo) {
31 | boolean result = fileService.offer(userId, offerVo);
32 | return ResultUtil.ResultByFlag(result);
33 | }
34 |
35 | /**
36 | * 发送answer
37 | */
38 | @UrlLimit
39 | @PostMapping("/answer")
40 | public JSONObject answer(@Userid String userId, @RequestBody AnswerVo answerVo) {
41 | boolean result = fileService.answer(userId, answerVo);
42 | return ResultUtil.ResultByFlag(result);
43 | }
44 |
45 | /**
46 | * 发送candidate
47 | */
48 | @UrlLimit
49 | @PostMapping("/candidate")
50 | public JSONObject candidate(@Userid String userId, @RequestBody CandidateVo candidateVo) {
51 | boolean result = fileService.candidate(userId, candidateVo);
52 | return ResultUtil.ResultByFlag(result);
53 | }
54 |
55 | /**
56 | * 取消
57 | */
58 | @UrlLimit
59 | @PostMapping("/cancel")
60 | public JSONObject hangup(@Userid String userId, @RequestBody CancelVo cancelVo) {
61 | boolean result = fileService.cancel(userId, cancelVo);
62 | return ResultUtil.ResultByFlag(result);
63 | }
64 |
65 | /**
66 | * 邀请
67 | */
68 | @UrlLimit
69 | @PostMapping("/invite")
70 | public JSONObject invite(@Userid String userId, @RequestBody InviteVo inviteVo) {
71 | boolean result = fileService.invite(userId, inviteVo);
72 | return ResultUtil.ResultByFlag(result);
73 | }
74 |
75 | /**
76 | * 同意
77 | */
78 | @UrlLimit
79 | @PostMapping("/accept")
80 | public JSONObject accept(@Userid String userId, @RequestBody AcceptVo acceptVo) {
81 | boolean result = fileService.accept(userId, acceptVo);
82 | return ResultUtil.ResultByFlag(result);
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/src/main/java/com/cershy/linyuminiserver/controller/LoginController.java:
--------------------------------------------------------------------------------
1 | package com.cershy.linyuminiserver.controller;
2 |
3 | import cn.hutool.json.JSONObject;
4 | import com.cershy.linyuminiserver.annotation.UrlFree;
5 | import com.cershy.linyuminiserver.annotation.UrlLimit;
6 | import com.cershy.linyuminiserver.constant.LimitKeyType;
7 | import com.cershy.linyuminiserver.service.LoginService;
8 | import com.cershy.linyuminiserver.utils.ResultUtil;
9 | import com.cershy.linyuminiserver.utils.SecurityUtil;
10 | import com.cershy.linyuminiserver.vo.login.LoginVo;
11 | import com.cershy.linyuminiserver.vo.login.VerifyVo;
12 | import org.springframework.web.bind.annotation.*;
13 |
14 | import javax.annotation.Resource;
15 | import javax.validation.Valid;
16 |
17 | @RestController
18 | @RequestMapping("/api/v1/login")
19 | public class LoginController {
20 |
21 | @Resource
22 | private LoginService loginService;
23 |
24 | @UrlFree
25 | @PostMapping("/verify")
26 | @UrlLimit(keyType = LimitKeyType.IP)
27 | public Object verify(@RequestBody @Valid VerifyVo verifyVo) {
28 | String result = loginService.verify(verifyVo.getPassword());
29 | return ResultUtil.Succeed(result);
30 | }
31 |
32 | @UrlFree
33 | @GetMapping("/public-key")
34 | @UrlLimit(keyType = LimitKeyType.IP)
35 | public Object getPublicKey() {
36 | String result = SecurityUtil.getPublicKey();
37 | return ResultUtil.Succeed(result);
38 | }
39 |
40 | @UrlFree
41 | @PostMapping("")
42 | @UrlLimit(keyType = LimitKeyType.IP)
43 | public Object login(@RequestBody @Valid LoginVo loginVo) {
44 | JSONObject result = loginService.login(loginVo);
45 | return ResultUtil.Succeed(result);
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/main/java/com/cershy/linyuminiserver/controller/MessageController.java:
--------------------------------------------------------------------------------
1 | package com.cershy.linyuminiserver.controller;
2 |
3 | import com.cershy.linyuminiserver.annotation.UrlLimit;
4 | import com.cershy.linyuminiserver.annotation.UserIp;
5 | import com.cershy.linyuminiserver.annotation.Userid;
6 | import com.cershy.linyuminiserver.entity.Message;
7 | import com.cershy.linyuminiserver.service.MessageService;
8 | import com.cershy.linyuminiserver.utils.ResultUtil;
9 | import com.cershy.linyuminiserver.vo.message.RecallVo;
10 | import com.cershy.linyuminiserver.vo.message.RecordVo;
11 | import com.cershy.linyuminiserver.vo.message.SendMessageVo;
12 | import org.springframework.web.bind.annotation.PostMapping;
13 | import org.springframework.web.bind.annotation.RequestBody;
14 | import org.springframework.web.bind.annotation.RequestMapping;
15 | import org.springframework.web.bind.annotation.RestController;
16 |
17 | import javax.annotation.Resource;
18 | import javax.validation.Valid;
19 | import java.util.List;
20 |
21 | @RestController
22 | @RequestMapping("/api/v1/message")
23 | public class MessageController {
24 |
25 | @Resource
26 | MessageService messageService;
27 |
28 | @UrlLimit(maxRequests = 100)
29 | @PostMapping("/send")
30 | public Object send(@Userid String userId, @UserIp String userIp,
31 | @RequestBody @Valid SendMessageVo sendMessageVo) {
32 | sendMessageVo.setUserIp(userIp);
33 | Message result = messageService.send(userId, sendMessageVo);
34 | return ResultUtil.Succeed(result);
35 | }
36 |
37 | @UrlLimit
38 | @PostMapping("/record")
39 | public Object record(@Userid String userId, @RequestBody @Valid RecordVo recordVo) {
40 | List result = messageService.record(userId, recordVo);
41 | return ResultUtil.Succeed(result);
42 | }
43 |
44 | @UrlLimit
45 | @PostMapping("/recall")
46 | public Object recall(@Userid String userId, @RequestBody @Valid RecallVo recallVo) {
47 | Message result = messageService.recall(userId, recallVo);
48 | return ResultUtil.Succeed(result);
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/main/java/com/cershy/linyuminiserver/controller/UserController.java:
--------------------------------------------------------------------------------
1 | package com.cershy.linyuminiserver.controller;
2 |
3 | import com.cershy.linyuminiserver.annotation.UrlLimit;
4 | import com.cershy.linyuminiserver.annotation.Userid;
5 | import com.cershy.linyuminiserver.dto.UserDto;
6 | import com.cershy.linyuminiserver.entity.User;
7 | import com.cershy.linyuminiserver.service.UserService;
8 | import com.cershy.linyuminiserver.utils.ResultUtil;
9 | import com.cershy.linyuminiserver.vo.user.CreateUserVo;
10 | import com.cershy.linyuminiserver.vo.user.UpdateUserVo;
11 | import org.springframework.web.bind.annotation.*;
12 |
13 | import javax.annotation.Resource;
14 | import javax.validation.Valid;
15 | import java.util.List;
16 | import java.util.Map;
17 |
18 | @RestController
19 | @RequestMapping("/api/v1/user")
20 | public class UserController {
21 |
22 | @Resource
23 | UserService userService;
24 |
25 | // @PostMapping("/create")
26 | public Object createUser(@RequestBody @Valid CreateUserVo createUserVo) {
27 | User result = userService.createUser(createUserVo);
28 | return ResultUtil.Succeed(result);
29 | }
30 |
31 | @UrlLimit
32 | @GetMapping("/list")
33 | public Object listUser() {
34 | List result = userService.listUser();
35 | return ResultUtil.Succeed(result);
36 | }
37 |
38 | @UrlLimit
39 | @GetMapping("/list/map")
40 | public Object listMapUser() {
41 | Map result = userService.listMapUser();
42 | return ResultUtil.Succeed(result);
43 | }
44 |
45 | @UrlLimit
46 | @GetMapping("/online/web")
47 | public Object onlineWeb() {
48 | List result = userService.onlineWeb();
49 | return ResultUtil.Succeed(result);
50 | }
51 |
52 | @UrlLimit
53 | @PostMapping("/update")
54 | public Object updateUser(@Userid String userid, @RequestBody @Valid UpdateUserVo updateUserVo) {
55 | boolean result = userService.updateUser(userid, updateUserVo);
56 | return ResultUtil.ResultByFlag(result);
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/main/java/com/cershy/linyuminiserver/controller/VideoController.java:
--------------------------------------------------------------------------------
1 | package com.cershy.linyuminiserver.controller;
2 |
3 | import cn.hutool.json.JSONObject;
4 | import com.cershy.linyuminiserver.annotation.UrlLimit;
5 | import com.cershy.linyuminiserver.annotation.Userid;
6 | import com.cershy.linyuminiserver.service.VideoService;
7 | import com.cershy.linyuminiserver.utils.ResultUtil;
8 | import com.cershy.linyuminiserver.vo.video.*;
9 | import lombok.extern.slf4j.Slf4j;
10 | import org.springframework.web.bind.annotation.PostMapping;
11 | import org.springframework.web.bind.annotation.RequestBody;
12 | import org.springframework.web.bind.annotation.RequestMapping;
13 | import org.springframework.web.bind.annotation.RestController;
14 |
15 | import javax.annotation.Resource;
16 |
17 | @RestController
18 | @RequestMapping("/api/v1/video")
19 | @Slf4j
20 | public class VideoController {
21 |
22 | @Resource
23 | VideoService videoService;
24 |
25 | /**
26 | * 发送offer
27 | */
28 | @UrlLimit
29 | @PostMapping("/offer")
30 | public JSONObject offer(@Userid String userId, @RequestBody OfferVo offerVo) {
31 | boolean result = videoService.offer(userId, offerVo);
32 | return ResultUtil.ResultByFlag(result);
33 | }
34 |
35 | /**
36 | * 发送answer
37 | */
38 | @UrlLimit
39 | @PostMapping("/answer")
40 | public JSONObject answer(@Userid String userId, @RequestBody AnswerVo answerVo) {
41 | boolean result = videoService.answer(userId, answerVo);
42 | return ResultUtil.ResultByFlag(result);
43 | }
44 |
45 | /**
46 | * 发送candidate
47 | */
48 | @UrlLimit
49 | @PostMapping("/candidate")
50 | public JSONObject candidate(@Userid String userId, @RequestBody CandidateVo candidateVo) {
51 | boolean result = videoService.candidate(userId, candidateVo);
52 | return ResultUtil.ResultByFlag(result);
53 | }
54 |
55 | /**
56 | * 挂断
57 | */
58 | @UrlLimit
59 | @PostMapping("/hangup")
60 | public JSONObject hangup(@Userid String userId, @RequestBody HangupVo hangupVo) {
61 | boolean result = videoService.hangup(userId, hangupVo);
62 | return ResultUtil.ResultByFlag(result);
63 | }
64 |
65 | /**
66 | * 邀请
67 | */
68 | @UrlLimit
69 | @PostMapping("/invite")
70 | public JSONObject invite(@Userid String userId, @RequestBody InviteVo inviteVo) {
71 | boolean result = videoService.invite(userId, inviteVo);
72 | return ResultUtil.ResultByFlag(result);
73 | }
74 |
75 | /**
76 | * 邀请
77 | */
78 | @UrlLimit
79 | @PostMapping("/accept")
80 | public JSONObject accept(@Userid String userId, @RequestBody AcceptVo acceptVo) {
81 | boolean result = videoService.accept(userId, acceptVo);
82 | return ResultUtil.ResultByFlag(result);
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/src/main/java/com/cershy/linyuminiserver/dto/NotifyDto.java:
--------------------------------------------------------------------------------
1 | package com.cershy.linyuminiserver.dto;
2 |
3 | import lombok.Data;
4 |
5 | import java.util.Date;
6 |
7 | @Data
8 | public class NotifyDto {
9 | private String type;
10 | private String content;
11 | private Date time;
12 | private String ext;
13 | }
14 |
--------------------------------------------------------------------------------
/src/main/java/com/cershy/linyuminiserver/dto/UrlLimitStats.java:
--------------------------------------------------------------------------------
1 | package com.cershy.linyuminiserver.dto;
2 |
3 | import lombok.Data;
4 |
5 | import java.time.LocalDateTime;
6 |
7 | @Data
8 | public class UrlLimitStats {
9 | private int violationCount = 0;
10 | private boolean blocked = false;
11 | private LocalDateTime lastViolationTime;
12 | }
13 |
--------------------------------------------------------------------------------
/src/main/java/com/cershy/linyuminiserver/dto/UserDto.java:
--------------------------------------------------------------------------------
1 | package com.cershy.linyuminiserver.dto;
2 |
3 | import lombok.Data;
4 |
5 | import java.util.List;
6 |
7 | @Data
8 | public class UserDto {
9 | private String id;
10 | private String name;
11 | private String avatar;
12 | private String type;
13 | private List badge;
14 | private String ipOwnership;
15 | }
16 |
--------------------------------------------------------------------------------
/src/main/java/com/cershy/linyuminiserver/entity/ChatList.java:
--------------------------------------------------------------------------------
1 | package com.cershy.linyuminiserver.entity;
2 |
3 | import com.baomidou.mybatisplus.annotation.FieldFill;
4 | import com.baomidou.mybatisplus.annotation.TableField;
5 | import com.baomidou.mybatisplus.annotation.TableId;
6 | import com.baomidou.mybatisplus.annotation.TableName;
7 | import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
8 | import com.cershy.linyuminiserver.dto.UserDto;
9 | import lombok.Data;
10 | import lombok.EqualsAndHashCode;
11 | import lombok.experimental.Accessors;
12 |
13 | import java.util.Date;
14 |
15 | @Data
16 | @EqualsAndHashCode(callSuper = false)
17 | @Accessors(chain = true)
18 | @TableName(value = "chat_list", autoResultMap = true)
19 | public class ChatList {
20 | private static final long serialVersionUID = 1L;
21 |
22 | /**
23 | * 聊天记录的唯一标识
24 | */
25 | @TableId("id")
26 | private String id;
27 |
28 | /**
29 | * 用户ID
30 | */
31 | @TableField("user_id")
32 | private String userId;
33 |
34 | /**
35 | * 目标用户ID
36 | */
37 | @TableField("target_id")
38 | private String targetId;
39 |
40 | /**
41 | * 目标用户信息
42 | */
43 | @TableField(value = "target_info", typeHandler = JacksonTypeHandler.class)
44 | private UserDto targetInfo;
45 |
46 | /**
47 | * 未读消息数
48 | */
49 | @TableField("unread_count")
50 | private Integer unreadCount;
51 |
52 | /**
53 | * 最后一条消息
54 | */
55 | @TableField(value = "last_message", typeHandler = JacksonTypeHandler.class)
56 | private Message lastMessage;
57 |
58 | /**
59 | * 聊天类型
60 | */
61 | @TableField("type")
62 | private String type;
63 |
64 | /**
65 | * 创建时间
66 | */
67 | @TableField(value = "create_time", fill = FieldFill.INSERT)
68 | private Date createTime;
69 |
70 | /**
71 | * 更新时间
72 | */
73 | @TableField(value = "update_time", fill = FieldFill.INSERT_UPDATE)
74 | private Date updateTime;
75 | }
76 |
--------------------------------------------------------------------------------
/src/main/java/com/cershy/linyuminiserver/entity/Group.java:
--------------------------------------------------------------------------------
1 | package com.cershy.linyuminiserver.entity;
2 |
3 | import com.baomidou.mybatisplus.annotation.FieldFill;
4 | import com.baomidou.mybatisplus.annotation.TableField;
5 | import com.baomidou.mybatisplus.annotation.TableId;
6 | import com.baomidou.mybatisplus.annotation.TableName;
7 | import lombok.Data;
8 | import lombok.EqualsAndHashCode;
9 | import lombok.experimental.Accessors;
10 |
11 | import java.util.Date;
12 |
13 | @Data
14 | @EqualsAndHashCode(callSuper = false)
15 | @Accessors(chain = true)
16 | @TableName("`group`")
17 | public class Group {
18 | private static final long serialVersionUID = 1L;
19 |
20 | @TableId("id")
21 | private String id;
22 |
23 | /**
24 | * 用户名
25 | */
26 | @TableField("name")
27 | private String name;
28 |
29 | /**
30 | * 头像
31 | */
32 | @TableField("avatar")
33 | private String avatar;
34 |
35 | /**
36 | * 创建时间
37 | */
38 | @TableField(value = "create_time", fill = FieldFill.INSERT)
39 | private Date createTime;
40 |
41 | /**
42 | * 更新时间
43 | */
44 | @TableField(value = "update_time", fill = FieldFill.INSERT_UPDATE)
45 | private Date updateTime;
46 | }
47 |
--------------------------------------------------------------------------------
/src/main/java/com/cershy/linyuminiserver/entity/Message.java:
--------------------------------------------------------------------------------
1 | package com.cershy.linyuminiserver.entity;
2 |
3 | import com.baomidou.mybatisplus.annotation.FieldFill;
4 | import com.baomidou.mybatisplus.annotation.TableField;
5 | import com.baomidou.mybatisplus.annotation.TableId;
6 | import com.baomidou.mybatisplus.annotation.TableName;
7 | import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
8 | import com.cershy.linyuminiserver.dto.UserDto;
9 | import lombok.Data;
10 | import lombok.EqualsAndHashCode;
11 | import lombok.experimental.Accessors;
12 |
13 | import java.util.Date;
14 |
15 | @Data
16 | @EqualsAndHashCode(callSuper = false)
17 | @Accessors(chain = true)
18 | @TableName(value = "message", autoResultMap = true)
19 | public class Message {
20 | private static final long serialVersionUID = 1L;
21 |
22 | /**
23 | * 消息唯一标识
24 | */
25 | @TableId("id")
26 | private String id;
27 |
28 | /**
29 | * 发送方用户ID
30 | */
31 | @TableField("from_id")
32 | private String fromId;
33 |
34 | /**
35 | * 接收方用户ID
36 | */
37 | @TableField("to_id")
38 | private String toId;
39 |
40 | /**
41 | * 发送方用户信息
42 | */
43 | @TableField(value = "from_info", typeHandler = JacksonTypeHandler.class)
44 | private UserDto fromInfo;
45 |
46 | /**
47 | * 消息内容
48 | */
49 | @TableField("message")
50 | private String message;
51 |
52 | /**
53 | * 引用的消息信息
54 | */
55 | @TableField(value = "reference_msg", typeHandler = JacksonTypeHandler.class)
56 | private Message referenceMsg;
57 |
58 | /**
59 | * at的用户信息
60 | */
61 | @TableField(value = "at_user", typeHandler = JacksonTypeHandler.class)
62 | private UserDto userDto;
63 |
64 | /**
65 | * 是否显示时间(布尔值)
66 | */
67 | @TableField("is_show_time")
68 | private Boolean isShowTime;
69 |
70 | /**
71 | * 消息类型
72 | */
73 | @TableField("type")
74 | private String type;
75 |
76 | /**
77 | * 消息来源
78 | */
79 | @TableField("source")
80 | private String source;
81 |
82 | /**
83 | * 创建时间
84 | */
85 | @TableField(value = "create_time", fill = FieldFill.INSERT)
86 | private Date createTime;
87 |
88 | /**
89 | * 更新时间
90 | */
91 | @TableField(value = "update_time", fill = FieldFill.INSERT_UPDATE)
92 | private Date updateTime;
93 | }
94 |
--------------------------------------------------------------------------------
/src/main/java/com/cershy/linyuminiserver/entity/User.java:
--------------------------------------------------------------------------------
1 | package com.cershy.linyuminiserver.entity;
2 |
3 | import com.baomidou.mybatisplus.annotation.FieldFill;
4 | import com.baomidou.mybatisplus.annotation.TableField;
5 | import com.baomidou.mybatisplus.annotation.TableId;
6 | import com.baomidou.mybatisplus.annotation.TableName;
7 | import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
8 | import lombok.Data;
9 | import lombok.EqualsAndHashCode;
10 | import lombok.experimental.Accessors;
11 |
12 | import java.util.Date;
13 | import java.util.List;
14 |
15 | @Data
16 | @EqualsAndHashCode(callSuper = false)
17 | @Accessors(chain = true)
18 | @TableName(value = "user", autoResultMap = true)
19 | public class User {
20 | private static final long serialVersionUID = 1L;
21 |
22 | @TableId("id")
23 | private String id;
24 |
25 | /**
26 | * 用户名
27 | */
28 | @TableField("name")
29 | private String name;
30 |
31 |
32 | /**
33 | * 用户类型
34 | */
35 | @TableField("type")
36 | private String type;
37 |
38 |
39 | /**
40 | * 头像
41 | */
42 | @TableField("avatar")
43 | private String avatar;
44 |
45 | /**
46 | * 邮箱
47 | */
48 | @TableField("email")
49 | private String email;
50 |
51 | /**
52 | * 徽章
53 | */
54 | @TableField(value = "badge", typeHandler = JacksonTypeHandler.class)
55 | private List badge;
56 |
57 | /**
58 | * 登录时间
59 | */
60 | @TableField(value = "login_time")
61 | private Date loginTime;
62 |
63 | /**
64 | * 创建时间
65 | */
66 | @TableField(value = "create_time", fill = FieldFill.INSERT)
67 | private Date createTime;
68 |
69 | /**
70 | * 更新时间
71 | */
72 | @TableField(value = "update_time", fill = FieldFill.INSERT_UPDATE)
73 | private Date updateTime;
74 | }
75 |
--------------------------------------------------------------------------------
/src/main/java/com/cershy/linyuminiserver/exception/GlobalExceptionHandler.java:
--------------------------------------------------------------------------------
1 | package com.cershy.linyuminiserver.exception;
2 |
3 | import com.cershy.linyuminiserver.utils.ResultUtil;
4 | import lombok.extern.slf4j.Slf4j;
5 | import org.springframework.web.bind.MethodArgumentNotValidException;
6 | import org.springframework.web.bind.annotation.ExceptionHandler;
7 | import org.springframework.web.bind.annotation.RestControllerAdvice;
8 |
9 | import javax.servlet.http.HttpServletRequest;
10 |
11 | /**
12 | * @author: dwh
13 | **/
14 | @RestControllerAdvice
15 | @Slf4j
16 | public class GlobalExceptionHandler {
17 |
18 | /**
19 | * 捕获未处理异常
20 | */
21 | @ExceptionHandler(value = Exception.class)
22 | public Object handleException(Exception e, HttpServletRequest request) {
23 | log.error("未处理异常 -> {}", e.getClass());
24 | log.error("url -> {}", request.getRequestURL());
25 | log.error("msg -> {}", e.getMessage());
26 | log.error("stack trace -> {}", e.getStackTrace());
27 | return ResultUtil.Fail("Internal service error");
28 | }
29 |
30 | /**
31 | * 捕获未处理异常
32 | */
33 | @ExceptionHandler(value = MethodArgumentNotValidException.class)
34 | public Object validationException(MethodArgumentNotValidException e, HttpServletRequest request) {
35 | log.error("未处理异常 -> {}", e.getClass());
36 | log.error("url -> {}", request.getRequestURL());
37 | log.error("msg -> {}", e.getBindingResult().getFieldError().getDefaultMessage());
38 | log.error("stack trace -> {}", e.getStackTrace());
39 | return ResultUtil.Fail(e.getBindingResult().getFieldError().getDefaultMessage());
40 | }
41 |
42 |
43 | /**
44 | * 自定义异常
45 | */
46 | @ExceptionHandler(value = com.cershy.linyuminiserver.exception.LinyuException.class)
47 | public Object LinyuException(LinyuException e, HttpServletRequest request) {
48 | log.error("自定义异常 -> {}", e.getClass());
49 | log.error("url -> {}", request.getRequestURL());
50 | log.error("msg -> {}", e.getMessage());
51 | log.error("stack trace -> {}", e.getStackTrace());
52 | if (null != e.paramToString())
53 | log.error("exception param -> {}", e.paramToString());
54 | e.empty();
55 | return ResultUtil.Result(e.getCode(), e.getMessage());
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/main/java/com/cershy/linyuminiserver/exception/LinyuException.java:
--------------------------------------------------------------------------------
1 | package com.cershy.linyuminiserver.exception;
2 |
3 |
4 | import cn.hutool.json.JSONUtil;
5 | import com.cershy.linyuminiserver.utils.ResultUtil;
6 |
7 | import java.util.HashMap;
8 |
9 | /**
10 | * @author: dwh
11 | **/
12 | public class LinyuException extends RuntimeException {
13 |
14 | private int code;
15 | private String message;
16 | private HashMap param;
17 |
18 | public LinyuException(String message) {
19 | this.code = ResultUtil.ResponseEnum.FAIL.getType();
20 | this.message = message;
21 | }
22 |
23 | /***
24 | * 添加异常信息 键值对
25 | */
26 | public LinyuException param(String key, Object value) {
27 | if (null == this.param) {
28 | this.param = new HashMap<>();
29 | }
30 | param.put(key, value);
31 | return this;
32 | }
33 |
34 | /***
35 | * 置空param
36 | */
37 | public LinyuException empty() {
38 | this.param = new HashMap<>();
39 | return this;
40 | }
41 |
42 | public LinyuException(int code, String message) {
43 | this.code = code;
44 | this.message = message;
45 | }
46 |
47 | public int getCode() {
48 | return code;
49 | }
50 |
51 | @Override
52 | public String getMessage() {
53 | return message;
54 | }
55 |
56 | public String paramToString() {
57 | if (null == this.param || this.param.size() <= 0)
58 | return null;
59 | return JSONUtil.toJsonStr(this.param);
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/main/java/com/cershy/linyuminiserver/filter/AuthenticationTokenFilter.java:
--------------------------------------------------------------------------------
1 | package com.cershy.linyuminiserver.filter;
2 |
3 | import cn.hutool.core.util.StrUtil;
4 | import com.cershy.linyuminiserver.utils.CacheUtil;
5 | import com.cershy.linyuminiserver.utils.JwtUtil;
6 | import com.cershy.linyuminiserver.utils.ResultUtil;
7 | import com.cershy.linyuminiserver.utils.UrlPermitUtil;
8 | import io.jsonwebtoken.Claims;
9 | import lombok.extern.slf4j.Slf4j;
10 | import org.springframework.stereotype.Component;
11 | import org.springframework.web.filter.OncePerRequestFilter;
12 |
13 | import javax.annotation.Resource;
14 | import javax.servlet.FilterChain;
15 | import javax.servlet.ServletException;
16 | import javax.servlet.http.HttpServletRequest;
17 | import javax.servlet.http.HttpServletResponse;
18 | import java.io.IOException;
19 | import java.io.PrintWriter;
20 | import java.util.HashMap;
21 | import java.util.Map;
22 |
23 | /**
24 | * @author: dwh
25 | **/
26 | @Component
27 | @Slf4j
28 | public class AuthenticationTokenFilter extends OncePerRequestFilter {
29 |
30 | private final String TokenName = "x-token";
31 |
32 | @Resource
33 | private UrlPermitUtil urlPermitUtil;
34 |
35 | @Resource
36 | CacheUtil cacheUtil;
37 |
38 | @Override
39 | protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
40 |
41 | if ("OPTIONS".equalsIgnoreCase(httpServletRequest.getMethod())) {
42 | return;
43 | }
44 |
45 | String token = httpServletRequest.getHeader(TokenName);
46 | String url = httpServletRequest.getRequestURI();
47 | // 验证url是否需要验证
48 | if (!urlPermitUtil.isPermitUrl(url)) {
49 | try {
50 | Claims claims = JwtUtil.parseToken(token);
51 | //验证是否在其他地方登录
52 | String userId = claims.get("userId").toString();
53 | String cacheToken = cacheUtil.getUserSessionCache(userId);
54 | if (StrUtil.isBlank(cacheToken)) {
55 | tokenInvalid(httpServletResponse, ResultUtil.TokenInvalid().toJSONString(0));
56 | return;
57 | } else if (!cacheToken.equals(token)) {
58 | tokenInvalid(httpServletResponse, ResultUtil.LoginElsewhere().toJSONString(0));
59 | return;
60 | }
61 | setUserInfo(claims, url, httpServletRequest, httpServletResponse);
62 | } catch (Exception e) {
63 | tokenInvalid(httpServletResponse, ResultUtil.TokenInvalid().toJSONString(0));
64 | return;
65 | }
66 | } else {
67 | if (StrUtil.isNotBlank(token)) {
68 | try {
69 | Claims claims = JwtUtil.parseToken(token);
70 | setUserInfo(claims, url, httpServletRequest, httpServletResponse);
71 | } catch (Exception e) {
72 | }
73 | }
74 | }
75 | filterChain.doFilter(httpServletRequest, httpServletResponse);
76 | }
77 |
78 | public void tokenInvalid(HttpServletResponse httpServletResponse, String msg) {
79 | try {
80 | httpServletResponse.setContentType("application/json;charset=UTF-8");
81 | httpServletResponse.setStatus(HttpServletResponse.SC_OK);
82 | PrintWriter out = httpServletResponse.getWriter();
83 | out.write(msg);
84 | out.flush();
85 | out.close();
86 | } catch (Exception e) {
87 | logger.error(e.getMessage());
88 | }
89 | }
90 |
91 | public void setUserInfo(Claims claims, String url,
92 | HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) {
93 | // 设置用户信息
94 | Map map = new HashMap<>();
95 | claims.entrySet().stream().forEach(e -> map.put(e.getKey(), e.getValue()));
96 | //验证角色是否有权限
97 | String role = (String) map.get("role");
98 | if (!urlPermitUtil.isRoleUrl(role, url)) {
99 | tokenInvalid(httpServletResponse, ResultUtil.Forbidden().toJSONString(0));
100 | return;
101 | }
102 | httpServletRequest.setAttribute("userinfo", map);
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/src/main/java/com/cershy/linyuminiserver/mapper/ChatListMapper.java:
--------------------------------------------------------------------------------
1 | package com.cershy.linyuminiserver.mapper;
2 |
3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper;
4 | import com.cershy.linyuminiserver.entity.ChatList;
5 |
6 | public interface ChatListMapper extends BaseMapper {
7 | }
8 |
--------------------------------------------------------------------------------
/src/main/java/com/cershy/linyuminiserver/mapper/GroupMapper.java:
--------------------------------------------------------------------------------
1 | package com.cershy.linyuminiserver.mapper;
2 |
3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper;
4 | import com.cershy.linyuminiserver.entity.Group;
5 |
6 | public interface GroupMapper extends BaseMapper {
7 | }
8 |
9 |
--------------------------------------------------------------------------------
/src/main/java/com/cershy/linyuminiserver/mapper/MessageMapper.java:
--------------------------------------------------------------------------------
1 | package com.cershy.linyuminiserver.mapper;
2 |
3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper;
4 | import com.cershy.linyuminiserver.entity.Message;
5 | import org.apache.ibatis.annotations.ResultMap;
6 | import org.apache.ibatis.annotations.Select;
7 |
8 | import java.util.List;
9 |
10 | public interface MessageMapper extends BaseMapper {
11 |
12 | @Select("SELECT * " +
13 | "FROM `message` " +
14 | "WHERE (`from_id` = #{userId} AND `to_id` = #{targetId}) " +
15 | " OR (`from_id` = #{targetId} AND `to_id` = #{userId}) " +
16 | "ORDER BY `create_time` DESC LIMIT 1")
17 | Message getPreviousShowTimeMsg(String userId, String targetId);
18 |
19 |
20 | @Select("SELECT * " +
21 | " FROM `message` " +
22 | " WHERE (`from_id` = #{userId} AND `to_id` = #{targetId}) " +
23 | " OR (`from_id` = #{targetId} AND `to_id` = #{userId}) " +
24 | " OR (`source` = 'group' AND `to_id` = #{targetId}) " +
25 | " ORDER BY `create_time` DESC LIMIT #{index}, #{num} ")
26 | @ResultMap("mybatis-plus_Message")
27 | List record(String userId, String targetId, int index, int num);
28 | }
29 |
30 |
--------------------------------------------------------------------------------
/src/main/java/com/cershy/linyuminiserver/mapper/UserMapper.java:
--------------------------------------------------------------------------------
1 | package com.cershy.linyuminiserver.mapper;
2 |
3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper;
4 | import com.cershy.linyuminiserver.dto.UserDto;
5 | import com.cershy.linyuminiserver.entity.User;
6 | import org.apache.ibatis.annotations.MapKey;
7 | import org.apache.ibatis.annotations.ResultMap;
8 | import org.apache.ibatis.annotations.Select;
9 |
10 | import java.util.List;
11 | import java.util.Map;
12 |
13 | public interface UserMapper extends BaseMapper {
14 |
15 | @Select("SELECT * FROM user WHERE id = #{userId}")
16 | @ResultMap("UserDtoResultMap")
17 | UserDto getUserById(String userId);
18 |
19 | @Select("SELECT * FROM user ORDER BY type DESC")
20 | @ResultMap("UserDtoResultMap")
21 | List listUser();
22 |
23 | @Select("SELECT * FROM user ORDER BY type DESC")
24 | @MapKey("id")
25 | @ResultMap("UserDtoResultMap")
26 | Map listMapUser();
27 | }
28 |
--------------------------------------------------------------------------------
/src/main/java/com/cershy/linyuminiserver/runner/UrlPassRunner.java:
--------------------------------------------------------------------------------
1 | package com.cershy.linyuminiserver.runner;
2 |
3 | import com.cershy.linyuminiserver.annotation.UrlFree;
4 | import com.cershy.linyuminiserver.annotation.UrlResource;
5 | import com.cershy.linyuminiserver.utils.UrlPermitUtil;
6 | import org.slf4j.Logger;
7 | import org.slf4j.LoggerFactory;
8 | import org.springframework.boot.ApplicationArguments;
9 | import org.springframework.boot.ApplicationRunner;
10 | import org.springframework.stereotype.Component;
11 | import org.springframework.web.method.HandlerMethod;
12 | import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
13 | import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
14 |
15 | import javax.annotation.Resource;
16 | import java.lang.annotation.Annotation;
17 | import java.util.ArrayList;
18 | import java.util.List;
19 | import java.util.Map;
20 | import java.util.Set;
21 |
22 | /**
23 | * @author: dwh
24 | **/
25 | @Component
26 | public class UrlPassRunner implements ApplicationRunner {
27 |
28 | private static final Logger logger = LoggerFactory.getLogger(UrlPassRunner.class);
29 |
30 | @Resource
31 | private UrlPermitUtil urlPermitUtil;
32 |
33 | @Resource
34 | private RequestMappingHandlerMapping requestMappingHandlerMapping;
35 |
36 | @Override
37 | public void run(ApplicationArguments args) {
38 | Map methodMap = requestMappingHandlerMapping.getHandlerMethods();
39 | List urlList = new ArrayList<>();
40 | for (Map.Entry entry : methodMap.entrySet()) {
41 | RequestMappingInfo requestMappingInfo = entry.getKey();
42 | HandlerMethod handlerMethod = entry.getValue();
43 | Annotation[] annotations = handlerMethod.getMethod().getAnnotations();
44 | for (Annotation annotation : annotations) {
45 | // 免验证url
46 | if (annotation.annotationType().equals(UrlFree.class)) {
47 | //获取请求路径
48 | Set directPaths = requestMappingInfo.getPatternValues();
49 | for (String url : directPaths) {
50 | urlList.add(url.replaceAll("\\{[^\\}]+\\}", "**"));
51 | }
52 | }
53 | // 免验证url
54 | if (annotation.annotationType().equals(UrlResource.class)) {
55 | UrlResource urlResource = (UrlResource) annotation;
56 | String value = urlResource.value();
57 | //获取请求路径
58 | Set directPaths = requestMappingInfo.getPatternValues();
59 | for (String url : directPaths) {
60 | urlPermitUtil.addRoleUrl(value, url);
61 | }
62 | }
63 | }
64 | }
65 | urlPermitUtil.addUrls(urlList);
66 | logger.info("-----not verify that the url is successfully loaded-----");
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/src/main/java/com/cershy/linyuminiserver/schedule/ExpiredClearTask.java:
--------------------------------------------------------------------------------
1 | package com.cershy.linyuminiserver.schedule;
2 |
3 | import com.cershy.linyuminiserver.service.MessageService;
4 | import com.cershy.linyuminiserver.service.UserService;
5 | import org.springframework.beans.factory.annotation.Value;
6 | import org.springframework.scheduling.annotation.Scheduled;
7 | import org.springframework.stereotype.Component;
8 |
9 | import javax.annotation.Resource;
10 | import java.time.LocalDate;
11 |
12 | @Component
13 | public class ExpiredClearTask {
14 |
15 | @Resource
16 | MessageService messageService;
17 |
18 | @Resource
19 | UserService userService;
20 |
21 | @Value("${linyu.expires}")
22 | int expirationDays;
23 |
24 |
25 | @Scheduled(cron = "0 0 0 * * ?")
26 | public void deleteExpiredContent() {
27 | LocalDate expirationDate = LocalDate.now().minusDays(expirationDays);
28 | messageService.deleteExpiredMessages(expirationDate);
29 | userService.deleteExpiredUsers(expirationDate);
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/main/java/com/cershy/linyuminiserver/service/AiChatService.java:
--------------------------------------------------------------------------------
1 | package com.cershy.linyuminiserver.service;
2 |
3 | import cn.hutool.json.JSONArray;
4 | import cn.hutool.json.JSONConfig;
5 | import cn.hutool.json.JSONUtil;
6 | import com.cershy.linyuminiserver.constant.MessageSource;
7 | import com.cershy.linyuminiserver.constant.MessageType;
8 | import com.cershy.linyuminiserver.constant.TextContentType;
9 | import com.cershy.linyuminiserver.dto.UserDto;
10 | import com.cershy.linyuminiserver.vo.message.SendMessageVo;
11 | import com.cershy.linyuminiserver.vo.message.TextMessageContent;
12 | import lombok.extern.slf4j.Slf4j;
13 | import org.springframework.context.annotation.Lazy;
14 | import org.springframework.scheduling.annotation.Async;
15 | import org.springframework.stereotype.Service;
16 |
17 | import javax.annotation.Resource;
18 |
19 | @Service
20 | @Slf4j
21 | public class AiChatService {
22 |
23 | @Resource
24 | @Lazy
25 | UserService userService;
26 |
27 | @Resource
28 | @Lazy
29 | MessageService messageService;
30 |
31 | @Resource
32 | DoubaoAiService doubaoAiService;
33 |
34 | @Resource
35 | DeepSeekAiService deepSeekAiService;
36 |
37 | @Async("taskExecutor")
38 | public void sendBotReply(String userId, String targetId, UserDto botUser, String content) {
39 | UserDto user = userService.getUserById(userId);
40 | // 创建消息
41 | // at内容
42 | TextMessageContent atUser = new TextMessageContent();
43 | atUser.setType(TextContentType.At);
44 | JSONConfig config = new JSONConfig().setIgnoreNullValue(true);
45 | atUser.setContent(JSONUtil.toJsonStr(user, config));
46 | // 文本消息内容
47 | String ask = "请稍后尝试~";
48 | switch (botUser.getId()) {
49 | case "doubao":
50 | ask = doubaoAiService.ask(userId, content);
51 | break;
52 | case "deepseek":
53 | ask = deepSeekAiService.ask(userId, content);
54 | break;
55 | }
56 | TextMessageContent msgText = new TextMessageContent();
57 | msgText.setType(TextContentType.Text);
58 | msgText.setContent(ask);
59 | // 合并消息内容
60 | JSONArray msgContent = new JSONArray();
61 | msgContent.add(atUser);
62 | msgContent.add(msgText);
63 | // 发送消息
64 | SendMessageVo sendMessageVo = new SendMessageVo();
65 | sendMessageVo.setTargetId(targetId);
66 | sendMessageVo.setSource(MessageSource.Group);
67 | sendMessageVo.setMsgContent(msgContent.toJSONString(0));
68 | sendMessageVo.setUserIp("机器人");
69 | sendMessageVo.setType(MessageType.Text);
70 | messageService.sendMessageToGroup(botUser.getId(), sendMessageVo);
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/src/main/java/com/cershy/linyuminiserver/service/ChatListService.java:
--------------------------------------------------------------------------------
1 | package com.cershy.linyuminiserver.service;
2 |
3 | import com.baomidou.mybatisplus.extension.service.IService;
4 | import com.cershy.linyuminiserver.entity.ChatList;
5 | import com.cershy.linyuminiserver.entity.Message;
6 |
7 | import java.util.List;
8 |
9 | public interface ChatListService extends IService {
10 | List privateList(String userId);
11 |
12 | ChatList getGroup(String userId);
13 |
14 | ChatList create(String userId, String targetId);
15 |
16 | boolean updateChatListGroup(Message message);
17 |
18 | boolean updateChatListPrivate(String userId, String targetId, Message message);
19 |
20 | boolean read(String userId, String targetId);
21 |
22 | boolean delete(String userId, String chatListId);
23 | }
24 |
--------------------------------------------------------------------------------
/src/main/java/com/cershy/linyuminiserver/service/DeepSeekAiService.java:
--------------------------------------------------------------------------------
1 | package com.cershy.linyuminiserver.service;
2 |
3 | import cn.hutool.core.util.StrUtil;
4 | import com.cershy.linyuminiserver.configs.LinyuConfig;
5 | import com.github.benmanes.caffeine.cache.Cache;
6 | import com.github.benmanes.caffeine.cache.Caffeine;
7 | import org.springframework.http.HttpEntity;
8 | import org.springframework.http.HttpHeaders;
9 | import org.springframework.http.HttpMethod;
10 | import org.springframework.http.ResponseEntity;
11 | import org.springframework.stereotype.Service;
12 | import org.springframework.web.client.RestTemplate;
13 |
14 | import javax.annotation.PostConstruct;
15 | import javax.annotation.Resource;
16 | import java.util.ArrayList;
17 | import java.util.HashMap;
18 | import java.util.List;
19 | import java.util.Map;
20 | import java.util.concurrent.TimeUnit;
21 | import java.util.concurrent.atomic.AtomicInteger;
22 |
23 | @Service
24 | public class DeepSeekAiService {
25 |
26 | private final Cache limitCache;
27 |
28 | @Resource
29 | private LinyuConfig linyuConfig;
30 | private RestTemplate restTemplate;
31 | private HttpHeaders headers;
32 |
33 | DeepSeekAiService() {
34 | this.limitCache = Caffeine.newBuilder()
35 | .expireAfterWrite(24, TimeUnit.HOURS)
36 | .build();
37 | }
38 |
39 | @PostConstruct
40 | public void init() {
41 | // 初始化 RestTemplate
42 | restTemplate = new RestTemplate();
43 | // 设置请求头,包括 API Key
44 | headers = new HttpHeaders();
45 | headers.set("Authorization", "Bearer " + linyuConfig.getDeepSeek().getApiKey());
46 | headers.set("Content-Type", "application/json");
47 | }
48 |
49 | public String ask(String userId, String content) {
50 | AtomicInteger count = limitCache.getIfPresent(userId);
51 | if (count == null) {
52 | count = new AtomicInteger(0);
53 | limitCache.put(userId, count);
54 | }
55 |
56 | // 检查用户调用次数限制
57 | if (linyuConfig.getDeepSeek().getCountLimit() > 0
58 | && count.incrementAndGet() > linyuConfig.getDeepSeek().getCountLimit()) {
59 | return "您已经达到限制了,请24小时后再来吧~";
60 | }
61 |
62 | // 检查内容是否为空
63 | if (StrUtil.isBlank(content)) {
64 | return "内容不能为空~";
65 | }
66 |
67 | // 检查内容长度限制
68 | if (linyuConfig.getDeepSeek().getLengthLimit() > 0
69 | && content.length() > linyuConfig.getDeepSeek().getLengthLimit()) {
70 | return "问一些简单的问题吧~";
71 | }
72 |
73 | // 调用 DeepSeek API
74 | try {
75 | // 构建请求体
76 | Map requestBody = new HashMap<>();
77 | requestBody.put("model", linyuConfig.getDeepSeek().getModel());
78 | requestBody.put("stream", false);
79 | List