测试
This commit is contained in:
commit
2f9a3cdbac
147
.gitignore
vendored
Normal file
147
.gitignore
vendored
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
### @author <a href="https://github.com/liyupi">程序员鱼皮</a> ###
|
||||||
|
### @from <a href="https://yupi.icu">编程导航知识星球</a> ###
|
||||||
|
|
||||||
|
HELP.md
|
||||||
|
target/
|
||||||
|
!.mvn/wrapper/maven-wrapper.jar
|
||||||
|
!**/src/main/**/target/
|
||||||
|
!**/src/test/**/target/
|
||||||
|
|
||||||
|
### STS ###
|
||||||
|
.apt_generated
|
||||||
|
.classpath
|
||||||
|
.factorypath
|
||||||
|
.project
|
||||||
|
.settings
|
||||||
|
.springBeans
|
||||||
|
.sts4-cache
|
||||||
|
|
||||||
|
### IntelliJ IDEA ###
|
||||||
|
.idea
|
||||||
|
*.iws
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
|
||||||
|
### NetBeans ###
|
||||||
|
/nbproject/private/
|
||||||
|
/nbbuild/
|
||||||
|
/dist/
|
||||||
|
/nbdist/
|
||||||
|
/.nb-gradle/
|
||||||
|
build/
|
||||||
|
!**/src/main/**/build/
|
||||||
|
!**/src/test/**/build/
|
||||||
|
|
||||||
|
### VS Code ###
|
||||||
|
.vscode/
|
||||||
|
### Java template
|
||||||
|
# Compiled class file
|
||||||
|
*.class
|
||||||
|
|
||||||
|
# Log file
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# BlueJ files
|
||||||
|
*.ctxt
|
||||||
|
|
||||||
|
# Mobile Tools for Java (J2ME)
|
||||||
|
.mtj.tmp/
|
||||||
|
|
||||||
|
# Package Files #
|
||||||
|
*.jar
|
||||||
|
*.war
|
||||||
|
*.nar
|
||||||
|
*.ear
|
||||||
|
*.zip
|
||||||
|
*.tar.gz
|
||||||
|
*.rar
|
||||||
|
|
||||||
|
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||||
|
hs_err_pid*
|
||||||
|
|
||||||
|
### Maven template
|
||||||
|
target/
|
||||||
|
pom.xml.tag
|
||||||
|
pom.xml.releaseBackup
|
||||||
|
pom.xml.versionsBackup
|
||||||
|
pom.xml.next
|
||||||
|
release.properties
|
||||||
|
dependency-reduced-pom.xml
|
||||||
|
buildNumber.properties
|
||||||
|
.mvn/timing.properties
|
||||||
|
# https://github.com/takari/maven-wrapper#usage-without-binary-jar
|
||||||
|
.mvn/wrapper/maven-wrapper.jar
|
||||||
|
|
||||||
|
### JetBrains template
|
||||||
|
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
|
||||||
|
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||||
|
|
||||||
|
# User-specific stuff
|
||||||
|
.idea/**/workspace.xml
|
||||||
|
.idea/**/tasks.xml
|
||||||
|
.idea/**/usage.statistics.xml
|
||||||
|
.idea/**/dictionaries
|
||||||
|
.idea/**/shelf
|
||||||
|
|
||||||
|
# Generated files
|
||||||
|
.idea/**/contentModel.xml
|
||||||
|
|
||||||
|
# Sensitive or high-churn files
|
||||||
|
.idea/**/dataSources/
|
||||||
|
.idea/**/dataSources.ids
|
||||||
|
.idea/**/dataSources.local.xml
|
||||||
|
.idea/**/sqlDataSources.xml
|
||||||
|
.idea/**/dynamic.xml
|
||||||
|
.idea/**/uiDesigner.xml
|
||||||
|
.idea/**/dbnavigator.xml
|
||||||
|
|
||||||
|
# Gradle
|
||||||
|
.idea/**/gradle.xml
|
||||||
|
.idea/**/libraries
|
||||||
|
|
||||||
|
# Gradle and Maven with auto-import
|
||||||
|
# When using Gradle or Maven with auto-import, you should exclude module files,
|
||||||
|
# since they will be recreated, and may cause churn. Uncomment if using
|
||||||
|
# auto-import.
|
||||||
|
# .idea/artifacts
|
||||||
|
# .idea/compiler.xml
|
||||||
|
# .idea/jarRepositories.xml
|
||||||
|
# .idea/modules.xml
|
||||||
|
# .idea/*.iml
|
||||||
|
# .idea/modules
|
||||||
|
# *.iml
|
||||||
|
# *.ipr
|
||||||
|
|
||||||
|
# CMake
|
||||||
|
cmake-build-*/
|
||||||
|
|
||||||
|
# Mongo Explorer plugin
|
||||||
|
.idea/**/mongoSettings.xml
|
||||||
|
|
||||||
|
# File-based project format
|
||||||
|
*.iws
|
||||||
|
|
||||||
|
# IntelliJ
|
||||||
|
out/
|
||||||
|
|
||||||
|
# mpeltonen/sbt-idea plugin
|
||||||
|
.idea_modules/
|
||||||
|
|
||||||
|
# JIRA plugin
|
||||||
|
atlassian-ide-plugin.xml
|
||||||
|
|
||||||
|
# Cursive Clojure plugin
|
||||||
|
.idea/replstate.xml
|
||||||
|
|
||||||
|
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||||
|
com_crashlytics_export_strings.xml
|
||||||
|
crashlytics.properties
|
||||||
|
crashlytics-build.properties
|
||||||
|
fabric.properties
|
||||||
|
|
||||||
|
# Editor-based Rest Client
|
||||||
|
.idea/httpRequests
|
||||||
|
|
||||||
|
# Android studio 3.1+ serialized cache file
|
||||||
|
.idea/caches/build_file_checksums.ser
|
||||||
|
|
2
.mvn/wrapper/maven-wrapper.properties
vendored
Normal file
2
.mvn/wrapper/maven-wrapper.properties
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.1/apache-maven-3.8.1-bin.zip
|
||||||
|
wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar
|
15
Dockerfile
Normal file
15
Dockerfile
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
# Docker 镜像构建
|
||||||
|
# @author <a href="https://github.com/liyupi">程序员鱼皮</a>
|
||||||
|
# @from <a href="https://yupi.icu">编程导航知识星球</a>
|
||||||
|
FROM maven:3.8.1-jdk-8-slim as builder
|
||||||
|
|
||||||
|
# Copy local code to the container image.
|
||||||
|
WORKDIR /app
|
||||||
|
COPY pom.xml .
|
||||||
|
COPY src ./src
|
||||||
|
|
||||||
|
# Build a release artifact.
|
||||||
|
RUN mvn package -DskipTests
|
||||||
|
|
||||||
|
# Run the web service on container startup.
|
||||||
|
CMD ["java","-jar","/app/target/hebi-backend-0.0.1-SNAPSHOT.jar","--spring.profiles.active=prod"]
|
181
README.md
Normal file
181
README.md
Normal file
@ -0,0 +1,181 @@
|
|||||||
|
# SpringBoot 项目初始模板
|
||||||
|
|
||||||
|
> 作者:[程序员鱼皮](https://github.com/liyupi)
|
||||||
|
> 仅分享于 [编程导航知识星球](https://yupi.icu)
|
||||||
|
|
||||||
|
基于 Java SpringBoot 的项目初始模板,整合了常用框架和主流业务的示例代码。
|
||||||
|
|
||||||
|
只需 1 分钟即可完成内容网站的后端!!!大家还可以在此基础上快速开发自己的项目。
|
||||||
|
|
||||||
|
[toc]
|
||||||
|
|
||||||
|
## 模板特点
|
||||||
|
|
||||||
|
### 主流框架 & 特性
|
||||||
|
|
||||||
|
- Spring Boot 2.7.x(贼新)
|
||||||
|
- Spring MVC
|
||||||
|
- MyBatis + MyBatis Plus 数据访问(开启分页)
|
||||||
|
- Spring Boot 调试工具和项目处理器
|
||||||
|
- Spring AOP 切面编程
|
||||||
|
- Spring Scheduler 定时任务
|
||||||
|
- Spring 事务注解
|
||||||
|
|
||||||
|
### 数据存储
|
||||||
|
|
||||||
|
- MySQL 数据库
|
||||||
|
- Redis 内存数据库
|
||||||
|
- Elasticsearch 搜索引擎
|
||||||
|
- 腾讯云 COS 对象存储
|
||||||
|
|
||||||
|
### 工具类
|
||||||
|
|
||||||
|
- Easy Excel 表格处理
|
||||||
|
- Hutool 工具库
|
||||||
|
- Apache Commons Lang3 工具类
|
||||||
|
- Lombok 注解
|
||||||
|
|
||||||
|
### 业务特性
|
||||||
|
|
||||||
|
- 业务代码生成器(支持自动生成 Service、Controller、数据模型代码)
|
||||||
|
- Spring Session Redis 分布式登录
|
||||||
|
- 全局请求响应拦截器(记录日志)
|
||||||
|
- 全局异常处理器
|
||||||
|
- 自定义错误码
|
||||||
|
- 封装通用响应类
|
||||||
|
- Swagger + Knife4j 接口文档
|
||||||
|
- 自定义权限注解 + 全局校验
|
||||||
|
- 全局跨域处理
|
||||||
|
- 长整数丢失精度解决
|
||||||
|
- 多环境配置
|
||||||
|
|
||||||
|
|
||||||
|
## 业务功能
|
||||||
|
|
||||||
|
- 提供示例 SQL(用户、帖子、帖子点赞、帖子收藏表)
|
||||||
|
- 用户登录、注册、注销、更新、检索、权限管理
|
||||||
|
- 帖子创建、删除、编辑、更新、数据库检索、ES 灵活检索
|
||||||
|
- 帖子点赞、取消点赞
|
||||||
|
- 帖子收藏、取消收藏、检索已收藏帖子
|
||||||
|
- 帖子全量同步 ES、增量同步 ES 定时任务
|
||||||
|
- 支持微信开放平台登录
|
||||||
|
- 支持微信公众号订阅、收发消息、设置菜单
|
||||||
|
- 支持分业务的文件上传
|
||||||
|
|
||||||
|
### 单元测试
|
||||||
|
|
||||||
|
- JUnit5 单元测试
|
||||||
|
- 示例单元测试类
|
||||||
|
|
||||||
|
### 架构设计
|
||||||
|
|
||||||
|
- 合理分层
|
||||||
|
|
||||||
|
|
||||||
|
## 快速上手
|
||||||
|
|
||||||
|
> 所有需要修改的地方鱼皮都标记了 `todo`,便于大家找到修改的位置~
|
||||||
|
|
||||||
|
### MySQL 数据库
|
||||||
|
|
||||||
|
1)修改 `application.yml` 的数据库配置为你自己的:
|
||||||
|
|
||||||
|
```yml
|
||||||
|
spring:
|
||||||
|
datasource:
|
||||||
|
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||||
|
url: jdbc:mysql://localhost:3306/my_db
|
||||||
|
username: root
|
||||||
|
password: 123456
|
||||||
|
```
|
||||||
|
|
||||||
|
2)执行 `sql/create_table.sql` 中的数据库语句,自动创建库表
|
||||||
|
|
||||||
|
3)启动项目,访问 `http://localhost:8101/api/doc.html` 即可打开接口文档,不需要写前端就能在线调试接口了~
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### Redis 分布式登录
|
||||||
|
|
||||||
|
1)修改 `application.yml` 的 Redis 配置为你自己的:
|
||||||
|
|
||||||
|
```yml
|
||||||
|
spring:
|
||||||
|
redis:
|
||||||
|
database: 1
|
||||||
|
host: localhost
|
||||||
|
port: 6379
|
||||||
|
timeout: 5000
|
||||||
|
password: 123456
|
||||||
|
```
|
||||||
|
|
||||||
|
2)修改 `application.yml` 中的 session 存储方式:
|
||||||
|
|
||||||
|
```yml
|
||||||
|
spring:
|
||||||
|
session:
|
||||||
|
store-type: redis
|
||||||
|
```
|
||||||
|
|
||||||
|
3)移除 `MainApplication` 类开头 `@SpringBootApplication` 注解内的 exclude 参数:
|
||||||
|
|
||||||
|
修改前:
|
||||||
|
|
||||||
|
```java
|
||||||
|
@SpringBootApplication(exclude = {RedisAutoConfiguration.class})
|
||||||
|
```
|
||||||
|
|
||||||
|
修改后:
|
||||||
|
|
||||||
|
|
||||||
|
```java
|
||||||
|
@SpringBootApplication
|
||||||
|
```
|
||||||
|
|
||||||
|
### Elasticsearch 搜索引擎
|
||||||
|
|
||||||
|
1)修改 `application.yml` 的 Elasticsearch 配置为你自己的:
|
||||||
|
|
||||||
|
```yml
|
||||||
|
spring:
|
||||||
|
elasticsearch:
|
||||||
|
uris: http://localhost:9200
|
||||||
|
username: root
|
||||||
|
password: 123456
|
||||||
|
```
|
||||||
|
|
||||||
|
2)复制 `sql/post_es_mapping.json` 文件中的内容,通过调用 Elasticsearch 的接口或者 Kibana Dev Tools 来创建索引(相当于数据库建表)
|
||||||
|
|
||||||
|
```
|
||||||
|
PUT post_v1
|
||||||
|
{
|
||||||
|
参数见 sql/post_es_mapping.json 文件
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
这步不会操作的话需要补充下 Elasticsearch 的知识,或者自行百度一下~
|
||||||
|
|
||||||
|
3)开启同步任务,将数据库的帖子同步到 Elasticsearch
|
||||||
|
|
||||||
|
找到 job 目录下的 `FullSyncPostToEs` 和 `IncSyncPostToEs` 文件,取消掉 `@Component` 注解的注释,再次执行程序即可触发同步:
|
||||||
|
|
||||||
|
```java
|
||||||
|
// todo 取消注释开启任务
|
||||||
|
//@Component
|
||||||
|
```
|
||||||
|
|
||||||
|
### 业务代码生成器
|
||||||
|
|
||||||
|
支持自动生成 Service、Controller、数据模型代码,配合 MyBatisX 插件,可以快速开发增删改查等实用基础功能。
|
||||||
|
|
||||||
|
找到 `generate.CodeGenerator` 类,修改生成参数和生成路径,并且支持注释掉不需要的生成逻辑,然后运行即可。
|
||||||
|
|
||||||
|
```
|
||||||
|
// 指定生成参数
|
||||||
|
String packageName = "com.yupi.springbootinit";
|
||||||
|
String dataName = "用户评论";
|
||||||
|
String dataKey = "userComment";
|
||||||
|
String upperDataKey = "UserComment";
|
||||||
|
```
|
||||||
|
|
||||||
|
生成代码后,可以移动到实际项目中,并且按照 `// todo` 注释的提示来针对自己的业务需求进行修改。
|
5
chart.sql
Normal file
5
chart.sql
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
INSERT INTO `chart` (`id`, `goal`, `chartData`, `chartType`, `genChart`, `genResult`, `userId`, `createTime`, `updateTime`, `isDelete`, `name`, `status`, `execMessage`) VALUES (1892831461256523777, '我想要分析网站近一年的用户增长趋势', '日期,用户\n1号,10\n2号,11\n3号,12', '柱状图', '{ \"title\": { \"text\": \"用户增长情况\" }, \"xAxis\": { \"type\": \"category\", \"data\": [\"1号\", \"2号\", \"3号\"] }, \"yAxis\": { \"type\": \"value\" }, \"series\": [ { \"name\": \"用户数\", \"type\": \"line\", \"data\": [10, 11, 12] } ] }', ' 结论: 根据提供的数据,从1号到3号,网站的用户数量呈现了稳定的增长趋势。具体来看,1号有10个用户,2号增加到11个用户,3号进一步增加到12个用户。这种逐日递增的趋势可能表明网站的内容或服务在这段时间内逐渐吸引了更多的访问者,或者网站的推广活动取得了一定的成效。不过,为了更准确地评估用户增长的原因和持续性,还需要更多的数据和深入的分析。', 1892569734694617090, '2025-02-21 14:59:22', '2025-03-15 03:16:11', 0, 'cs', 'succeed', NULL);
|
||||||
|
INSERT INTO `chart` (`id`, `goal`, `chartData`, `chartType`, `genChart`, `genResult`, `userId`, `createTime`, `updateTime`, `isDelete`, `name`, `status`, `execMessage`) VALUES (1895802041324875777, '分析网站用户增长情况', '日期,用户\n1号,10\n2号,11\n3号,12\n4号,80\n5号,0\n', '饼图', '{ \"title\": { \"text\": \"网站用户增长情况分析\" }, \"tooltip\": { \"trigger\": \"item\" }, \"legend\": { \"data\":[\"用户\"] }, \"series\": [ { \"name\":\"用户\", \"type\":\"pie\", \"radius\":\"50%\", \"data\":[ {\"value\":10,\"name\":\"1号\"}, {\"value\":11,\"name\":\"2号\"}, {\"value\":12,\"name\":\"3号\"}, {\"value\":80,\"name\":\"4号\"}, {\"value\":0,\"name\":\"5号\"} ] } ] }', '从数据来看,网站用户在各天的增长情况如下:1号有10个用户,2号有11个用户,3号有12个用户,4号有显著增长至80个用户,而5号用户数为0。饼图显示4号的用户数占据了绝大多数比例,表明该日可能有重大事件或活动吸引了大量新用户访问。而其他日期用户数相对较少且变化不大,尤其是5号用户数归零,可能由于某些原因导致用户流失或无访问。', 1892569734694617090, '2025-03-01 19:43:23', '2025-03-15 03:16:11', 0, 'cs示例7', 'succeed', NULL);
|
||||||
|
INSERT INTO `chart` (`id`, `goal`, `chartData`, `chartType`, `genChart`, `genResult`, `userId`, `createTime`, `updateTime`, `isDelete`, `name`, `status`, `execMessage`) VALUES (1895803701958881282, '分析用户增长情况', '日期,用户\n1号,10\n2号,30\n3号,12\n4号,80\n5号,7\n', '堆叠图', '{ \"title\": { \"text\": \"分析用户增长情况\" }, \"tooltip\": { \"trigger\": \"axis\", \"axisPointer\": { \"type\": \"cross\" } }, \"legend\": { \"data\": [\"用户\"] }, \"xAxis\": { \"type\": \"category\", \"boundaryGap\": false, \"data\": [\"1号\", \"2号\", \"3号\", \"4号\", \"5号\"] }, \"yAxis\": { \"type\": \"value\", \"axisLabel\": { \"formatter\": \"{value}\" } }, \"series\": [ { \"name\": \"用户\", \"type\": \"line\", \"stack\": \"总量\", \"areaStyle\": {}, \"data\": [10, 30, 12, 80, 7] } ] }', '从数据看,用户数在1号到5号期间呈现出显著的增长趋势。具体来看,1号的用户数为10人,之后迅速上升至2号的30人,3号稍有回落至12人,但随后在4号大幅上升至80人,最终在5号降至7人。整体上,除了个别日期外,用户数呈现明显的上升趋势,尤其在4号达到峰值,表明该日可能有重大活动或事件吸引了大量新用户。', 1892569734694617090, '2025-03-01 19:49:59', '2025-03-15 03:16:11', 0, 'cs示例9', 'succeed', NULL);
|
||||||
|
INSERT INTO `chart` (`id`, `goal`, `chartData`, `chartType`, `genChart`, `genResult`, `userId`, `createTime`, `updateTime`, `isDelete`, `name`, `status`, `execMessage`) VALUES (1903804404245499905, '分析网站用户增长情况', '日期,用户数\n1号,10\n2号,20\n3号,30\n4号,90\n5号,0\n6号,10\n7号,20\n', '折线图', '{ \"title\": { \"text\": \"分析网站用户增长情况\" }, \"xAxis\": { \"type\": \"category\", \"data\": [\"1号\", \"2号\", \"3号\", \"4号\", \"5号\", \"6号\", \"7号\"] }, \"yAxis\": { \"type\": \"value\" }, \"series\": [ { \"name\": \"用户数\", \"type\": \"line\", \"data\": [10, 20, 30, 90, 0, 10, 20] } ] }', '从数据看,网站用户数在观察期间经历了显著的波动。初始三天用户数稳步上升,从10人增至30人,但在第4天突然激增至90人,之后在第5天骤降至0人,最后两天又恢复增长至20人。这种波动可能与特定事件或活动有关,如推广、系统维护或用户行为变化等。', 1892569734694617090, '2025-03-23 21:41:55', '2025-03-23 21:54:49', 0, '测试1', 'succeed', NULL);
|
||||||
|
INSERT INTO `chart` (`id`, `goal`, `chartData`, `chartType`, `genChart`, `genResult`, `userId`, `createTime`, `updateTime`, `isDelete`, `name`, `status`, `execMessage`) VALUES (1912876933903224834, '帮我分析用户数量', '工号,姓名,性别,职称,学院,生日,手机,状态\n76564,孙赫,男,4,2024-04-09T20:29:09.000+00:00,17791641989,0\n213010177,张增煊,男,教授,7,2002-04-04T00:00:00.000+00:00,18509144920,0\n213010176,张增煊,男,教授,7,2002-04-04T00:00:00.000+00:00,18509144920,0\n213010173,张增煊,男,教授,7,2002-04-04T00:00:00.000+00:00,18509144920,0\n213010174,张增煊,男,教授,7,2002-04-04T00:00:00.000+00:00,18509144920,0\n213010175,张增煊,男,教授,7,2002-04-04T00:00:00.000+00:00,18509144920,0\n00000001,教师1,7,17342351231,0\n213010171,张增煊,男,教授,7,2002-04-04T00:00:00.000+00:00,18509144920,0\n213010172,张增煊,男,教授,7,2002-04-04T00:00:00.000+00:00,18509144920,0\n213010170,张增煊,男,教授,7,2002-04-04T00:00:00.000+00:00,18509144920,0\n', '折线图', '{\n\"title\": {\n\"text\": \"用户数量分析\"\n},\n\"tooltip\": {\n\"trigger\": \"axis\"\n},\n\"legend\": {\n\"data\": [\"用户数量\"]\n},\n\"xAxis\": {\n\"type\": \"category\",\n\"data\": [\"76564\", \"213010177\", \"213010176\", \"213010173\", \"213010174\", \"213010175\", \"00000001\", \"213010171\", \"213010172\", \"213010170\"]\n},\n\"yAxis\": {\n\"type\": \"value\"\n},\n\"series\": [\n{\n\"name\": \"用户数量\",\n\"type\": \"line\",\n\"data\": [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]\n}\n]\n}', '从数据来看,每个工号对应的用户数量均为1,这表明在所提供的数据集中,每个用户都是唯一的,没有重复的用户记录。这可能意味着数据集已经经过去重处理,或者在收集数据时确保了每个用户只被记录一次。此外,所有用户的状态都为0,这可能表示他们处于非活跃状态或者是数据集中的一个特定标记。', 1892569734694617090, '2025-04-17 22:32:43', '2025-04-17 22:32:43', 0, 'swl', 'wait', NULL);
|
BIN
doc/swagger.png
Normal file
BIN
doc/swagger.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 445 KiB |
316
mvnw
vendored
Normal file
316
mvnw
vendored
Normal file
@ -0,0 +1,316 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
# Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
# or more contributor license agreements. See the NOTICE file
|
||||||
|
# distributed with this work for additional information
|
||||||
|
# regarding copyright ownership. The ASF licenses this file
|
||||||
|
# to you under the Apache License, Version 2.0 (the
|
||||||
|
# "License"); you may not use this file except in compliance
|
||||||
|
# with the License. You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing,
|
||||||
|
# software distributed under the License is distributed on an
|
||||||
|
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
# KIND, either express or implied. See the License for the
|
||||||
|
# specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
# Maven Start Up Batch script
|
||||||
|
#
|
||||||
|
# Required ENV vars:
|
||||||
|
# ------------------
|
||||||
|
# JAVA_HOME - location of a JDK home dir
|
||||||
|
#
|
||||||
|
# Optional ENV vars
|
||||||
|
# -----------------
|
||||||
|
# M2_HOME - location of maven2's installed home dir
|
||||||
|
# MAVEN_OPTS - parameters passed to the Java VM when running Maven
|
||||||
|
# e.g. to debug Maven itself, use
|
||||||
|
# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
|
||||||
|
# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
if [ -z "$MAVEN_SKIP_RC" ] ; then
|
||||||
|
|
||||||
|
if [ -f /usr/local/etc/mavenrc ] ; then
|
||||||
|
. /usr/local/etc/mavenrc
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -f /etc/mavenrc ] ; then
|
||||||
|
. /etc/mavenrc
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -f "$HOME/.mavenrc" ] ; then
|
||||||
|
. "$HOME/.mavenrc"
|
||||||
|
fi
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
|
# OS specific support. $var _must_ be set to either true or false.
|
||||||
|
cygwin=false;
|
||||||
|
darwin=false;
|
||||||
|
mingw=false
|
||||||
|
case "`uname`" in
|
||||||
|
CYGWIN*) cygwin=true ;;
|
||||||
|
MINGW*) mingw=true;;
|
||||||
|
Darwin*) darwin=true
|
||||||
|
# Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
|
||||||
|
# See https://developer.apple.com/library/mac/qa/qa1170/_index.html
|
||||||
|
if [ -z "$JAVA_HOME" ]; then
|
||||||
|
if [ -x "/usr/libexec/java_home" ]; then
|
||||||
|
export JAVA_HOME="`/usr/libexec/java_home`"
|
||||||
|
else
|
||||||
|
export JAVA_HOME="/Library/Java/Home"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [ -z "$JAVA_HOME" ] ; then
|
||||||
|
if [ -r /etc/gentoo-release ] ; then
|
||||||
|
JAVA_HOME=`java-config --jre-home`
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$M2_HOME" ] ; then
|
||||||
|
## resolve links - $0 may be a link to maven's home
|
||||||
|
PRG="$0"
|
||||||
|
|
||||||
|
# need this for relative symlinks
|
||||||
|
while [ -h "$PRG" ] ; do
|
||||||
|
ls=`ls -ld "$PRG"`
|
||||||
|
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||||
|
if expr "$link" : '/.*' > /dev/null; then
|
||||||
|
PRG="$link"
|
||||||
|
else
|
||||||
|
PRG="`dirname "$PRG"`/$link"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
saveddir=`pwd`
|
||||||
|
|
||||||
|
M2_HOME=`dirname "$PRG"`/..
|
||||||
|
|
||||||
|
# make it fully qualified
|
||||||
|
M2_HOME=`cd "$M2_HOME" && pwd`
|
||||||
|
|
||||||
|
cd "$saveddir"
|
||||||
|
# echo Using m2 at $M2_HOME
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For Cygwin, ensure paths are in UNIX format before anything is touched
|
||||||
|
if $cygwin ; then
|
||||||
|
[ -n "$M2_HOME" ] &&
|
||||||
|
M2_HOME=`cygpath --unix "$M2_HOME"`
|
||||||
|
[ -n "$JAVA_HOME" ] &&
|
||||||
|
JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
|
||||||
|
[ -n "$CLASSPATH" ] &&
|
||||||
|
CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For Mingw, ensure paths are in UNIX format before anything is touched
|
||||||
|
if $mingw ; then
|
||||||
|
[ -n "$M2_HOME" ] &&
|
||||||
|
M2_HOME="`(cd "$M2_HOME"; pwd)`"
|
||||||
|
[ -n "$JAVA_HOME" ] &&
|
||||||
|
JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$JAVA_HOME" ]; then
|
||||||
|
javaExecutable="`which javac`"
|
||||||
|
if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
|
||||||
|
# readlink(1) is not available as standard on Solaris 10.
|
||||||
|
readLink=`which readlink`
|
||||||
|
if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
|
||||||
|
if $darwin ; then
|
||||||
|
javaHome="`dirname \"$javaExecutable\"`"
|
||||||
|
javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
|
||||||
|
else
|
||||||
|
javaExecutable="`readlink -f \"$javaExecutable\"`"
|
||||||
|
fi
|
||||||
|
javaHome="`dirname \"$javaExecutable\"`"
|
||||||
|
javaHome=`expr "$javaHome" : '\(.*\)/bin'`
|
||||||
|
JAVA_HOME="$javaHome"
|
||||||
|
export JAVA_HOME
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$JAVACMD" ] ; then
|
||||||
|
if [ -n "$JAVA_HOME" ] ; then
|
||||||
|
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||||
|
# IBM's JDK on AIX uses strange locations for the executables
|
||||||
|
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||||
|
else
|
||||||
|
JAVACMD="$JAVA_HOME/bin/java"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
JAVACMD="`\\unset -f command; \\command -v java`"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -x "$JAVACMD" ] ; then
|
||||||
|
echo "Error: JAVA_HOME is not defined correctly." >&2
|
||||||
|
echo " We cannot execute $JAVACMD" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$JAVA_HOME" ] ; then
|
||||||
|
echo "Warning: JAVA_HOME environment variable is not set."
|
||||||
|
fi
|
||||||
|
|
||||||
|
CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
|
||||||
|
|
||||||
|
# traverses directory structure from process work directory to filesystem root
|
||||||
|
# first directory with .mvn subdirectory is considered project base directory
|
||||||
|
find_maven_basedir() {
|
||||||
|
|
||||||
|
if [ -z "$1" ]
|
||||||
|
then
|
||||||
|
echo "Path not specified to find_maven_basedir"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
basedir="$1"
|
||||||
|
wdir="$1"
|
||||||
|
while [ "$wdir" != '/' ] ; do
|
||||||
|
if [ -d "$wdir"/.mvn ] ; then
|
||||||
|
basedir=$wdir
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
# workaround for JBEAP-8937 (on Solaris 10/Sparc)
|
||||||
|
if [ -d "${wdir}" ]; then
|
||||||
|
wdir=`cd "$wdir/.."; pwd`
|
||||||
|
fi
|
||||||
|
# end of workaround
|
||||||
|
done
|
||||||
|
echo "${basedir}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# concatenates all lines of a file
|
||||||
|
concat_lines() {
|
||||||
|
if [ -f "$1" ]; then
|
||||||
|
echo "$(tr -s '\n' ' ' < "$1")"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
BASE_DIR=`find_maven_basedir "$(pwd)"`
|
||||||
|
if [ -z "$BASE_DIR" ]; then
|
||||||
|
exit 1;
|
||||||
|
fi
|
||||||
|
|
||||||
|
##########################################################################################
|
||||||
|
# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
|
||||||
|
# This allows using the maven wrapper in projects that prohibit checking in binary data.
|
||||||
|
##########################################################################################
|
||||||
|
if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then
|
||||||
|
if [ "$MVNW_VERBOSE" = true ]; then
|
||||||
|
echo "Found .mvn/wrapper/maven-wrapper.jar"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
if [ "$MVNW_VERBOSE" = true ]; then
|
||||||
|
echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..."
|
||||||
|
fi
|
||||||
|
if [ -n "$MVNW_REPOURL" ]; then
|
||||||
|
jarUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
|
||||||
|
else
|
||||||
|
jarUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
|
||||||
|
fi
|
||||||
|
while IFS="=" read key value; do
|
||||||
|
case "$key" in (wrapperUrl) jarUrl="$value"; break ;;
|
||||||
|
esac
|
||||||
|
done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties"
|
||||||
|
if [ "$MVNW_VERBOSE" = true ]; then
|
||||||
|
echo "Downloading from: $jarUrl"
|
||||||
|
fi
|
||||||
|
wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar"
|
||||||
|
if $cygwin; then
|
||||||
|
wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"`
|
||||||
|
fi
|
||||||
|
|
||||||
|
if command -v wget > /dev/null; then
|
||||||
|
if [ "$MVNW_VERBOSE" = true ]; then
|
||||||
|
echo "Found wget ... using wget"
|
||||||
|
fi
|
||||||
|
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
|
||||||
|
wget "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
|
||||||
|
else
|
||||||
|
wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
|
||||||
|
fi
|
||||||
|
elif command -v curl > /dev/null; then
|
||||||
|
if [ "$MVNW_VERBOSE" = true ]; then
|
||||||
|
echo "Found curl ... using curl"
|
||||||
|
fi
|
||||||
|
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
|
||||||
|
curl -o "$wrapperJarPath" "$jarUrl" -f
|
||||||
|
else
|
||||||
|
curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f
|
||||||
|
fi
|
||||||
|
|
||||||
|
else
|
||||||
|
if [ "$MVNW_VERBOSE" = true ]; then
|
||||||
|
echo "Falling back to using Java to download"
|
||||||
|
fi
|
||||||
|
javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
|
||||||
|
# For Cygwin, switch paths to Windows format before running javac
|
||||||
|
if $cygwin; then
|
||||||
|
javaClass=`cygpath --path --windows "$javaClass"`
|
||||||
|
fi
|
||||||
|
if [ -e "$javaClass" ]; then
|
||||||
|
if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
|
||||||
|
if [ "$MVNW_VERBOSE" = true ]; then
|
||||||
|
echo " - Compiling MavenWrapperDownloader.java ..."
|
||||||
|
fi
|
||||||
|
# Compiling the Java class
|
||||||
|
("$JAVA_HOME/bin/javac" "$javaClass")
|
||||||
|
fi
|
||||||
|
if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
|
||||||
|
# Running the downloader
|
||||||
|
if [ "$MVNW_VERBOSE" = true ]; then
|
||||||
|
echo " - Running MavenWrapperDownloader.java ..."
|
||||||
|
fi
|
||||||
|
("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR")
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
##########################################################################################
|
||||||
|
# End of extension
|
||||||
|
##########################################################################################
|
||||||
|
|
||||||
|
export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
|
||||||
|
if [ "$MVNW_VERBOSE" = true ]; then
|
||||||
|
echo $MAVEN_PROJECTBASEDIR
|
||||||
|
fi
|
||||||
|
MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
|
||||||
|
|
||||||
|
# For Cygwin, switch paths to Windows format before running java
|
||||||
|
if $cygwin; then
|
||||||
|
[ -n "$M2_HOME" ] &&
|
||||||
|
M2_HOME=`cygpath --path --windows "$M2_HOME"`
|
||||||
|
[ -n "$JAVA_HOME" ] &&
|
||||||
|
JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
|
||||||
|
[ -n "$CLASSPATH" ] &&
|
||||||
|
CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
|
||||||
|
[ -n "$MAVEN_PROJECTBASEDIR" ] &&
|
||||||
|
MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Provide a "standardized" way to retrieve the CLI args that will
|
||||||
|
# work with both Windows and non-Windows executions.
|
||||||
|
MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
|
||||||
|
export MAVEN_CMD_LINE_ARGS
|
||||||
|
|
||||||
|
WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
|
||||||
|
|
||||||
|
exec "$JAVACMD" \
|
||||||
|
$MAVEN_OPTS \
|
||||||
|
$MAVEN_DEBUG_OPTS \
|
||||||
|
-classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
|
||||||
|
"-Dmaven.home=${M2_HOME}" \
|
||||||
|
"-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
|
||||||
|
${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
|
188
mvnw.cmd
vendored
Normal file
188
mvnw.cmd
vendored
Normal file
@ -0,0 +1,188 @@
|
|||||||
|
@REM ----------------------------------------------------------------------------
|
||||||
|
@REM Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
@REM or more contributor license agreements. See the NOTICE file
|
||||||
|
@REM distributed with this work for additional information
|
||||||
|
@REM regarding copyright ownership. The ASF licenses this file
|
||||||
|
@REM to you under the Apache License, Version 2.0 (the
|
||||||
|
@REM "License"); you may not use this file except in compliance
|
||||||
|
@REM with the License. You may obtain a copy of the License at
|
||||||
|
@REM
|
||||||
|
@REM https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
@REM
|
||||||
|
@REM Unless required by applicable law or agreed to in writing,
|
||||||
|
@REM software distributed under the License is distributed on an
|
||||||
|
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
@REM KIND, either express or implied. See the License for the
|
||||||
|
@REM specific language governing permissions and limitations
|
||||||
|
@REM under the License.
|
||||||
|
@REM ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@REM ----------------------------------------------------------------------------
|
||||||
|
@REM Maven Start Up Batch script
|
||||||
|
@REM
|
||||||
|
@REM Required ENV vars:
|
||||||
|
@REM JAVA_HOME - location of a JDK home dir
|
||||||
|
@REM
|
||||||
|
@REM Optional ENV vars
|
||||||
|
@REM M2_HOME - location of maven2's installed home dir
|
||||||
|
@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
|
||||||
|
@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
|
||||||
|
@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
|
||||||
|
@REM e.g. to debug Maven itself, use
|
||||||
|
@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
|
||||||
|
@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
|
||||||
|
@REM ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
|
||||||
|
@echo off
|
||||||
|
@REM set title of command window
|
||||||
|
title %0
|
||||||
|
@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
|
||||||
|
@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
|
||||||
|
|
||||||
|
@REM set %HOME% to equivalent of $HOME
|
||||||
|
if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
|
||||||
|
|
||||||
|
@REM Execute a user defined script before this one
|
||||||
|
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
|
||||||
|
@REM check for pre script, once with legacy .bat ending and once with .cmd ending
|
||||||
|
if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %*
|
||||||
|
if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %*
|
||||||
|
:skipRcPre
|
||||||
|
|
||||||
|
@setlocal
|
||||||
|
|
||||||
|
set ERROR_CODE=0
|
||||||
|
|
||||||
|
@REM To isolate internal variables from possible post scripts, we use another setlocal
|
||||||
|
@setlocal
|
||||||
|
|
||||||
|
@REM ==== START VALIDATION ====
|
||||||
|
if not "%JAVA_HOME%" == "" goto OkJHome
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo Error: JAVA_HOME not found in your environment. >&2
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the >&2
|
||||||
|
echo location of your Java installation. >&2
|
||||||
|
echo.
|
||||||
|
goto error
|
||||||
|
|
||||||
|
:OkJHome
|
||||||
|
if exist "%JAVA_HOME%\bin\java.exe" goto init
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo Error: JAVA_HOME is set to an invalid directory. >&2
|
||||||
|
echo JAVA_HOME = "%JAVA_HOME%" >&2
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the >&2
|
||||||
|
echo location of your Java installation. >&2
|
||||||
|
echo.
|
||||||
|
goto error
|
||||||
|
|
||||||
|
@REM ==== END VALIDATION ====
|
||||||
|
|
||||||
|
:init
|
||||||
|
|
||||||
|
@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
|
||||||
|
@REM Fallback to current working directory if not found.
|
||||||
|
|
||||||
|
set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
|
||||||
|
IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
|
||||||
|
|
||||||
|
set EXEC_DIR=%CD%
|
||||||
|
set WDIR=%EXEC_DIR%
|
||||||
|
:findBaseDir
|
||||||
|
IF EXIST "%WDIR%"\.mvn goto baseDirFound
|
||||||
|
cd ..
|
||||||
|
IF "%WDIR%"=="%CD%" goto baseDirNotFound
|
||||||
|
set WDIR=%CD%
|
||||||
|
goto findBaseDir
|
||||||
|
|
||||||
|
:baseDirFound
|
||||||
|
set MAVEN_PROJECTBASEDIR=%WDIR%
|
||||||
|
cd "%EXEC_DIR%"
|
||||||
|
goto endDetectBaseDir
|
||||||
|
|
||||||
|
:baseDirNotFound
|
||||||
|
set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
|
||||||
|
cd "%EXEC_DIR%"
|
||||||
|
|
||||||
|
:endDetectBaseDir
|
||||||
|
|
||||||
|
IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
|
||||||
|
|
||||||
|
@setlocal EnableExtensions EnableDelayedExpansion
|
||||||
|
for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
|
||||||
|
@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
|
||||||
|
|
||||||
|
:endReadAdditionalConfig
|
||||||
|
|
||||||
|
SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
|
||||||
|
set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
|
||||||
|
set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
|
||||||
|
|
||||||
|
set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
|
||||||
|
|
||||||
|
FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
|
||||||
|
IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B
|
||||||
|
)
|
||||||
|
|
||||||
|
@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
|
||||||
|
@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
|
||||||
|
if exist %WRAPPER_JAR% (
|
||||||
|
if "%MVNW_VERBOSE%" == "true" (
|
||||||
|
echo Found %WRAPPER_JAR%
|
||||||
|
)
|
||||||
|
) else (
|
||||||
|
if not "%MVNW_REPOURL%" == "" (
|
||||||
|
SET DOWNLOAD_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
|
||||||
|
)
|
||||||
|
if "%MVNW_VERBOSE%" == "true" (
|
||||||
|
echo Couldn't find %WRAPPER_JAR%, downloading it ...
|
||||||
|
echo Downloading from: %DOWNLOAD_URL%
|
||||||
|
)
|
||||||
|
|
||||||
|
powershell -Command "&{"^
|
||||||
|
"$webclient = new-object System.Net.WebClient;"^
|
||||||
|
"if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
|
||||||
|
"$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
|
||||||
|
"}"^
|
||||||
|
"[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^
|
||||||
|
"}"
|
||||||
|
if "%MVNW_VERBOSE%" == "true" (
|
||||||
|
echo Finished downloading %WRAPPER_JAR%
|
||||||
|
)
|
||||||
|
)
|
||||||
|
@REM End of extension
|
||||||
|
|
||||||
|
@REM Provide a "standardized" way to retrieve the CLI args that will
|
||||||
|
@REM work with both Windows and non-Windows executions.
|
||||||
|
set MAVEN_CMD_LINE_ARGS=%*
|
||||||
|
|
||||||
|
%MAVEN_JAVA_EXE% ^
|
||||||
|
%JVM_CONFIG_MAVEN_PROPS% ^
|
||||||
|
%MAVEN_OPTS% ^
|
||||||
|
%MAVEN_DEBUG_OPTS% ^
|
||||||
|
-classpath %WRAPPER_JAR% ^
|
||||||
|
"-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^
|
||||||
|
%WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
|
||||||
|
if ERRORLEVEL 1 goto error
|
||||||
|
goto end
|
||||||
|
|
||||||
|
:error
|
||||||
|
set ERROR_CODE=1
|
||||||
|
|
||||||
|
:end
|
||||||
|
@endlocal & set ERROR_CODE=%ERROR_CODE%
|
||||||
|
|
||||||
|
if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost
|
||||||
|
@REM check for post script, once with legacy .bat ending and once with .cmd ending
|
||||||
|
if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat"
|
||||||
|
if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd"
|
||||||
|
:skipRcPost
|
||||||
|
|
||||||
|
@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
|
||||||
|
if "%MAVEN_BATCH_PAUSE%"=="on" pause
|
||||||
|
|
||||||
|
if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE%
|
||||||
|
|
||||||
|
cmd /C exit /B %ERROR_CODE%
|
156
pom.xml
Normal file
156
pom.xml
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!-- @author <a href="https://github.com/liyupi">程序员鱼皮</a> -->
|
||||||
|
<!-- @from <a href="https://yupi.icu">编程导航知识星球</a> -->
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-parent</artifactId>
|
||||||
|
<version>2.7.2</version>
|
||||||
|
<relativePath/> <!-- lookup parent from repository -->
|
||||||
|
</parent>
|
||||||
|
<groupId>com.yupi</groupId>
|
||||||
|
<artifactId>hebi-backend</artifactId>
|
||||||
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
|
<name>hebi-backend</name>
|
||||||
|
<properties>
|
||||||
|
<java.version>1.8</java.version>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- 讯飞星火 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.github.briqt</groupId>
|
||||||
|
<artifactId>xunfei-spark4j</artifactId>
|
||||||
|
<version>1.3.0</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- https://redisson.pro/docs/getting-started/-->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.redisson</groupId>
|
||||||
|
<artifactId>redisson</artifactId>
|
||||||
|
<version>3.21.3</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-freemarker</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-aop</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.mybatis.spring.boot</groupId>
|
||||||
|
<artifactId>mybatis-spring-boot-starter</artifactId>
|
||||||
|
<version>2.2.2</version>
|
||||||
|
</dependency>
|
||||||
|
<!-- https://mvnrepository.com/artifact/com.baomidou/mybatis-plus-boot-starter -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.baomidou</groupId>
|
||||||
|
<artifactId>mybatis-plus-boot-starter</artifactId>
|
||||||
|
<version>3.5.2</version>
|
||||||
|
</dependency>
|
||||||
|
<!-- redis -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.session</groupId>
|
||||||
|
<artifactId>spring-session-data-redis</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<!-- elasticsearch-->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<!-- https://mvnrepository.com/artifact/com.github.binarywang/wx-java-mp-spring-boot-starter -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.binarywang</groupId>
|
||||||
|
<artifactId>wx-java-mp-spring-boot-starter</artifactId>
|
||||||
|
<version>4.4.0</version>
|
||||||
|
</dependency>
|
||||||
|
<!-- https://doc.xiaominfo.com/docs/quick-start#openapi2 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.xiaoymin</groupId>
|
||||||
|
<artifactId>knife4j-openapi2-spring-boot-starter</artifactId>
|
||||||
|
<version>4.4.0</version>
|
||||||
|
</dependency>
|
||||||
|
<!-- https://cloud.tencent.com/document/product/436/10199-->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.qcloud</groupId>
|
||||||
|
<artifactId>cos_api</artifactId>
|
||||||
|
<version>5.6.89</version>
|
||||||
|
</dependency>
|
||||||
|
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.commons</groupId>
|
||||||
|
<artifactId>commons-lang3</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<!-- https://github.com/alibaba/easyexcel -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba</groupId>
|
||||||
|
<artifactId>easyexcel</artifactId>
|
||||||
|
<version>3.1.1</version>
|
||||||
|
</dependency>
|
||||||
|
<!-- https://hutool.cn/docs/index.html#/-->
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.hutool</groupId>
|
||||||
|
<artifactId>hutool-all</artifactId>
|
||||||
|
<version>5.8.8</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-devtools</artifactId>
|
||||||
|
<scope>runtime</scope>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>mysql</groupId>
|
||||||
|
<artifactId>mysql-connector-java</artifactId>
|
||||||
|
<scope>runtime</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<excludes>
|
||||||
|
<exclude>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
</exclude>
|
||||||
|
</excludes>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
</project>
|
40
sql/create_table.sql
Normal file
40
sql/create_table.sql
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
|
||||||
|
|
||||||
|
-- 创建库
|
||||||
|
create database if not exists hebi;
|
||||||
|
|
||||||
|
-- 切换库
|
||||||
|
use hebi;
|
||||||
|
|
||||||
|
-- 用户表
|
||||||
|
create table if not exists user
|
||||||
|
(
|
||||||
|
id bigint auto_increment comment 'id' primary key,
|
||||||
|
userAccount varchar(256) not null comment '账号',
|
||||||
|
userPassword varchar(512) not null comment '密码',
|
||||||
|
userName varchar(256) null comment '用户昵称',
|
||||||
|
userAvatar varchar(1024) null comment '用户头像',
|
||||||
|
userRole varchar(256) default 'user' not null comment '用户角色:user/admin',
|
||||||
|
createTime datetime default CURRENT_TIMESTAMP not null comment '创建时间',
|
||||||
|
updateTime datetime default CURRENT_TIMESTAMP not null on update CURRENT_TIMESTAMP comment '更新时间',
|
||||||
|
isDelete tinyint default 0 not null comment '是否删除',
|
||||||
|
index idx_userAccount (userAccount)
|
||||||
|
) comment '用户' collate = utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
-- 图表信息表
|
||||||
|
create table if not exists chart
|
||||||
|
(
|
||||||
|
id bigint auto_increment comment 'id' primary key,
|
||||||
|
goal text null comment '分析目标',
|
||||||
|
`name` varchar(128) null comment '图标名称',
|
||||||
|
chartData text null comment '图表数据',
|
||||||
|
chartType varchar(128) null comment '图表类型',
|
||||||
|
genChart text null comment '生成的图表数据',
|
||||||
|
genResult text null comment '生成的分析结论',
|
||||||
|
status varchar(128) not null default 'wait' comment'wait,running,succeed,failed',
|
||||||
|
execMessage text null comment '执行信息',
|
||||||
|
userId bigint null comment '创建用户 id',
|
||||||
|
createTime datetime default CURRENT_TIMESTAMP not null comment '创建时间',
|
||||||
|
updateTime datetime default CURRENT_TIMESTAMP not null on update CURRENT_TIMESTAMP comment '更新时间',
|
||||||
|
isDelete tinyint default 0 not null comment '是否删除'
|
||||||
|
) comment '图表信息表' collate = utf8mb4_unicode_ci;
|
52
sql/post_es_mapping.json
Normal file
52
sql/post_es_mapping.json
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
{
|
||||||
|
"aliases": {
|
||||||
|
"post": {}
|
||||||
|
},
|
||||||
|
"mappings": {
|
||||||
|
"properties": {
|
||||||
|
"title": {
|
||||||
|
"type": "text",
|
||||||
|
"analyzer": "ik_max_word",
|
||||||
|
"search_analyzer": "ik_smart",
|
||||||
|
"fields": {
|
||||||
|
"keyword": {
|
||||||
|
"type": "keyword",
|
||||||
|
"ignore_above": 256
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"content": {
|
||||||
|
"type": "text",
|
||||||
|
"analyzer": "ik_max_word",
|
||||||
|
"search_analyzer": "ik_smart",
|
||||||
|
"fields": {
|
||||||
|
"keyword": {
|
||||||
|
"type": "keyword",
|
||||||
|
"ignore_above": 256
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"tags": {
|
||||||
|
"type": "keyword"
|
||||||
|
},
|
||||||
|
"thumbNum": {
|
||||||
|
"type": "long"
|
||||||
|
},
|
||||||
|
"favourNum": {
|
||||||
|
"type": "long"
|
||||||
|
},
|
||||||
|
"userId": {
|
||||||
|
"type": "keyword"
|
||||||
|
},
|
||||||
|
"createTime": {
|
||||||
|
"type": "date"
|
||||||
|
},
|
||||||
|
"updateTime": {
|
||||||
|
"type": "date"
|
||||||
|
},
|
||||||
|
"isDelete": {
|
||||||
|
"type": "keyword"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
27
src/main/java/com/yupi/springbootinit/MainApplication.java
Normal file
27
src/main/java/com/yupi/springbootinit/MainApplication.java
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
package com.yupi.springbootinit;
|
||||||
|
|
||||||
|
import org.mybatis.spring.annotation.MapperScan;
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
|
||||||
|
import org.springframework.context.annotation.EnableAspectJAutoProxy;
|
||||||
|
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主类(项目启动入口)
|
||||||
|
*
|
||||||
|
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
|
||||||
|
* @from <a href="https://yupi.icu">编程导航知识星球</a>
|
||||||
|
*/
|
||||||
|
// todo 如需开启 Redis,须移除 exclude 中的内容
|
||||||
|
@SpringBootApplication(exclude = {RedisAutoConfiguration.class})
|
||||||
|
@MapperScan("com.yupi.springbootinit.mapper")
|
||||||
|
@EnableScheduling
|
||||||
|
@EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true)
|
||||||
|
public class MainApplication {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
SpringApplication.run(MainApplication.class, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
package com.yupi.springbootinit.annotation;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 权限校验
|
||||||
|
*
|
||||||
|
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
|
||||||
|
* @from <a href="https://yupi.icu">编程导航知识星球</a>
|
||||||
|
*/
|
||||||
|
@Target(ElementType.METHOD)
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
public @interface AuthCheck {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 必须有某个角色
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
String mustRole() default "";
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,72 @@
|
|||||||
|
package com.yupi.springbootinit.aop;
|
||||||
|
|
||||||
|
import com.yupi.springbootinit.annotation.AuthCheck;
|
||||||
|
import com.yupi.springbootinit.common.ErrorCode;
|
||||||
|
import com.yupi.springbootinit.exception.BusinessException;
|
||||||
|
import com.yupi.springbootinit.model.entity.User;
|
||||||
|
import com.yupi.springbootinit.model.enums.UserRoleEnum;
|
||||||
|
import com.yupi.springbootinit.service.UserService;
|
||||||
|
import org.aspectj.lang.ProceedingJoinPoint;
|
||||||
|
import org.aspectj.lang.annotation.Around;
|
||||||
|
import org.aspectj.lang.annotation.Aspect;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.web.context.request.RequestAttributes;
|
||||||
|
import org.springframework.web.context.request.RequestContextHolder;
|
||||||
|
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 权限校验 AOP
|
||||||
|
*
|
||||||
|
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
|
||||||
|
* @from <a href="https://yupi.icu">编程导航知识星球</a>
|
||||||
|
*/
|
||||||
|
@Aspect
|
||||||
|
@Component
|
||||||
|
public class AuthInterceptor {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private UserService userService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行拦截
|
||||||
|
*
|
||||||
|
* @param joinPoint
|
||||||
|
* @param authCheck
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Around("@annotation(authCheck)")
|
||||||
|
public Object doInterceptor(ProceedingJoinPoint joinPoint, AuthCheck authCheck) throws Throwable {
|
||||||
|
String mustRole = authCheck.mustRole();
|
||||||
|
RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes();
|
||||||
|
HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();
|
||||||
|
// 当前登录用户
|
||||||
|
User loginUser = userService.getLoginUser(request);
|
||||||
|
UserRoleEnum mustRoleEnum = UserRoleEnum.getEnumByValue(mustRole);
|
||||||
|
// 不需要权限,放行
|
||||||
|
if (mustRoleEnum == null) {
|
||||||
|
return joinPoint.proceed();
|
||||||
|
}
|
||||||
|
// 必须有该权限才通过
|
||||||
|
UserRoleEnum userRoleEnum = UserRoleEnum.getEnumByValue(loginUser.getUserRole());
|
||||||
|
if (userRoleEnum == null) {
|
||||||
|
throw new BusinessException(ErrorCode.NO_AUTH_ERROR);
|
||||||
|
}
|
||||||
|
// 如果被封号,直接拒绝
|
||||||
|
if (UserRoleEnum.BAN.equals(userRoleEnum)) {
|
||||||
|
throw new BusinessException(ErrorCode.NO_AUTH_ERROR);
|
||||||
|
}
|
||||||
|
// 必须有管理员权限
|
||||||
|
if (UserRoleEnum.ADMIN.equals(mustRoleEnum)) {
|
||||||
|
// 用户没有管理员权限,拒绝
|
||||||
|
if (!UserRoleEnum.ADMIN.equals(userRoleEnum)) {
|
||||||
|
throw new BusinessException(ErrorCode.NO_AUTH_ERROR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 通过权限校验,放行
|
||||||
|
return joinPoint.proceed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,56 @@
|
|||||||
|
package com.yupi.springbootinit.aop;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.aspectj.lang.ProceedingJoinPoint;
|
||||||
|
import org.aspectj.lang.annotation.Around;
|
||||||
|
import org.aspectj.lang.annotation.Aspect;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.util.StopWatch;
|
||||||
|
import org.springframework.web.context.request.RequestAttributes;
|
||||||
|
import org.springframework.web.context.request.RequestContextHolder;
|
||||||
|
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 请求响应日志 AOP
|
||||||
|
*
|
||||||
|
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
|
||||||
|
* @from <a href="https://yupi.icu">编程导航知识星球</a>
|
||||||
|
**/
|
||||||
|
@Aspect
|
||||||
|
@Component
|
||||||
|
@Slf4j
|
||||||
|
public class LogInterceptor {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行拦截
|
||||||
|
*/
|
||||||
|
@Around("execution(* com.yupi.springbootinit.controller.*.*(..))")
|
||||||
|
public Object doInterceptor(ProceedingJoinPoint point) throws Throwable {
|
||||||
|
// 计时
|
||||||
|
StopWatch stopWatch = new StopWatch();
|
||||||
|
stopWatch.start();
|
||||||
|
// 获取请求路径
|
||||||
|
RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes();
|
||||||
|
HttpServletRequest httpServletRequest = ((ServletRequestAttributes) requestAttributes).getRequest();
|
||||||
|
// 生成请求唯一 id
|
||||||
|
String requestId = UUID.randomUUID().toString();
|
||||||
|
String url = httpServletRequest.getRequestURI();
|
||||||
|
// 获取请求参数
|
||||||
|
Object[] args = point.getArgs();
|
||||||
|
String reqParam = "[" + StringUtils.join(args, ", ") + "]";
|
||||||
|
// 输出请求日志
|
||||||
|
log.info("request start,id: {}, path: {}, ip: {}, params: {}", requestId, url,
|
||||||
|
httpServletRequest.getRemoteHost(), reqParam);
|
||||||
|
// 执行原方法
|
||||||
|
Object result = point.proceed();
|
||||||
|
// 输出响应日志
|
||||||
|
stopWatch.stop();
|
||||||
|
long totalTimeMillis = stopWatch.getTotalTimeMillis();
|
||||||
|
log.info("request end, id: {}, cost: {}ms", requestId, totalTimeMillis);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
38
src/main/java/com/yupi/springbootinit/api/OpenAiApi.java
Normal file
38
src/main/java/com/yupi/springbootinit/api/OpenAiApi.java
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
package com.yupi.springbootinit.api;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 调用 AI 平台的示例代码
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
public class OpenAiApi {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * AI 对话(需要自己创建请求响应对象)
|
||||||
|
// *
|
||||||
|
// * @param request
|
||||||
|
// * @param openAiApiKey
|
||||||
|
// * @return
|
||||||
|
// */
|
||||||
|
// public CreateChatCompletionResponse createChatCompletion(CreateChatCompletionRequest request, String openAiApiKey) {
|
||||||
|
// if (StringUtils.isBlank(openAiApiKey)) {
|
||||||
|
// throw new BusinessException(ErrorCode.PARAMS_ERROR, "未传 openAiApiKey");
|
||||||
|
// }
|
||||||
|
// String url = "https://api.openai.com/v1/chat/completions";
|
||||||
|
// String json = JSONUtil.toJsonStr(request);
|
||||||
|
// String result = HttpRequest.post(url)
|
||||||
|
// .header("Authorization", "Bearer " + openAiApiKey)
|
||||||
|
// .body(json)
|
||||||
|
// .execute()
|
||||||
|
// .body();
|
||||||
|
// return JSONUtil.toBean(result, CreateChatCompletionResponse.class);
|
||||||
|
// }
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,35 @@
|
|||||||
|
package com.yupi.springbootinit.common;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通用返回类
|
||||||
|
*
|
||||||
|
* @param <T>
|
||||||
|
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
|
||||||
|
* @from <a href="https://yupi.icu">编程导航知识星球</a>
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class BaseResponse<T> implements Serializable {
|
||||||
|
|
||||||
|
private int code;
|
||||||
|
|
||||||
|
private T data;
|
||||||
|
|
||||||
|
private String message;
|
||||||
|
|
||||||
|
public BaseResponse(int code, T data, String message) {
|
||||||
|
this.code = code;
|
||||||
|
this.data = data;
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BaseResponse(int code, T data) {
|
||||||
|
this(code, data, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
public BaseResponse(ErrorCode errorCode) {
|
||||||
|
this(errorCode.getCode(), null, errorCode.getMessage());
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
package com.yupi.springbootinit.common;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除请求
|
||||||
|
*
|
||||||
|
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
|
||||||
|
* @from <a href="https://yupi.icu">编程导航知识星球</a>
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class DeleteRequest implements Serializable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* id
|
||||||
|
*/
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
}
|
44
src/main/java/com/yupi/springbootinit/common/ErrorCode.java
Normal file
44
src/main/java/com/yupi/springbootinit/common/ErrorCode.java
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
package com.yupi.springbootinit.common;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自定义错误码
|
||||||
|
*
|
||||||
|
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
|
||||||
|
* @from <a href="https://yupi.icu">编程导航知识星球</a>
|
||||||
|
*/
|
||||||
|
public enum ErrorCode {
|
||||||
|
|
||||||
|
SUCCESS(0, "ok"),
|
||||||
|
PARAMS_ERROR(40000, "请求参数错误"),
|
||||||
|
NOT_LOGIN_ERROR(40100, "未登录"),
|
||||||
|
NO_AUTH_ERROR(40101, "无权限"),
|
||||||
|
NOT_FOUND_ERROR(40400, "请求数据不存在"),
|
||||||
|
TOO_MANY_REQUEST(42900, "请求过于频繁"),
|
||||||
|
FORBIDDEN_ERROR(40300, "禁止访问"),
|
||||||
|
SYSTEM_ERROR(50000, "系统内部异常"),
|
||||||
|
OPERATION_ERROR(50001, "操作失败");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 状态码
|
||||||
|
*/
|
||||||
|
private final int code;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 信息
|
||||||
|
*/
|
||||||
|
private final String message;
|
||||||
|
|
||||||
|
ErrorCode(int code, String message) {
|
||||||
|
this.code = code;
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCode() {
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMessage() {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,34 @@
|
|||||||
|
package com.yupi.springbootinit.common;
|
||||||
|
|
||||||
|
import com.yupi.springbootinit.constant.CommonConstant;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页请求
|
||||||
|
*
|
||||||
|
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
|
||||||
|
* @from <a href="https://yupi.icu">编程导航知识星球</a>
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class PageRequest {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当前页号
|
||||||
|
*/
|
||||||
|
private int current = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 页面大小
|
||||||
|
*/
|
||||||
|
private int pageSize = 10;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 排序字段
|
||||||
|
*/
|
||||||
|
private String sortField;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 排序顺序(默认升序)
|
||||||
|
*/
|
||||||
|
private String sortOrder = CommonConstant.SORT_ORDER_ASC;
|
||||||
|
}
|
@ -0,0 +1,52 @@
|
|||||||
|
package com.yupi.springbootinit.common;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回工具类
|
||||||
|
*
|
||||||
|
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
|
||||||
|
* @from <a href="https://yupi.icu">编程导航知识星球</a>
|
||||||
|
*/
|
||||||
|
public class ResultUtils {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 成功
|
||||||
|
*
|
||||||
|
* @param data
|
||||||
|
* @param <T>
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static <T> BaseResponse<T> success(T data) {
|
||||||
|
return new BaseResponse<>(0, data, "ok");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 失败
|
||||||
|
*
|
||||||
|
* @param errorCode
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static BaseResponse error(ErrorCode errorCode) {
|
||||||
|
return new BaseResponse<>(errorCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 失败
|
||||||
|
*
|
||||||
|
* @param code
|
||||||
|
* @param message
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static BaseResponse error(int code, String message) {
|
||||||
|
return new BaseResponse(code, null, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 失败
|
||||||
|
*
|
||||||
|
* @param errorCode
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static BaseResponse error(ErrorCode errorCode, String message) {
|
||||||
|
return new BaseResponse(errorCode.getCode(), null, message);
|
||||||
|
}
|
||||||
|
}
|
28
src/main/java/com/yupi/springbootinit/config/CorsConfig.java
Normal file
28
src/main/java/com/yupi/springbootinit/config/CorsConfig.java
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
package com.yupi.springbootinit.config;
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.web.servlet.config.annotation.CorsRegistry;
|
||||||
|
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 全局跨域配置
|
||||||
|
*
|
||||||
|
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
|
||||||
|
* @from <a href="https://yupi.icu">编程导航知识星球</a>
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
public class CorsConfig implements WebMvcConfigurer {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addCorsMappings(CorsRegistry registry) {
|
||||||
|
// 覆盖所有请求
|
||||||
|
registry.addMapping("/**")
|
||||||
|
// 允许发送 Cookie
|
||||||
|
.allowCredentials(true)
|
||||||
|
// 放行哪些域名(必须用 patterns,否则 * 会和 allowCredentials 冲突)
|
||||||
|
.allowedOriginPatterns("*")
|
||||||
|
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
|
||||||
|
.allowedHeaders("*")
|
||||||
|
.exposedHeaders("*");
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,53 @@
|
|||||||
|
package com.yupi.springbootinit.config;
|
||||||
|
|
||||||
|
import com.qcloud.cos.COSClient;
|
||||||
|
import com.qcloud.cos.ClientConfig;
|
||||||
|
import com.qcloud.cos.auth.BasicCOSCredentials;
|
||||||
|
import com.qcloud.cos.auth.COSCredentials;
|
||||||
|
import com.qcloud.cos.region.Region;
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 腾讯云对象存储客户端
|
||||||
|
*
|
||||||
|
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
|
||||||
|
* @from <a href="https://yupi.icu">编程导航知识星球</a>
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
@ConfigurationProperties(prefix = "cos.client")
|
||||||
|
@Data
|
||||||
|
public class CosClientConfig {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* accessKey
|
||||||
|
*/
|
||||||
|
private String accessKey;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* secretKey
|
||||||
|
*/
|
||||||
|
private String secretKey;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 区域
|
||||||
|
*/
|
||||||
|
private String region;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 桶名
|
||||||
|
*/
|
||||||
|
private String bucket;
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public COSClient cosClient() {
|
||||||
|
// 初始化用户身份信息(secretId, secretKey)
|
||||||
|
COSCredentials cred = new BasicCOSCredentials(accessKey, secretKey);
|
||||||
|
// 设置bucket的区域, COS地域的简称请参照 https://www.qcloud.com/document/product/436/6224
|
||||||
|
ClientConfig clientConfig = new ClientConfig(new Region(region));
|
||||||
|
// 生成cos客户端
|
||||||
|
return new COSClient(cred, clientConfig);
|
||||||
|
}
|
||||||
|
}
|
31
src/main/java/com/yupi/springbootinit/config/JsonConfig.java
Normal file
31
src/main/java/com/yupi/springbootinit/config/JsonConfig.java
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
package com.yupi.springbootinit.config;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.fasterxml.jackson.databind.module.SimpleModule;
|
||||||
|
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
|
||||||
|
import org.springframework.boot.jackson.JsonComponent;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Spring MVC Json 配置
|
||||||
|
*
|
||||||
|
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
|
||||||
|
* @from <a href="https://yupi.icu">编程导航知识星球</a>
|
||||||
|
*/
|
||||||
|
@JsonComponent
|
||||||
|
public class JsonConfig {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加 Long 转 json 精度丢失的配置
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
public ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) {
|
||||||
|
ObjectMapper objectMapper = builder.createXmlMapper(false).build();
|
||||||
|
SimpleModule module = new SimpleModule();
|
||||||
|
module.addSerializer(Long.class, ToStringSerializer.instance);
|
||||||
|
module.addSerializer(Long.TYPE, ToStringSerializer.instance);
|
||||||
|
objectMapper.registerModule(module);
|
||||||
|
return objectMapper;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,31 @@
|
|||||||
|
package com.yupi.springbootinit.config;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.DbType;
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
|
||||||
|
import org.mybatis.spring.annotation.MapperScan;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MyBatis Plus 配置
|
||||||
|
*
|
||||||
|
* @author https://github.com/liyupi
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
@MapperScan("com.yupi.springbootinit.mapper")
|
||||||
|
public class MyBatisPlusConfig {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 拦截器配置
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
public MybatisPlusInterceptor mybatisPlusInterceptor() {
|
||||||
|
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
|
||||||
|
// 分页插件
|
||||||
|
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
|
||||||
|
return interceptor;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,34 @@
|
|||||||
|
package com.yupi.springbootinit.config;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import org.redisson.Redisson;
|
||||||
|
import org.redisson.api.RedissonClient;
|
||||||
|
import org.redisson.config.Config;
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@ConfigurationProperties(prefix = "spring.redis")
|
||||||
|
@Data
|
||||||
|
public class RedissonConfig {
|
||||||
|
|
||||||
|
private Integer database;
|
||||||
|
|
||||||
|
private String host;
|
||||||
|
|
||||||
|
private Integer port;
|
||||||
|
|
||||||
|
private String password;
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public RedissonClient redissonClient() {
|
||||||
|
Config config = new Config();
|
||||||
|
config.useSingleServer()
|
||||||
|
.setDatabase(database)
|
||||||
|
.setAddress("redis://" + host + ":" + port);
|
||||||
|
// .setPassword("123456");
|
||||||
|
RedissonClient redisson = Redisson.create(config);
|
||||||
|
return redisson;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,51 @@
|
|||||||
|
package com.yupi.springbootinit.config;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import me.chanjar.weixin.mp.api.WxMpService;
|
||||||
|
import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl;
|
||||||
|
import me.chanjar.weixin.mp.config.impl.WxMpDefaultConfigImpl;
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 微信开放平台配置
|
||||||
|
*
|
||||||
|
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
|
||||||
|
* @from <a href="https://yupi.icu">编程导航知识星球</a>
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Configuration
|
||||||
|
@ConfigurationProperties(prefix = "wx.open")
|
||||||
|
@Data
|
||||||
|
public class WxOpenConfig {
|
||||||
|
|
||||||
|
private String appId;
|
||||||
|
|
||||||
|
private String appSecret;
|
||||||
|
|
||||||
|
private WxMpService wxMpService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 单例模式(不用 @Bean 是为了防止和公众号的 service 冲突)
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public WxMpService getWxMpService() {
|
||||||
|
if (wxMpService != null) {
|
||||||
|
return wxMpService;
|
||||||
|
}
|
||||||
|
synchronized (this) {
|
||||||
|
if (wxMpService != null) {
|
||||||
|
return wxMpService;
|
||||||
|
}
|
||||||
|
WxMpDefaultConfigImpl config = new WxMpDefaultConfigImpl();
|
||||||
|
config.setAppId(appId);
|
||||||
|
config.setSecret(appSecret);
|
||||||
|
WxMpService service = new WxMpServiceImpl();
|
||||||
|
service.setWxMpConfigStorage(config);
|
||||||
|
wxMpService = service;
|
||||||
|
return wxMpService;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
package com.yupi.springbootinit.config;
|
||||||
|
|
||||||
|
|
||||||
|
import io.github.briqt.spark4j.SparkClient;
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author leikooo
|
||||||
|
* @Description 星火 AI 配置类
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
@ConfigurationProperties(prefix = "xun-fei.client")
|
||||||
|
@Data
|
||||||
|
public class XingHuoConfig {
|
||||||
|
private String appId;
|
||||||
|
private String apiSecret;
|
||||||
|
private String apiKey;
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public SparkClient sparkClient() {
|
||||||
|
SparkClient sparkClient = new SparkClient();
|
||||||
|
sparkClient.apiKey = apiKey;
|
||||||
|
sparkClient.apiSecret = apiSecret;
|
||||||
|
sparkClient.appid = appId;
|
||||||
|
return sparkClient;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
package com.yupi.springbootinit.constant;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通用常量
|
||||||
|
*
|
||||||
|
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
|
||||||
|
* @from <a href="https://yupi.icu">编程导航知识星球</a>
|
||||||
|
*/
|
||||||
|
public interface CommonConstant {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 升序
|
||||||
|
*/
|
||||||
|
String SORT_ORDER_ASC = "ascend";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 降序
|
||||||
|
*/
|
||||||
|
String SORT_ORDER_DESC = " descend";
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
package com.yupi.springbootinit.constant;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件常量
|
||||||
|
*
|
||||||
|
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
|
||||||
|
* @from <a href="https://yupi.icu">编程导航知识星球</a>
|
||||||
|
*/
|
||||||
|
public interface FileConstant {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* COS 访问地址
|
||||||
|
* todo 需替换配置
|
||||||
|
*/
|
||||||
|
String COS_HOST = "https://yupi.icu";
|
||||||
|
}
|
@ -0,0 +1,34 @@
|
|||||||
|
package com.yupi.springbootinit.constant;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户常量
|
||||||
|
*
|
||||||
|
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
|
||||||
|
* @from <a href="https://yupi.icu">编程导航知识星球</a>
|
||||||
|
*/
|
||||||
|
public interface UserConstant {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户登录态键
|
||||||
|
*/
|
||||||
|
String USER_LOGIN_STATE = "user_login";
|
||||||
|
|
||||||
|
// region 权限
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 默认角色
|
||||||
|
*/
|
||||||
|
String DEFAULT_ROLE = "user";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 管理员角色
|
||||||
|
*/
|
||||||
|
String ADMIN_ROLE = "admin";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 被封号
|
||||||
|
*/
|
||||||
|
String BAN_ROLE = "ban";
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
}
|
@ -0,0 +1,631 @@
|
|||||||
|
package com.yupi.springbootinit.controller;
|
||||||
|
|
||||||
|
import cn.hutool.core.io.FileUtil;
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import com.yupi.springbootinit.annotation.AuthCheck;
|
||||||
|
import com.yupi.springbootinit.common.BaseResponse;
|
||||||
|
import com.yupi.springbootinit.common.DeleteRequest;
|
||||||
|
import com.yupi.springbootinit.common.ErrorCode;
|
||||||
|
import com.yupi.springbootinit.common.ResultUtils;
|
||||||
|
import com.yupi.springbootinit.constant.CommonConstant;
|
||||||
|
import com.yupi.springbootinit.constant.FileConstant;
|
||||||
|
import com.yupi.springbootinit.constant.UserConstant;
|
||||||
|
import com.yupi.springbootinit.exception.BusinessException;
|
||||||
|
import com.yupi.springbootinit.exception.ThrowUtils;
|
||||||
|
import com.yupi.springbootinit.manager.AiManager;
|
||||||
|
import com.yupi.springbootinit.manager.RedisLimiterManager;
|
||||||
|
import com.yupi.springbootinit.model.dto.chart.*;
|
||||||
|
import com.yupi.springbootinit.model.dto.file.UploadFileRequest;
|
||||||
|
import com.yupi.springbootinit.model.entity.Chart;
|
||||||
|
import com.yupi.springbootinit.model.entity.User;
|
||||||
|
import com.yupi.springbootinit.model.enums.FileUploadBizEnum;
|
||||||
|
import com.yupi.springbootinit.model.vo.BiResponse;
|
||||||
|
import com.yupi.springbootinit.service.ChartService;
|
||||||
|
import com.yupi.springbootinit.service.UserService;
|
||||||
|
import com.yupi.springbootinit.utils.ExcelUtils;
|
||||||
|
import com.yupi.springbootinit.utils.SqlUtils;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.commons.lang3.ObjectUtils;
|
||||||
|
import org.apache.commons.lang3.RandomStringUtils;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.springframework.beans.BeanUtils;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.RejectedExecutionException;
|
||||||
|
import java.util.concurrent.ThreadPoolExecutor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 帖子接口
|
||||||
|
*
|
||||||
|
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
|
||||||
|
* @from <a href="https://yupi.icu">编程导航知识星球</a>
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/chart")
|
||||||
|
@Slf4j
|
||||||
|
public class ChartController {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private ChartService chartService;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private UserService userService;
|
||||||
|
|
||||||
|
// region 增删改查
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private AiManager aiManager;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private RedisLimiterManager redisLimiterManager;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private ThreadPoolExecutor threadPoolExecutor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建
|
||||||
|
*
|
||||||
|
* @param chartAddRequest
|
||||||
|
* @param request
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@PostMapping("/add")
|
||||||
|
public BaseResponse<Long> addChart(@RequestBody ChartAddRequest chartAddRequest, HttpServletRequest request) {
|
||||||
|
if (chartAddRequest == null) {
|
||||||
|
throw new BusinessException(ErrorCode.PARAMS_ERROR);
|
||||||
|
}
|
||||||
|
Chart chart = new Chart();
|
||||||
|
BeanUtils.copyProperties(chartAddRequest, chart);
|
||||||
|
User loginUser = userService.getLoginUser(request);
|
||||||
|
chart.setUserId(loginUser.getId());
|
||||||
|
boolean result = chartService.save(chart);
|
||||||
|
ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);
|
||||||
|
long newChartId = chart.getId();
|
||||||
|
return ResultUtils.success(newChartId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除
|
||||||
|
*
|
||||||
|
* @param deleteRequest
|
||||||
|
* @param request
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@PostMapping("/delete")
|
||||||
|
public BaseResponse<Boolean> deleteChart(@RequestBody DeleteRequest deleteRequest, HttpServletRequest request) {
|
||||||
|
if (deleteRequest == null || deleteRequest.getId() <= 0) {
|
||||||
|
throw new BusinessException(ErrorCode.PARAMS_ERROR);
|
||||||
|
}
|
||||||
|
User user = userService.getLoginUser(request);
|
||||||
|
long id = deleteRequest.getId();
|
||||||
|
// 判断是否存在
|
||||||
|
Chart oldChart = chartService.getById(id);
|
||||||
|
ThrowUtils.throwIf(oldChart == null, ErrorCode.NOT_FOUND_ERROR);
|
||||||
|
// 仅本人或管理员可删除
|
||||||
|
if (!oldChart.getUserId().equals(user.getId()) && !userService.isAdmin(request)) {
|
||||||
|
throw new BusinessException(ErrorCode.NO_AUTH_ERROR);
|
||||||
|
}
|
||||||
|
boolean b = chartService.removeById(id);
|
||||||
|
return ResultUtils.success(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新(仅管理员)
|
||||||
|
*
|
||||||
|
* @param chartUpdateRequest
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@PostMapping("/update")
|
||||||
|
@AuthCheck(mustRole = UserConstant.ADMIN_ROLE)
|
||||||
|
public BaseResponse<Boolean> updateChart(@RequestBody ChartUpdateRequest chartUpdateRequest) {
|
||||||
|
if (chartUpdateRequest == null || chartUpdateRequest.getId() <= 0) {
|
||||||
|
throw new BusinessException(ErrorCode.PARAMS_ERROR);
|
||||||
|
}
|
||||||
|
Chart chart = new Chart();
|
||||||
|
BeanUtils.copyProperties(chartUpdateRequest, chart);
|
||||||
|
long id = chartUpdateRequest.getId();
|
||||||
|
// 判断是否存在
|
||||||
|
Chart oldChart = chartService.getById(id);
|
||||||
|
ThrowUtils.throwIf(oldChart == null, ErrorCode.NOT_FOUND_ERROR);
|
||||||
|
boolean result = chartService.updateById(chart);
|
||||||
|
return ResultUtils.success(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据 id 获取
|
||||||
|
*
|
||||||
|
* @param id
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@GetMapping("/get")
|
||||||
|
public BaseResponse<Chart> getChartById(long id, HttpServletRequest request) {
|
||||||
|
if (id <= 0) {
|
||||||
|
throw new BusinessException(ErrorCode.PARAMS_ERROR);
|
||||||
|
}
|
||||||
|
Chart chart = chartService.getById(id);
|
||||||
|
if (chart == null) {
|
||||||
|
throw new BusinessException(ErrorCode.NOT_FOUND_ERROR);
|
||||||
|
}
|
||||||
|
return ResultUtils.success(chart);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页获取列表(封装类)
|
||||||
|
*
|
||||||
|
* @param chartQueryRequest
|
||||||
|
* @param request
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@PostMapping("/list/page")
|
||||||
|
public BaseResponse<Page<Chart>> listChartByPage(@RequestBody ChartQueryRequest chartQueryRequest,
|
||||||
|
HttpServletRequest request) {
|
||||||
|
long current = chartQueryRequest.getCurrent();
|
||||||
|
long size = chartQueryRequest.getPageSize();
|
||||||
|
// 限制爬虫
|
||||||
|
ThrowUtils.throwIf(size > 20, ErrorCode.PARAMS_ERROR);
|
||||||
|
Page<Chart> chartPage = chartService.page(new Page<>(current, size),
|
||||||
|
getQueryWrapper(chartQueryRequest));
|
||||||
|
return ResultUtils.success(chartPage);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页获取当前用户创建的资源列表
|
||||||
|
*
|
||||||
|
* @param chartQueryRequest
|
||||||
|
* @param request
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@PostMapping("/my/list/page")
|
||||||
|
public BaseResponse<Page<Chart>> listMyChartByPage(@RequestBody ChartQueryRequest chartQueryRequest,
|
||||||
|
HttpServletRequest request) {
|
||||||
|
if (chartQueryRequest == null) {
|
||||||
|
throw new BusinessException(ErrorCode.PARAMS_ERROR);
|
||||||
|
}
|
||||||
|
User loginUser = userService.getLoginUser(request);
|
||||||
|
chartQueryRequest.setUserId(loginUser.getId());
|
||||||
|
long current = chartQueryRequest.getCurrent();
|
||||||
|
long size = chartQueryRequest.getPageSize();
|
||||||
|
// 限制爬虫
|
||||||
|
ThrowUtils.throwIf(size > 20, ErrorCode.PARAMS_ERROR);
|
||||||
|
Page<Chart> chartPage = chartService.page(new Page<>(current, size),
|
||||||
|
getQueryWrapper(chartQueryRequest));
|
||||||
|
return ResultUtils.success(chartPage);
|
||||||
|
}
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 编辑(用户)
|
||||||
|
*
|
||||||
|
* @param chartEditRequest
|
||||||
|
* @param request
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@PostMapping("/edit")
|
||||||
|
public BaseResponse<Boolean> editChart(@RequestBody ChartEditRequest chartEditRequest, HttpServletRequest request) {
|
||||||
|
if (chartEditRequest == null || chartEditRequest.getId() <= 0) {
|
||||||
|
throw new BusinessException(ErrorCode.PARAMS_ERROR);
|
||||||
|
}
|
||||||
|
Chart chart = new Chart();
|
||||||
|
BeanUtils.copyProperties(chartEditRequest, chart);
|
||||||
|
User loginUser = userService.getLoginUser(request);
|
||||||
|
long id = chartEditRequest.getId();
|
||||||
|
// 判断是否存在
|
||||||
|
Chart oldChart = chartService.getById(id);
|
||||||
|
ThrowUtils.throwIf(oldChart == null, ErrorCode.NOT_FOUND_ERROR);
|
||||||
|
// 仅本人或管理员可编辑
|
||||||
|
if (!oldChart.getUserId().equals(loginUser.getId()) && !userService.isAdmin(loginUser)) {
|
||||||
|
throw new BusinessException(ErrorCode.NO_AUTH_ERROR);
|
||||||
|
}
|
||||||
|
boolean result = chartService.updateById(chart);
|
||||||
|
return ResultUtils.success(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 智能分析(同步)
|
||||||
|
*
|
||||||
|
* @param multipartFile
|
||||||
|
* @param genChartByAiRequest
|
||||||
|
* @param request
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@PostMapping("/gen")
|
||||||
|
public BaseResponse<BiResponse> genChartByAi(@RequestPart("file") MultipartFile multipartFile,
|
||||||
|
GenChartByAiRequest genChartByAiRequest, HttpServletRequest request) {
|
||||||
|
String name = genChartByAiRequest.getName();
|
||||||
|
String goal = genChartByAiRequest.getGoal();
|
||||||
|
String chartType = genChartByAiRequest.getChartType();
|
||||||
|
// 校验
|
||||||
|
ThrowUtils.throwIf(StringUtils.isBlank(goal), ErrorCode.PARAMS_ERROR, "目标为空");
|
||||||
|
ThrowUtils.throwIf(StringUtils.isNotBlank(name) && name.length() > 100, ErrorCode.PARAMS_ERROR, "名称过长");
|
||||||
|
|
||||||
|
//校验文件
|
||||||
|
long size =multipartFile.getSize();
|
||||||
|
String originalFilename = multipartFile.getOriginalFilename();
|
||||||
|
//校验文件大小
|
||||||
|
final long ONE_MB = 10 *1024*1024L;
|
||||||
|
ThrowUtils.throwIf(size>ONE_MB,ErrorCode.PARAMS_ERROR,"文件超过10M");
|
||||||
|
//校验文件后缀
|
||||||
|
String suffix = FileUtil.getSuffix(originalFilename);
|
||||||
|
final List<String> validFileSuffixList = Arrays.asList("xlsx","xls");
|
||||||
|
ThrowUtils.throwIf(!validFileSuffixList.contains(suffix),ErrorCode.PARAMS_ERROR,"文件后缀不合法");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// final String prompt = "你是一个数据分析师和前端开发专家,接下来我会按照以下固定格式给你提供内容:\n" +
|
||||||
|
// "分析需求:\n" +
|
||||||
|
// "{数据分析的需求或者目标}\n" +
|
||||||
|
// "原始数据:\n" +
|
||||||
|
// "{csv格式的原始数据,用,作为分隔符}\n" +
|
||||||
|
// "请根据这两部分内容,按照以下指定格式生成内容(此外不要输出任何多余的开头、结尾、注释)\n" +
|
||||||
|
// "'【【【【'\n" +
|
||||||
|
// "{前端 Echarts V5 的 option 配置对象js代码,合理地将数据进行可视化,不要生成任何多余的内容,比如注释}\n" +
|
||||||
|
// "'【【【【'\n" +
|
||||||
|
// "{明确的数据分析结论、越详细越好,不要生成多余的注释}";
|
||||||
|
|
||||||
|
User loginUser = userService.getLoginUser(request);
|
||||||
|
// 限流判断,每个用户一个限流器
|
||||||
|
redisLimiterManager.doRateLimit("genChartByAi_" + loginUser.getId());
|
||||||
|
|
||||||
|
|
||||||
|
// 分析需求:
|
||||||
|
// 分析网站用户的增长情况
|
||||||
|
// 原始数据:
|
||||||
|
// 日期,用户数
|
||||||
|
// 1号,10
|
||||||
|
// 2号,20
|
||||||
|
// 3号,30
|
||||||
|
|
||||||
|
// 构造用户输入
|
||||||
|
StringBuilder userInput = new StringBuilder();
|
||||||
|
userInput.append("分析需求:").append("\n");
|
||||||
|
|
||||||
|
// 拼接分析目标
|
||||||
|
String userGoal = goal;
|
||||||
|
if (StringUtils.isNotBlank(chartType)) {
|
||||||
|
userGoal += ",请使用" + chartType;
|
||||||
|
}
|
||||||
|
userInput.append(userGoal).append("\n");
|
||||||
|
userInput.append("原始数据:").append("\n");
|
||||||
|
// 压缩后的数据
|
||||||
|
String csvData = ExcelUtils.excelToCsv(multipartFile);
|
||||||
|
userInput.append(csvData).append("\n");
|
||||||
|
|
||||||
|
String result = aiManager.sendMsgToXingHuo(true,userInput.toString());
|
||||||
|
|
||||||
|
|
||||||
|
String[] splits = result.split("'【【【【'");
|
||||||
|
if (splits.length < 3) {
|
||||||
|
throw new BusinessException(ErrorCode.SYSTEM_ERROR, "AI 生成错误");
|
||||||
|
}
|
||||||
|
|
||||||
|
String genChart = splits[1].trim();
|
||||||
|
String genResult = splits[2].trim();
|
||||||
|
|
||||||
|
|
||||||
|
// 插入到数据库
|
||||||
|
Chart chart = new Chart();
|
||||||
|
chart.setName(name);
|
||||||
|
chart.setGoal(goal);
|
||||||
|
chart.setChartData(csvData);
|
||||||
|
chart.setChartType(chartType);
|
||||||
|
chart.setGenChart(genChart);
|
||||||
|
chart.setGenResult(genResult);
|
||||||
|
chart.setUserId(loginUser.getId());
|
||||||
|
boolean saveResult = chartService.save(chart);
|
||||||
|
ThrowUtils.throwIf(!saveResult, ErrorCode.SYSTEM_ERROR, "图表保存失败");
|
||||||
|
|
||||||
|
BiResponse biResponse = new BiResponse();
|
||||||
|
biResponse.setGenChart(genChart);
|
||||||
|
biResponse.setGenResult(genResult);
|
||||||
|
biResponse.setChartId(chart.getId());
|
||||||
|
return ResultUtils.success(biResponse);
|
||||||
|
|
||||||
|
// // 校验文件
|
||||||
|
// long size = multipartFile.getSize();
|
||||||
|
// String originalFilename = multipartFile.getOriginalFilename();
|
||||||
|
// // 校验文件大小
|
||||||
|
// final long ONE_MB = 1024 * 1024L;
|
||||||
|
// ThrowUtils.throwIf(size > ONE_MB, ErrorCode.PARAMS_ERROR, "文件超过 1M");
|
||||||
|
// // 校验文件后缀 aaa.png
|
||||||
|
// String suffix = FileUtil.getSuffix(originalFilename);
|
||||||
|
// final List<String> validFileSuffixList = Arrays.asList("xlsx");
|
||||||
|
// ThrowUtils.throwIf(!validFileSuffixList.contains(suffix), ErrorCode.PARAMS_ERROR, "文件后缀非法");
|
||||||
|
//
|
||||||
|
// User loginUser = userService.getLoginUser(request);
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// return ResultUtils.success("");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 智能分析(异步)
|
||||||
|
*
|
||||||
|
* @param multipartFile
|
||||||
|
* @param genChartByAiRequest
|
||||||
|
* @param request
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@PostMapping("/gen/async")
|
||||||
|
public BaseResponse<BiResponse> genChartByAiAsync(@RequestPart("file") MultipartFile multipartFile,
|
||||||
|
GenChartByAiRequest genChartByAiRequest, HttpServletRequest request) {
|
||||||
|
String name = genChartByAiRequest.getName();
|
||||||
|
String goal = genChartByAiRequest.getGoal();
|
||||||
|
String chartType = genChartByAiRequest.getChartType();
|
||||||
|
// 校验
|
||||||
|
ThrowUtils.throwIf(StringUtils.isBlank(goal), ErrorCode.PARAMS_ERROR, "目标为空");
|
||||||
|
ThrowUtils.throwIf(StringUtils.isNotBlank(name) && name.length() > 100, ErrorCode.PARAMS_ERROR, "名称过长");
|
||||||
|
// 校验文件
|
||||||
|
long size = multipartFile.getSize();
|
||||||
|
String originalFilename = multipartFile.getOriginalFilename();
|
||||||
|
// 校验文件大小
|
||||||
|
final long ONE_MB = 1024 * 1024L;
|
||||||
|
ThrowUtils.throwIf(size > ONE_MB, ErrorCode.PARAMS_ERROR, "文件超过 1M");
|
||||||
|
// 校验文件后缀 aaa.png
|
||||||
|
String suffix = FileUtil.getSuffix(originalFilename);
|
||||||
|
final List<String> validFileSuffixList = Arrays.asList("xlsx", "xls");
|
||||||
|
ThrowUtils.throwIf(!validFileSuffixList.contains(suffix), ErrorCode.PARAMS_ERROR, "文件后缀非法");
|
||||||
|
|
||||||
|
User loginUser = userService.getLoginUser(request);
|
||||||
|
// 限流判断,每个用户一个限流器
|
||||||
|
redisLimiterManager.doRateLimit("genChartByAi_" + loginUser.getId());
|
||||||
|
|
||||||
|
|
||||||
|
// 构造用户输入
|
||||||
|
StringBuilder userInput = new StringBuilder();
|
||||||
|
userInput.append("分析需求:").append("\n");
|
||||||
|
|
||||||
|
// 拼接分析目标
|
||||||
|
String userGoal = goal;
|
||||||
|
if (StringUtils.isNotBlank(chartType)) {
|
||||||
|
userGoal += ",请使用" + chartType;
|
||||||
|
}
|
||||||
|
userInput.append(userGoal).append("\n");
|
||||||
|
userInput.append("原始数据:").append("\n");
|
||||||
|
// 压缩后的数据
|
||||||
|
String csvData = ExcelUtils.excelToCsv(multipartFile);
|
||||||
|
userInput.append(csvData).append("\n");
|
||||||
|
|
||||||
|
// 插入到数据库
|
||||||
|
Chart chart = new Chart();
|
||||||
|
chart.setName(name);
|
||||||
|
chart.setGoal(goal);
|
||||||
|
chart.setChartData(csvData);
|
||||||
|
chart.setChartType(chartType);
|
||||||
|
chart.setStatus("wait");
|
||||||
|
chart.setUserId(loginUser.getId());
|
||||||
|
boolean saveResult = chartService.save(chart); // 直接插入
|
||||||
|
ThrowUtils.throwIf(!saveResult, ErrorCode.SYSTEM_ERROR, "图表保存失败");
|
||||||
|
|
||||||
|
// todo 建议处理任务队列满了后,抛异常的情况
|
||||||
|
try{
|
||||||
|
CompletableFuture.runAsync(() -> {
|
||||||
|
// 先修改图表任务状态为 “执行中”。等执行成功后,修改为 “已完成”、保存执行结果;执行失败后,状态修改为 “失败”,记录任务失败信息。
|
||||||
|
Chart updateChart = new Chart();
|
||||||
|
updateChart.setId(chart.getId());
|
||||||
|
updateChart.setStatus("running");
|
||||||
|
boolean b = chartService.updateById(updateChart);
|
||||||
|
if (!b) {
|
||||||
|
handleChartUpdateError(chart.getId(), "更新图表执行中状态失败");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 调用 AI
|
||||||
|
String result = aiManager.sendMsgToXingHuo(true,userInput.toString());
|
||||||
|
String[] splits = result.split("'【【【【'");
|
||||||
|
if (splits.length < 3) {
|
||||||
|
handleChartUpdateError(chart.getId(), "AI 生成错误");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String genChart = splits[1].trim();
|
||||||
|
String genResult = splits[2].trim();
|
||||||
|
Chart updateChartResult = new Chart();
|
||||||
|
updateChartResult.setId(chart.getId());
|
||||||
|
updateChartResult.setGenChart(genChart);
|
||||||
|
updateChartResult.setGenResult(genResult);
|
||||||
|
// todo 建议定义状态为枚举值
|
||||||
|
updateChartResult.setStatus("succeed");
|
||||||
|
boolean updateResult = chartService.updateById(updateChartResult);
|
||||||
|
if (!updateResult) {
|
||||||
|
handleChartUpdateError(chart.getId(), "更新图表成功状态失败");
|
||||||
|
}
|
||||||
|
}, threadPoolExecutor);
|
||||||
|
}catch (RejectedExecutionException e) {
|
||||||
|
log.error("任务队列已满,无法提交异步任务", e);
|
||||||
|
// 更新图表状态为失败
|
||||||
|
handleChartUpdateError(chart.getId(), "任务队列已满,无法处理");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BiResponse biResponse = new BiResponse();
|
||||||
|
biResponse.setChartId(chart.getId());
|
||||||
|
return ResultUtils.success(biResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//工具类
|
||||||
|
private void handleChartUpdateError(long chartId, String execMessage) {
|
||||||
|
Chart updateChartResult = new Chart();
|
||||||
|
updateChartResult.setId(chartId);
|
||||||
|
updateChartResult.setStatus("failed");
|
||||||
|
updateChartResult.setExecMessage("execMessage");
|
||||||
|
boolean updateResult = chartService.updateById(updateChartResult);
|
||||||
|
if (!updateResult) {
|
||||||
|
log.error("更新图表失败状态失败" + chartId + "," + execMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * 智能分析
|
||||||
|
// *
|
||||||
|
// * @param multipartFile
|
||||||
|
// * @param genChartByAiRequest
|
||||||
|
// * @param request
|
||||||
|
// * @return
|
||||||
|
// */
|
||||||
|
// @PostMapping("/gen")
|
||||||
|
// public BaseResponse<BiResponse> genChartByAi(@RequestPart("file") MultipartFile multipartFile,
|
||||||
|
// GenChartByAiRequest genChartByAiRequest, HttpServletRequest request) {
|
||||||
|
// String name = genChartByAiRequest.getName();
|
||||||
|
// String goal = genChartByAiRequest.getGoal();
|
||||||
|
// String chartType = genChartByAiRequest.getChartType();
|
||||||
|
// //校验
|
||||||
|
// ThrowUtils.throwIf(StringUtils.isBlank(goal),ErrorCode.PARAMS_ERROR,"目标为空");
|
||||||
|
// ThrowUtils.throwIf(StringUtils.isNotBlank(name) && name.length()>100,ErrorCode.PARAMS_ERROR,"名称过长");
|
||||||
|
//
|
||||||
|
// //校验文件
|
||||||
|
// long size =multipartFile.getSize();
|
||||||
|
// String originalFilename = multipartFile.getOriginalFilename();
|
||||||
|
// //校验文件大小
|
||||||
|
// final long ONE_MB = 10 *1024*1024L;
|
||||||
|
// ThrowUtils.throwIf(size>ONE_MB,ErrorCode.PARAMS_ERROR,"文件超过10M");
|
||||||
|
// //校验文件后缀
|
||||||
|
// String suffix = FileUtil.getSuffix(originalFilename);
|
||||||
|
// final List<String> validFileSuffixList = Arrays.asList("xlsx","xls");
|
||||||
|
// ThrowUtils.throwIf(!validFileSuffixList.contains(suffix),ErrorCode.PARAMS_ERROR,"文件后缀不合法");
|
||||||
|
//
|
||||||
|
// //获取用户信息
|
||||||
|
// User loginUser = userService.getLoginUser(request);
|
||||||
|
// //限流判断,每个用户一个限流器(方法名+用户id)
|
||||||
|
// redisLimiterManager.doRateLimit("genChartByAi_"+loginUser.getId());
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// //系统预设
|
||||||
|
//// final String prompt = "你是一个数据分析师和前端开发专家,接下来我会按照以下固定格式给你提供内容:\n" +
|
||||||
|
//// "分析需求:\n" +
|
||||||
|
//// "(数据分析的需求或者目标}\n" +
|
||||||
|
//// "原始数据:\n" +
|
||||||
|
//// "{csv格式的原始数据,用,作为分隔符]\n" +
|
||||||
|
//// "请根据这两部分内容,按照以下指定格式生成内容(此外不要输出任何多余的开头、结尾、注释)\n" +
|
||||||
|
//// "'【【【【'\n" +
|
||||||
|
//// "{前端Echarts V5的option配置对象js代码,合理地将数据进行可视化,不要生成任何多余的内容,比如注释}\n" +
|
||||||
|
//// "'【【【【'\n" +
|
||||||
|
//// "(明确的数据分析结论、越详细越好,不要生成多余的注释}";
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// //构造用户输入
|
||||||
|
// StringBuilder userInput = new StringBuilder();
|
||||||
|
// userInput.append("分析需求:").append("\n");
|
||||||
|
//
|
||||||
|
// //拼接分析目标
|
||||||
|
// String userGoal =goal;
|
||||||
|
// if(StringUtils.isNotBlank(chartType)){
|
||||||
|
// userGoal+=",请使用"+chartType;
|
||||||
|
// }
|
||||||
|
// userInput.append(userGoal).append("\n");
|
||||||
|
// userInput.append("原始数据:").append("\n");
|
||||||
|
//
|
||||||
|
// //压缩后的数据
|
||||||
|
// String csvData=ExcelUtils.excelToCsv(multipartFile);
|
||||||
|
// userInput.append(csvData).append("\n");
|
||||||
|
//
|
||||||
|
// //插入到数据库中
|
||||||
|
// Chart chart = new Chart();
|
||||||
|
// chart.setName(name);
|
||||||
|
// chart.setGoal(goal);
|
||||||
|
// chart.setChartData(csvData);
|
||||||
|
// chart.setChartType(chartType);
|
||||||
|
// chart.setStatus("wait");
|
||||||
|
// chart.setUserId(loginUser.getId());
|
||||||
|
// boolean saveResult = chartService.save(chart);
|
||||||
|
// ThrowUtils.throwIf(!saveResult,ErrorCode.SYSTEM_ERROR,"图表保存失败");
|
||||||
|
//
|
||||||
|
// //处理任务队列满了后,抛异常的情况
|
||||||
|
// try {
|
||||||
|
// CompletableFuture.runAsync(() -> {
|
||||||
|
// // 先修改图表任务状态为 “执行中”。等执行成功后,修改为 “已完成”、保存执行结果;执行失败后,状态修改为 “失败”,记录任务失败信息。
|
||||||
|
// Chart updateChart = new Chart();
|
||||||
|
// updateChart.setId(chart.getId());
|
||||||
|
// updateChart.setStatus("running");
|
||||||
|
// boolean b = chartService.updateById(updateChart);
|
||||||
|
// if (!b) {
|
||||||
|
// handleChartUpdateError(chart.getId(), "更新图表执行中状态失败");
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// //调用ai
|
||||||
|
// String result = aiManager.sendMsgToXingHuo(true,userInput.toString());
|
||||||
|
// String[] splits = result.split("'【【【【'");
|
||||||
|
// if(splits.length<3){
|
||||||
|
// handleChartUpdateError(chart.getId(), "AI 生成错误");
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// String genChart =splits[1].trim();
|
||||||
|
// String genResult = splits[2].trim();
|
||||||
|
// Chart updateChartResult = new Chart();
|
||||||
|
// updateChartResult.setId(chart.getId());
|
||||||
|
// updateChartResult.setGenChart(genChart);
|
||||||
|
// updateChartResult.setGenResult(genResult);
|
||||||
|
// updateChartResult.setStatus("success");
|
||||||
|
// boolean updateResult = chartService.updateById(updateChartResult);
|
||||||
|
// if (!updateResult) {
|
||||||
|
// handleChartUpdateError(chart.getId(), "更新图表成功状态失败");
|
||||||
|
// }
|
||||||
|
// },threadPoolExecutor);
|
||||||
|
// }catch (RejectedExecutionException e) {
|
||||||
|
// System.err.println("任务被拒绝,线程池任务队列已满: " + e.getMessage());
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// BiResponse biResponse =new BiResponse();
|
||||||
|
// biResponse.setChartId(chart.getId());
|
||||||
|
// return ResultUtils.success(biResponse);
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// //工具类
|
||||||
|
// private void handleChartUpdateError(long chartId, String execMessage) {
|
||||||
|
// Chart updateChartResult = new Chart();
|
||||||
|
// updateChartResult.setId(chartId);
|
||||||
|
// updateChartResult.setStatus("failed");
|
||||||
|
// updateChartResult.setExecMessage("execMessage");
|
||||||
|
// boolean updateResult = chartService.updateById(updateChartResult);
|
||||||
|
// if (!updateResult) {
|
||||||
|
// log.error("更新图表失败状态失败" + chartId + "," + execMessage);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取查询包装类
|
||||||
|
*
|
||||||
|
* @param chartQueryRequest
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private QueryWrapper<Chart> getQueryWrapper(ChartQueryRequest chartQueryRequest) {
|
||||||
|
QueryWrapper<Chart> queryWrapper = new QueryWrapper<>();
|
||||||
|
if (chartQueryRequest == null) {
|
||||||
|
return queryWrapper;
|
||||||
|
}
|
||||||
|
Long id = chartQueryRequest.getId();
|
||||||
|
String name = chartQueryRequest.getName();
|
||||||
|
String goal = chartQueryRequest.getGoal();
|
||||||
|
String chartType = chartQueryRequest.getChartType();
|
||||||
|
Long userId = chartQueryRequest.getUserId();
|
||||||
|
String sortField = chartQueryRequest.getSortField();
|
||||||
|
String sortOrder = chartQueryRequest.getSortOrder();
|
||||||
|
|
||||||
|
queryWrapper.eq(id!=null && id>0,"id",id);
|
||||||
|
queryWrapper.like(StringUtils.isNotBlank(name),"name",name);
|
||||||
|
queryWrapper.eq(StringUtils.isNotBlank(goal),"goal",goal);
|
||||||
|
queryWrapper.eq(StringUtils.isNotBlank(chartType),"chartType",chartType);
|
||||||
|
queryWrapper.eq(ObjectUtils.isNotEmpty(userId), "userId", userId);
|
||||||
|
queryWrapper.eq("isDelete",false);
|
||||||
|
queryWrapper.orderBy(SqlUtils.validSortField(sortField), sortOrder.equals(CommonConstant.SORT_ORDER_ASC),
|
||||||
|
sortField);
|
||||||
|
return queryWrapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,108 @@
|
|||||||
|
package com.yupi.springbootinit.controller;
|
||||||
|
|
||||||
|
import cn.hutool.core.io.FileUtil;
|
||||||
|
import com.yupi.springbootinit.common.BaseResponse;
|
||||||
|
import com.yupi.springbootinit.common.ErrorCode;
|
||||||
|
import com.yupi.springbootinit.common.ResultUtils;
|
||||||
|
import com.yupi.springbootinit.constant.FileConstant;
|
||||||
|
import com.yupi.springbootinit.exception.BusinessException;
|
||||||
|
import com.yupi.springbootinit.manager.CosManager;
|
||||||
|
import com.yupi.springbootinit.model.dto.file.UploadFileRequest;
|
||||||
|
import com.yupi.springbootinit.model.entity.User;
|
||||||
|
import com.yupi.springbootinit.model.enums.FileUploadBizEnum;
|
||||||
|
import com.yupi.springbootinit.service.UserService;
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.commons.lang3.RandomStringUtils;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestPart;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件接口
|
||||||
|
*
|
||||||
|
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
|
||||||
|
* @from <a href="https://yupi.icu">编程导航知识星球</a>
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/file")
|
||||||
|
@Slf4j
|
||||||
|
public class FileController {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private UserService userService;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private CosManager cosManager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件上传
|
||||||
|
*
|
||||||
|
* @param multipartFile
|
||||||
|
* @param uploadFileRequest
|
||||||
|
* @param request
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@PostMapping("/upload")
|
||||||
|
public BaseResponse<String> uploadFile(@RequestPart("file") MultipartFile multipartFile,
|
||||||
|
UploadFileRequest uploadFileRequest, HttpServletRequest request) {
|
||||||
|
String biz = uploadFileRequest.getBiz();
|
||||||
|
FileUploadBizEnum fileUploadBizEnum = FileUploadBizEnum.getEnumByValue(biz);
|
||||||
|
if (fileUploadBizEnum == null) {
|
||||||
|
throw new BusinessException(ErrorCode.PARAMS_ERROR);
|
||||||
|
}
|
||||||
|
validFile(multipartFile, fileUploadBizEnum);
|
||||||
|
User loginUser = userService.getLoginUser(request);
|
||||||
|
// 文件目录:根据业务、用户来划分
|
||||||
|
String uuid = RandomStringUtils.randomAlphanumeric(8);
|
||||||
|
String filename = uuid + "-" + multipartFile.getOriginalFilename();
|
||||||
|
String filepath = String.format("/%s/%s/%s", fileUploadBizEnum.getValue(), loginUser.getId(), filename);
|
||||||
|
File file = null;
|
||||||
|
try {
|
||||||
|
// 上传文件
|
||||||
|
file = File.createTempFile(filepath, null);
|
||||||
|
multipartFile.transferTo(file);
|
||||||
|
cosManager.putObject(filepath, file);
|
||||||
|
// 返回可访问地址
|
||||||
|
return ResultUtils.success(FileConstant.COS_HOST + filepath);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("file upload error, filepath = " + filepath, e);
|
||||||
|
throw new BusinessException(ErrorCode.SYSTEM_ERROR, "上传失败");
|
||||||
|
} finally {
|
||||||
|
if (file != null) {
|
||||||
|
// 删除临时文件
|
||||||
|
boolean delete = file.delete();
|
||||||
|
if (!delete) {
|
||||||
|
log.error("file delete error, filepath = {}", filepath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验文件
|
||||||
|
*
|
||||||
|
* @param multipartFile
|
||||||
|
* @param fileUploadBizEnum 业务类型
|
||||||
|
*/
|
||||||
|
private void validFile(MultipartFile multipartFile, FileUploadBizEnum fileUploadBizEnum) {
|
||||||
|
// 文件大小
|
||||||
|
long fileSize = multipartFile.getSize();
|
||||||
|
// 文件后缀
|
||||||
|
String fileSuffix = FileUtil.getSuffix(multipartFile.getOriginalFilename());
|
||||||
|
final long ONE_M = 1024 * 1024L;
|
||||||
|
if (FileUploadBizEnum.USER_AVATAR.equals(fileUploadBizEnum)) {
|
||||||
|
if (fileSize > ONE_M) {
|
||||||
|
throw new BusinessException(ErrorCode.PARAMS_ERROR, "文件大小不能超过 1M");
|
||||||
|
}
|
||||||
|
if (!Arrays.asList("jpeg", "jpg", "svg", "png", "webp").contains(fileSuffix)) {
|
||||||
|
throw new BusinessException(ErrorCode.PARAMS_ERROR, "文件类型错误");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,263 @@
|
|||||||
|
package com.yupi.springbootinit.controller;
|
||||||
|
|
||||||
|
import cn.hutool.json.JSONUtil;
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import com.yupi.springbootinit.annotation.AuthCheck;
|
||||||
|
import com.yupi.springbootinit.common.BaseResponse;
|
||||||
|
import com.yupi.springbootinit.common.DeleteRequest;
|
||||||
|
import com.yupi.springbootinit.common.ErrorCode;
|
||||||
|
import com.yupi.springbootinit.common.ResultUtils;
|
||||||
|
import com.yupi.springbootinit.constant.UserConstant;
|
||||||
|
import com.yupi.springbootinit.exception.BusinessException;
|
||||||
|
import com.yupi.springbootinit.exception.ThrowUtils;
|
||||||
|
import com.yupi.springbootinit.model.dto.post.PostAddRequest;
|
||||||
|
import com.yupi.springbootinit.model.dto.post.PostEditRequest;
|
||||||
|
import com.yupi.springbootinit.model.dto.post.PostQueryRequest;
|
||||||
|
import com.yupi.springbootinit.model.dto.post.PostUpdateRequest;
|
||||||
|
import com.yupi.springbootinit.model.entity.Post;
|
||||||
|
import com.yupi.springbootinit.model.entity.User;
|
||||||
|
import com.yupi.springbootinit.model.vo.PostVO;
|
||||||
|
import com.yupi.springbootinit.service.PostService;
|
||||||
|
import com.yupi.springbootinit.service.UserService;
|
||||||
|
import java.util.List;
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.BeanUtils;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 帖子接口
|
||||||
|
*
|
||||||
|
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
|
||||||
|
* @from <a href="https://yupi.icu">编程导航知识星球</a>
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/post")
|
||||||
|
@Slf4j
|
||||||
|
public class PostController {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private PostService postService;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private UserService userService;
|
||||||
|
|
||||||
|
// region 增删改查
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建
|
||||||
|
*
|
||||||
|
* @param postAddRequest
|
||||||
|
* @param request
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@PostMapping("/add")
|
||||||
|
public BaseResponse<Long> addPost(@RequestBody PostAddRequest postAddRequest, HttpServletRequest request) {
|
||||||
|
if (postAddRequest == null) {
|
||||||
|
throw new BusinessException(ErrorCode.PARAMS_ERROR);
|
||||||
|
}
|
||||||
|
Post post = new Post();
|
||||||
|
BeanUtils.copyProperties(postAddRequest, post);
|
||||||
|
List<String> tags = postAddRequest.getTags();
|
||||||
|
if (tags != null) {
|
||||||
|
post.setTags(JSONUtil.toJsonStr(tags));
|
||||||
|
}
|
||||||
|
postService.validPost(post, true);
|
||||||
|
User loginUser = userService.getLoginUser(request);
|
||||||
|
post.setUserId(loginUser.getId());
|
||||||
|
post.setFavourNum(0);
|
||||||
|
post.setThumbNum(0);
|
||||||
|
boolean result = postService.save(post);
|
||||||
|
ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);
|
||||||
|
long newPostId = post.getId();
|
||||||
|
return ResultUtils.success(newPostId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除
|
||||||
|
*
|
||||||
|
* @param deleteRequest
|
||||||
|
* @param request
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@PostMapping("/delete")
|
||||||
|
public BaseResponse<Boolean> deletePost(@RequestBody DeleteRequest deleteRequest, HttpServletRequest request) {
|
||||||
|
if (deleteRequest == null || deleteRequest.getId() <= 0) {
|
||||||
|
throw new BusinessException(ErrorCode.PARAMS_ERROR);
|
||||||
|
}
|
||||||
|
User user = userService.getLoginUser(request);
|
||||||
|
long id = deleteRequest.getId();
|
||||||
|
// 判断是否存在
|
||||||
|
Post oldPost = postService.getById(id);
|
||||||
|
ThrowUtils.throwIf(oldPost == null, ErrorCode.NOT_FOUND_ERROR);
|
||||||
|
// 仅本人或管理员可删除
|
||||||
|
if (!oldPost.getUserId().equals(user.getId()) && !userService.isAdmin(request)) {
|
||||||
|
throw new BusinessException(ErrorCode.NO_AUTH_ERROR);
|
||||||
|
}
|
||||||
|
boolean b = postService.removeById(id);
|
||||||
|
return ResultUtils.success(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新(仅管理员)
|
||||||
|
*
|
||||||
|
* @param postUpdateRequest
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@PostMapping("/update")
|
||||||
|
@AuthCheck(mustRole = UserConstant.ADMIN_ROLE)
|
||||||
|
public BaseResponse<Boolean> updatePost(@RequestBody PostUpdateRequest postUpdateRequest) {
|
||||||
|
if (postUpdateRequest == null || postUpdateRequest.getId() <= 0) {
|
||||||
|
throw new BusinessException(ErrorCode.PARAMS_ERROR);
|
||||||
|
}
|
||||||
|
Post post = new Post();
|
||||||
|
BeanUtils.copyProperties(postUpdateRequest, post);
|
||||||
|
List<String> tags = postUpdateRequest.getTags();
|
||||||
|
if (tags != null) {
|
||||||
|
post.setTags(JSONUtil.toJsonStr(tags));
|
||||||
|
}
|
||||||
|
// 参数校验
|
||||||
|
postService.validPost(post, false);
|
||||||
|
long id = postUpdateRequest.getId();
|
||||||
|
// 判断是否存在
|
||||||
|
Post oldPost = postService.getById(id);
|
||||||
|
ThrowUtils.throwIf(oldPost == null, ErrorCode.NOT_FOUND_ERROR);
|
||||||
|
boolean result = postService.updateById(post);
|
||||||
|
return ResultUtils.success(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据 id 获取
|
||||||
|
*
|
||||||
|
* @param id
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@GetMapping("/get/vo")
|
||||||
|
public BaseResponse<PostVO> getPostVOById(long id, HttpServletRequest request) {
|
||||||
|
if (id <= 0) {
|
||||||
|
throw new BusinessException(ErrorCode.PARAMS_ERROR);
|
||||||
|
}
|
||||||
|
Post post = postService.getById(id);
|
||||||
|
if (post == null) {
|
||||||
|
throw new BusinessException(ErrorCode.NOT_FOUND_ERROR);
|
||||||
|
}
|
||||||
|
return ResultUtils.success(postService.getPostVO(post, request));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页获取列表(仅管理员)
|
||||||
|
*
|
||||||
|
* @param postQueryRequest
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@PostMapping("/list/page")
|
||||||
|
@AuthCheck(mustRole = UserConstant.ADMIN_ROLE)
|
||||||
|
public BaseResponse<Page<Post>> listPostByPage(@RequestBody PostQueryRequest postQueryRequest) {
|
||||||
|
long current = postQueryRequest.getCurrent();
|
||||||
|
long size = postQueryRequest.getPageSize();
|
||||||
|
Page<Post> postPage = postService.page(new Page<>(current, size),
|
||||||
|
postService.getQueryWrapper(postQueryRequest));
|
||||||
|
return ResultUtils.success(postPage);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页获取列表(封装类)
|
||||||
|
*
|
||||||
|
* @param postQueryRequest
|
||||||
|
* @param request
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@PostMapping("/list/page/vo")
|
||||||
|
public BaseResponse<Page<PostVO>> listPostVOByPage(@RequestBody PostQueryRequest postQueryRequest,
|
||||||
|
HttpServletRequest request) {
|
||||||
|
long current = postQueryRequest.getCurrent();
|
||||||
|
long size = postQueryRequest.getPageSize();
|
||||||
|
// 限制爬虫
|
||||||
|
ThrowUtils.throwIf(size > 20, ErrorCode.PARAMS_ERROR);
|
||||||
|
Page<Post> postPage = postService.page(new Page<>(current, size),
|
||||||
|
postService.getQueryWrapper(postQueryRequest));
|
||||||
|
return ResultUtils.success(postService.getPostVOPage(postPage, request));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页获取当前用户创建的资源列表
|
||||||
|
*
|
||||||
|
* @param postQueryRequest
|
||||||
|
* @param request
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@PostMapping("/my/list/page/vo")
|
||||||
|
public BaseResponse<Page<PostVO>> listMyPostVOByPage(@RequestBody PostQueryRequest postQueryRequest,
|
||||||
|
HttpServletRequest request) {
|
||||||
|
if (postQueryRequest == null) {
|
||||||
|
throw new BusinessException(ErrorCode.PARAMS_ERROR);
|
||||||
|
}
|
||||||
|
User loginUser = userService.getLoginUser(request);
|
||||||
|
postQueryRequest.setUserId(loginUser.getId());
|
||||||
|
long current = postQueryRequest.getCurrent();
|
||||||
|
long size = postQueryRequest.getPageSize();
|
||||||
|
// 限制爬虫
|
||||||
|
ThrowUtils.throwIf(size > 20, ErrorCode.PARAMS_ERROR);
|
||||||
|
Page<Post> postPage = postService.page(new Page<>(current, size),
|
||||||
|
postService.getQueryWrapper(postQueryRequest));
|
||||||
|
return ResultUtils.success(postService.getPostVOPage(postPage, request));
|
||||||
|
}
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页搜索(从 ES 查询,封装类)
|
||||||
|
*
|
||||||
|
* @param postQueryRequest
|
||||||
|
* @param request
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@PostMapping("/search/page/vo")
|
||||||
|
public BaseResponse<Page<PostVO>> searchPostVOByPage(@RequestBody PostQueryRequest postQueryRequest,
|
||||||
|
HttpServletRequest request) {
|
||||||
|
long size = postQueryRequest.getPageSize();
|
||||||
|
// 限制爬虫
|
||||||
|
ThrowUtils.throwIf(size > 20, ErrorCode.PARAMS_ERROR);
|
||||||
|
Page<Post> postPage = postService.searchFromEs(postQueryRequest);
|
||||||
|
return ResultUtils.success(postService.getPostVOPage(postPage, request));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 编辑(用户)
|
||||||
|
*
|
||||||
|
* @param postEditRequest
|
||||||
|
* @param request
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@PostMapping("/edit")
|
||||||
|
public BaseResponse<Boolean> editPost(@RequestBody PostEditRequest postEditRequest, HttpServletRequest request) {
|
||||||
|
if (postEditRequest == null || postEditRequest.getId() <= 0) {
|
||||||
|
throw new BusinessException(ErrorCode.PARAMS_ERROR);
|
||||||
|
}
|
||||||
|
Post post = new Post();
|
||||||
|
BeanUtils.copyProperties(postEditRequest, post);
|
||||||
|
List<String> tags = postEditRequest.getTags();
|
||||||
|
if (tags != null) {
|
||||||
|
post.setTags(JSONUtil.toJsonStr(tags));
|
||||||
|
}
|
||||||
|
// 参数校验
|
||||||
|
postService.validPost(post, false);
|
||||||
|
User loginUser = userService.getLoginUser(request);
|
||||||
|
long id = postEditRequest.getId();
|
||||||
|
// 判断是否存在
|
||||||
|
Post oldPost = postService.getById(id);
|
||||||
|
ThrowUtils.throwIf(oldPost == null, ErrorCode.NOT_FOUND_ERROR);
|
||||||
|
// 仅本人或管理员可编辑
|
||||||
|
if (!oldPost.getUserId().equals(loginUser.getId()) && !userService.isAdmin(loginUser)) {
|
||||||
|
throw new BusinessException(ErrorCode.NO_AUTH_ERROR);
|
||||||
|
}
|
||||||
|
boolean result = postService.updateById(post);
|
||||||
|
return ResultUtils.success(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,109 @@
|
|||||||
|
package com.yupi.springbootinit.controller;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import com.yupi.springbootinit.common.BaseResponse;
|
||||||
|
import com.yupi.springbootinit.common.ErrorCode;
|
||||||
|
import com.yupi.springbootinit.common.ResultUtils;
|
||||||
|
import com.yupi.springbootinit.exception.BusinessException;
|
||||||
|
import com.yupi.springbootinit.exception.ThrowUtils;
|
||||||
|
import com.yupi.springbootinit.model.dto.post.PostQueryRequest;
|
||||||
|
import com.yupi.springbootinit.model.dto.postfavour.PostFavourAddRequest;
|
||||||
|
import com.yupi.springbootinit.model.dto.postfavour.PostFavourQueryRequest;
|
||||||
|
import com.yupi.springbootinit.model.entity.Post;
|
||||||
|
import com.yupi.springbootinit.model.entity.User;
|
||||||
|
import com.yupi.springbootinit.model.vo.PostVO;
|
||||||
|
import com.yupi.springbootinit.service.PostFavourService;
|
||||||
|
import com.yupi.springbootinit.service.PostService;
|
||||||
|
import com.yupi.springbootinit.service.UserService;
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 帖子收藏接口
|
||||||
|
*
|
||||||
|
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
|
||||||
|
* @from <a href="https://yupi.icu">编程导航知识星球</a>
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/post_favour")
|
||||||
|
@Slf4j
|
||||||
|
public class PostFavourController {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private PostFavourService postFavourService;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private PostService postService;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private UserService userService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 收藏 / 取消收藏
|
||||||
|
*
|
||||||
|
* @param postFavourAddRequest
|
||||||
|
* @param request
|
||||||
|
* @return resultNum 收藏变化数
|
||||||
|
*/
|
||||||
|
@PostMapping("/")
|
||||||
|
public BaseResponse<Integer> doPostFavour(@RequestBody PostFavourAddRequest postFavourAddRequest,
|
||||||
|
HttpServletRequest request) {
|
||||||
|
if (postFavourAddRequest == null || postFavourAddRequest.getPostId() <= 0) {
|
||||||
|
throw new BusinessException(ErrorCode.PARAMS_ERROR);
|
||||||
|
}
|
||||||
|
// 登录才能操作
|
||||||
|
final User loginUser = userService.getLoginUser(request);
|
||||||
|
long postId = postFavourAddRequest.getPostId();
|
||||||
|
int result = postFavourService.doPostFavour(postId, loginUser);
|
||||||
|
return ResultUtils.success(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取我收藏的帖子列表
|
||||||
|
*
|
||||||
|
* @param postQueryRequest
|
||||||
|
* @param request
|
||||||
|
*/
|
||||||
|
@PostMapping("/my/list/page")
|
||||||
|
public BaseResponse<Page<PostVO>> listMyFavourPostByPage(@RequestBody PostQueryRequest postQueryRequest,
|
||||||
|
HttpServletRequest request) {
|
||||||
|
if (postQueryRequest == null) {
|
||||||
|
throw new BusinessException(ErrorCode.PARAMS_ERROR);
|
||||||
|
}
|
||||||
|
User loginUser = userService.getLoginUser(request);
|
||||||
|
long current = postQueryRequest.getCurrent();
|
||||||
|
long size = postQueryRequest.getPageSize();
|
||||||
|
// 限制爬虫
|
||||||
|
ThrowUtils.throwIf(size > 20, ErrorCode.PARAMS_ERROR);
|
||||||
|
Page<Post> postPage = postFavourService.listFavourPostByPage(new Page<>(current, size),
|
||||||
|
postService.getQueryWrapper(postQueryRequest), loginUser.getId());
|
||||||
|
return ResultUtils.success(postService.getPostVOPage(postPage, request));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取用户收藏的帖子列表
|
||||||
|
*
|
||||||
|
* @param postFavourQueryRequest
|
||||||
|
* @param request
|
||||||
|
*/
|
||||||
|
@PostMapping("/list/page")
|
||||||
|
public BaseResponse<Page<PostVO>> listFavourPostByPage(@RequestBody PostFavourQueryRequest postFavourQueryRequest,
|
||||||
|
HttpServletRequest request) {
|
||||||
|
if (postFavourQueryRequest == null) {
|
||||||
|
throw new BusinessException(ErrorCode.PARAMS_ERROR);
|
||||||
|
}
|
||||||
|
long current = postFavourQueryRequest.getCurrent();
|
||||||
|
long size = postFavourQueryRequest.getPageSize();
|
||||||
|
Long userId = postFavourQueryRequest.getUserId();
|
||||||
|
// 限制爬虫
|
||||||
|
ThrowUtils.throwIf(size > 20 || userId == null, ErrorCode.PARAMS_ERROR);
|
||||||
|
Page<Post> postPage = postFavourService.listFavourPostByPage(new Page<>(current, size),
|
||||||
|
postService.getQueryWrapper(postFavourQueryRequest.getPostQueryRequest()), userId);
|
||||||
|
return ResultUtils.success(postService.getPostVOPage(postPage, request));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,56 @@
|
|||||||
|
package com.yupi.springbootinit.controller;
|
||||||
|
|
||||||
|
import com.yupi.springbootinit.common.BaseResponse;
|
||||||
|
import com.yupi.springbootinit.common.ErrorCode;
|
||||||
|
import com.yupi.springbootinit.common.ResultUtils;
|
||||||
|
import com.yupi.springbootinit.exception.BusinessException;
|
||||||
|
import com.yupi.springbootinit.model.dto.postthumb.PostThumbAddRequest;
|
||||||
|
import com.yupi.springbootinit.model.entity.User;
|
||||||
|
import com.yupi.springbootinit.service.PostThumbService;
|
||||||
|
import com.yupi.springbootinit.service.UserService;
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 帖子点赞接口
|
||||||
|
*
|
||||||
|
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
|
||||||
|
* @from <a href="https://yupi.icu">编程导航知识星球</a>
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/post_thumb")
|
||||||
|
@Slf4j
|
||||||
|
public class PostThumbController {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private PostThumbService postThumbService;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private UserService userService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 点赞 / 取消点赞
|
||||||
|
*
|
||||||
|
* @param postThumbAddRequest
|
||||||
|
* @param request
|
||||||
|
* @return resultNum 本次点赞变化数
|
||||||
|
*/
|
||||||
|
@PostMapping("/")
|
||||||
|
public BaseResponse<Integer> doThumb(@RequestBody PostThumbAddRequest postThumbAddRequest,
|
||||||
|
HttpServletRequest request) {
|
||||||
|
if (postThumbAddRequest == null || postThumbAddRequest.getPostId() <= 0) {
|
||||||
|
throw new BusinessException(ErrorCode.PARAMS_ERROR);
|
||||||
|
}
|
||||||
|
// 登录才能点赞
|
||||||
|
final User loginUser = userService.getLoginUser(request);
|
||||||
|
long postId = postThumbAddRequest.getPostId();
|
||||||
|
int result = postThumbService.doPostThumb(postId, loginUser);
|
||||||
|
return ResultUtils.success(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,56 @@
|
|||||||
|
package com.yupi.springbootinit.controller;
|
||||||
|
|
||||||
|
import cn.hutool.json.JSONUtil;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.context.annotation.Profile;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.ThreadPoolExecutor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 队列测试
|
||||||
|
*
|
||||||
|
* @author Ding Jiaxiong
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/queue")
|
||||||
|
@Slf4j
|
||||||
|
@Profile({ "dev", "local" })
|
||||||
|
public class QueueController {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private ThreadPoolExecutor threadPoolExecutor;
|
||||||
|
|
||||||
|
@GetMapping("/add")
|
||||||
|
public void add(String name) {
|
||||||
|
CompletableFuture.runAsync(() -> {
|
||||||
|
// System.out.println("任务执行中:" + name + ",执行人:" + Thread.currentThread().getName());
|
||||||
|
log.info("任务执行中:" + name + ",执行人:" + Thread.currentThread().getName());
|
||||||
|
try {
|
||||||
|
Thread.sleep(600000);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}, threadPoolExecutor);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/get")
|
||||||
|
public String get() {
|
||||||
|
Map<String, Object> map = new HashMap<>();
|
||||||
|
int size = threadPoolExecutor.getQueue().size();
|
||||||
|
map.put("队列长度", size);
|
||||||
|
long taskCount = threadPoolExecutor.getTaskCount();
|
||||||
|
map.put("任务总数", taskCount);
|
||||||
|
long completedTaskCount = threadPoolExecutor.getCompletedTaskCount();
|
||||||
|
map.put("已完成任务数", completedTaskCount);
|
||||||
|
int activeCount = threadPoolExecutor.getActiveCount();
|
||||||
|
map.put("正在工作的线程数", activeCount);
|
||||||
|
return JSONUtil.toJsonStr(map);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,293 @@
|
|||||||
|
package com.yupi.springbootinit.controller;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import com.yupi.springbootinit.annotation.AuthCheck;
|
||||||
|
import com.yupi.springbootinit.common.BaseResponse;
|
||||||
|
import com.yupi.springbootinit.common.DeleteRequest;
|
||||||
|
import com.yupi.springbootinit.common.ErrorCode;
|
||||||
|
import com.yupi.springbootinit.common.ResultUtils;
|
||||||
|
import com.yupi.springbootinit.config.WxOpenConfig;
|
||||||
|
import com.yupi.springbootinit.constant.UserConstant;
|
||||||
|
import com.yupi.springbootinit.exception.BusinessException;
|
||||||
|
import com.yupi.springbootinit.exception.ThrowUtils;
|
||||||
|
import com.yupi.springbootinit.model.dto.user.UserAddRequest;
|
||||||
|
import com.yupi.springbootinit.model.dto.user.UserLoginRequest;
|
||||||
|
import com.yupi.springbootinit.model.dto.user.UserQueryRequest;
|
||||||
|
import com.yupi.springbootinit.model.dto.user.UserRegisterRequest;
|
||||||
|
import com.yupi.springbootinit.model.dto.user.UserUpdateMyRequest;
|
||||||
|
import com.yupi.springbootinit.model.dto.user.UserUpdateRequest;
|
||||||
|
import com.yupi.springbootinit.model.entity.User;
|
||||||
|
import com.yupi.springbootinit.model.vo.LoginUserVO;
|
||||||
|
import com.yupi.springbootinit.model.vo.UserVO;
|
||||||
|
import com.yupi.springbootinit.service.UserService;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.springframework.beans.BeanUtils;
|
||||||
|
import org.springframework.util.DigestUtils;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import static com.yupi.springbootinit.service.impl.UserServiceImpl.SALT;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户接口
|
||||||
|
*
|
||||||
|
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
|
||||||
|
* @from <a href="https://yupi.icu">编程导航知识星球</a>
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/user")
|
||||||
|
@Slf4j
|
||||||
|
public class UserController {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private UserService userService;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private WxOpenConfig wxOpenConfig;
|
||||||
|
|
||||||
|
// region 登录相关
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户注册
|
||||||
|
*
|
||||||
|
* @param userRegisterRequest
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@PostMapping("/register")
|
||||||
|
public BaseResponse<Long> userRegister(@RequestBody UserRegisterRequest userRegisterRequest) {
|
||||||
|
if (userRegisterRequest == null) {
|
||||||
|
throw new BusinessException(ErrorCode.PARAMS_ERROR);
|
||||||
|
}
|
||||||
|
String userAccount = userRegisterRequest.getUserAccount();
|
||||||
|
String userPassword = userRegisterRequest.getUserPassword();
|
||||||
|
String checkPassword = userRegisterRequest.getCheckPassword();
|
||||||
|
if (StringUtils.isAnyBlank(userAccount, userPassword, checkPassword)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
long result = userService.userRegister(userAccount, userPassword, checkPassword);
|
||||||
|
return ResultUtils.success(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户登录
|
||||||
|
*
|
||||||
|
* @param userLoginRequest
|
||||||
|
* @param request
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@PostMapping("/login")
|
||||||
|
public BaseResponse<LoginUserVO> userLogin(@RequestBody UserLoginRequest userLoginRequest, HttpServletRequest request) {
|
||||||
|
if (userLoginRequest == null) {
|
||||||
|
throw new BusinessException(ErrorCode.PARAMS_ERROR);
|
||||||
|
}
|
||||||
|
String userAccount = userLoginRequest.getUserAccount();
|
||||||
|
String userPassword = userLoginRequest.getUserPassword();
|
||||||
|
if (StringUtils.isAnyBlank(userAccount, userPassword)) {
|
||||||
|
throw new BusinessException(ErrorCode.PARAMS_ERROR);
|
||||||
|
}
|
||||||
|
LoginUserVO loginUserVO = userService.userLogin(userAccount, userPassword, request);
|
||||||
|
return ResultUtils.success(loginUserVO);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户注销
|
||||||
|
*
|
||||||
|
* @param request
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@PostMapping("/logout")
|
||||||
|
public BaseResponse<Boolean> userLogout(HttpServletRequest request) {
|
||||||
|
if (request == null) {
|
||||||
|
throw new BusinessException(ErrorCode.PARAMS_ERROR);
|
||||||
|
}
|
||||||
|
boolean result = userService.userLogout(request);
|
||||||
|
return ResultUtils.success(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前登录用户
|
||||||
|
*
|
||||||
|
* @param request
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@GetMapping("/get/login")
|
||||||
|
public BaseResponse<LoginUserVO> getLoginUser(HttpServletRequest request) {
|
||||||
|
User user = userService.getLoginUser(request);
|
||||||
|
return ResultUtils.success(userService.getLoginUserVO(user));
|
||||||
|
}
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
// region 增删改查
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建用户
|
||||||
|
*
|
||||||
|
* @param userAddRequest
|
||||||
|
* @param request
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@PostMapping("/add")
|
||||||
|
@AuthCheck(mustRole = UserConstant.ADMIN_ROLE)
|
||||||
|
public BaseResponse<Long> addUser(@RequestBody UserAddRequest userAddRequest, HttpServletRequest request) {
|
||||||
|
if (userAddRequest == null) {
|
||||||
|
throw new BusinessException(ErrorCode.PARAMS_ERROR);
|
||||||
|
}
|
||||||
|
User user = new User();
|
||||||
|
BeanUtils.copyProperties(userAddRequest, user);
|
||||||
|
// 默认密码 12345678
|
||||||
|
String defaultPassword = "12345678";
|
||||||
|
String encryptPassword = DigestUtils.md5DigestAsHex((SALT + defaultPassword).getBytes());
|
||||||
|
user.setUserPassword(encryptPassword);
|
||||||
|
boolean result = userService.save(user);
|
||||||
|
ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);
|
||||||
|
return ResultUtils.success(user.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除用户
|
||||||
|
*
|
||||||
|
* @param deleteRequest
|
||||||
|
* @param request
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@PostMapping("/delete")
|
||||||
|
@AuthCheck(mustRole = UserConstant.ADMIN_ROLE)
|
||||||
|
public BaseResponse<Boolean> deleteUser(@RequestBody DeleteRequest deleteRequest, HttpServletRequest request) {
|
||||||
|
if (deleteRequest == null || deleteRequest.getId() <= 0) {
|
||||||
|
throw new BusinessException(ErrorCode.PARAMS_ERROR);
|
||||||
|
}
|
||||||
|
boolean b = userService.removeById(deleteRequest.getId());
|
||||||
|
return ResultUtils.success(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新用户
|
||||||
|
*
|
||||||
|
* @param userUpdateRequest
|
||||||
|
* @param request
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@PostMapping("/update")
|
||||||
|
@AuthCheck(mustRole = UserConstant.ADMIN_ROLE)
|
||||||
|
public BaseResponse<Boolean> updateUser(@RequestBody UserUpdateRequest userUpdateRequest,
|
||||||
|
HttpServletRequest request) {
|
||||||
|
if (userUpdateRequest == null || userUpdateRequest.getId() == null) {
|
||||||
|
throw new BusinessException(ErrorCode.PARAMS_ERROR);
|
||||||
|
}
|
||||||
|
User user = new User();
|
||||||
|
BeanUtils.copyProperties(userUpdateRequest, user);
|
||||||
|
boolean result = userService.updateById(user);
|
||||||
|
ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);
|
||||||
|
return ResultUtils.success(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据 id 获取用户(仅管理员)
|
||||||
|
*
|
||||||
|
* @param id
|
||||||
|
* @param request
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@GetMapping("/get")
|
||||||
|
@AuthCheck(mustRole = UserConstant.ADMIN_ROLE)
|
||||||
|
public BaseResponse<User> getUserById(long id, HttpServletRequest request) {
|
||||||
|
if (id <= 0) {
|
||||||
|
throw new BusinessException(ErrorCode.PARAMS_ERROR);
|
||||||
|
}
|
||||||
|
User user = userService.getById(id);
|
||||||
|
ThrowUtils.throwIf(user == null, ErrorCode.NOT_FOUND_ERROR);
|
||||||
|
return ResultUtils.success(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据 id 获取包装类
|
||||||
|
*
|
||||||
|
* @param id
|
||||||
|
* @param request
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@GetMapping("/get/vo")
|
||||||
|
public BaseResponse<UserVO> getUserVOById(long id, HttpServletRequest request) {
|
||||||
|
BaseResponse<User> response = getUserById(id, request);
|
||||||
|
User user = response.getData();
|
||||||
|
return ResultUtils.success(userService.getUserVO(user));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页获取用户列表(仅管理员)
|
||||||
|
*
|
||||||
|
* @param userQueryRequest
|
||||||
|
* @param request
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@PostMapping("/list/page")
|
||||||
|
@AuthCheck(mustRole = UserConstant.ADMIN_ROLE)
|
||||||
|
public BaseResponse<Page<User>> listUserByPage(@RequestBody UserQueryRequest userQueryRequest,
|
||||||
|
HttpServletRequest request) {
|
||||||
|
long current = userQueryRequest.getCurrent();
|
||||||
|
long size = userQueryRequest.getPageSize();
|
||||||
|
Page<User> userPage = userService.page(new Page<>(current, size),
|
||||||
|
userService.getQueryWrapper(userQueryRequest));
|
||||||
|
return ResultUtils.success(userPage);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页获取用户封装列表
|
||||||
|
*
|
||||||
|
* @param userQueryRequest
|
||||||
|
* @param request
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@PostMapping("/list/page/vo")
|
||||||
|
public BaseResponse<Page<UserVO>> listUserVOByPage(@RequestBody UserQueryRequest userQueryRequest,
|
||||||
|
HttpServletRequest request) {
|
||||||
|
if (userQueryRequest == null) {
|
||||||
|
throw new BusinessException(ErrorCode.PARAMS_ERROR);
|
||||||
|
}
|
||||||
|
long current = userQueryRequest.getCurrent();
|
||||||
|
long size = userQueryRequest.getPageSize();
|
||||||
|
// 限制爬虫
|
||||||
|
ThrowUtils.throwIf(size > 20, ErrorCode.PARAMS_ERROR);
|
||||||
|
Page<User> userPage = userService.page(new Page<>(current, size),
|
||||||
|
userService.getQueryWrapper(userQueryRequest));
|
||||||
|
Page<UserVO> userVOPage = new Page<>(current, size, userPage.getTotal());
|
||||||
|
List<UserVO> userVO = userService.getUserVO(userPage.getRecords());
|
||||||
|
userVOPage.setRecords(userVO);
|
||||||
|
return ResultUtils.success(userVOPage);
|
||||||
|
}
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新个人信息
|
||||||
|
*
|
||||||
|
* @param userUpdateMyRequest
|
||||||
|
* @param request
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@PostMapping("/update/my")
|
||||||
|
public BaseResponse<Boolean> updateMyUser(@RequestBody UserUpdateMyRequest userUpdateMyRequest,
|
||||||
|
HttpServletRequest request) {
|
||||||
|
if (userUpdateMyRequest == null) {
|
||||||
|
throw new BusinessException(ErrorCode.PARAMS_ERROR);
|
||||||
|
}
|
||||||
|
User loginUser = userService.getLoginUser(request);
|
||||||
|
User user = new User();
|
||||||
|
BeanUtils.copyProperties(userUpdateMyRequest, user);
|
||||||
|
user.setId(loginUser.getId());
|
||||||
|
boolean result = userService.updateById(user);
|
||||||
|
ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);
|
||||||
|
return ResultUtils.success(true);
|
||||||
|
}
|
||||||
|
}
|
16
src/main/java/com/yupi/springbootinit/esdao/PostEsDao.java
Normal file
16
src/main/java/com/yupi/springbootinit/esdao/PostEsDao.java
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
package com.yupi.springbootinit.esdao;
|
||||||
|
|
||||||
|
import com.yupi.springbootinit.model.dto.post.PostEsDTO;
|
||||||
|
import java.util.List;
|
||||||
|
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 帖子 ES 操作
|
||||||
|
*
|
||||||
|
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
|
||||||
|
* @from <a href="https://yupi.icu">编程导航知识星球</a>
|
||||||
|
*/
|
||||||
|
public interface PostEsDao extends ElasticsearchRepository<PostEsDTO, Long> {
|
||||||
|
|
||||||
|
List<PostEsDTO> findByUserId(Long userId);
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
package com.yupi.springbootinit.exception;
|
||||||
|
|
||||||
|
import com.yupi.springbootinit.common.ErrorCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自定义异常类
|
||||||
|
*
|
||||||
|
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
|
||||||
|
* @from <a href="https://yupi.icu">编程导航知识星球</a>
|
||||||
|
*/
|
||||||
|
public class BusinessException extends RuntimeException {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 错误码
|
||||||
|
*/
|
||||||
|
private final int code;
|
||||||
|
|
||||||
|
public BusinessException(int code, String message) {
|
||||||
|
super(message);
|
||||||
|
this.code = code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BusinessException(ErrorCode errorCode) {
|
||||||
|
super(errorCode.getMessage());
|
||||||
|
this.code = errorCode.getCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
public BusinessException(ErrorCode errorCode, String message) {
|
||||||
|
super(message);
|
||||||
|
this.code = errorCode.getCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCode() {
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,31 @@
|
|||||||
|
package com.yupi.springbootinit.exception;
|
||||||
|
|
||||||
|
import com.yupi.springbootinit.common.BaseResponse;
|
||||||
|
import com.yupi.springbootinit.common.ErrorCode;
|
||||||
|
import com.yupi.springbootinit.common.ResultUtils;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||||
|
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 全局异常处理器
|
||||||
|
*
|
||||||
|
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
|
||||||
|
* @from <a href="https://yupi.icu">编程导航知识星球</a>
|
||||||
|
*/
|
||||||
|
@RestControllerAdvice
|
||||||
|
@Slf4j
|
||||||
|
public class GlobalExceptionHandler {
|
||||||
|
|
||||||
|
@ExceptionHandler(BusinessException.class)
|
||||||
|
public BaseResponse<?> businessExceptionHandler(BusinessException e) {
|
||||||
|
log.error("BusinessException", e);
|
||||||
|
return ResultUtils.error(e.getCode(), e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExceptionHandler(RuntimeException.class)
|
||||||
|
public BaseResponse<?> runtimeExceptionHandler(RuntimeException e) {
|
||||||
|
log.error("RuntimeException", e);
|
||||||
|
return ResultUtils.error(ErrorCode.SYSTEM_ERROR, "系统错误");
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,45 @@
|
|||||||
|
package com.yupi.springbootinit.exception;
|
||||||
|
|
||||||
|
import com.yupi.springbootinit.common.ErrorCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 抛异常工具类
|
||||||
|
*
|
||||||
|
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
|
||||||
|
* @from <a href="https://yupi.icu">编程导航知识星球</a>
|
||||||
|
*/
|
||||||
|
public class ThrowUtils {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 条件成立则抛异常
|
||||||
|
*
|
||||||
|
* @param condition
|
||||||
|
* @param runtimeException
|
||||||
|
*/
|
||||||
|
public static void throwIf(boolean condition, RuntimeException runtimeException) {
|
||||||
|
if (condition) {
|
||||||
|
throw runtimeException;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 条件成立则抛异常
|
||||||
|
*
|
||||||
|
* @param condition
|
||||||
|
* @param errorCode
|
||||||
|
*/
|
||||||
|
public static void throwIf(boolean condition, ErrorCode errorCode) {
|
||||||
|
throwIf(condition, new BusinessException(errorCode));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 条件成立则抛异常
|
||||||
|
*
|
||||||
|
* @param condition
|
||||||
|
* @param errorCode
|
||||||
|
* @param message
|
||||||
|
*/
|
||||||
|
public static void throwIf(boolean condition, ErrorCode errorCode, String message) {
|
||||||
|
throwIf(condition, new BusinessException(errorCode, message));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,128 @@
|
|||||||
|
package com.yupi.springbootinit.generate;
|
||||||
|
|
||||||
|
import cn.hutool.core.io.FileUtil;
|
||||||
|
import freemarker.template.Configuration;
|
||||||
|
import freemarker.template.Template;
|
||||||
|
import freemarker.template.TemplateException;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.io.FileWriter;
|
||||||
|
import java.io.Writer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 代码生成器
|
||||||
|
*
|
||||||
|
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
|
||||||
|
* @from <a href="https://www.code-nav.cn">编程导航学习圈</a>
|
||||||
|
*/
|
||||||
|
public class CodeGenerator {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用法:修改生成参数和生成路径,注释掉不需要的生成逻辑,然后运行即可
|
||||||
|
*
|
||||||
|
* @param args
|
||||||
|
* @throws TemplateException
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public static void main(String[] args) throws TemplateException, IOException {
|
||||||
|
// 指定生成参数
|
||||||
|
String packageName = "com.yupi.springbootinit";
|
||||||
|
String dataName = "用户评论";
|
||||||
|
String dataKey = "userComment";
|
||||||
|
String upperDataKey = "UserComment";
|
||||||
|
|
||||||
|
// 封装生成参数
|
||||||
|
Map<String, Object> dataModel = new HashMap<>();
|
||||||
|
dataModel.put("packageName", packageName);
|
||||||
|
dataModel.put("dataName", dataName);
|
||||||
|
dataModel.put("dataKey", dataKey);
|
||||||
|
dataModel.put("upperDataKey", upperDataKey);
|
||||||
|
|
||||||
|
// 生成路径默认值
|
||||||
|
String projectPath = System.getProperty("user.dir");
|
||||||
|
// 参考路径,可以自己调整下面的 outputPath
|
||||||
|
String inputPath = projectPath + File.separator + "src/main/resources/templates/模板名称.java.ftl";
|
||||||
|
String outputPath = String.format("%s/generator/包名/%s类后缀.java", projectPath, upperDataKey);
|
||||||
|
|
||||||
|
// 1、生成 Controller
|
||||||
|
// 指定生成路径
|
||||||
|
inputPath = projectPath + File.separator + "src/main/resources/templates/TemplateController.java.ftl";
|
||||||
|
outputPath = String.format("%s/generator/controller/%sController.java", projectPath, upperDataKey);
|
||||||
|
// 生成
|
||||||
|
doGenerate(inputPath, outputPath, dataModel);
|
||||||
|
System.out.println("生成 Controller 成功,文件路径:" + outputPath);
|
||||||
|
|
||||||
|
// 2、生成 Service 接口和实现类
|
||||||
|
// 生成 Service 接口
|
||||||
|
inputPath = projectPath + File.separator + "src/main/resources/templates/TemplateService.java.ftl";
|
||||||
|
outputPath = String.format("%s/generator/service/%sService.java", projectPath, upperDataKey);
|
||||||
|
doGenerate(inputPath, outputPath, dataModel);
|
||||||
|
System.out.println("生成 Service 接口成功,文件路径:" + outputPath);
|
||||||
|
// 生成 Service 实现类
|
||||||
|
inputPath = projectPath + File.separator + "src/main/resources/templates/TemplateServiceImpl.java.ftl";
|
||||||
|
outputPath = String.format("%s/generator/service/impl/%sServiceImpl.java", projectPath, upperDataKey);
|
||||||
|
doGenerate(inputPath, outputPath, dataModel);
|
||||||
|
System.out.println("生成 Service 实现类成功,文件路径:" + outputPath);
|
||||||
|
|
||||||
|
// 3、生成数据模型封装类(包括 DTO 和 VO)
|
||||||
|
// 生成 DTO
|
||||||
|
inputPath = projectPath + File.separator + "src/main/resources/templates/model/TemplateAddRequest.java.ftl";
|
||||||
|
outputPath = String.format("%s/generator/model/dto/%sAddRequest.java", projectPath, upperDataKey);
|
||||||
|
doGenerate(inputPath, outputPath, dataModel);
|
||||||
|
inputPath = projectPath + File.separator + "src/main/resources/templates/model/TemplateQueryRequest.java.ftl";
|
||||||
|
outputPath = String.format("%s/generator/model/dto/%sQueryRequest.java", projectPath, upperDataKey);
|
||||||
|
doGenerate(inputPath, outputPath, dataModel);
|
||||||
|
inputPath = projectPath + File.separator + "src/main/resources/templates/model/TemplateEditRequest.java.ftl";
|
||||||
|
outputPath = String.format("%s/generator/model/dto/%sEditRequest.java", projectPath, upperDataKey);
|
||||||
|
doGenerate(inputPath, outputPath, dataModel);
|
||||||
|
inputPath = projectPath + File.separator + "src/main/resources/templates/model/TemplateUpdateRequest.java.ftl";
|
||||||
|
outputPath = String.format("%s/generator/model/dto/%sUpdateRequest.java", projectPath, upperDataKey);
|
||||||
|
doGenerate(inputPath, outputPath, dataModel);
|
||||||
|
System.out.println("生成 DTO 成功,文件路径:" + outputPath);
|
||||||
|
// 生成 VO
|
||||||
|
inputPath = projectPath + File.separator + "src/main/resources/templates/model/TemplateVO.java.ftl";
|
||||||
|
outputPath = String.format("%s/generator/model/vo/%sVO.java", projectPath, upperDataKey);
|
||||||
|
doGenerate(inputPath, outputPath, dataModel);
|
||||||
|
System.out.println("生成 VO 成功,文件路径:" + outputPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成文件
|
||||||
|
*
|
||||||
|
* @param inputPath 模板文件输入路径
|
||||||
|
* @param outputPath 输出路径
|
||||||
|
* @param model 数据模型
|
||||||
|
* @throws IOException
|
||||||
|
* @throws TemplateException
|
||||||
|
*/
|
||||||
|
public static void doGenerate(String inputPath, String outputPath, Object model) throws IOException, TemplateException {
|
||||||
|
// new 出 Configuration 对象,参数为 FreeMarker 版本号
|
||||||
|
Configuration configuration = new Configuration(Configuration.VERSION_2_3_31);
|
||||||
|
|
||||||
|
// 指定模板文件所在的路径
|
||||||
|
File templateDir = new File(inputPath).getParentFile();
|
||||||
|
configuration.setDirectoryForTemplateLoading(templateDir);
|
||||||
|
|
||||||
|
// 设置模板文件使用的字符集
|
||||||
|
configuration.setDefaultEncoding("utf-8");
|
||||||
|
|
||||||
|
// 创建模板对象,加载指定模板
|
||||||
|
String templateName = new File(inputPath).getName();
|
||||||
|
Template template = configuration.getTemplate(templateName);
|
||||||
|
|
||||||
|
// 文件不存在则创建文件和父目录
|
||||||
|
if (!FileUtil.exist(outputPath)) {
|
||||||
|
FileUtil.touch(outputPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成
|
||||||
|
Writer out = new FileWriter(outputPath);
|
||||||
|
template.process(model, out);
|
||||||
|
|
||||||
|
// 生成文件后别忘了关闭哦
|
||||||
|
out.close();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,57 @@
|
|||||||
|
package com.yupi.springbootinit.job.cycle;
|
||||||
|
|
||||||
|
import com.yupi.springbootinit.esdao.PostEsDao;
|
||||||
|
import com.yupi.springbootinit.mapper.PostMapper;
|
||||||
|
import com.yupi.springbootinit.model.dto.post.PostEsDTO;
|
||||||
|
import com.yupi.springbootinit.model.entity.Post;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import cn.hutool.core.collection.CollUtil;
|
||||||
|
import org.springframework.scheduling.annotation.Scheduled;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 增量同步帖子到 es
|
||||||
|
*
|
||||||
|
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
|
||||||
|
* @from <a href="https://yupi.icu">编程导航知识星球</a>
|
||||||
|
*/
|
||||||
|
// todo 取消注释开启任务
|
||||||
|
//@Component
|
||||||
|
@Slf4j
|
||||||
|
public class IncSyncPostToEs {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private PostMapper postMapper;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private PostEsDao postEsDao;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 每分钟执行一次
|
||||||
|
*/
|
||||||
|
@Scheduled(fixedRate = 60 * 1000)
|
||||||
|
public void run() {
|
||||||
|
// 查询近 5 分钟内的数据
|
||||||
|
Date fiveMinutesAgoDate = new Date(new Date().getTime() - 5 * 60 * 1000L);
|
||||||
|
List<Post> postList = postMapper.listPostWithDelete(fiveMinutesAgoDate);
|
||||||
|
if (CollUtil.isEmpty(postList)) {
|
||||||
|
log.info("no inc post");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
List<PostEsDTO> postEsDTOList = postList.stream()
|
||||||
|
.map(PostEsDTO::objToDto)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
final int pageSize = 500;
|
||||||
|
int total = postEsDTOList.size();
|
||||||
|
log.info("IncSyncPostToEs start, total {}", total);
|
||||||
|
for (int i = 0; i < total; i += pageSize) {
|
||||||
|
int end = Math.min(i + pageSize, total);
|
||||||
|
log.info("sync from {} to {}", i, end);
|
||||||
|
postEsDao.saveAll(postEsDTOList.subList(i, end));
|
||||||
|
}
|
||||||
|
log.info("IncSyncPostToEs end, total {}", total);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,48 @@
|
|||||||
|
package com.yupi.springbootinit.job.once;
|
||||||
|
|
||||||
|
import com.yupi.springbootinit.esdao.PostEsDao;
|
||||||
|
import com.yupi.springbootinit.model.dto.post.PostEsDTO;
|
||||||
|
import com.yupi.springbootinit.model.entity.Post;
|
||||||
|
import com.yupi.springbootinit.service.PostService;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import cn.hutool.core.collection.CollUtil;
|
||||||
|
import org.springframework.boot.CommandLineRunner;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 全量同步帖子到 es
|
||||||
|
*
|
||||||
|
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
|
||||||
|
* @from <a href="https://yupi.icu">编程导航知识星球</a>
|
||||||
|
*/
|
||||||
|
// todo 取消注释开启任务
|
||||||
|
//@Component
|
||||||
|
@Slf4j
|
||||||
|
public class FullSyncPostToEs implements CommandLineRunner {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private PostService postService;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private PostEsDao postEsDao;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run(String... args) {
|
||||||
|
List<Post> postList = postService.list();
|
||||||
|
if (CollUtil.isEmpty(postList)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
List<PostEsDTO> postEsDTOList = postList.stream().map(PostEsDTO::objToDto).collect(Collectors.toList());
|
||||||
|
final int pageSize = 500;
|
||||||
|
int total = postEsDTOList.size();
|
||||||
|
log.info("FullSyncPostToEs start, total {}", total);
|
||||||
|
for (int i = 0; i < total; i += pageSize) {
|
||||||
|
int end = Math.min(i + pageSize, total);
|
||||||
|
log.info("sync from {} to {}", i, end);
|
||||||
|
postEsDao.saveAll(postEsDTOList.subList(i, end));
|
||||||
|
}
|
||||||
|
log.info("FullSyncPostToEs end, total {}", total);
|
||||||
|
}
|
||||||
|
}
|
85
src/main/java/com/yupi/springbootinit/manager/AiManager.java
Normal file
85
src/main/java/com/yupi/springbootinit/manager/AiManager.java
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
package com.yupi.springbootinit.manager;
|
||||||
|
|
||||||
|
import com.yupi.springbootinit.common.ErrorCode;
|
||||||
|
import com.yupi.springbootinit.exception.BusinessException;
|
||||||
|
import io.github.briqt.spark4j.SparkClient;
|
||||||
|
import io.github.briqt.spark4j.constant.SparkApiVersion;
|
||||||
|
import io.github.briqt.spark4j.model.SparkMessage;
|
||||||
|
import io.github.briqt.spark4j.model.request.SparkRequest;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@Slf4j
|
||||||
|
public class AiManager {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private SparkClient sparkClient;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 向 AI 发送请求
|
||||||
|
*
|
||||||
|
* @param isNeedTemplate 是否使用模板,进行 AI 生成; true 使用 、false 不使用 ,false 的情况是只想用 AI 不只是生成前端代码
|
||||||
|
* @param content 内容
|
||||||
|
* 分析需求:
|
||||||
|
* 分析网站用户的增长情况
|
||||||
|
* 原始数据:
|
||||||
|
* 日期,用户数
|
||||||
|
* 1号,10
|
||||||
|
* 2号,20
|
||||||
|
* 3号,30
|
||||||
|
* @return AI 返回的内容
|
||||||
|
* '【【【【'
|
||||||
|
* <p>
|
||||||
|
* '【【【【'
|
||||||
|
*/
|
||||||
|
public String sendMsgToXingHuo(boolean isNeedTemplate, String content) {
|
||||||
|
List<SparkMessage> messages = new ArrayList<>();
|
||||||
|
if (isNeedTemplate) {
|
||||||
|
// AI 生成问题的预设条件
|
||||||
|
String predefinedInformation = "请严格按照下面的输出格式生成结果,且不得添加任何多余内容(例如无关文字、注释、代码块标记或反引号):\n" +
|
||||||
|
"\n" +
|
||||||
|
"'【【【【'" +
|
||||||
|
"{ 生成 Echarts V5 的 option 配置对象 JSON 代码,要求为合法 JSON 格式且不含任何额外内容(如注释或多余字符) } '【【【【' 结论: {\n" +
|
||||||
|
"提供对数据的详细分析结论,内容应尽可能准确、详细,不允许添加其他无关文字或注释 }\n" +
|
||||||
|
"\n" +
|
||||||
|
"示例: 输入: 分析需求: 分析网站用户增长情况,请使用柱状图展示 原始数据: 日期,用户数 1号,10 2号,20 3号,30\n" +
|
||||||
|
"\n" +
|
||||||
|
"期望输出: '【【【【' { \"title\": { \"text\": \"分析网站用户增长情况\" }, \"xAxis\": { \"type\": \"category\", \"data\": [\"1号\", \"2号\", \"3号\"] }, \"yAxis\": { \"type\": \"value\" }, \"series\": [ { \"name\": \"用户数\", \"type\": \"bar\", \"data\": [10, 20, 30] } ] } '【【【【' 从数据看,网站用户数由1号的10人增长到2号的20人,再到3号的30人,呈现出明显的上升趋势。这表明在这段时间内网站用户吸引力增强,可能与推广活动、内容更新或其他外部因素有关。";
|
||||||
|
messages.add(SparkMessage.systemContent(predefinedInformation + "\n" + "----------------------------------"));
|
||||||
|
}
|
||||||
|
messages.add(SparkMessage.userContent(content));
|
||||||
|
// 构造请求
|
||||||
|
SparkRequest sparkRequest = SparkRequest.builder()
|
||||||
|
// 消息列表
|
||||||
|
.messages(messages)
|
||||||
|
// 模型回答的tokens的最大长度,非必传,取值为[1,4096],默认为2048
|
||||||
|
.maxTokens(2048)
|
||||||
|
// 核采样阈值。用于决定结果随机性,取值越高随机性越强即相同的问题得到的不同答案的可能性越高 非必传,取值为[0,1],默认为0.5
|
||||||
|
.temperature(0.6)
|
||||||
|
// 指定请求版本
|
||||||
|
.apiVersion(SparkApiVersion.V4_0)
|
||||||
|
.build();
|
||||||
|
// 同步调用
|
||||||
|
String responseContent = sparkClient.chatSync(sparkRequest).getContent().trim();
|
||||||
|
if (!isNeedTemplate) {
|
||||||
|
return responseContent;
|
||||||
|
}
|
||||||
|
log.info("星火 AI 返回的结果 {}", responseContent);
|
||||||
|
AtomicInteger atomicInteger = new AtomicInteger(1);
|
||||||
|
while (responseContent.split("'【【【【'").length < 3) {
|
||||||
|
responseContent = sparkClient.chatSync(sparkRequest).getContent().trim();
|
||||||
|
if (atomicInteger.incrementAndGet() >= 4) {
|
||||||
|
throw new BusinessException(ErrorCode.SYSTEM_ERROR, "星火 AI 生成失败");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return responseContent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,51 @@
|
|||||||
|
package com.yupi.springbootinit.manager;
|
||||||
|
|
||||||
|
import com.qcloud.cos.COSClient;
|
||||||
|
import com.qcloud.cos.model.PutObjectRequest;
|
||||||
|
import com.qcloud.cos.model.PutObjectResult;
|
||||||
|
import com.yupi.springbootinit.config.CosClientConfig;
|
||||||
|
import java.io.File;
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cos 对象存储操作
|
||||||
|
*
|
||||||
|
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
|
||||||
|
* @from <a href="https://yupi.icu">编程导航知识星球</a>
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class CosManager {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private CosClientConfig cosClientConfig;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private COSClient cosClient;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传对象
|
||||||
|
*
|
||||||
|
* @param key 唯一键
|
||||||
|
* @param localFilePath 本地文件路径
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public PutObjectResult putObject(String key, String localFilePath) {
|
||||||
|
PutObjectRequest putObjectRequest = new PutObjectRequest(cosClientConfig.getBucket(), key,
|
||||||
|
new File(localFilePath));
|
||||||
|
return cosClient.putObject(putObjectRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传对象
|
||||||
|
*
|
||||||
|
* @param key 唯一键
|
||||||
|
* @param file 文件
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public PutObjectResult putObject(String key, File file) {
|
||||||
|
PutObjectRequest putObjectRequest = new PutObjectRequest(cosClientConfig.getBucket(), key,
|
||||||
|
file);
|
||||||
|
return cosClient.putObject(putObjectRequest);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
package com.yupi.springbootinit.manager;
|
||||||
|
|
||||||
|
import com.yupi.springbootinit.common.ErrorCode;
|
||||||
|
import com.yupi.springbootinit.exception.BusinessException;
|
||||||
|
import org.redisson.api.RRateLimiter;
|
||||||
|
import org.redisson.api.RateIntervalUnit;
|
||||||
|
import org.redisson.api.RateType;
|
||||||
|
import org.redisson.api.RedissonClient;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 专门提供 RedisLimiter 限流基础服务的(提供了通用的能力)
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
public class RedisLimiterManager {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private RedissonClient redissonClient;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 限流操作
|
||||||
|
*
|
||||||
|
* @param key 区分不同的限流器,比如不同的用户 id 应该分别统计
|
||||||
|
*/
|
||||||
|
public void doRateLimit(String key) {
|
||||||
|
// 创建一个名称为user_limiter的限流器,每秒最多访问 2 次
|
||||||
|
RRateLimiter rateLimiter = redissonClient.getRateLimiter(key);
|
||||||
|
rateLimiter.trySetRate(RateType.OVERALL, 2, 1, RateIntervalUnit.SECONDS);
|
||||||
|
// 每当一个操作来了后,请求一个令牌
|
||||||
|
boolean canOp = rateLimiter.tryAcquire(1);
|
||||||
|
if (!canOp) {
|
||||||
|
throw new BusinessException(ErrorCode.TOO_MANY_REQUEST);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,45 @@
|
|||||||
|
package com.yupi.springbootinit.manager;
|
||||||
|
|
||||||
|
import org.apache.poi.ss.formula.functions.T;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
import java.util.concurrent.ArrayBlockingQueue;
|
||||||
|
import java.util.concurrent.ThreadFactory;
|
||||||
|
import java.util.concurrent.ThreadPoolExecutor;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
public class ThreadPoolExecutorConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public ThreadPoolExecutor threadPoolExecutor() {
|
||||||
|
ThreadFactory threadFactory = new ThreadFactory() {
|
||||||
|
private int count = 1;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Thread newThread(@NotNull Runnable r) {
|
||||||
|
Thread thread = new Thread(r);
|
||||||
|
thread.setName("线程" + count);
|
||||||
|
count++;
|
||||||
|
return thread;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// corePoolSize(核心线程数=>正式员工数):正常情况下,我们的系统应该同时工作的线程数(随时就绪的状态)
|
||||||
|
// maximumPoolSize(最大线程数=>哪怕任务再多,你也最多招这些人):极限情况下,我们的线程池最多有多少个线程?
|
||||||
|
// keepAliveTime(空闲线程存活时间):非核心线程在没有任务的情况下,过多久要删除(理解为开除临时工),从而释放无用的线程资源。
|
||||||
|
// TimeUnit unit(空闲线程存活时间的单位):分钟、秒
|
||||||
|
// workQueue(工作队列):用于存放给线程执行的任务,存在一个队列的长度(一定要设置,不要说队列长度无限,因为也会占用资源)
|
||||||
|
// threadFactory(线程工厂):控制每个线程的生成、线程的属性(比如线程名)
|
||||||
|
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
|
||||||
|
2,
|
||||||
|
4,
|
||||||
|
100,
|
||||||
|
TimeUnit.SECONDS,
|
||||||
|
new ArrayBlockingQueue<>(4),
|
||||||
|
threadFactory);
|
||||||
|
return threadPoolExecutor;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
package com.yupi.springbootinit.mapper;
|
||||||
|
|
||||||
|
import com.yupi.springbootinit.model.entity.Chart;
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author HeSun
|
||||||
|
* @description 针对表【chart(图表信息表)】的数据库操作Mapper
|
||||||
|
* @createDate 2025-02-20 20:52:40
|
||||||
|
* @Entity com.yupi.springbootinit.model.entity.Chart
|
||||||
|
*/
|
||||||
|
public interface ChartMapper extends BaseMapper<Chart> {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,35 @@
|
|||||||
|
package com.yupi.springbootinit.mapper;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.Wrapper;
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
|
import com.baomidou.mybatisplus.core.toolkit.Constants;
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import com.yupi.springbootinit.model.entity.Post;
|
||||||
|
import com.yupi.springbootinit.model.entity.PostFavour;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 帖子收藏数据库操作
|
||||||
|
*
|
||||||
|
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
|
||||||
|
* @from <a href="https://yupi.icu">编程导航知识星球</a>
|
||||||
|
*/
|
||||||
|
public interface PostFavourMapper extends BaseMapper<PostFavour> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页查询收藏帖子列表
|
||||||
|
*
|
||||||
|
* @param page
|
||||||
|
* @param queryWrapper
|
||||||
|
* @param favourUserId
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
Page<Post> listFavourPostByPage(IPage<Post> page, @Param(Constants.WRAPPER) Wrapper<Post> queryWrapper,
|
||||||
|
long favourUserId);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
25
src/main/java/com/yupi/springbootinit/mapper/PostMapper.java
Normal file
25
src/main/java/com/yupi/springbootinit/mapper/PostMapper.java
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
package com.yupi.springbootinit.mapper;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import com.yupi.springbootinit.model.entity.Post;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 帖子数据库操作
|
||||||
|
*
|
||||||
|
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
|
||||||
|
* @from <a href="https://yupi.icu">编程导航知识星球</a>
|
||||||
|
*/
|
||||||
|
public interface PostMapper extends BaseMapper<Post> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询帖子列表(包括已被删除的数据)
|
||||||
|
*/
|
||||||
|
List<Post> listPostWithDelete(Date minUpdateTime);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,18 @@
|
|||||||
|
package com.yupi.springbootinit.mapper;
|
||||||
|
|
||||||
|
import com.yupi.springbootinit.model.entity.PostThumb;
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 帖子点赞数据库操作
|
||||||
|
*
|
||||||
|
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
|
||||||
|
* @from <a href="https://yupi.icu">编程导航知识星球</a>
|
||||||
|
*/
|
||||||
|
public interface PostThumbMapper extends BaseMapper<PostThumb> {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
18
src/main/java/com/yupi/springbootinit/mapper/UserMapper.java
Normal file
18
src/main/java/com/yupi/springbootinit/mapper/UserMapper.java
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
package com.yupi.springbootinit.mapper;
|
||||||
|
|
||||||
|
import com.yupi.springbootinit.model.entity.User;
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author HeSun
|
||||||
|
* @description 针对表【user(用户)】的数据库操作Mapper
|
||||||
|
* @createDate 2025-02-20 20:52:40
|
||||||
|
* @Entity com.yupi.springbootinit.model.entity.User
|
||||||
|
*/
|
||||||
|
public interface UserMapper extends BaseMapper<User> {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,39 @@
|
|||||||
|
package com.yupi.springbootinit.model.dto.chart;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建请求
|
||||||
|
*
|
||||||
|
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
|
||||||
|
* @from <a href="https://yupi.icu">编程导航知识星球</a>
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class ChartAddRequest implements Serializable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 名称
|
||||||
|
*/
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分析目标
|
||||||
|
*/
|
||||||
|
private String goal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 图表数据
|
||||||
|
*/
|
||||||
|
private String chartData;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 图表类型
|
||||||
|
*/
|
||||||
|
private String chartType;
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
}
|
@ -0,0 +1,47 @@
|
|||||||
|
package com.yupi.springbootinit.model.dto.chart;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.IdType;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableLogic;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 编辑请求
|
||||||
|
*
|
||||||
|
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
|
||||||
|
* @from <a href="https://yupi.icu">编程导航知识星球</a>
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class ChartEditRequest implements Serializable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 名称
|
||||||
|
*/
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* id
|
||||||
|
*/
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分析目标
|
||||||
|
*/
|
||||||
|
private String goal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 图表数据
|
||||||
|
*/
|
||||||
|
private String chartData;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 图表类型
|
||||||
|
*/
|
||||||
|
private String chartType;
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
}
|
@ -0,0 +1,42 @@
|
|||||||
|
package com.yupi.springbootinit.model.dto.chart;
|
||||||
|
|
||||||
|
import com.yupi.springbootinit.common.PageRequest;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询请求
|
||||||
|
*
|
||||||
|
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
|
||||||
|
* @from <a href="https://yupi.icu">编程导航知识星球</a>
|
||||||
|
*/
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@Data
|
||||||
|
public class ChartQueryRequest extends PageRequest implements Serializable {
|
||||||
|
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 名称
|
||||||
|
*/
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分析目标
|
||||||
|
*/
|
||||||
|
private String goal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 图表类型
|
||||||
|
*/
|
||||||
|
private String chartType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户id
|
||||||
|
*/
|
||||||
|
private Long userId;
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
}
|
@ -0,0 +1,78 @@
|
|||||||
|
package com.yupi.springbootinit.model.dto.chart;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.IdType;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableLogic;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新请求
|
||||||
|
*
|
||||||
|
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
|
||||||
|
* @from <a href="https://yupi.icu">编程导航知识星球</a>
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class ChartUpdateRequest implements Serializable {
|
||||||
|
/**
|
||||||
|
* id
|
||||||
|
*/
|
||||||
|
@TableId(type = IdType.ASSIGN_ID)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 名称
|
||||||
|
*/
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分析目标
|
||||||
|
*/
|
||||||
|
private String goal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 图表数据
|
||||||
|
*/
|
||||||
|
private String chartData;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 图表类型
|
||||||
|
*/
|
||||||
|
private String chartType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成的图表数据
|
||||||
|
*/
|
||||||
|
private String genChart;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成的分析结论
|
||||||
|
*/
|
||||||
|
private String genResult;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建用户 id
|
||||||
|
*/
|
||||||
|
private Long userId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
private Date createTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新时间
|
||||||
|
*/
|
||||||
|
private Date updateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否删除
|
||||||
|
*/
|
||||||
|
@TableLogic
|
||||||
|
private Integer isDelete;
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
package com.yupi.springbootinit.model.dto.chart;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件上传请求
|
||||||
|
*
|
||||||
|
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
|
||||||
|
* @from <a href="https://yupi.icu">编程导航知识星球</a>
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class GenChartByAiRequest implements Serializable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 名称
|
||||||
|
*/
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分析目标
|
||||||
|
*/
|
||||||
|
private String goal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 图标类型
|
||||||
|
*/
|
||||||
|
private String chartType;
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
package com.yupi.springbootinit.model.dto.file;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件上传请求
|
||||||
|
*
|
||||||
|
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
|
||||||
|
* @from <a href="https://yupi.icu">编程导航知识星球</a>
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class UploadFileRequest implements Serializable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 业务
|
||||||
|
*/
|
||||||
|
private String biz;
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
package com.yupi.springbootinit.model.dto.post;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.List;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建请求
|
||||||
|
*
|
||||||
|
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
|
||||||
|
* @from <a href="https://yupi.icu">编程导航知识星球</a>
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class PostAddRequest implements Serializable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标题
|
||||||
|
*/
|
||||||
|
private String title;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 内容
|
||||||
|
*/
|
||||||
|
private String content;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标签列表
|
||||||
|
*/
|
||||||
|
private List<String> tags;
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
package com.yupi.springbootinit.model.dto.post;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.List;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 编辑请求
|
||||||
|
*
|
||||||
|
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
|
||||||
|
* @from <a href="https://yupi.icu">编程导航知识星球</a>
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class PostEditRequest implements Serializable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* id
|
||||||
|
*/
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标题
|
||||||
|
*/
|
||||||
|
private String title;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 内容
|
||||||
|
*/
|
||||||
|
private String content;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标签列表
|
||||||
|
*/
|
||||||
|
private List<String> tags;
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
}
|
@ -0,0 +1,123 @@
|
|||||||
|
package com.yupi.springbootinit.model.dto.post;
|
||||||
|
|
||||||
|
import cn.hutool.core.collection.CollUtil;
|
||||||
|
import cn.hutool.json.JSONUtil;
|
||||||
|
import com.yupi.springbootinit.model.entity.Post;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.springframework.beans.BeanUtils;
|
||||||
|
import org.springframework.data.annotation.Id;
|
||||||
|
import org.springframework.data.elasticsearch.annotations.Field;
|
||||||
|
import org.springframework.data.elasticsearch.annotations.FieldType;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 帖子 ES 包装类
|
||||||
|
*
|
||||||
|
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
|
||||||
|
* @from <a href="https://yupi.icu">编程导航知识星球</a>
|
||||||
|
**/
|
||||||
|
// todo 取消注释开启 ES(须先配置 ES)
|
||||||
|
//@Document(indexName = "post")
|
||||||
|
@Data
|
||||||
|
public class PostEsDTO implements Serializable {
|
||||||
|
|
||||||
|
private static final String DATE_TIME_PATTERN = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* id
|
||||||
|
*/
|
||||||
|
@Id
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标题
|
||||||
|
*/
|
||||||
|
private String title;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 内容
|
||||||
|
*/
|
||||||
|
private String content;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标签列表
|
||||||
|
*/
|
||||||
|
private List<String> tags;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 点赞数
|
||||||
|
*/
|
||||||
|
private Integer thumbNum;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 收藏数
|
||||||
|
*/
|
||||||
|
private Integer favourNum;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建用户 id
|
||||||
|
*/
|
||||||
|
private Long userId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
@Field(index = false, store = true, type = FieldType.Date, format = {}, pattern = DATE_TIME_PATTERN)
|
||||||
|
private Date createTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新时间
|
||||||
|
*/
|
||||||
|
@Field(index = false, store = true, type = FieldType.Date, format = {}, pattern = DATE_TIME_PATTERN)
|
||||||
|
private Date updateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否删除
|
||||||
|
*/
|
||||||
|
private Integer isDelete;
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对象转包装类
|
||||||
|
*
|
||||||
|
* @param post
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static PostEsDTO objToDto(Post post) {
|
||||||
|
if (post == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
PostEsDTO postEsDTO = new PostEsDTO();
|
||||||
|
BeanUtils.copyProperties(post, postEsDTO);
|
||||||
|
String tagsStr = post.getTags();
|
||||||
|
if (StringUtils.isNotBlank(tagsStr)) {
|
||||||
|
postEsDTO.setTags(JSONUtil.toList(tagsStr, String.class));
|
||||||
|
}
|
||||||
|
return postEsDTO;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 包装类转对象
|
||||||
|
*
|
||||||
|
* @param postEsDTO
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static Post dtoToObj(PostEsDTO postEsDTO) {
|
||||||
|
if (postEsDTO == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Post post = new Post();
|
||||||
|
BeanUtils.copyProperties(postEsDTO, post);
|
||||||
|
List<String> tagList = postEsDTO.getTags();
|
||||||
|
if (CollUtil.isNotEmpty(tagList)) {
|
||||||
|
post.setTags(JSONUtil.toJsonStr(tagList));
|
||||||
|
}
|
||||||
|
return post;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,65 @@
|
|||||||
|
package com.yupi.springbootinit.model.dto.post;
|
||||||
|
|
||||||
|
import com.yupi.springbootinit.common.PageRequest;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.List;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询请求
|
||||||
|
*
|
||||||
|
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
|
||||||
|
* @from <a href="https://yupi.icu">编程导航知识星球</a>
|
||||||
|
*/
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@Data
|
||||||
|
public class PostQueryRequest extends PageRequest implements Serializable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* id
|
||||||
|
*/
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* id
|
||||||
|
*/
|
||||||
|
private Long notId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 搜索词
|
||||||
|
*/
|
||||||
|
private String searchText;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标题
|
||||||
|
*/
|
||||||
|
private String title;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 内容
|
||||||
|
*/
|
||||||
|
private String content;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标签列表
|
||||||
|
*/
|
||||||
|
private List<String> tags;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 至少有一个标签
|
||||||
|
*/
|
||||||
|
private List<String> orTags;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建用户 id
|
||||||
|
*/
|
||||||
|
private Long userId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 收藏用户 id
|
||||||
|
*/
|
||||||
|
private Long favourUserId;
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
package com.yupi.springbootinit.model.dto.post;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.List;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新请求
|
||||||
|
*
|
||||||
|
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
|
||||||
|
* @from <a href="https://yupi.icu">编程导航知识星球</a>
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class PostUpdateRequest implements Serializable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* id
|
||||||
|
*/
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标题
|
||||||
|
*/
|
||||||
|
private String title;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 内容
|
||||||
|
*/
|
||||||
|
private String content;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标签列表
|
||||||
|
*/
|
||||||
|
private List<String> tags;
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
package com.yupi.springbootinit.model.dto.postfavour;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 帖子收藏 / 取消收藏请求
|
||||||
|
*
|
||||||
|
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
|
||||||
|
* @from <a href="https://yupi.icu">编程导航知识星球</a>
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class PostFavourAddRequest implements Serializable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 帖子 id
|
||||||
|
*/
|
||||||
|
private Long postId;
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
package com.yupi.springbootinit.model.dto.postfavour;
|
||||||
|
|
||||||
|
import com.yupi.springbootinit.common.PageRequest;
|
||||||
|
import com.yupi.springbootinit.model.dto.post.PostQueryRequest;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 帖子收藏查询请求
|
||||||
|
*
|
||||||
|
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
|
||||||
|
* @from <a href="https://yupi.icu">编程导航知识星球</a>
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
public class PostFavourQueryRequest extends PageRequest implements Serializable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 帖子查询请求
|
||||||
|
*/
|
||||||
|
private PostQueryRequest postQueryRequest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户 id
|
||||||
|
*/
|
||||||
|
private Long userId;
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
package com.yupi.springbootinit.model.dto.postthumb;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 帖子点赞请求
|
||||||
|
*
|
||||||
|
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
|
||||||
|
* @from <a href="https://yupi.icu">编程导航知识星球</a>
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class PostThumbAddRequest implements Serializable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 帖子 id
|
||||||
|
*/
|
||||||
|
private Long postId;
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
package com.yupi.springbootinit.model.dto.user;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户创建请求
|
||||||
|
*
|
||||||
|
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
|
||||||
|
* @from <a href="https://yupi.icu">编程导航知识星球</a>
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class UserAddRequest implements Serializable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户昵称
|
||||||
|
*/
|
||||||
|
private String userName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 账号
|
||||||
|
*/
|
||||||
|
private String userAccount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户头像
|
||||||
|
*/
|
||||||
|
private String userAvatar;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户角色: user, admin
|
||||||
|
*/
|
||||||
|
private String userRole;
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
package com.yupi.springbootinit.model.dto.user;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户登录请求
|
||||||
|
*
|
||||||
|
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
|
||||||
|
* @from <a href="https://yupi.icu">编程导航知识星球</a>
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class UserLoginRequest implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 3191241716373120793L;
|
||||||
|
|
||||||
|
private String userAccount;
|
||||||
|
|
||||||
|
private String userPassword;
|
||||||
|
}
|
@ -0,0 +1,48 @@
|
|||||||
|
package com.yupi.springbootinit.model.dto.user;
|
||||||
|
|
||||||
|
import com.yupi.springbootinit.common.PageRequest;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户查询请求
|
||||||
|
*
|
||||||
|
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
|
||||||
|
* @from <a href="https://yupi.icu">编程导航知识星球</a>
|
||||||
|
*/
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@Data
|
||||||
|
public class UserQueryRequest extends PageRequest implements Serializable {
|
||||||
|
/**
|
||||||
|
* id
|
||||||
|
*/
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 开放平台id
|
||||||
|
*/
|
||||||
|
private String unionId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 公众号openId
|
||||||
|
*/
|
||||||
|
private String mpOpenId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户昵称
|
||||||
|
*/
|
||||||
|
private String userName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 简介
|
||||||
|
*/
|
||||||
|
private String userProfile;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户角色:user/admin/ban
|
||||||
|
*/
|
||||||
|
private String userRole;
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
package com.yupi.springbootinit.model.dto.user;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户注册请求体
|
||||||
|
*
|
||||||
|
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
|
||||||
|
* @from <a href="https://yupi.icu">编程导航知识星球</a>
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class UserRegisterRequest implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 3191241716373120793L;
|
||||||
|
|
||||||
|
private String userAccount;
|
||||||
|
|
||||||
|
private String userPassword;
|
||||||
|
|
||||||
|
private String checkPassword;
|
||||||
|
}
|
@ -0,0 +1,31 @@
|
|||||||
|
package com.yupi.springbootinit.model.dto.user;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户更新个人信息请求
|
||||||
|
*
|
||||||
|
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
|
||||||
|
* @from <a href="https://yupi.icu">编程导航知识星球</a>
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class UserUpdateMyRequest implements Serializable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户昵称
|
||||||
|
*/
|
||||||
|
private String userName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户头像
|
||||||
|
*/
|
||||||
|
private String userAvatar;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 简介
|
||||||
|
*/
|
||||||
|
private String userProfile;
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
}
|
@ -0,0 +1,40 @@
|
|||||||
|
package com.yupi.springbootinit.model.dto.user;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户更新请求
|
||||||
|
*
|
||||||
|
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
|
||||||
|
* @from <a href="https://yupi.icu">编程导航知识星球</a>
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class UserUpdateRequest implements Serializable {
|
||||||
|
/**
|
||||||
|
* id
|
||||||
|
*/
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户昵称
|
||||||
|
*/
|
||||||
|
private String userName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户头像
|
||||||
|
*/
|
||||||
|
private String userAvatar;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 简介
|
||||||
|
*/
|
||||||
|
private String userProfile;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户角色:user/admin/ban
|
||||||
|
*/
|
||||||
|
private String userRole;
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
}
|
@ -0,0 +1,81 @@
|
|||||||
|
package com.yupi.springbootinit.model.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.*;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 图表信息表
|
||||||
|
* @TableName chart
|
||||||
|
*/
|
||||||
|
@TableName(value ="chart")
|
||||||
|
@Data
|
||||||
|
public class Chart {
|
||||||
|
/**
|
||||||
|
* id
|
||||||
|
*/
|
||||||
|
@TableId(type = IdType.ASSIGN_ID)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 名称
|
||||||
|
*/
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分析目标
|
||||||
|
*/
|
||||||
|
private String goal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 图表数据
|
||||||
|
*/
|
||||||
|
private String chartData;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 图表类型
|
||||||
|
*/
|
||||||
|
private String chartType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成的图表数据
|
||||||
|
*/
|
||||||
|
private String genChart;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成的分析结论
|
||||||
|
*/
|
||||||
|
private String genResult;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务状态
|
||||||
|
*/
|
||||||
|
private String status;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行信息
|
||||||
|
*/
|
||||||
|
private String execMessage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建用户 id
|
||||||
|
*/
|
||||||
|
private Long userId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
private Date createTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新时间
|
||||||
|
*/
|
||||||
|
private Date updateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否删除
|
||||||
|
*/
|
||||||
|
@TableLogic
|
||||||
|
private Integer isDelete;
|
||||||
|
}
|
76
src/main/java/com/yupi/springbootinit/model/entity/Post.java
Normal file
76
src/main/java/com/yupi/springbootinit/model/entity/Post.java
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
package com.yupi.springbootinit.model.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.IdType;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableLogic;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Date;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 帖子
|
||||||
|
*
|
||||||
|
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
|
||||||
|
* @from <a href="https://yupi.icu">编程导航知识星球</a>
|
||||||
|
*/
|
||||||
|
@TableName(value = "post")
|
||||||
|
@Data
|
||||||
|
public class Post implements Serializable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* id
|
||||||
|
*/
|
||||||
|
@TableId(type = IdType.ASSIGN_ID)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标题
|
||||||
|
*/
|
||||||
|
private String title;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 内容
|
||||||
|
*/
|
||||||
|
private String content;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标签列表 json
|
||||||
|
*/
|
||||||
|
private String tags;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 点赞数
|
||||||
|
*/
|
||||||
|
private Integer thumbNum;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 收藏数
|
||||||
|
*/
|
||||||
|
private Integer favourNum;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建用户 id
|
||||||
|
*/
|
||||||
|
private Long userId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
private Date createTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新时间
|
||||||
|
*/
|
||||||
|
private Date updateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否删除
|
||||||
|
*/
|
||||||
|
// @TableLogic
|
||||||
|
// private Integer isDelete;
|
||||||
|
|
||||||
|
@TableField(exist = false)
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
}
|
@ -0,0 +1,49 @@
|
|||||||
|
package com.yupi.springbootinit.model.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.IdType;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Date;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 帖子收藏
|
||||||
|
*
|
||||||
|
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
|
||||||
|
* @from <a href="https://yupi.icu">编程导航知识星球</a>
|
||||||
|
**/
|
||||||
|
@TableName(value = "post_favour")
|
||||||
|
@Data
|
||||||
|
public class PostFavour implements Serializable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* id
|
||||||
|
*/
|
||||||
|
@TableId(type = IdType.AUTO)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 帖子 id
|
||||||
|
*/
|
||||||
|
private Long postId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建用户 id
|
||||||
|
*/
|
||||||
|
private Long userId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
private Date createTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新时间
|
||||||
|
*/
|
||||||
|
private Date updateTime;
|
||||||
|
|
||||||
|
@TableField(exist = false)
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
}
|
@ -0,0 +1,49 @@
|
|||||||
|
package com.yupi.springbootinit.model.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.IdType;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Date;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 帖子点赞
|
||||||
|
*
|
||||||
|
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
|
||||||
|
* @from <a href="https://yupi.icu">编程导航知识星球</a>
|
||||||
|
*/
|
||||||
|
@TableName(value = "post_thumb")
|
||||||
|
@Data
|
||||||
|
public class PostThumb implements Serializable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* id
|
||||||
|
*/
|
||||||
|
@TableId(type = IdType.AUTO)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 帖子 id
|
||||||
|
*/
|
||||||
|
private Long postId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建用户 id
|
||||||
|
*/
|
||||||
|
private Long userId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
private Date createTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新时间
|
||||||
|
*/
|
||||||
|
private Date updateTime;
|
||||||
|
|
||||||
|
@TableField(exist = false)
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
}
|
61
src/main/java/com/yupi/springbootinit/model/entity/User.java
Normal file
61
src/main/java/com/yupi/springbootinit/model/entity/User.java
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
package com.yupi.springbootinit.model.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.*;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户
|
||||||
|
* @TableName user
|
||||||
|
*/
|
||||||
|
@TableName(value ="user")
|
||||||
|
@Data
|
||||||
|
public class User {
|
||||||
|
/**
|
||||||
|
* id
|
||||||
|
*/
|
||||||
|
@TableId(type = IdType.ASSIGN_ID)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 账号
|
||||||
|
*/
|
||||||
|
private String userAccount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 密码
|
||||||
|
*/
|
||||||
|
private String userPassword;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户昵称
|
||||||
|
*/
|
||||||
|
private String userName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户头像
|
||||||
|
*/
|
||||||
|
private String userAvatar;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户角色:user/admin
|
||||||
|
*/
|
||||||
|
private String userRole;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
private Date createTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新时间
|
||||||
|
*/
|
||||||
|
private Date updateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否删除
|
||||||
|
*/
|
||||||
|
// @TableLogic
|
||||||
|
// private Integer isDelete;
|
||||||
|
}
|
@ -0,0 +1,61 @@
|
|||||||
|
package com.yupi.springbootinit.model.enums;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import org.apache.commons.lang3.ObjectUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件上传业务类型枚举
|
||||||
|
*
|
||||||
|
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
|
||||||
|
* @from <a href="https://yupi.icu">编程导航知识星球</a>
|
||||||
|
*/
|
||||||
|
public enum FileUploadBizEnum {
|
||||||
|
|
||||||
|
USER_AVATAR("用户头像", "user_avatar");
|
||||||
|
|
||||||
|
private final String text;
|
||||||
|
|
||||||
|
private final String value;
|
||||||
|
|
||||||
|
FileUploadBizEnum(String text, String value) {
|
||||||
|
this.text = text;
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取值列表
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static List<String> getValues() {
|
||||||
|
return Arrays.stream(values()).map(item -> item.value).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据 value 获取枚举
|
||||||
|
*
|
||||||
|
* @param value
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static FileUploadBizEnum getEnumByValue(String value) {
|
||||||
|
if (ObjectUtils.isEmpty(value)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
for (FileUploadBizEnum anEnum : FileUploadBizEnum.values()) {
|
||||||
|
if (anEnum.value.equals(value)) {
|
||||||
|
return anEnum;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getText() {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,63 @@
|
|||||||
|
package com.yupi.springbootinit.model.enums;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import org.apache.commons.lang3.ObjectUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户角色枚举
|
||||||
|
*
|
||||||
|
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
|
||||||
|
* @from <a href="https://yupi.icu">编程导航知识星球</a>
|
||||||
|
*/
|
||||||
|
public enum UserRoleEnum {
|
||||||
|
|
||||||
|
USER("用户", "user"),
|
||||||
|
ADMIN("管理员", "admin"),
|
||||||
|
BAN("被封号", "ban");
|
||||||
|
|
||||||
|
private final String text;
|
||||||
|
|
||||||
|
private final String value;
|
||||||
|
|
||||||
|
UserRoleEnum(String text, String value) {
|
||||||
|
this.text = text;
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取值列表
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static List<String> getValues() {
|
||||||
|
return Arrays.stream(values()).map(item -> item.value).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据 value 获取枚举
|
||||||
|
*
|
||||||
|
* @param value
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static UserRoleEnum getEnumByValue(String value) {
|
||||||
|
if (ObjectUtils.isEmpty(value)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
for (UserRoleEnum anEnum : UserRoleEnum.values()) {
|
||||||
|
if (anEnum.value.equals(value)) {
|
||||||
|
return anEnum;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getText() {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
package com.yupi.springbootinit.model.vo;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bi的返回结果
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class BiResponse {
|
||||||
|
|
||||||
|
private String genChart;
|
||||||
|
|
||||||
|
private String genResult;
|
||||||
|
|
||||||
|
private Long chartId;
|
||||||
|
}
|
@ -0,0 +1,52 @@
|
|||||||
|
package com.yupi.springbootinit.model.vo;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Date;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 已登录用户视图(脱敏)
|
||||||
|
*
|
||||||
|
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
|
||||||
|
* @from <a href="https://yupi.icu">编程导航知识星球</a>
|
||||||
|
**/
|
||||||
|
@Data
|
||||||
|
public class LoginUserVO implements Serializable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户 id
|
||||||
|
*/
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户昵称
|
||||||
|
*/
|
||||||
|
private String userName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户头像
|
||||||
|
*/
|
||||||
|
private String userAvatar;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户简介
|
||||||
|
*/
|
||||||
|
private String userProfile;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户角色:user/admin/ban
|
||||||
|
*/
|
||||||
|
private String userRole;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
private Date createTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新时间
|
||||||
|
*/
|
||||||
|
private Date updateTime;
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
}
|
112
src/main/java/com/yupi/springbootinit/model/vo/PostVO.java
Normal file
112
src/main/java/com/yupi/springbootinit/model/vo/PostVO.java
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
package com.yupi.springbootinit.model.vo;
|
||||||
|
|
||||||
|
import cn.hutool.json.JSONUtil;
|
||||||
|
import com.yupi.springbootinit.model.entity.Post;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springframework.beans.BeanUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 帖子视图
|
||||||
|
*
|
||||||
|
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
|
||||||
|
* @from <a href="https://yupi.icu">编程导航知识星球</a>
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class PostVO implements Serializable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* id
|
||||||
|
*/
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标题
|
||||||
|
*/
|
||||||
|
private String title;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 内容
|
||||||
|
*/
|
||||||
|
private String content;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 点赞数
|
||||||
|
*/
|
||||||
|
private Integer thumbNum;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 收藏数
|
||||||
|
*/
|
||||||
|
private Integer favourNum;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建用户 id
|
||||||
|
*/
|
||||||
|
private Long userId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
private Date createTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新时间
|
||||||
|
*/
|
||||||
|
private Date updateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标签列表
|
||||||
|
*/
|
||||||
|
private List<String> tagList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建人信息
|
||||||
|
*/
|
||||||
|
private UserVO user;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否已点赞
|
||||||
|
*/
|
||||||
|
private Boolean hasThumb;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否已收藏
|
||||||
|
*/
|
||||||
|
private Boolean hasFavour;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 包装类转对象
|
||||||
|
*
|
||||||
|
* @param postVO
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static Post voToObj(PostVO postVO) {
|
||||||
|
if (postVO == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Post post = new Post();
|
||||||
|
BeanUtils.copyProperties(postVO, post);
|
||||||
|
List<String> tagList = postVO.getTagList();
|
||||||
|
post.setTags(JSONUtil.toJsonStr(tagList));
|
||||||
|
return post;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对象转包装类
|
||||||
|
*
|
||||||
|
* @param post
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static PostVO objToVo(Post post) {
|
||||||
|
if (post == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
PostVO postVO = new PostVO();
|
||||||
|
BeanUtils.copyProperties(post, postVO);
|
||||||
|
postVO.setTagList(JSONUtil.toList(post.getTags(), String.class));
|
||||||
|
return postVO;
|
||||||
|
}
|
||||||
|
}
|
47
src/main/java/com/yupi/springbootinit/model/vo/UserVO.java
Normal file
47
src/main/java/com/yupi/springbootinit/model/vo/UserVO.java
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
package com.yupi.springbootinit.model.vo;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Date;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户视图(脱敏)
|
||||||
|
*
|
||||||
|
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
|
||||||
|
* @from <a href="https://yupi.icu">编程导航知识星球</a>
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class UserVO implements Serializable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* id
|
||||||
|
*/
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户昵称
|
||||||
|
*/
|
||||||
|
private String userName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户头像
|
||||||
|
*/
|
||||||
|
private String userAvatar;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户简介
|
||||||
|
*/
|
||||||
|
private String userProfile;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户角色:user/admin/ban
|
||||||
|
*/
|
||||||
|
private String userRole;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
private Date createTime;
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
package com.yupi.springbootinit.service;
|
||||||
|
|
||||||
|
import com.yupi.springbootinit.model.entity.Chart;
|
||||||
|
import com.baomidou.mybatisplus.extension.service.IService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author HeSun
|
||||||
|
* @description 针对表【chart(图表信息表)】的数据库操作Service
|
||||||
|
* @createDate 2025-02-20 20:52:40
|
||||||
|
*/
|
||||||
|
public interface ChartService extends IService<Chart> {
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,47 @@
|
|||||||
|
package com.yupi.springbootinit.service;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.Wrapper;
|
||||||
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import com.baomidou.mybatisplus.extension.service.IService;
|
||||||
|
import com.yupi.springbootinit.model.entity.Post;
|
||||||
|
import com.yupi.springbootinit.model.entity.PostFavour;
|
||||||
|
import com.yupi.springbootinit.model.entity.User;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 帖子收藏服务
|
||||||
|
*
|
||||||
|
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
|
||||||
|
* @from <a href="https://yupi.icu">编程导航知识星球</a>
|
||||||
|
*/
|
||||||
|
public interface PostFavourService extends IService<PostFavour> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 帖子收藏
|
||||||
|
*
|
||||||
|
* @param postId
|
||||||
|
* @param loginUser
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
int doPostFavour(long postId, User loginUser);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页获取用户收藏的帖子列表
|
||||||
|
*
|
||||||
|
* @param page
|
||||||
|
* @param queryWrapper
|
||||||
|
* @param favourUserId
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
Page<Post> listFavourPostByPage(IPage<Post> page, Wrapper<Post> queryWrapper,
|
||||||
|
long favourUserId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 帖子收藏(内部服务)
|
||||||
|
*
|
||||||
|
* @param userId
|
||||||
|
* @param postId
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
int doPostFavourInner(long userId, long postId);
|
||||||
|
}
|
@ -0,0 +1,60 @@
|
|||||||
|
package com.yupi.springbootinit.service;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import com.baomidou.mybatisplus.extension.service.IService;
|
||||||
|
import com.yupi.springbootinit.model.dto.post.PostQueryRequest;
|
||||||
|
import com.yupi.springbootinit.model.entity.Post;
|
||||||
|
import com.yupi.springbootinit.model.vo.PostVO;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 帖子服务
|
||||||
|
*
|
||||||
|
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
|
||||||
|
* @from <a href="https://yupi.icu">编程导航知识星球</a>
|
||||||
|
*/
|
||||||
|
public interface PostService extends IService<Post> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验
|
||||||
|
*
|
||||||
|
* @param post
|
||||||
|
* @param add
|
||||||
|
*/
|
||||||
|
void validPost(Post post, boolean add);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取查询条件
|
||||||
|
*
|
||||||
|
* @param postQueryRequest
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
QueryWrapper<Post> getQueryWrapper(PostQueryRequest postQueryRequest);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从 ES 查询
|
||||||
|
*
|
||||||
|
* @param postQueryRequest
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
Page<Post> searchFromEs(PostQueryRequest postQueryRequest);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取帖子封装
|
||||||
|
*
|
||||||
|
* @param post
|
||||||
|
* @param request
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
PostVO getPostVO(Post post, HttpServletRequest request);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页获取帖子封装
|
||||||
|
*
|
||||||
|
* @param postPage
|
||||||
|
* @param request
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
Page<PostVO> getPostVOPage(Page<Post> postPage, HttpServletRequest request);
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
package com.yupi.springbootinit.service;
|
||||||
|
|
||||||
|
import com.yupi.springbootinit.model.entity.PostThumb;
|
||||||
|
import com.baomidou.mybatisplus.extension.service.IService;
|
||||||
|
import com.yupi.springbootinit.model.entity.User;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 帖子点赞服务
|
||||||
|
*
|
||||||
|
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
|
||||||
|
* @from <a href="https://yupi.icu">编程导航知识星球</a>
|
||||||
|
*/
|
||||||
|
public interface PostThumbService extends IService<PostThumb> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 点赞
|
||||||
|
*
|
||||||
|
* @param postId
|
||||||
|
* @param loginUser
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
int doPostThumb(long postId, User loginUser);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 帖子点赞(内部服务)
|
||||||
|
*
|
||||||
|
* @param userId
|
||||||
|
* @param postId
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
int doPostThumbInner(long userId, long postId);
|
||||||
|
}
|
112
src/main/java/com/yupi/springbootinit/service/UserService.java
Normal file
112
src/main/java/com/yupi/springbootinit/service/UserService.java
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
package com.yupi.springbootinit.service;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||||
|
import com.baomidou.mybatisplus.extension.service.IService;
|
||||||
|
import com.yupi.springbootinit.model.dto.user.UserQueryRequest;
|
||||||
|
import com.yupi.springbootinit.model.entity.User;
|
||||||
|
import com.yupi.springbootinit.model.vo.LoginUserVO;
|
||||||
|
import com.yupi.springbootinit.model.vo.UserVO;
|
||||||
|
import java.util.List;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import me.chanjar.weixin.common.bean.WxOAuth2UserInfo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户服务
|
||||||
|
*
|
||||||
|
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
|
||||||
|
* @from <a href="https://yupi.icu">编程导航知识星球</a>
|
||||||
|
*/
|
||||||
|
public interface UserService extends IService<User> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户注册
|
||||||
|
*
|
||||||
|
* @param userAccount 用户账户
|
||||||
|
* @param userPassword 用户密码
|
||||||
|
* @param checkPassword 校验密码
|
||||||
|
* @return 新用户 id
|
||||||
|
*/
|
||||||
|
long userRegister(String userAccount, String userPassword, String checkPassword);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户登录
|
||||||
|
*
|
||||||
|
* @param userAccount 用户账户
|
||||||
|
* @param userPassword 用户密码
|
||||||
|
* @param request
|
||||||
|
* @return 脱敏后的用户信息
|
||||||
|
*/
|
||||||
|
LoginUserVO userLogin(String userAccount, String userPassword, HttpServletRequest request);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前登录用户
|
||||||
|
*
|
||||||
|
* @param request
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
User getLoginUser(HttpServletRequest request);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前登录用户(允许未登录)
|
||||||
|
*
|
||||||
|
* @param request
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
User getLoginUserPermitNull(HttpServletRequest request);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否为管理员
|
||||||
|
*
|
||||||
|
* @param request
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
boolean isAdmin(HttpServletRequest request);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否为管理员
|
||||||
|
*
|
||||||
|
* @param user
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
boolean isAdmin(User user);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户注销
|
||||||
|
*
|
||||||
|
* @param request
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
boolean userLogout(HttpServletRequest request);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取脱敏的已登录用户信息
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
LoginUserVO getLoginUserVO(User user);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取脱敏的用户信息
|
||||||
|
*
|
||||||
|
* @param user
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
UserVO getUserVO(User user);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取脱敏的用户信息
|
||||||
|
*
|
||||||
|
* @param userList
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
List<UserVO> getUserVO(List<User> userList);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取查询条件
|
||||||
|
*
|
||||||
|
* @param userQueryRequest
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
QueryWrapper<User> getQueryWrapper(UserQueryRequest userQueryRequest);
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
package com.yupi.springbootinit.service.impl;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
|
import com.yupi.springbootinit.model.entity.Chart;
|
||||||
|
import com.yupi.springbootinit.service.ChartService;
|
||||||
|
import com.yupi.springbootinit.mapper.ChartMapper;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author HeSun
|
||||||
|
* @description 针对表【chart(图表信息表)】的数据库操作Service实现
|
||||||
|
* @createDate 2025-02-20 20:52:40
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
public class ChartServiceImpl extends ServiceImpl<ChartMapper, Chart>
|
||||||
|
implements ChartService{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,116 @@
|
|||||||
|
package com.yupi.springbootinit.service.impl;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.Wrapper;
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||||
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
|
import com.yupi.springbootinit.common.ErrorCode;
|
||||||
|
import com.yupi.springbootinit.exception.BusinessException;
|
||||||
|
import com.yupi.springbootinit.mapper.PostFavourMapper;
|
||||||
|
import com.yupi.springbootinit.model.entity.Post;
|
||||||
|
import com.yupi.springbootinit.model.entity.PostFavour;
|
||||||
|
import com.yupi.springbootinit.model.entity.User;
|
||||||
|
import com.yupi.springbootinit.service.PostFavourService;
|
||||||
|
import com.yupi.springbootinit.service.PostService;
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import org.springframework.aop.framework.AopContext;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 帖子收藏服务实现
|
||||||
|
*
|
||||||
|
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
|
||||||
|
* @from <a href="https://yupi.icu">编程导航知识星球</a>
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
public class PostFavourServiceImpl extends ServiceImpl<PostFavourMapper, PostFavour>
|
||||||
|
implements PostFavourService {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private PostService postService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 帖子收藏
|
||||||
|
*
|
||||||
|
* @param postId
|
||||||
|
* @param loginUser
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public int doPostFavour(long postId, User loginUser) {
|
||||||
|
// 判断是否存在
|
||||||
|
Post post = postService.getById(postId);
|
||||||
|
if (post == null) {
|
||||||
|
throw new BusinessException(ErrorCode.NOT_FOUND_ERROR);
|
||||||
|
}
|
||||||
|
// 是否已帖子收藏
|
||||||
|
long userId = loginUser.getId();
|
||||||
|
// 每个用户串行帖子收藏
|
||||||
|
// 锁必须要包裹住事务方法
|
||||||
|
PostFavourService postFavourService = (PostFavourService) AopContext.currentProxy();
|
||||||
|
synchronized (String.valueOf(userId).intern()) {
|
||||||
|
return postFavourService.doPostFavourInner(userId, postId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Page<Post> listFavourPostByPage(IPage<Post> page, Wrapper<Post> queryWrapper, long favourUserId) {
|
||||||
|
if (favourUserId <= 0) {
|
||||||
|
return new Page<>();
|
||||||
|
}
|
||||||
|
return baseMapper.listFavourPostByPage(page, queryWrapper, favourUserId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 封装了事务的方法
|
||||||
|
*
|
||||||
|
* @param userId
|
||||||
|
* @param postId
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public int doPostFavourInner(long userId, long postId) {
|
||||||
|
PostFavour postFavour = new PostFavour();
|
||||||
|
postFavour.setUserId(userId);
|
||||||
|
postFavour.setPostId(postId);
|
||||||
|
QueryWrapper<PostFavour> postFavourQueryWrapper = new QueryWrapper<>(postFavour);
|
||||||
|
PostFavour oldPostFavour = this.getOne(postFavourQueryWrapper);
|
||||||
|
boolean result;
|
||||||
|
// 已收藏
|
||||||
|
if (oldPostFavour != null) {
|
||||||
|
result = this.remove(postFavourQueryWrapper);
|
||||||
|
if (result) {
|
||||||
|
// 帖子收藏数 - 1
|
||||||
|
result = postService.update()
|
||||||
|
.eq("id", postId)
|
||||||
|
.gt("favourNum", 0)
|
||||||
|
.setSql("favourNum = favourNum - 1")
|
||||||
|
.update();
|
||||||
|
return result ? -1 : 0;
|
||||||
|
} else {
|
||||||
|
throw new BusinessException(ErrorCode.SYSTEM_ERROR);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 未帖子收藏
|
||||||
|
result = this.save(postFavour);
|
||||||
|
if (result) {
|
||||||
|
// 帖子收藏数 + 1
|
||||||
|
result = postService.update()
|
||||||
|
.eq("id", postId)
|
||||||
|
.setSql("favourNum = favourNum + 1")
|
||||||
|
.update();
|
||||||
|
return result ? 1 : 0;
|
||||||
|
} else {
|
||||||
|
throw new BusinessException(ErrorCode.SYSTEM_ERROR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,312 @@
|
|||||||
|
package com.yupi.springbootinit.service.impl;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
|
import com.yupi.springbootinit.common.ErrorCode;
|
||||||
|
import com.yupi.springbootinit.constant.CommonConstant;
|
||||||
|
import com.yupi.springbootinit.exception.BusinessException;
|
||||||
|
import com.yupi.springbootinit.exception.ThrowUtils;
|
||||||
|
import com.yupi.springbootinit.mapper.PostFavourMapper;
|
||||||
|
import com.yupi.springbootinit.mapper.PostMapper;
|
||||||
|
import com.yupi.springbootinit.mapper.PostThumbMapper;
|
||||||
|
import com.yupi.springbootinit.model.dto.post.PostEsDTO;
|
||||||
|
import com.yupi.springbootinit.model.dto.post.PostQueryRequest;
|
||||||
|
import com.yupi.springbootinit.model.entity.Post;
|
||||||
|
import com.yupi.springbootinit.model.entity.PostFavour;
|
||||||
|
import com.yupi.springbootinit.model.entity.PostThumb;
|
||||||
|
import com.yupi.springbootinit.model.entity.User;
|
||||||
|
import com.yupi.springbootinit.model.vo.PostVO;
|
||||||
|
import com.yupi.springbootinit.model.vo.UserVO;
|
||||||
|
import com.yupi.springbootinit.service.PostService;
|
||||||
|
import com.yupi.springbootinit.service.UserService;
|
||||||
|
import com.yupi.springbootinit.utils.SqlUtils;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import cn.hutool.core.collection.CollUtil;
|
||||||
|
import org.apache.commons.lang3.ObjectUtils;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.elasticsearch.index.query.BoolQueryBuilder;
|
||||||
|
import org.elasticsearch.index.query.QueryBuilders;
|
||||||
|
import org.elasticsearch.search.sort.SortBuilder;
|
||||||
|
import org.elasticsearch.search.sort.SortBuilders;
|
||||||
|
import org.elasticsearch.search.sort.SortOrder;
|
||||||
|
import org.springframework.data.domain.PageRequest;
|
||||||
|
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
|
||||||
|
import org.springframework.data.elasticsearch.core.SearchHit;
|
||||||
|
import org.springframework.data.elasticsearch.core.SearchHits;
|
||||||
|
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
|
||||||
|
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 帖子服务实现
|
||||||
|
*
|
||||||
|
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
|
||||||
|
* @from <a href="https://yupi.icu">编程导航知识星球</a>
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
@Slf4j
|
||||||
|
public class PostServiceImpl extends ServiceImpl<PostMapper, Post> implements PostService {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private UserService userService;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private PostThumbMapper postThumbMapper;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private PostFavourMapper postFavourMapper;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private ElasticsearchRestTemplate elasticsearchRestTemplate;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void validPost(Post post, boolean add) {
|
||||||
|
if (post == null) {
|
||||||
|
throw new BusinessException(ErrorCode.PARAMS_ERROR);
|
||||||
|
}
|
||||||
|
String title = post.getTitle();
|
||||||
|
String content = post.getContent();
|
||||||
|
String tags = post.getTags();
|
||||||
|
// 创建时,参数不能为空
|
||||||
|
if (add) {
|
||||||
|
ThrowUtils.throwIf(StringUtils.isAnyBlank(title, content, tags), ErrorCode.PARAMS_ERROR);
|
||||||
|
}
|
||||||
|
// 有参数则校验
|
||||||
|
if (StringUtils.isNotBlank(title) && title.length() > 80) {
|
||||||
|
throw new BusinessException(ErrorCode.PARAMS_ERROR, "标题过长");
|
||||||
|
}
|
||||||
|
if (StringUtils.isNotBlank(content) && content.length() > 8192) {
|
||||||
|
throw new BusinessException(ErrorCode.PARAMS_ERROR, "内容过长");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取查询包装类
|
||||||
|
*
|
||||||
|
* @param postQueryRequest
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public QueryWrapper<Post> getQueryWrapper(PostQueryRequest postQueryRequest) {
|
||||||
|
QueryWrapper<Post> queryWrapper = new QueryWrapper<>();
|
||||||
|
if (postQueryRequest == null) {
|
||||||
|
return queryWrapper;
|
||||||
|
}
|
||||||
|
String searchText = postQueryRequest.getSearchText();
|
||||||
|
String sortField = postQueryRequest.getSortField();
|
||||||
|
String sortOrder = postQueryRequest.getSortOrder();
|
||||||
|
Long id = postQueryRequest.getId();
|
||||||
|
String title = postQueryRequest.getTitle();
|
||||||
|
String content = postQueryRequest.getContent();
|
||||||
|
List<String> tagList = postQueryRequest.getTags();
|
||||||
|
Long userId = postQueryRequest.getUserId();
|
||||||
|
Long notId = postQueryRequest.getNotId();
|
||||||
|
// 拼接查询条件
|
||||||
|
if (StringUtils.isNotBlank(searchText)) {
|
||||||
|
queryWrapper.and(qw -> qw.like("title", searchText).or().like("content", searchText));
|
||||||
|
}
|
||||||
|
queryWrapper.like(StringUtils.isNotBlank(title), "title", title);
|
||||||
|
queryWrapper.like(StringUtils.isNotBlank(content), "content", content);
|
||||||
|
if (CollUtil.isNotEmpty(tagList)) {
|
||||||
|
for (String tag : tagList) {
|
||||||
|
queryWrapper.like("tags", "\"" + tag + "\"");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
queryWrapper.ne(ObjectUtils.isNotEmpty(notId), "id", notId);
|
||||||
|
queryWrapper.eq(ObjectUtils.isNotEmpty(id), "id", id);
|
||||||
|
queryWrapper.eq(ObjectUtils.isNotEmpty(userId), "userId", userId);
|
||||||
|
queryWrapper.orderBy(SqlUtils.validSortField(sortField), sortOrder.equals(CommonConstant.SORT_ORDER_ASC),
|
||||||
|
sortField);
|
||||||
|
return queryWrapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Page<Post> searchFromEs(PostQueryRequest postQueryRequest) {
|
||||||
|
Long id = postQueryRequest.getId();
|
||||||
|
Long notId = postQueryRequest.getNotId();
|
||||||
|
String searchText = postQueryRequest.getSearchText();
|
||||||
|
String title = postQueryRequest.getTitle();
|
||||||
|
String content = postQueryRequest.getContent();
|
||||||
|
List<String> tagList = postQueryRequest.getTags();
|
||||||
|
List<String> orTagList = postQueryRequest.getOrTags();
|
||||||
|
Long userId = postQueryRequest.getUserId();
|
||||||
|
// es 起始页为 0
|
||||||
|
long current = postQueryRequest.getCurrent() - 1;
|
||||||
|
long pageSize = postQueryRequest.getPageSize();
|
||||||
|
String sortField = postQueryRequest.getSortField();
|
||||||
|
String sortOrder = postQueryRequest.getSortOrder();
|
||||||
|
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
|
||||||
|
// 过滤
|
||||||
|
boolQueryBuilder.filter(QueryBuilders.termQuery("isDelete", 0));
|
||||||
|
if (id != null) {
|
||||||
|
boolQueryBuilder.filter(QueryBuilders.termQuery("id", id));
|
||||||
|
}
|
||||||
|
if (notId != null) {
|
||||||
|
boolQueryBuilder.mustNot(QueryBuilders.termQuery("id", notId));
|
||||||
|
}
|
||||||
|
if (userId != null) {
|
||||||
|
boolQueryBuilder.filter(QueryBuilders.termQuery("userId", userId));
|
||||||
|
}
|
||||||
|
// 必须包含所有标签
|
||||||
|
if (CollUtil.isNotEmpty(tagList)) {
|
||||||
|
for (String tag : tagList) {
|
||||||
|
boolQueryBuilder.filter(QueryBuilders.termQuery("tags", tag));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 包含任何一个标签即可
|
||||||
|
if (CollUtil.isNotEmpty(orTagList)) {
|
||||||
|
BoolQueryBuilder orTagBoolQueryBuilder = QueryBuilders.boolQuery();
|
||||||
|
for (String tag : orTagList) {
|
||||||
|
orTagBoolQueryBuilder.should(QueryBuilders.termQuery("tags", tag));
|
||||||
|
}
|
||||||
|
orTagBoolQueryBuilder.minimumShouldMatch(1);
|
||||||
|
boolQueryBuilder.filter(orTagBoolQueryBuilder);
|
||||||
|
}
|
||||||
|
// 按关键词检索
|
||||||
|
if (StringUtils.isNotBlank(searchText)) {
|
||||||
|
boolQueryBuilder.should(QueryBuilders.matchQuery("title", searchText));
|
||||||
|
boolQueryBuilder.should(QueryBuilders.matchQuery("description", searchText));
|
||||||
|
boolQueryBuilder.should(QueryBuilders.matchQuery("content", searchText));
|
||||||
|
boolQueryBuilder.minimumShouldMatch(1);
|
||||||
|
}
|
||||||
|
// 按标题检索
|
||||||
|
if (StringUtils.isNotBlank(title)) {
|
||||||
|
boolQueryBuilder.should(QueryBuilders.matchQuery("title", title));
|
||||||
|
boolQueryBuilder.minimumShouldMatch(1);
|
||||||
|
}
|
||||||
|
// 按内容检索
|
||||||
|
if (StringUtils.isNotBlank(content)) {
|
||||||
|
boolQueryBuilder.should(QueryBuilders.matchQuery("content", content));
|
||||||
|
boolQueryBuilder.minimumShouldMatch(1);
|
||||||
|
}
|
||||||
|
// 排序
|
||||||
|
SortBuilder<?> sortBuilder = SortBuilders.scoreSort();
|
||||||
|
if (StringUtils.isNotBlank(sortField)) {
|
||||||
|
sortBuilder = SortBuilders.fieldSort(sortField);
|
||||||
|
sortBuilder.order(CommonConstant.SORT_ORDER_ASC.equals(sortOrder) ? SortOrder.ASC : SortOrder.DESC);
|
||||||
|
}
|
||||||
|
// 分页
|
||||||
|
PageRequest pageRequest = PageRequest.of((int) current, (int) pageSize);
|
||||||
|
// 构造查询
|
||||||
|
NativeSearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(boolQueryBuilder)
|
||||||
|
.withPageable(pageRequest).withSorts(sortBuilder).build();
|
||||||
|
SearchHits<PostEsDTO> searchHits = elasticsearchRestTemplate.search(searchQuery, PostEsDTO.class);
|
||||||
|
Page<Post> page = new Page<>();
|
||||||
|
page.setTotal(searchHits.getTotalHits());
|
||||||
|
List<Post> resourceList = new ArrayList<>();
|
||||||
|
// 查出结果后,从 db 获取最新动态数据(比如点赞数)
|
||||||
|
if (searchHits.hasSearchHits()) {
|
||||||
|
List<SearchHit<PostEsDTO>> searchHitList = searchHits.getSearchHits();
|
||||||
|
List<Long> postIdList = searchHitList.stream().map(searchHit -> searchHit.getContent().getId())
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
List<Post> postList = baseMapper.selectBatchIds(postIdList);
|
||||||
|
if (postList != null) {
|
||||||
|
Map<Long, List<Post>> idPostMap = postList.stream().collect(Collectors.groupingBy(Post::getId));
|
||||||
|
postIdList.forEach(postId -> {
|
||||||
|
if (idPostMap.containsKey(postId)) {
|
||||||
|
resourceList.add(idPostMap.get(postId).get(0));
|
||||||
|
} else {
|
||||||
|
// 从 es 清空 db 已物理删除的数据
|
||||||
|
String delete = elasticsearchRestTemplate.delete(String.valueOf(postId), PostEsDTO.class);
|
||||||
|
log.info("delete post {}", delete);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
page.setRecords(resourceList);
|
||||||
|
return page;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PostVO getPostVO(Post post, HttpServletRequest request) {
|
||||||
|
PostVO postVO = PostVO.objToVo(post);
|
||||||
|
long postId = post.getId();
|
||||||
|
// 1. 关联查询用户信息
|
||||||
|
Long userId = post.getUserId();
|
||||||
|
User user = null;
|
||||||
|
if (userId != null && userId > 0) {
|
||||||
|
user = userService.getById(userId);
|
||||||
|
}
|
||||||
|
UserVO userVO = userService.getUserVO(user);
|
||||||
|
postVO.setUser(userVO);
|
||||||
|
// 2. 已登录,获取用户点赞、收藏状态
|
||||||
|
User loginUser = userService.getLoginUserPermitNull(request);
|
||||||
|
if (loginUser != null) {
|
||||||
|
// 获取点赞
|
||||||
|
QueryWrapper<PostThumb> postThumbQueryWrapper = new QueryWrapper<>();
|
||||||
|
postThumbQueryWrapper.in("postId", postId);
|
||||||
|
postThumbQueryWrapper.eq("userId", loginUser.getId());
|
||||||
|
PostThumb postThumb = postThumbMapper.selectOne(postThumbQueryWrapper);
|
||||||
|
postVO.setHasThumb(postThumb != null);
|
||||||
|
// 获取收藏
|
||||||
|
QueryWrapper<PostFavour> postFavourQueryWrapper = new QueryWrapper<>();
|
||||||
|
postFavourQueryWrapper.in("postId", postId);
|
||||||
|
postFavourQueryWrapper.eq("userId", loginUser.getId());
|
||||||
|
PostFavour postFavour = postFavourMapper.selectOne(postFavourQueryWrapper);
|
||||||
|
postVO.setHasFavour(postFavour != null);
|
||||||
|
}
|
||||||
|
return postVO;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Page<PostVO> getPostVOPage(Page<Post> postPage, HttpServletRequest request) {
|
||||||
|
List<Post> postList = postPage.getRecords();
|
||||||
|
Page<PostVO> postVOPage = new Page<>(postPage.getCurrent(), postPage.getSize(), postPage.getTotal());
|
||||||
|
if (CollUtil.isEmpty(postList)) {
|
||||||
|
return postVOPage;
|
||||||
|
}
|
||||||
|
// 1. 关联查询用户信息
|
||||||
|
Set<Long> userIdSet = postList.stream().map(Post::getUserId).collect(Collectors.toSet());
|
||||||
|
Map<Long, List<User>> userIdUserListMap = userService.listByIds(userIdSet).stream()
|
||||||
|
.collect(Collectors.groupingBy(User::getId));
|
||||||
|
// 2. 已登录,获取用户点赞、收藏状态
|
||||||
|
Map<Long, Boolean> postIdHasThumbMap = new HashMap<>();
|
||||||
|
Map<Long, Boolean> postIdHasFavourMap = new HashMap<>();
|
||||||
|
User loginUser = userService.getLoginUserPermitNull(request);
|
||||||
|
if (loginUser != null) {
|
||||||
|
Set<Long> postIdSet = postList.stream().map(Post::getId).collect(Collectors.toSet());
|
||||||
|
loginUser = userService.getLoginUser(request);
|
||||||
|
// 获取点赞
|
||||||
|
QueryWrapper<PostThumb> postThumbQueryWrapper = new QueryWrapper<>();
|
||||||
|
postThumbQueryWrapper.in("postId", postIdSet);
|
||||||
|
postThumbQueryWrapper.eq("userId", loginUser.getId());
|
||||||
|
List<PostThumb> postPostThumbList = postThumbMapper.selectList(postThumbQueryWrapper);
|
||||||
|
postPostThumbList.forEach(postPostThumb -> postIdHasThumbMap.put(postPostThumb.getPostId(), true));
|
||||||
|
// 获取收藏
|
||||||
|
QueryWrapper<PostFavour> postFavourQueryWrapper = new QueryWrapper<>();
|
||||||
|
postFavourQueryWrapper.in("postId", postIdSet);
|
||||||
|
postFavourQueryWrapper.eq("userId", loginUser.getId());
|
||||||
|
List<PostFavour> postFavourList = postFavourMapper.selectList(postFavourQueryWrapper);
|
||||||
|
postFavourList.forEach(postFavour -> postIdHasFavourMap.put(postFavour.getPostId(), true));
|
||||||
|
}
|
||||||
|
// 填充信息
|
||||||
|
List<PostVO> postVOList = postList.stream().map(post -> {
|
||||||
|
PostVO postVO = PostVO.objToVo(post);
|
||||||
|
Long userId = post.getUserId();
|
||||||
|
User user = null;
|
||||||
|
if (userIdUserListMap.containsKey(userId)) {
|
||||||
|
user = userIdUserListMap.get(userId).get(0);
|
||||||
|
}
|
||||||
|
postVO.setUser(userService.getUserVO(user));
|
||||||
|
postVO.setHasThumb(postIdHasThumbMap.getOrDefault(post.getId(), false));
|
||||||
|
postVO.setHasFavour(postIdHasFavourMap.getOrDefault(post.getId(), false));
|
||||||
|
return postVO;
|
||||||
|
}).collect(Collectors.toList());
|
||||||
|
postVOPage.setRecords(postVOList);
|
||||||
|
return postVOPage;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,105 @@
|
|||||||
|
package com.yupi.springbootinit.service.impl;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||||
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
|
import com.yupi.springbootinit.common.ErrorCode;
|
||||||
|
import com.yupi.springbootinit.exception.BusinessException;
|
||||||
|
import com.yupi.springbootinit.mapper.PostThumbMapper;
|
||||||
|
import com.yupi.springbootinit.model.entity.Post;
|
||||||
|
import com.yupi.springbootinit.model.entity.PostThumb;
|
||||||
|
import com.yupi.springbootinit.model.entity.User;
|
||||||
|
import com.yupi.springbootinit.service.PostService;
|
||||||
|
import com.yupi.springbootinit.service.PostThumbService;
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import org.springframework.aop.framework.AopContext;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 帖子点赞服务实现
|
||||||
|
*
|
||||||
|
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
|
||||||
|
* @from <a href="https://yupi.icu">编程导航知识星球</a>
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
public class PostThumbServiceImpl extends ServiceImpl<PostThumbMapper, PostThumb>
|
||||||
|
implements PostThumbService {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private PostService postService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 点赞
|
||||||
|
*
|
||||||
|
* @param postId
|
||||||
|
* @param loginUser
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public int doPostThumb(long postId, User loginUser) {
|
||||||
|
// 判断实体是否存在,根据类别获取实体
|
||||||
|
Post post = postService.getById(postId);
|
||||||
|
if (post == null) {
|
||||||
|
throw new BusinessException(ErrorCode.NOT_FOUND_ERROR);
|
||||||
|
}
|
||||||
|
// 是否已点赞
|
||||||
|
long userId = loginUser.getId();
|
||||||
|
// 每个用户串行点赞
|
||||||
|
// 锁必须要包裹住事务方法
|
||||||
|
PostThumbService postThumbService = (PostThumbService) AopContext.currentProxy();
|
||||||
|
synchronized (String.valueOf(userId).intern()) {
|
||||||
|
return postThumbService.doPostThumbInner(userId, postId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 封装了事务的方法
|
||||||
|
*
|
||||||
|
* @param userId
|
||||||
|
* @param postId
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public int doPostThumbInner(long userId, long postId) {
|
||||||
|
PostThumb postThumb = new PostThumb();
|
||||||
|
postThumb.setUserId(userId);
|
||||||
|
postThumb.setPostId(postId);
|
||||||
|
QueryWrapper<PostThumb> thumbQueryWrapper = new QueryWrapper<>(postThumb);
|
||||||
|
PostThumb oldPostThumb = this.getOne(thumbQueryWrapper);
|
||||||
|
boolean result;
|
||||||
|
// 已点赞
|
||||||
|
if (oldPostThumb != null) {
|
||||||
|
result = this.remove(thumbQueryWrapper);
|
||||||
|
if (result) {
|
||||||
|
// 点赞数 - 1
|
||||||
|
result = postService.update()
|
||||||
|
.eq("id", postId)
|
||||||
|
.gt("thumbNum", 0)
|
||||||
|
.setSql("thumbNum = thumbNum - 1")
|
||||||
|
.update();
|
||||||
|
return result ? -1 : 0;
|
||||||
|
} else {
|
||||||
|
throw new BusinessException(ErrorCode.SYSTEM_ERROR);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 未点赞
|
||||||
|
result = this.save(postThumb);
|
||||||
|
if (result) {
|
||||||
|
// 点赞数 + 1
|
||||||
|
result = postService.update()
|
||||||
|
.eq("id", postId)
|
||||||
|
.setSql("thumbNum = thumbNum + 1")
|
||||||
|
.update();
|
||||||
|
return result ? 1 : 0;
|
||||||
|
} else {
|
||||||
|
throw new BusinessException(ErrorCode.SYSTEM_ERROR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,240 @@
|
|||||||
|
package com.yupi.springbootinit.service.impl;
|
||||||
|
|
||||||
|
import static com.yupi.springbootinit.constant.UserConstant.USER_LOGIN_STATE;
|
||||||
|
|
||||||
|
import cn.hutool.core.collection.CollUtil;
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||||
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
|
import com.yupi.springbootinit.common.ErrorCode;
|
||||||
|
import com.yupi.springbootinit.constant.CommonConstant;
|
||||||
|
import com.yupi.springbootinit.exception.BusinessException;
|
||||||
|
import com.yupi.springbootinit.mapper.UserMapper;
|
||||||
|
import com.yupi.springbootinit.model.dto.user.UserQueryRequest;
|
||||||
|
import com.yupi.springbootinit.model.entity.User;
|
||||||
|
import com.yupi.springbootinit.model.enums.UserRoleEnum;
|
||||||
|
import com.yupi.springbootinit.model.vo.LoginUserVO;
|
||||||
|
import com.yupi.springbootinit.model.vo.UserVO;
|
||||||
|
import com.yupi.springbootinit.service.UserService;
|
||||||
|
import com.yupi.springbootinit.utils.SqlUtils;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import me.chanjar.weixin.common.bean.WxOAuth2UserInfo;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.springframework.beans.BeanUtils;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.util.DigestUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户服务实现
|
||||||
|
*
|
||||||
|
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
|
||||||
|
* @from <a href="https://yupi.icu">编程导航知识星球</a>
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
@Slf4j
|
||||||
|
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 盐值,混淆密码
|
||||||
|
*/
|
||||||
|
public static final String SALT = "yupi";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long userRegister(String userAccount, String userPassword, String checkPassword) {
|
||||||
|
// 1. 校验
|
||||||
|
if (StringUtils.isAnyBlank(userAccount, userPassword, checkPassword)) {
|
||||||
|
throw new BusinessException(ErrorCode.PARAMS_ERROR, "参数为空");
|
||||||
|
}
|
||||||
|
if (userAccount.length() < 4) {
|
||||||
|
throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户账号过短");
|
||||||
|
}
|
||||||
|
if (userPassword.length() < 8 || checkPassword.length() < 8) {
|
||||||
|
throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户密码过短");
|
||||||
|
}
|
||||||
|
// 密码和校验密码相同
|
||||||
|
if (!userPassword.equals(checkPassword)) {
|
||||||
|
throw new BusinessException(ErrorCode.PARAMS_ERROR, "两次输入的密码不一致");
|
||||||
|
}
|
||||||
|
synchronized (userAccount.intern()) {
|
||||||
|
// 账户不能重复
|
||||||
|
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
|
||||||
|
queryWrapper.eq("userAccount", userAccount);
|
||||||
|
long count = this.baseMapper.selectCount(queryWrapper);
|
||||||
|
if (count > 0) {
|
||||||
|
throw new BusinessException(ErrorCode.PARAMS_ERROR, "账号重复");
|
||||||
|
}
|
||||||
|
// 2. 加密
|
||||||
|
String encryptPassword = DigestUtils.md5DigestAsHex((SALT + userPassword).getBytes());
|
||||||
|
// 3. 插入数据
|
||||||
|
User user = new User();
|
||||||
|
user.setUserAccount(userAccount);
|
||||||
|
user.setUserPassword(encryptPassword);
|
||||||
|
boolean saveResult = this.save(user);
|
||||||
|
if (!saveResult) {
|
||||||
|
throw new BusinessException(ErrorCode.SYSTEM_ERROR, "注册失败,数据库错误");
|
||||||
|
}
|
||||||
|
return user.getId();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LoginUserVO userLogin(String userAccount, String userPassword, HttpServletRequest request) {
|
||||||
|
// 1. 校验
|
||||||
|
if (StringUtils.isAnyBlank(userAccount, userPassword)) {
|
||||||
|
throw new BusinessException(ErrorCode.PARAMS_ERROR, "参数为空");
|
||||||
|
}
|
||||||
|
if (userAccount.length() < 4) {
|
||||||
|
throw new BusinessException(ErrorCode.PARAMS_ERROR, "账号错误");
|
||||||
|
}
|
||||||
|
if (userPassword.length() < 8) {
|
||||||
|
throw new BusinessException(ErrorCode.PARAMS_ERROR, "密码错误");
|
||||||
|
}
|
||||||
|
// 2. 加密
|
||||||
|
String encryptPassword = DigestUtils.md5DigestAsHex((SALT + userPassword).getBytes());
|
||||||
|
// 查询用户是否存在
|
||||||
|
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
|
||||||
|
queryWrapper.eq("userAccount", userAccount);
|
||||||
|
queryWrapper.eq("userPassword", encryptPassword);
|
||||||
|
User user = this.baseMapper.selectOne(queryWrapper);
|
||||||
|
// 用户不存在
|
||||||
|
if (user == null) {
|
||||||
|
log.info("user login failed, userAccount cannot match userPassword");
|
||||||
|
throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户不存在或密码错误");
|
||||||
|
}
|
||||||
|
// 3. 记录用户的登录态
|
||||||
|
request.getSession().setAttribute(USER_LOGIN_STATE, user);
|
||||||
|
return this.getLoginUserVO(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前登录用户
|
||||||
|
*
|
||||||
|
* @param request
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public User getLoginUser(HttpServletRequest request) {
|
||||||
|
// 先判断是否已登录
|
||||||
|
Object userObj = request.getSession().getAttribute(USER_LOGIN_STATE);
|
||||||
|
User currentUser = (User) userObj;
|
||||||
|
if (currentUser == null || currentUser.getId() == null) {
|
||||||
|
throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR);
|
||||||
|
}
|
||||||
|
// 从数据库查询(追求性能的话可以注释,直接走缓存)
|
||||||
|
long userId = currentUser.getId();
|
||||||
|
currentUser = this.getById(userId);
|
||||||
|
if (currentUser == null) {
|
||||||
|
throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR);
|
||||||
|
}
|
||||||
|
return currentUser;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前登录用户(允许未登录)
|
||||||
|
*
|
||||||
|
* @param request
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public User getLoginUserPermitNull(HttpServletRequest request) {
|
||||||
|
// 先判断是否已登录
|
||||||
|
Object userObj = request.getSession().getAttribute(USER_LOGIN_STATE);
|
||||||
|
User currentUser = (User) userObj;
|
||||||
|
if (currentUser == null || currentUser.getId() == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// 从数据库查询(追求性能的话可以注释,直接走缓存)
|
||||||
|
long userId = currentUser.getId();
|
||||||
|
return this.getById(userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否为管理员
|
||||||
|
*
|
||||||
|
* @param request
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean isAdmin(HttpServletRequest request) {
|
||||||
|
// 仅管理员可查询
|
||||||
|
Object userObj = request.getSession().getAttribute(USER_LOGIN_STATE);
|
||||||
|
User user = (User) userObj;
|
||||||
|
return isAdmin(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAdmin(User user) {
|
||||||
|
return user != null && UserRoleEnum.ADMIN.getValue().equals(user.getUserRole());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户注销
|
||||||
|
*
|
||||||
|
* @param request
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean userLogout(HttpServletRequest request) {
|
||||||
|
if (request.getSession().getAttribute(USER_LOGIN_STATE) == null) {
|
||||||
|
throw new BusinessException(ErrorCode.OPERATION_ERROR, "未登录");
|
||||||
|
}
|
||||||
|
// 移除登录态
|
||||||
|
request.getSession().removeAttribute(USER_LOGIN_STATE);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LoginUserVO getLoginUserVO(User user) {
|
||||||
|
if (user == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
LoginUserVO loginUserVO = new LoginUserVO();
|
||||||
|
BeanUtils.copyProperties(user, loginUserVO);
|
||||||
|
return loginUserVO;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UserVO getUserVO(User user) {
|
||||||
|
if (user == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
UserVO userVO = new UserVO();
|
||||||
|
BeanUtils.copyProperties(user, userVO);
|
||||||
|
return userVO;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<UserVO> getUserVO(List<User> userList) {
|
||||||
|
if (CollUtil.isEmpty(userList)) {
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
return userList.stream().map(this::getUserVO).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public QueryWrapper<User> getQueryWrapper(UserQueryRequest userQueryRequest) {
|
||||||
|
if (userQueryRequest == null) {
|
||||||
|
throw new BusinessException(ErrorCode.PARAMS_ERROR, "请求参数为空");
|
||||||
|
}
|
||||||
|
Long id = userQueryRequest.getId();
|
||||||
|
String unionId = userQueryRequest.getUnionId();
|
||||||
|
String mpOpenId = userQueryRequest.getMpOpenId();
|
||||||
|
String userName = userQueryRequest.getUserName();
|
||||||
|
String userProfile = userQueryRequest.getUserProfile();
|
||||||
|
String userRole = userQueryRequest.getUserRole();
|
||||||
|
String sortField = userQueryRequest.getSortField();
|
||||||
|
String sortOrder = userQueryRequest.getSortOrder();
|
||||||
|
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
|
||||||
|
queryWrapper.eq(id != null, "id", id);
|
||||||
|
queryWrapper.eq(StringUtils.isNotBlank(unionId), "unionId", unionId);
|
||||||
|
queryWrapper.eq(StringUtils.isNotBlank(mpOpenId), "mpOpenId", mpOpenId);
|
||||||
|
queryWrapper.eq(StringUtils.isNotBlank(userRole), "userRole", userRole);
|
||||||
|
queryWrapper.like(StringUtils.isNotBlank(userProfile), "userProfile", userProfile);
|
||||||
|
queryWrapper.like(StringUtils.isNotBlank(userName), "userName", userName);
|
||||||
|
queryWrapper.orderBy(SqlUtils.validSortField(sortField), sortOrder.equals(CommonConstant.SORT_ORDER_ASC),
|
||||||
|
sortField);
|
||||||
|
return queryWrapper;
|
||||||
|
}
|
||||||
|
}
|
71
src/main/java/com/yupi/springbootinit/utils/ExcelUtils.java
Normal file
71
src/main/java/com/yupi/springbootinit/utils/ExcelUtils.java
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
package com.yupi.springbootinit.utils;
|
||||||
|
import cn.hutool.core.collection.CollUtil;
|
||||||
|
import com.alibaba.excel.EasyExcel;
|
||||||
|
import com.alibaba.excel.support.ExcelTypeEnum;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.commons.lang3.ObjectUtils;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.springframework.util.ResourceUtils;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Excel 相关工具类
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public class ExcelUtils {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* excel 转 csv
|
||||||
|
*
|
||||||
|
* @param multipartFile
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static String excelToCsv(MultipartFile multipartFile) {
|
||||||
|
// File file = null;
|
||||||
|
// try {
|
||||||
|
// file = ResourceUtils.getFile("classpath:网站数据.xlsx");
|
||||||
|
// } catch (FileNotFoundException e) {
|
||||||
|
// e.printStackTrace();
|
||||||
|
// }
|
||||||
|
// 读取数据
|
||||||
|
List<Map<Integer, String>> list = null;
|
||||||
|
try {
|
||||||
|
list = EasyExcel.read(multipartFile.getInputStream())
|
||||||
|
.excelType(ExcelTypeEnum.XLSX)
|
||||||
|
.sheet()
|
||||||
|
.headRowNumber(0)
|
||||||
|
.doReadSync();
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error("表格处理错误", e);
|
||||||
|
}
|
||||||
|
if (CollUtil.isEmpty(list)) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
// 转换为 csv
|
||||||
|
StringBuilder stringBuilder = new StringBuilder();
|
||||||
|
// 读取表头
|
||||||
|
LinkedHashMap<Integer, String> headerMap = (LinkedHashMap) list.get(0);
|
||||||
|
List<String> headerList = headerMap.values().stream().filter(ObjectUtils::isNotEmpty).collect(Collectors.toList());
|
||||||
|
stringBuilder.append(StringUtils.join(headerList, ",")).append("\n");
|
||||||
|
// 读取数据
|
||||||
|
for (int i = 1; i < list.size(); i++) {
|
||||||
|
LinkedHashMap<Integer, String> dataMap = (LinkedHashMap) list.get(i);
|
||||||
|
List<String> dataList = dataMap.values().stream().filter(ObjectUtils::isNotEmpty).collect(Collectors.toList());
|
||||||
|
stringBuilder.append(StringUtils.join(dataList, ",")).append("\n");
|
||||||
|
}
|
||||||
|
return stringBuilder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
excelToCsv(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
55
src/main/java/com/yupi/springbootinit/utils/NetUtils.java
Normal file
55
src/main/java/com/yupi/springbootinit/utils/NetUtils.java
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
package com.yupi.springbootinit.utils;
|
||||||
|
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 网络工具类
|
||||||
|
*
|
||||||
|
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
|
||||||
|
* @from <a href="https://yupi.icu">编程导航知识星球</a>
|
||||||
|
*/
|
||||||
|
public class NetUtils {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取客户端 IP 地址
|
||||||
|
*
|
||||||
|
* @param request
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static String getIpAddress(HttpServletRequest request) {
|
||||||
|
String ip = request.getHeader("x-forwarded-for");
|
||||||
|
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
||||||
|
ip = request.getHeader("Proxy-Client-IP");
|
||||||
|
}
|
||||||
|
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
||||||
|
ip = request.getHeader("WL-Proxy-Client-IP");
|
||||||
|
}
|
||||||
|
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
||||||
|
ip = request.getRemoteAddr();
|
||||||
|
if (ip.equals("127.0.0.1")) {
|
||||||
|
// 根据网卡取本机配置的 IP
|
||||||
|
InetAddress inet = null;
|
||||||
|
try {
|
||||||
|
inet = InetAddress.getLocalHost();
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
if (inet != null) {
|
||||||
|
ip = inet.getHostAddress();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割
|
||||||
|
if (ip != null && ip.length() > 15) {
|
||||||
|
if (ip.indexOf(",") > 0) {
|
||||||
|
ip = ip.substring(0, ip.indexOf(","));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ip == null) {
|
||||||
|
return "127.0.0.1";
|
||||||
|
}
|
||||||
|
return ip;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,57 @@
|
|||||||
|
package com.yupi.springbootinit.utils;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.springframework.beans.BeansException;
|
||||||
|
import org.springframework.context.ApplicationContext;
|
||||||
|
import org.springframework.context.ApplicationContextAware;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Spring 上下文获取工具
|
||||||
|
*
|
||||||
|
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
|
||||||
|
* @from <a href="https://yupi.icu">编程导航知识星球</a>
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class SpringContextUtils implements ApplicationContextAware {
|
||||||
|
|
||||||
|
private static ApplicationContext applicationContext;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setApplicationContext(@NotNull ApplicationContext applicationContext) throws BeansException {
|
||||||
|
SpringContextUtils.applicationContext = applicationContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过名称获取 Bean
|
||||||
|
*
|
||||||
|
* @param beanName
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static Object getBean(String beanName) {
|
||||||
|
return applicationContext.getBean(beanName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过 class 获取 Bean
|
||||||
|
*
|
||||||
|
* @param beanClass
|
||||||
|
* @param <T>
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static <T> T getBean(Class<T> beanClass) {
|
||||||
|
return applicationContext.getBean(beanClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过名称和类型获取 Bean
|
||||||
|
*
|
||||||
|
* @param beanName
|
||||||
|
* @param beanClass
|
||||||
|
* @param <T>
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static <T> T getBean(String beanName, Class<T> beanClass) {
|
||||||
|
return applicationContext.getBean(beanName, beanClass);
|
||||||
|
}
|
||||||
|
}
|
25
src/main/java/com/yupi/springbootinit/utils/SqlUtils.java
Normal file
25
src/main/java/com/yupi/springbootinit/utils/SqlUtils.java
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
package com.yupi.springbootinit.utils;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SQL 工具
|
||||||
|
*
|
||||||
|
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
|
||||||
|
* @from <a href="https://yupi.icu">编程导航知识星球</a>
|
||||||
|
*/
|
||||||
|
public class SqlUtils {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验排序字段是否合法(防止 SQL 注入)
|
||||||
|
*
|
||||||
|
* @param sortField
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static boolean validSortField(String sortField) {
|
||||||
|
if (StringUtils.isBlank(sortField)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return !StringUtils.containsAny(sortField, "=", "(", ")", " ");
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,52 @@
|
|||||||
|
{
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"name": "cos.client.accessKey",
|
||||||
|
"type": "java.lang.String",
|
||||||
|
"description": "Description for cos.client.accessKey."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "cos.client.secretKey",
|
||||||
|
"type": "java.lang.String",
|
||||||
|
"description": "Description for cos.client.secretKey."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "cos.client.region",
|
||||||
|
"type": "java.lang.String",
|
||||||
|
"description": "Description for cos.client.region."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "cos.client.bucket",
|
||||||
|
"type": "java.lang.String",
|
||||||
|
"description": "Description for cos.client.bucket."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "wx.open.appId",
|
||||||
|
"type": "java.lang.String",
|
||||||
|
"description": "Description for wx.open.appId."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "wx.open.appSecret",
|
||||||
|
"type": "java.lang.String",
|
||||||
|
"description": "Description for wx.open.appSecret."
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "xun-fei.client.appId",
|
||||||
|
"type": "java.lang.String"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "xun-fei.client.apiSecret",
|
||||||
|
"type": "java.lang.String"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "xun-fei.client.apiKey",
|
||||||
|
"type": "java.lang.String"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
]
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user