├── doc └── swagger.png ├── src ├── main │ ├── resources │ │ ├── test_excel.xlsx │ │ ├── _.leikooo.com.jks │ │ ├── application-dev.properties.enc │ │ ├── banner.txt │ │ ├── mapper │ │ │ ├── PostThumbMapper.xml │ │ │ ├── UserMapper.xml │ │ │ ├── PostFavourMapper.xml │ │ │ ├── PostMapper.xml │ │ │ └── ChartMapper.xml │ │ ├── META-INF │ │ │ └── additional-spring-configuration-metadata.json │ │ └── application.yml │ └── java │ │ └── com │ │ └── leikooo │ │ └── yubi │ │ ├── constant │ │ ├── ChartConstant.java │ │ ├── FileConstant.java │ │ ├── CommonConstant.java │ │ ├── BIMQConstant.java │ │ └── UserConstant.java │ │ ├── mapper │ │ ├── UserMapper.java │ │ ├── PostThumbMapper.java │ │ ├── PostMapper.java │ │ ├── ChartMapper.java │ │ └── PostFavourMapper.java │ │ ├── bizmq │ │ ├── MyMessageProducer.java │ │ ├── BIMessageProducer.java │ │ ├── MyMessageConsumer.java │ │ ├── MqInitMain.java │ │ ├── BIMqInitMain.java │ │ └── BIMessageConsumer.java │ │ ├── common │ │ ├── DeleteRequest.java │ │ ├── PageRequest.java │ │ ├── BaseResponse.java │ │ ├── ErrorCode.java │ │ └── ResultUtils.java │ │ ├── model │ │ ├── enums │ │ │ ├── ResultEnum.java │ │ │ ├── FileUploadBizEnum.java │ │ │ └── UserRoleEnum.java │ │ ├── dto │ │ │ ├── file │ │ │ │ └── UploadFileRequest.java │ │ │ ├── postthumb │ │ │ │ └── PostThumbAddRequest.java │ │ │ ├── postfavour │ │ │ │ ├── PostFavourAddRequest.java │ │ │ │ └── PostFavourQueryRequest.java │ │ │ ├── user │ │ │ │ ├── UserLoginRequest.java │ │ │ │ ├── UserRegisterRequest.java │ │ │ │ ├── UserUpdateMyRequest.java │ │ │ │ ├── UserAddRequest.java │ │ │ │ ├── UserUpdateRequest.java │ │ │ │ └── UserQueryRequest.java │ │ │ ├── chart │ │ │ │ ├── ChartCsvDataRequest.java │ │ │ │ ├── ChartRetryRequest.java │ │ │ │ ├── ChartGenRequest.java │ │ │ │ ├── ChartGenResult.java │ │ │ │ ├── ChartUpdateRequest.java │ │ │ │ └── ChartQueryRequest.java │ │ │ ├── post │ │ │ │ ├── PostAddRequest.java │ │ │ │ ├── PostEditRequest.java │ │ │ │ ├── PostUpdateRequest.java │ │ │ │ ├── PostQueryRequest.java │ │ │ │ └── PostEsDTO.java │ │ │ └── controller │ │ │ │ ├── ChartRetryController.java │ │ │ │ ├── ChartGenController.java │ │ │ │ └── ChartQueryController.java │ │ ├── user │ │ │ ├── UserLoginRequest.java │ │ │ ├── UserRegisterRequest.java │ │ │ ├── UserUpdateMyRequest.java │ │ │ ├── UserAddRequest.java │ │ │ ├── UserUpdateRequest.java │ │ │ └── UserQueryRequest.java │ │ ├── vo │ │ │ ├── UserVO.java │ │ │ ├── BiResponse.java │ │ │ ├── LoginUserVO.java │ │ │ ├── ChartVO.java │ │ │ └── PostVO.java │ │ └── entity │ │ │ ├── PostThumb.java │ │ │ ├── PostFavour.java │ │ │ ├── User.java │ │ │ ├── Post.java │ │ │ └── Chart.java │ │ ├── esdao │ │ └── PostEsDao.java │ │ ├── annotation │ │ └── AuthCheck.java │ │ ├── utils │ │ ├── SqlUtils.java │ │ ├── SpringContextUtils.java │ │ ├── NetUtils.java │ │ ├── ExcelUtils.java │ │ └── ChartDataUtil.java │ │ ├── config │ │ ├── RabbitMQClientConfig.java │ │ ├── BaiLianConfig.java │ │ ├── MySqlConfig.java │ │ ├── XingHuoConfig.java │ │ ├── ThreadPoolExecutorConfig.java │ │ ├── MyBatisPlusConfig.java │ │ ├── CorsConfig.java │ │ ├── JsonConfig.java │ │ ├── RedissonConfig.java │ │ ├── CosClientConfig.java │ │ ├── Knife4jConfig.java │ │ ├── WxOpenConfig.java │ │ └── RabbitMQConfig.java │ │ ├── service │ │ ├── PostThumbService.java │ │ ├── PostFavourService.java │ │ ├── PostService.java │ │ ├── ChartService.java │ │ ├── UserService.java │ │ └── impl │ │ │ ├── PostThumbServiceImpl.java │ │ │ └── PostFavourServiceImpl.java │ │ ├── MainApplication.java │ │ ├── exception │ │ ├── BusinessException.java │ │ ├── GlobalExceptionHandler.java │ │ └── ThrowUtils.java │ │ ├── mq │ │ ├── SingleProducer.java │ │ ├── MultiProducer.java │ │ ├── FanoutProducer.java │ │ ├── SingleConsumer.java │ │ ├── TTLProduct.java │ │ ├── DirectProducer.java │ │ ├── TopicProducer.java │ │ ├── TTLConsumer.java │ │ ├── MultiConsumer.java │ │ ├── DirectConsumer.java │ │ ├── FanoutConsumer.java │ │ ├── DLXDirectConsumer.java │ │ ├── TopicConsumer.java │ │ └── DLXDirectProducer.java │ │ ├── manager │ │ ├── RedisLimiterManager.java │ │ ├── CosManager.java │ │ └── AIManager.java │ │ ├── job │ │ ├── once │ │ │ └── FullSyncPostToEs.java │ │ └── cycle │ │ │ ├── IncSyncPostToEs.java │ │ │ └── ReGenChartData.java │ │ ├── controller │ │ ├── QueueController.java │ │ ├── PostThumbController.java │ │ ├── FileController.java │ │ └── PostFavourController.java │ │ └── aop │ │ ├── LogInterceptor.java │ │ └── AuthInterceptor.java └── test │ └── java │ └── com │ └── leikoo │ └── yubi │ └── TestJava.java ├── Dockerfile ├── .mvn └── wrapper │ └── maven-wrapper.properties ├── docker-compose.yml ├── README.md ├── sql ├── post_es_mapping.json └── create_table.sql ├── .github └── workflows │ └── github-actions-demo.yml ├── .gitignore └── mvnw.cmd /doc/swagger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lieeew/yubi-backend/HEAD/doc/swagger.png -------------------------------------------------------------------------------- /src/main/resources/test_excel.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lieeew/yubi-backend/HEAD/src/main/resources/test_excel.xlsx -------------------------------------------------------------------------------- /src/main/resources/_.leikooo.com.jks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lieeew/yubi-backend/HEAD/src/main/resources/_.leikooo.com.jks -------------------------------------------------------------------------------- /src/main/resources/application-dev.properties.enc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lieeew/yubi-backend/HEAD/src/main/resources/application-dev.properties.enc -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM openjdk:17 2 | 3 | WORKDIR /bi 4 | 5 | COPY ./target/yubi-backend-0.0.1-SNAPSHOT.jar /bi/your-application.jar 6 | 7 | EXPOSE 9090 8 | 9 | CMD ["java", "-jar", "your-application.jar"] 10 | #CMD ["echo", "Docker Image created success!"] 11 | -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.1/apache-maven-3.8.1-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar 3 | -------------------------------------------------------------------------------- /src/main/resources/banner.txt: -------------------------------------------------------------------------------- 1 | ,--. ,--.,--. 2 | | |,---. `--'| |,-. ,---. ,---. ,---. 3 | | | .-. :,--.| /| .-. | .-. | .-. | 4 | | \ --.| || \ \' '-' ' '-' ' '-' ' 5 | `--'`----'`--'`--'`--'`---' `---' `---' 6 | 7 | by leikooo:https://github.com/lieeew 8 | 可能是最好的编程学习圈子:https://yupi.icu 9 | -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/constant/ChartConstant.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.constant; 2 | 3 | /** 4 | * chart 状态常量 5 | * @author leikooo 6 | */ 7 | public interface ChartConstant { 8 | /** 9 | * csv 路径 10 | */ 11 | String CHART_CVS_PATH = "src/main/resources/chartCSV/"; 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/constant/FileConstant.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.constant; 2 | 3 | /** 4 | * 文件常量 5 | * 6 | * @author 程序员鱼皮 7 | * @from 编程导航知识星球 8 | */ 9 | public interface FileConstant { 10 | 11 | /** 12 | * COS 访问地址 13 | * todo 需替换配置 14 | */ 15 | String COS_HOST = "https://yupi.icu"; 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/mapper/UserMapper.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.mapper; 2 | 3 | import com.leikooo.yubi.model.entity.User; 4 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 5 | 6 | /** 7 | * @author liang 8 | * @description 针对表【user(用户)】的数据库操作Mapper 9 | * @createDate 2023-11-06 18:40:34 10 | * @Entity generator.domain.User 11 | */ 12 | public interface UserMapper extends BaseMapper { 13 | 14 | } 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/mapper/PostThumbMapper.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.mapper; 2 | 3 | import com.leikooo.yubi.model.entity.PostThumb; 4 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 5 | 6 | /** 7 | * 帖子点赞数据库操作 8 | * 9 | * @author 程序员鱼皮 10 | * @from 编程导航知识星球 11 | */ 12 | public interface PostThumbMapper extends BaseMapper { 13 | 14 | } 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/constant/CommonConstant.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.constant; 2 | 3 | /** 4 | * 通用常量 5 | * 6 | * @author 程序员鱼皮 7 | * @from 编程导航知识星球 8 | */ 9 | public interface CommonConstant { 10 | 11 | /** 12 | * 升序 13 | */ 14 | String SORT_ORDER_ASC = "ascend"; 15 | 16 | /** 17 | * 降序 18 | */ 19 | String SORT_ORDER_DESC = " descend"; 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/bizmq/MyMessageProducer.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.bizmq; 2 | 3 | import org.springframework.amqp.rabbit.core.RabbitTemplate; 4 | 5 | import javax.annotation.Resource; 6 | 7 | 8 | public class MyMessageProducer { 9 | 10 | @Resource 11 | private RabbitTemplate rabbitTemplate; 12 | 13 | public void sendMessage(String exchange, String routingKey, String message) { 14 | rabbitTemplate.convertAndSend(exchange, routingKey, message); 15 | } 16 | 17 | } -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/common/DeleteRequest.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.common; 2 | 3 | import java.io.Serializable; 4 | import lombok.Data; 5 | 6 | /** 7 | * 删除请求 8 | * 9 | * @author 程序员鱼皮 10 | * @from 编程导航知识星球 11 | */ 12 | @Data 13 | public class DeleteRequest implements Serializable { 14 | 15 | /** 16 | * id 17 | */ 18 | private Long id; 19 | 20 | private static final long serialVersionUID = 1L; 21 | } -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/model/enums/ResultEnum.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.model.enums; 2 | 3 | import lombok.Getter; 4 | 5 | /** 6 | * @author leikooo 7 | * @date 2024/4/12 8 | * @description 9 | */ 10 | @Getter 11 | public enum ResultEnum { 12 | WAIT("wait"), 13 | RUNNING("running"), 14 | SUCCEED("succeed"), 15 | FAILED("failed"); 16 | 17 | private final String des; 18 | 19 | ResultEnum(String des) { 20 | this.des = des; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/model/dto/file/UploadFileRequest.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.model.dto.file; 2 | 3 | import java.io.Serializable; 4 | import lombok.Data; 5 | 6 | /** 7 | * 文件上传请求 8 | * 9 | * @author 程序员鱼皮 10 | * @from 编程导航知识星球 11 | */ 12 | @Data 13 | public class UploadFileRequest implements Serializable { 14 | 15 | /** 16 | * 业务 17 | */ 18 | private String biz; 19 | 20 | private static final long serialVersionUID = 1L; 21 | } -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/esdao/PostEsDao.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.esdao; 2 | 3 | import com.leikooo.yubi.model.dto.post.PostEsDTO; 4 | import java.util.List; 5 | import org.springframework.data.elasticsearch.repository.ElasticsearchRepository; 6 | 7 | /** 8 | * 帖子 ES 操作 9 | * 10 | * @author 程序员鱼皮 11 | * @from 编程导航知识星球 12 | */ 13 | public interface PostEsDao extends ElasticsearchRepository { 14 | 15 | List findByUserId(Long userId); 16 | } -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/model/dto/postthumb/PostThumbAddRequest.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.model.dto.postthumb; 2 | 3 | import java.io.Serializable; 4 | import lombok.Data; 5 | 6 | /** 7 | * 帖子点赞请求 8 | * 9 | * @author 程序员鱼皮 10 | * @from 编程导航知识星球 11 | */ 12 | @Data 13 | public class PostThumbAddRequest implements Serializable { 14 | 15 | /** 16 | * 帖子 id 17 | */ 18 | private Long postId; 19 | 20 | private static final long serialVersionUID = 1L; 21 | } -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/model/dto/postfavour/PostFavourAddRequest.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.model.dto.postfavour; 2 | 3 | import java.io.Serializable; 4 | import lombok.Data; 5 | 6 | /** 7 | * 帖子收藏 / 取消收藏请求 8 | * 9 | * @author 程序员鱼皮 10 | * @from 编程导航知识星球 11 | */ 12 | @Data 13 | public class PostFavourAddRequest implements Serializable { 14 | 15 | /** 16 | * 帖子 id 17 | */ 18 | private Long postId; 19 | 20 | private static final long serialVersionUID = 1L; 21 | } -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/model/user/UserLoginRequest.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.model.user; 2 | 3 | import lombok.Data; 4 | 5 | import java.io.Serializable; 6 | 7 | /** 8 | * 用户登录请求 9 | * 10 | * @author 程序员鱼皮 11 | * @from 编程导航知识星球 12 | */ 13 | @Data 14 | public class UserLoginRequest implements Serializable { 15 | 16 | private static final long serialVersionUID = 3191241716373120793L; 17 | 18 | private String userAccount; 19 | 20 | private String userPassword; 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/model/dto/user/UserLoginRequest.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.model.dto.user; 2 | 3 | import lombok.Data; 4 | 5 | import java.io.Serializable; 6 | 7 | /** 8 | * 用户登录请求 9 | * 10 | * @author 程序员鱼皮 11 | * @from 编程导航知识星球 12 | */ 13 | @Data 14 | public class UserLoginRequest implements Serializable { 15 | 16 | private static final long serialVersionUID = 3191241716373120793L; 17 | 18 | private String userAccount; 19 | 20 | private String userPassword; 21 | } 22 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | services: 3 | nacos: 4 | image: nacos/nacos-server:v2.4.2 5 | ports: 6 | - "8848:8848" 7 | - "9848:9848" 8 | - "9555:9555" 9 | volumes: 10 | - ./docker/application.properties:/home/nacos/conf/application.properties 11 | environment: 12 | - NACOS_SERVERS=standalone 13 | - SPRING_DATASOURCE_PLATFORM=mysql 14 | - MYSQL_SERVICE_HOST=1.117.228.86 15 | - MYSQL_SERVICE_PORT=3306 16 | - MYSQL_SERVICE_DB_NAME=nacos 17 | - MYSQL_SERVICE_USER=nacos 18 | - MYSQL_SERVICE_PASSWORD=nacos 19 | -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/model/dto/chart/ChartCsvDataRequest.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.model.dto.chart; 2 | 3 | import lombok.Getter; 4 | import lombok.NoArgsConstructor; 5 | import lombok.Setter; 6 | 7 | /** 8 | * @author leikooo 9 | * @Description 10 | */ 11 | @Getter 12 | @NoArgsConstructor 13 | @Setter 14 | public class ChartCsvDataRequest { 15 | /** 16 | * 图标的 ID 17 | */ 18 | private String chartId; 19 | 20 | public ChartCsvDataRequest(String chartId) { 21 | this.chartId = chartId; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/model/user/UserRegisterRequest.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.model.user; 2 | 3 | import lombok.Data; 4 | 5 | import java.io.Serializable; 6 | 7 | /** 8 | * 用户注册请求体 9 | * 10 | * @author 程序员鱼皮 11 | * @from 编程导航知识星球 12 | */ 13 | @Data 14 | public class UserRegisterRequest implements Serializable { 15 | 16 | private static final long serialVersionUID = 3191241716373120793L; 17 | 18 | private String userAccount; 19 | 20 | private String userPassword; 21 | 22 | private String checkPassword; 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/mapper/PostMapper.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.mapper; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import com.leikooo.yubi.model.entity.Post; 5 | import java.util.Date; 6 | import java.util.List; 7 | 8 | /** 9 | * 帖子数据库操作 10 | * 11 | * @author 程序员鱼皮 12 | * @from 编程导航知识星球 13 | */ 14 | public interface PostMapper extends BaseMapper { 15 | 16 | /** 17 | * 查询帖子列表(包括已被删除的数据) 18 | */ 19 | List listPostWithDelete(Date minUpdateTime); 20 | 21 | } 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/model/dto/user/UserRegisterRequest.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.model.dto.user; 2 | 3 | import lombok.Data; 4 | 5 | import java.io.Serializable; 6 | 7 | /** 8 | * 用户注册请求体 9 | * 10 | * @author 程序员鱼皮 11 | * @from 编程导航知识星球 12 | */ 13 | @Data 14 | public class UserRegisterRequest implements Serializable { 15 | 16 | private static final long serialVersionUID = 3191241716373120793L; 17 | 18 | private String userAccount; 19 | 20 | private String userPassword; 21 | 22 | private String checkPassword; 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/constant/BIMQConstant.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.constant; 2 | 3 | /** 4 | * @author leikooo 5 | * @Description 6 | */ 7 | public interface BIMQConstant { 8 | 9 | String BI_EXCHANGE_NAME = "bi_exchange"; 10 | 11 | String BI_QUEUE_NAME = "bi_queue"; 12 | 13 | String BI_ROUTING_KEY = "bi_routing_key"; 14 | 15 | String BI_DLX_QUEUE_NAME = "bi_dlx_queue"; 16 | 17 | String BI_DLX_ROUTING_KEY = "bi_dlx_routing_key"; 18 | 19 | String BI_DLX_EXCHANGE_NAME = "bi_dlx_exchange"; 20 | 21 | // 限制同时生成的图表数量 22 | int MAX_CONCURRENT_CHARTS = 5; 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/annotation/AuthCheck.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * 权限校验 10 | * 11 | * @author 程序员鱼皮 12 | * @from 编程导航知识星球 13 | */ 14 | @Target(ElementType.METHOD) 15 | @Retention(RetentionPolicy.RUNTIME) 16 | public @interface AuthCheck { 17 | 18 | /** 19 | * 必须有某个角色 20 | * 21 | * @return 22 | */ 23 | String mustRole() default ""; 24 | 25 | } 26 | 27 | -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/bizmq/BIMessageProducer.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.bizmq; 2 | 3 | import org.springframework.amqp.rabbit.core.RabbitTemplate; 4 | import org.springframework.stereotype.Component; 5 | 6 | import javax.annotation.Resource; 7 | 8 | /** 9 | * @author leikooo 10 | * @Description BI 消息生产者 11 | */ 12 | @Component 13 | public class BIMessageProducer { 14 | 15 | @Resource 16 | private RabbitTemplate rabbitTemplate; 17 | 18 | public void sendMessage(String exchange, String routingKey, String message) { 19 | rabbitTemplate.convertAndSend(exchange, routingKey, message); 20 | } 21 | 22 | } -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/constant/UserConstant.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.constant; 2 | 3 | /** 4 | * 用户常量 5 | * 6 | * @author 程序员鱼皮 7 | * @from 编程导航知识星球 8 | */ 9 | public interface UserConstant { 10 | 11 | /** 12 | * 用户登录态键 13 | */ 14 | String USER_LOGIN_STATE = "user_login"; 15 | 16 | // region 权限 17 | 18 | /** 19 | * 默认角色 20 | */ 21 | String DEFAULT_ROLE = "user"; 22 | 23 | /** 24 | * 管理员角色 25 | */ 26 | String ADMIN_ROLE = "admin"; 27 | 28 | /** 29 | * 被封号 30 | */ 31 | String BAN_ROLE = "ban"; 32 | 33 | // endregion 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/utils/SqlUtils.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.utils; 2 | 3 | import org.apache.commons.lang3.StringUtils; 4 | 5 | /** 6 | * SQL 工具 7 | * 8 | * @author 程序员鱼皮 9 | * @from 编程导航知识星球 10 | */ 11 | public class SqlUtils { 12 | 13 | /** 14 | * 校验排序字段是否合法(防止 SQL 注入) 15 | * 16 | * @param sortField 17 | * @return 18 | */ 19 | public static boolean validSortField(String sortField) { 20 | if (StringUtils.isBlank(sortField)) { 21 | return false; 22 | } 23 | return !StringUtils.containsAny(sortField, "=", "(", ")", " "); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/model/user/UserUpdateMyRequest.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.model.user; 2 | 3 | import lombok.Data; 4 | 5 | import java.io.Serializable; 6 | 7 | /** 8 | * 用户更新个人信息请求 9 | * 10 | * @author 程序员鱼皮 11 | * @from 编程导航知识星球 12 | */ 13 | @Data 14 | public class UserUpdateMyRequest implements Serializable { 15 | 16 | /** 17 | * 用户昵称 18 | */ 19 | private String userName; 20 | 21 | /** 22 | * 用户头像 23 | */ 24 | private String userAvatar; 25 | 26 | /** 27 | * 简介 28 | */ 29 | private String userProfile; 30 | 31 | private static final long serialVersionUID = 1L; 32 | } -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/model/dto/post/PostAddRequest.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.model.dto.post; 2 | 3 | import java.io.Serializable; 4 | import java.util.List; 5 | import lombok.Data; 6 | 7 | /** 8 | * 创建请求 9 | * 10 | * @author 程序员鱼皮 11 | * @from 编程导航知识星球 12 | */ 13 | @Data 14 | public class PostAddRequest implements Serializable { 15 | 16 | /** 17 | * 标题 18 | */ 19 | private String title; 20 | 21 | /** 22 | * 内容 23 | */ 24 | private String content; 25 | 26 | /** 27 | * 标签列表 28 | */ 29 | private List tags; 30 | 31 | private static final long serialVersionUID = 1L; 32 | } -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/model/dto/user/UserUpdateMyRequest.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.model.dto.user; 2 | 3 | import lombok.Data; 4 | 5 | import java.io.Serializable; 6 | 7 | /** 8 | * 用户更新个人信息请求 9 | * 10 | * @author 程序员鱼皮 11 | * @from 编程导航知识星球 12 | */ 13 | @Data 14 | public class UserUpdateMyRequest implements Serializable { 15 | 16 | /** 17 | * 用户昵称 18 | */ 19 | private String userName; 20 | 21 | /** 22 | * 用户头像 23 | */ 24 | private String userAvatar; 25 | 26 | /** 27 | * 简介 28 | */ 29 | private String userProfile; 30 | 31 | private static final long serialVersionUID = 1L; 32 | } -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/config/RabbitMQClientConfig.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.config; 2 | 3 | import com.rabbitmq.client.ConnectionFactory; 4 | import lombok.Data; 5 | import org.springframework.boot.context.properties.ConfigurationProperties; 6 | import org.springframework.context.annotation.Bean; 7 | import org.springframework.context.annotation.Configuration; 8 | 9 | /** 10 | * @author leikooo 11 | * @Description 12 | */ 13 | @Configuration 14 | @ConfigurationProperties(prefix = "spring.rabbitmq") 15 | @Data 16 | public class RabbitMQClientConfig { 17 | 18 | private String host; 19 | 20 | private Integer port; 21 | 22 | private String username; 23 | 24 | private String password; 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/model/dto/chart/ChartRetryRequest.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.model.dto.chart; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | import lombok.NoArgsConstructor; 6 | import lombok.Setter; 7 | 8 | import java.io.Serializable; 9 | 10 | /** 11 | * 用户创建请求 12 | * 13 | * @author 程序员鱼皮 14 | * @from 编程导航知识星球 15 | */ 16 | @Getter 17 | @Setter 18 | @NoArgsConstructor 19 | @AllArgsConstructor 20 | public class ChartRetryRequest implements Serializable { 21 | 22 | private static final long serialVersionUID = -4015423666971233788L; 23 | /** 24 | * 图标的 ID 25 | */ 26 | private Long id; 27 | 28 | } -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/common/PageRequest.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.common; 2 | 3 | import com.leikooo.yubi.constant.CommonConstant; 4 | import lombok.Data; 5 | 6 | /** 7 | * 分页请求 8 | * 9 | * @author 程序员鱼皮 10 | * @from 编程导航知识星球 11 | */ 12 | @Data 13 | public class PageRequest { 14 | 15 | /** 16 | * 当前页号 17 | */ 18 | private long current = 1; 19 | 20 | /** 21 | * 页面大小 22 | */ 23 | private long pageSize = 10; 24 | 25 | /** 26 | * 排序字段 27 | */ 28 | private String sortField; 29 | 30 | /** 31 | * 排序顺序(默认升序) 32 | */ 33 | private String sortOrder = CommonConstant.SORT_ORDER_ASC; 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/model/dto/post/PostEditRequest.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.model.dto.post; 2 | 3 | import java.io.Serializable; 4 | import java.util.List; 5 | import lombok.Data; 6 | 7 | /** 8 | * 编辑请求 9 | * 10 | * @author 程序员鱼皮 11 | * @from 编程导航知识星球 12 | */ 13 | @Data 14 | public class PostEditRequest implements Serializable { 15 | 16 | /** 17 | * id 18 | */ 19 | private Long id; 20 | 21 | /** 22 | * 标题 23 | */ 24 | private String title; 25 | 26 | /** 27 | * 内容 28 | */ 29 | private String content; 30 | 31 | /** 32 | * 标签列表 33 | */ 34 | private List tags; 35 | 36 | private static final long serialVersionUID = 1L; 37 | } -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/model/dto/post/PostUpdateRequest.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.model.dto.post; 2 | 3 | import java.io.Serializable; 4 | import java.util.List; 5 | import lombok.Data; 6 | 7 | /** 8 | * 更新请求 9 | * 10 | * @author 程序员鱼皮 11 | * @from 编程导航知识星球 12 | */ 13 | @Data 14 | public class PostUpdateRequest implements Serializable { 15 | 16 | /** 17 | * id 18 | */ 19 | private Long id; 20 | 21 | /** 22 | * 标题 23 | */ 24 | private String title; 25 | 26 | /** 27 | * 内容 28 | */ 29 | private String content; 30 | 31 | /** 32 | * 标签列表 33 | */ 34 | private List tags; 35 | 36 | private static final long serialVersionUID = 1L; 37 | } -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/model/user/UserAddRequest.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.model.user; 2 | 3 | import lombok.Data; 4 | 5 | import java.io.Serializable; 6 | 7 | /** 8 | * 用户创建请求 9 | * 10 | * @author 程序员鱼皮 11 | * @from 编程导航知识星球 12 | */ 13 | @Data 14 | public class UserAddRequest implements Serializable { 15 | 16 | /** 17 | * 用户昵称 18 | */ 19 | private String userName; 20 | 21 | /** 22 | * 账号 23 | */ 24 | private String userAccount; 25 | 26 | /** 27 | * 用户头像 28 | */ 29 | private String userAvatar; 30 | 31 | /** 32 | * 用户角色: user, admin 33 | */ 34 | private String userRole; 35 | 36 | private static final long serialVersionUID = 1L; 37 | } -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/model/dto/user/UserAddRequest.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.model.dto.user; 2 | 3 | import lombok.Data; 4 | 5 | import java.io.Serializable; 6 | 7 | /** 8 | * 用户创建请求 9 | * 10 | * @author 程序员鱼皮 11 | * @from 编程导航知识星球 12 | */ 13 | @Data 14 | public class UserAddRequest implements Serializable { 15 | 16 | /** 17 | * 用户昵称 18 | */ 19 | private String userName; 20 | 21 | /** 22 | * 账号 23 | */ 24 | private String userAccount; 25 | 26 | /** 27 | * 用户头像 28 | */ 29 | private String userAvatar; 30 | 31 | /** 32 | * 用户角色: user, admin 33 | */ 34 | private String userRole; 35 | 36 | private static final long serialVersionUID = 1L; 37 | } -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/bizmq/MyMessageConsumer.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.bizmq; 2 | 3 | import com.rabbitmq.client.Channel; 4 | import lombok.SneakyThrows; 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.springframework.amqp.rabbit.annotation.RabbitListener; 7 | import org.springframework.amqp.support.AmqpHeaders; 8 | import org.springframework.messaging.handler.annotation.Header; 9 | 10 | 11 | @Slf4j 12 | public class MyMessageConsumer { 13 | 14 | // 指定程序监听的消息队列和确认机制 15 | @SneakyThrows 16 | @RabbitListener(queues = {"code_queue"}, ackMode = "MANUAL") 17 | public void receiveMessage(String message, Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long deliveryTag) { 18 | log.info("receiveMessage message = {}", message); 19 | channel.basicAck(deliveryTag, false); 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/model/user/UserUpdateRequest.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.model.user; 2 | 3 | import lombok.Data; 4 | 5 | import java.io.Serializable; 6 | 7 | /** 8 | * 用户更新请求 9 | * 10 | * @author 程序员鱼皮 11 | * @from 编程导航知识星球 12 | */ 13 | @Data 14 | public class UserUpdateRequest implements Serializable { 15 | /** 16 | * id 17 | */ 18 | private Long id; 19 | 20 | /** 21 | * 用户昵称 22 | */ 23 | private String userName; 24 | 25 | /** 26 | * 用户头像 27 | */ 28 | private String userAvatar; 29 | 30 | /** 31 | * 简介 32 | */ 33 | private String userProfile; 34 | 35 | /** 36 | * 用户角色:user/admin/ban 37 | */ 38 | private String userRole; 39 | 40 | private static final long serialVersionUID = 1L; 41 | } -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/service/PostThumbService.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.service; 2 | 3 | import com.leikooo.yubi.model.entity.PostThumb; 4 | import com.baomidou.mybatisplus.extension.service.IService; 5 | import com.leikooo.yubi.model.entity.User; 6 | 7 | /** 8 | * 帖子点赞服务 9 | * 10 | * @author 程序员鱼皮 11 | * @from 编程导航知识星球 12 | */ 13 | public interface PostThumbService extends IService { 14 | 15 | /** 16 | * 点赞 17 | * 18 | * @param postId 19 | * @param loginUser 20 | * @return 21 | */ 22 | int doPostThumb(long postId, User loginUser); 23 | 24 | /** 25 | * 帖子点赞(内部服务) 26 | * 27 | * @param userId 28 | * @param postId 29 | * @return 30 | */ 31 | int doPostThumbInner(long userId, long postId); 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/model/dto/postfavour/PostFavourQueryRequest.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.model.dto.postfavour; 2 | 3 | import com.leikooo.yubi.common.PageRequest; 4 | import com.leikooo.yubi.model.dto.post.PostQueryRequest; 5 | import java.io.Serializable; 6 | import lombok.Data; 7 | import lombok.EqualsAndHashCode; 8 | 9 | /** 10 | * 帖子收藏查询请求 11 | * 12 | * @author 程序员鱼皮 13 | * @from 编程导航知识星球 14 | */ 15 | @Data 16 | @EqualsAndHashCode(callSuper = true) 17 | public class PostFavourQueryRequest extends PageRequest implements Serializable { 18 | 19 | /** 20 | * 帖子查询请求 21 | */ 22 | private PostQueryRequest postQueryRequest; 23 | 24 | /** 25 | * 用户 id 26 | */ 27 | private Long userId; 28 | 29 | private static final long serialVersionUID = 1L; 30 | } -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/model/dto/user/UserUpdateRequest.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.model.dto.user; 2 | 3 | import lombok.Data; 4 | 5 | import java.io.Serializable; 6 | 7 | /** 8 | * 用户更新请求 9 | * 10 | * @author 程序员鱼皮 11 | * @from 编程导航知识星球 12 | */ 13 | @Data 14 | public class UserUpdateRequest implements Serializable { 15 | /** 16 | * id 17 | */ 18 | private Long id; 19 | 20 | /** 21 | * 用户昵称 22 | */ 23 | private String userName; 24 | 25 | /** 26 | * 用户头像 27 | */ 28 | private String userAvatar; 29 | 30 | /** 31 | * 简介 32 | */ 33 | private String userProfile; 34 | 35 | /** 36 | * 用户角色:user/admin/ban 37 | */ 38 | private String userRole; 39 | 40 | private static final long serialVersionUID = 1L; 41 | } -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/config/BaiLianConfig.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.config; 2 | 3 | import com.aliyun.broadscope.bailian.sdk.AccessTokenClient; 4 | import lombok.Data; 5 | import org.springframework.boot.context.properties.ConfigurationProperties; 6 | import org.springframework.context.annotation.Bean; 7 | import org.springframework.context.annotation.Configuration; 8 | 9 | /** 10 | * @author leikooo 11 | * @Description 12 | */ 13 | @Configuration 14 | @ConfigurationProperties(prefix = "bai-lian") 15 | @Data 16 | public class BaiLianConfig { 17 | private String agentKey; 18 | private String accessKeySecret; 19 | private String accessKeyId; 20 | private AccessTokenClient accessTokenClient; 21 | @Bean 22 | public AccessTokenClient accessTokenClient() { 23 | return new AccessTokenClient(accessKeyId, accessKeySecret, agentKey); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/config/MySqlConfig.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.config; 2 | 3 | import lombok.Data; 4 | import org.springframework.boot.context.properties.ConfigurationProperties; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | import java.sql.Connection; 8 | import java.sql.DriverManager; 9 | import java.sql.SQLException; 10 | 11 | /** 12 | * @author leikooo 13 | * @Description 14 | */ 15 | @Configuration 16 | @ConfigurationProperties(prefix = "spring.datasource") 17 | @Data 18 | public class MySqlConfig { 19 | 20 | private String url; 21 | 22 | private String username; 23 | 24 | private String password; 25 | 26 | @Bean 27 | public Connection connection() throws SQLException { 28 | return DriverManager.getConnection(url, username, password); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/model/dto/chart/ChartGenRequest.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.model.dto.chart; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | import lombok.NoArgsConstructor; 6 | import lombok.Setter; 7 | import java.io.Serializable; 8 | 9 | /** 10 | * 用户创建请求 11 | * 12 | * @author 程序员鱼皮 13 | * @from 编程导航知识星球 14 | */ 15 | @Getter 16 | @Setter 17 | @NoArgsConstructor 18 | @AllArgsConstructor 19 | public class ChartGenRequest implements Serializable { 20 | 21 | /** 22 | * 分析目标 23 | */ 24 | private String goal; 25 | 26 | /** 27 | * 图标名称 28 | */ 29 | private String chartName; 30 | 31 | /** 32 | * 图表类型 33 | */ 34 | private String chartType; 35 | 36 | private static final long serialVersionUID = 8986342078781702839L; 37 | } -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/model/dto/controller/ChartRetryController.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.model.dto.controller; 2 | 3 | import com.leikooo.yubi.model.entity.User; 4 | import io.swagger.models.auth.In; 5 | import lombok.AllArgsConstructor; 6 | import lombok.Getter; 7 | import lombok.NoArgsConstructor; 8 | 9 | import java.io.Serializable; 10 | 11 | /** 12 | * @author leikooo 13 | * @Description 14 | */ 15 | @Getter 16 | @NoArgsConstructor 17 | @AllArgsConstructor 18 | public class ChartRetryController implements Serializable { 19 | 20 | 21 | private static final long serialVersionUID = 2645307609377346713L; 22 | /** 23 | * chartId 24 | */ 25 | private Long chartId; 26 | 27 | /** 28 | * 登录的用户 29 | */ 30 | private User loginUser; 31 | 32 | public Long getLoginUserId() { 33 | return loginUser.getId(); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/MainApplication.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi; 2 | 3 | import org.mybatis.spring.annotation.MapperScan; 4 | import org.springframework.boot.SpringApplication; 5 | import org.springframework.boot.autoconfigure.SpringBootApplication; 6 | import org.springframework.context.annotation.EnableAspectJAutoProxy; 7 | import org.springframework.scheduling.annotation.EnableScheduling; 8 | 9 | /** 10 | * 主类(项目启动入口) 11 | * 12 | * @author 程序员鱼皮 13 | * @from 编程导航知识星球 14 | */ 15 | @SpringBootApplication 16 | @MapperScan("com.leikooo.yubi.mapper") 17 | @EnableScheduling 18 | @EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true) 19 | public class MainApplication { 20 | 21 | public static void main(String[] args) { 22 | SpringApplication.run(MainApplication.class, args); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/common/BaseResponse.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.common; 2 | 3 | import java.io.Serializable; 4 | import lombok.Data; 5 | 6 | /** 7 | * 通用返回类 8 | * 9 | * @param 10 | * @author 程序员鱼皮 11 | * @from 编程导航知识星球 12 | */ 13 | @Data 14 | public class BaseResponse implements Serializable { 15 | 16 | private int code; 17 | 18 | private T data; 19 | 20 | private String message; 21 | 22 | public BaseResponse(int code, T data, String message) { 23 | this.code = code; 24 | this.data = data; 25 | this.message = message; 26 | } 27 | 28 | public BaseResponse(int code, T data) { 29 | this(code, data, ""); 30 | } 31 | 32 | public BaseResponse(ErrorCode errorCode) { 33 | this(errorCode.getCode(), null, errorCode.getMessage()); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/model/dto/chart/ChartGenResult.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.model.dto.chart; 2 | 3 | import com.leikooo.yubi.common.ErrorCode; 4 | import com.leikooo.yubi.exception.ThrowUtils; 5 | import lombok.Getter; 6 | import lombok.NoArgsConstructor; 7 | import org.apache.commons.lang3.StringUtils; 8 | 9 | /** 10 | * @author leikooo 11 | * @Description 12 | */ 13 | @Getter 14 | @NoArgsConstructor 15 | public class ChartGenResult { 16 | /** 17 | * AI 生成的图标数据 18 | */ 19 | private String genChart; 20 | 21 | /** 22 | * AI 生成的分析结果 23 | */ 24 | private String genResult; 25 | 26 | public ChartGenResult(String genChart, String genResult) { 27 | ThrowUtils.throwIf(StringUtils.isAnyBlank(genChart, genResult), ErrorCode.PARAMS_ERROR); 28 | this.genChart = genChart; 29 | this.genResult = genResult; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/config/XingHuoConfig.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.config; 2 | 3 | import io.github.briqt.spark4j.SparkClient; 4 | import lombok.Data; 5 | import org.springframework.boot.context.properties.ConfigurationProperties; 6 | import org.springframework.context.annotation.Bean; 7 | import org.springframework.context.annotation.Configuration; 8 | 9 | /** 10 | * @author leikooo 11 | * @Description 12 | */ 13 | @Configuration 14 | @ConfigurationProperties(prefix = "xun-fei.client") 15 | @Data 16 | public class XingHuoConfig { 17 | private String appId; 18 | private String apiSecret; 19 | private String apiKey; 20 | 21 | @Bean 22 | public SparkClient sparkClient() { 23 | SparkClient sparkClient = new SparkClient(); 24 | sparkClient.apiKey = apiKey; 25 | sparkClient.apiSecret = apiSecret; 26 | sparkClient.appid = appId; 27 | return sparkClient; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/model/dto/chart/ChartUpdateRequest.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.model.dto.chart; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | import lombok.NoArgsConstructor; 6 | import java.io.Serializable; 7 | 8 | /** 9 | * 用户更新请求 10 | * 11 | * @author 程序员鱼皮 12 | * @from 编程导航知识星球 13 | */ 14 | @Getter 15 | @NoArgsConstructor 16 | @AllArgsConstructor 17 | public class ChartUpdateRequest implements Serializable { 18 | 19 | /** 20 | * 分析目标 21 | */ 22 | private String goal; 23 | 24 | /** 25 | * 图表数据 26 | */ 27 | private String chartData; 28 | 29 | /** 30 | * 图表类型 31 | */ 32 | private String chartType; 33 | 34 | 35 | /** 36 | * 创建用户 id 37 | */ 38 | private Long userId; 39 | 40 | 41 | private static final long serialVersionUID = 3527462006741457835L; 42 | } -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/model/vo/UserVO.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.model.vo; 2 | 3 | import java.io.Serializable; 4 | import java.util.Date; 5 | import lombok.Data; 6 | 7 | /** 8 | * 用户视图(脱敏) 9 | * 10 | * @author 程序员鱼皮 11 | * @from 编程导航知识星球 12 | */ 13 | @Data 14 | public class UserVO implements Serializable { 15 | 16 | /** 17 | * id 18 | */ 19 | private Long id; 20 | 21 | /** 22 | * 用户昵称 23 | */ 24 | private String userName; 25 | 26 | /** 27 | * 用户头像 28 | */ 29 | private String userAvatar; 30 | 31 | /** 32 | * 用户简介 33 | */ 34 | private String userProfile; 35 | 36 | /** 37 | * 用户角色:user/admin/ban 38 | */ 39 | private String userRole; 40 | 41 | /** 42 | * 创建时间 43 | */ 44 | private Date createTime; 45 | 46 | private static final long serialVersionUID = 1L; 47 | } -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/exception/BusinessException.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.exception; 2 | 3 | import com.leikooo.yubi.common.ErrorCode; 4 | 5 | /** 6 | * 自定义异常类 7 | * 8 | * @author 程序员鱼皮 9 | * @from 编程导航知识星球 10 | */ 11 | public class BusinessException extends RuntimeException { 12 | 13 | /** 14 | * 错误码 15 | */ 16 | private final int code; 17 | 18 | public BusinessException(int code, String message) { 19 | super(message); 20 | this.code = code; 21 | } 22 | 23 | public BusinessException(ErrorCode errorCode) { 24 | super(errorCode.getMessage()); 25 | this.code = errorCode.getCode(); 26 | } 27 | 28 | public BusinessException(ErrorCode errorCode, String message) { 29 | super(message); 30 | this.code = errorCode.getCode(); 31 | } 32 | 33 | public int getCode() { 34 | return code; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/model/dto/controller/ChartGenController.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.model.dto.controller; 2 | 3 | import com.leikooo.yubi.model.entity.User; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Getter; 6 | import lombok.NoArgsConstructor; 7 | import java.io.Serializable; 8 | 9 | /** 10 | * @author leikooo 11 | * @Description 12 | */ 13 | @Getter 14 | @NoArgsConstructor 15 | @AllArgsConstructor 16 | public class ChartGenController implements Serializable { 17 | 18 | private static final long serialVersionUID = 847541708929254846L; 19 | 20 | /** 21 | * 图标名称 22 | */ 23 | private String chartName; 24 | 25 | /** 26 | * 分析目标 27 | */ 28 | private String goal; 29 | 30 | /** 31 | * 图表类型 32 | */ 33 | private String chartType; 34 | 35 | /** 36 | * 登录的用户 37 | */ 38 | private User loginUser; 39 | 40 | public Long getLoginUserId() { 41 | return loginUser.getId(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/mapper/ChartMapper.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.mapper; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import com.leikooo.yubi.model.entity.Chart; 5 | 6 | import java.util.List; 7 | import java.util.Map; 8 | 9 | /** 10 | * @author liang 11 | * @description 针对表【chart(图表信息表)】的数据库操作Mapper 12 | * @createDate 2023-11-06 18:40:34 13 | * @Entity generator.domain.Chart 14 | */ 15 | public interface ChartMapper extends BaseMapper { 16 | /** 17 | * 动态的创建数据库 18 | * @param creatTableSQL 19 | */ 20 | void createTable(final String creatTableSQL); 21 | 22 | /** 23 | * 向动态创建的数据库之中插入数据 24 | * 25 | * @param insertCVSData 26 | * @return 27 | */ 28 | void insertValue(final String insertCVSData); 29 | 30 | /** 31 | * 查询保存数据表的信息 32 | * 33 | * @param tableName 34 | * @return 35 | */ 36 | List> queryChartData(final Long tableName); 37 | } 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /src/test/java/com/leikoo/yubi/TestJava.java: -------------------------------------------------------------------------------- 1 | package com.leikoo.yubi; 2 | 3 | import com.leikooo.yubi.MainApplication; 4 | import com.leikooo.yubi.manager.AIManager; 5 | import org.junit.jupiter.api.Test; 6 | import org.springframework.boot.test.context.SpringBootTest; 7 | 8 | import javax.annotation.Resource; 9 | 10 | /** 11 | * @author leikooo 12 | * @date 2024/9/12 13 | * @description 14 | */ 15 | @SpringBootTest(classes = MainApplication.class) 16 | public class TestJava { 17 | @Resource 18 | private AIManager ai2Manager; 19 | 20 | 21 | @Test 22 | public void test() { 23 | String c = "分析需求:\n" + 24 | "分析网站用户的增长情况 \n" + 25 | "请使用 柱状图 \n" + 26 | "原始数据:\n" + 27 | "日期,用户数\n" + 28 | "1号,10\n" + 29 | " 2号,20\n" + 30 | " 3号,30"; 31 | String s = ai2Manager.sendMsgToXingHuo(true, c); 32 | System.out.println("s = " + s); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/mq/SingleProducer.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.mq; 2 | 3 | import com.rabbitmq.client.Channel; 4 | import com.rabbitmq.client.Connection; 5 | import com.rabbitmq.client.ConnectionFactory; 6 | 7 | import java.nio.charset.StandardCharsets; 8 | 9 | /** 10 | * @author leikooo 11 | * 生产者 12 | */ 13 | public class SingleProducer { 14 | 15 | private final static String QUEUE_NAME = "hello"; 16 | 17 | public static void main(String[] argv) throws Exception { 18 | ConnectionFactory factory = new ConnectionFactory(); 19 | try (Connection connection = factory.newConnection(); 20 | Channel channel = connection.createChannel()) { 21 | channel.queueDeclare(QUEUE_NAME, false, false, false, null); 22 | String message = "编程真好玩!"; 23 | channel.basicPublish("", QUEUE_NAME, null, message.getBytes(StandardCharsets.UTF_8)); 24 | System.out.println(" [x] Sent '" + message + "'"); 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/bizmq/MqInitMain.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.bizmq; 2 | 3 | import com.rabbitmq.client.Channel; 4 | import com.rabbitmq.client.Connection; 5 | import com.rabbitmq.client.ConnectionFactory; 6 | 7 | /** 8 | * 用于创建测试程序用到的交换机和队列(只用在程序启动前执行一次) 9 | */ 10 | public class MqInitMain { 11 | public static void main(String[] args) { 12 | try { 13 | ConnectionFactory factory = new ConnectionFactory(); 14 | 15 | Connection connection = factory.newConnection(); 16 | Channel channel = connection.createChannel(); 17 | String EXCHANGE_NAME = "code_exchange"; 18 | channel.exchangeDeclare(EXCHANGE_NAME, "direct"); 19 | 20 | // 创建队列,随机分配一个队列名称 21 | String queueName = "code_queue"; 22 | channel.queueDeclare(queueName, true, false, false, null); 23 | channel.queueBind(queueName, EXCHANGE_NAME, "my_routingKey"); 24 | } catch (Exception e) { 25 | e.printStackTrace(); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/model/vo/BiResponse.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.model.vo; 2 | 3 | import com.leikooo.yubi.common.ErrorCode; 4 | import com.leikooo.yubi.exception.ThrowUtils; 5 | import lombok.Getter; 6 | import lombok.NoArgsConstructor; 7 | import org.apache.commons.lang3.StringUtils; 8 | 9 | /** 10 | * @author leikooo 11 | * @Description bi 返回的值 12 | */ 13 | @Getter 14 | @NoArgsConstructor 15 | public class BiResponse { 16 | private Long chartId; 17 | 18 | private String genChart; 19 | 20 | private String genResult; 21 | 22 | /** 23 | * 这里可以校验 AI 生成的内容 24 | */ 25 | public BiResponse(Long chartId, String genChart, String genResult) { 26 | ThrowUtils.throwIf(StringUtils.isAnyBlank(genChart, genResult) || (chartId != null && chartId <= 0), ErrorCode.PARAMS_ERROR); 27 | this.chartId = chartId; 28 | this.genChart = genChart; 29 | this.genResult = genResult; 30 | } 31 | 32 | public BiResponse(Long chartId) { 33 | this.chartId = chartId; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/resources/mapper/PostThumbMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | id,postId, 19 | userId,createTime,updateTime 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/config/ThreadPoolExecutorConfig.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.config; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import java.util.concurrent.ArrayBlockingQueue; 6 | import java.util.concurrent.ThreadPoolExecutor; 7 | import java.util.concurrent.TimeUnit; 8 | 9 | /** 10 | * @author leikooo 11 | * @Description 12 | */ 13 | @Configuration 14 | public class ThreadPoolExecutorConfig { 15 | 16 | @Bean 17 | public ThreadPoolExecutor threadPoolExecutor() { 18 | /* 19 | 1、corePoolSize 这个是最重要的一个参数,是在 「正常情况下」的一个员工数目 20 | 2、maximumPoolSize 这个是最大的员工数目 21 | 3、keepAliveTime 这个是当线程数大于 corePoolSize 的时候,多余的线程空闲多长时间之后会被销毁 22 | 4、unit 这个是 keepAliveTime 的单位 23 | 5、workQueue 这个是用来存放任务的队列,当线程数大于 corePoolSize 的时候,如果队列满了,就会把任务放到队列中,直到队列有空闲的线程 24 | */ 25 | return new ThreadPoolExecutor(2, 4, 60000, TimeUnit.SECONDS, new ArrayBlockingQueue<>(10)); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/model/vo/LoginUserVO.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.model.vo; 2 | 3 | import java.io.Serializable; 4 | import java.util.Date; 5 | import lombok.Data; 6 | 7 | /** 8 | * 已登录用户视图(脱敏) 9 | * 10 | * @author 程序员鱼皮 11 | * @from 编程导航知识星球 12 | **/ 13 | @Data 14 | public class LoginUserVO implements Serializable { 15 | 16 | /** 17 | * 用户 id 18 | */ 19 | private Long id; 20 | 21 | /** 22 | * 用户昵称 23 | */ 24 | private String userName; 25 | 26 | /** 27 | * 用户头像 28 | */ 29 | private String userAvatar; 30 | 31 | /** 32 | * 用户简介 33 | */ 34 | private String userProfile; 35 | 36 | /** 37 | * 用户角色:user/admin/ban 38 | */ 39 | private String userRole; 40 | 41 | /** 42 | * 创建时间 43 | */ 44 | private Date createTime; 45 | 46 | /** 47 | * 更新时间 48 | */ 49 | private Date updateTime; 50 | 51 | private static final long serialVersionUID = 1L; 52 | } -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/config/MyBatisPlusConfig.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.config; 2 | 3 | import com.baomidou.mybatisplus.annotation.DbType; 4 | import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; 5 | import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; 6 | import org.mybatis.spring.annotation.MapperScan; 7 | import org.springframework.context.annotation.Bean; 8 | import org.springframework.context.annotation.Configuration; 9 | 10 | /** 11 | * MyBatis Plus 配置 12 | * 13 | * @author https://github.com/liyupi 14 | */ 15 | @Configuration 16 | @MapperScan("com.yupi.springbootinit.mapper") 17 | public class MyBatisPlusConfig { 18 | 19 | /** 20 | * 拦截器配置 21 | * 22 | * @return 23 | */ 24 | @Bean 25 | public MybatisPlusInterceptor mybatisPlusInterceptor() { 26 | MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); 27 | // 分页插件 28 | interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); 29 | return interceptor; 30 | } 31 | } -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/model/user/UserQueryRequest.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.model.user; 2 | 3 | import com.leikooo.yubi.common.PageRequest; 4 | import lombok.Data; 5 | import lombok.EqualsAndHashCode; 6 | 7 | import java.io.Serializable; 8 | 9 | /** 10 | * 用户查询请求 11 | * 12 | * @author 程序员鱼皮 13 | * @from 编程导航知识星球 14 | */ 15 | @EqualsAndHashCode(callSuper = true) 16 | @Data 17 | public class UserQueryRequest extends PageRequest implements Serializable { 18 | /** 19 | * id 20 | */ 21 | private Long id; 22 | 23 | /** 24 | * 开放平台id 25 | */ 26 | private String unionId; 27 | 28 | /** 29 | * 公众号openId 30 | */ 31 | private String mpOpenId; 32 | 33 | /** 34 | * 用户昵称 35 | */ 36 | private String userName; 37 | 38 | /** 39 | * 简介 40 | */ 41 | private String userProfile; 42 | 43 | /** 44 | * 用户角色:user/admin/ban 45 | */ 46 | private String userRole; 47 | 48 | private static final long serialVersionUID = 1L; 49 | } -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/model/dto/user/UserQueryRequest.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.model.dto.user; 2 | 3 | import com.leikooo.yubi.common.PageRequest; 4 | import lombok.Data; 5 | import lombok.EqualsAndHashCode; 6 | 7 | import java.io.Serializable; 8 | 9 | /** 10 | * 用户查询请求 11 | * 12 | * @author 程序员鱼皮 13 | * @from 编程导航知识星球 14 | */ 15 | @EqualsAndHashCode(callSuper = true) 16 | @Data 17 | public class UserQueryRequest extends PageRequest implements Serializable { 18 | /** 19 | * id 20 | */ 21 | private Long id; 22 | 23 | /** 24 | * 开放平台id 25 | */ 26 | private String unionId; 27 | 28 | /** 29 | * 公众号openId 30 | */ 31 | private String mpOpenId; 32 | 33 | /** 34 | * 用户昵称 35 | */ 36 | private String userName; 37 | 38 | /** 39 | * 简介 40 | */ 41 | private String userProfile; 42 | 43 | /** 44 | * 用户角色:user/admin/ban 45 | */ 46 | private String userRole; 47 | 48 | private static final long serialVersionUID = 1L; 49 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## BI 项目后端 2 | 3 | 4 | 5 | 优化后的 promote 6 | ``` 7 | 请严格按照下面的输出格式生成结果,且不得添加任何多余内容(例如无关文字、注释、代码块标记或反引号): 8 | 9 | '【【【【' { 10 | 生成 Echarts V5 的 option 配置对象 JSON 代码,要求为合法 JSON 格式且不含任何额外内容(如注释或多余字符) } '【【【【' 结论: { 11 | 提供对数据的详细分析结论,内容应尽可能准确、详细,不允许添加其他无关文字或注释 } 12 | 13 | 示例: 输入: 分析需求: 分析网站用户增长情况,请使用柱状图展示 原始数据: 日期,用户数 1号,10 2号,20 3号,30 14 | 15 | 期望输出: '【【【【' { "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人,呈现出明显的上升趋势。这表明在这段时间内网站用户吸引力增强,可能与推广活动、内容更新或其他外部因素有关。 16 | ``` 17 | 18 | 19 | 20 | 21 | 22 | 23 | --- 24 | 25 | 原版本 promote 本地测试不可使用 26 | 27 | ``` 28 | 你是一个数据分析师和前端开发专家,接下来我会按照以下固定格式给你提供内容: 29 | 分析需求: 30 | {数据分析的需求或者目标} 31 | 原始数据: 32 | {csv格式的原始数据,用,作为分隔符} 33 | 请根据这两部分内容,严格按照以下指定格式生成内容(此外不要输出任何多余的开头、结尾、注释)同时不要使用这个符号 '】' 34 | '【【【【【' 35 | {前端 Echarts V5 的 option 配置对象 JSON 代码, 不要生成任何多余的内容,比如注释和代码块标记} 36 | '【【【【【' 37 | {明确的数据分析结论、越详细越好,不要生成多余的注释} 38 | 下面是一个具体的例子的模板: 39 | '【【【【【' 40 | JSON格式代码 41 | '【【【【【' 42 | 结论: 43 | ``` 44 | -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/common/ErrorCode.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.common; 2 | 3 | /** 4 | * 自定义错误码 5 | * 6 | * @author 程序员鱼皮 7 | * @from 编程导航知识星球 8 | */ 9 | public enum ErrorCode { 10 | 11 | SUCCESS(0, "ok"), 12 | PARAMS_ERROR(40000, "请求参数错误"), 13 | NOT_LOGIN_ERROR(40100, "未登录"), 14 | NO_AUTH_ERROR(40101, "无权限"), 15 | NOT_FOUND_ERROR(40400, "请求数据不存在"), 16 | FORBIDDEN_ERROR(40300, "禁止访问"), 17 | SYSTEM_ERROR(50000, "系统内部异常"), 18 | TOO_MANY_REQUESTS_ERROR(42900, "系统内部异常"), 19 | FILE_DOWNLOAD_ERROR(50002, "文件下载失败"), 20 | OPERATION_ERROR(50001, "操作失败"); 21 | 22 | /** 23 | * 状态码 24 | */ 25 | private final int code; 26 | 27 | /** 28 | * 信息 29 | */ 30 | private final String message; 31 | 32 | ErrorCode(int code, String message) { 33 | this.code = code; 34 | this.message = message; 35 | } 36 | 37 | public int getCode() { 38 | return code; 39 | } 40 | 41 | public String getMessage() { 42 | return message; 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/mapper/PostFavourMapper.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.mapper; 2 | 3 | import com.baomidou.mybatisplus.core.conditions.Wrapper; 4 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 5 | import com.baomidou.mybatisplus.core.metadata.IPage; 6 | import com.baomidou.mybatisplus.core.toolkit.Constants; 7 | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; 8 | import com.leikooo.yubi.model.entity.Post; 9 | import com.leikooo.yubi.model.entity.PostFavour; 10 | import org.apache.ibatis.annotations.Param; 11 | 12 | /** 13 | * 帖子收藏数据库操作 14 | * 15 | * @author 程序员鱼皮 16 | * @from 编程导航知识星球 17 | */ 18 | public interface PostFavourMapper extends BaseMapper { 19 | 20 | /** 21 | * 分页查询收藏帖子列表 22 | * 23 | * @param page 24 | * @param queryWrapper 25 | * @param favourUserId 26 | * @return 27 | */ 28 | Page listFavourPostByPage(IPage page, @Param(Constants.WRAPPER) Wrapper queryWrapper, 29 | long favourUserId); 30 | 31 | } 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/mq/MultiProducer.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.mq; 2 | 3 | import com.rabbitmq.client.Channel; 4 | import com.rabbitmq.client.Connection; 5 | import com.rabbitmq.client.ConnectionFactory; 6 | import com.rabbitmq.client.MessageProperties; 7 | 8 | /** 9 | * @author liang 10 | */ 11 | public class MultiProducer { 12 | 13 | private static final String TASK_QUEUE_NAME = "task_queue"; 14 | 15 | public static void main(String[] argv) throws Exception { 16 | ConnectionFactory factory = new ConnectionFactory(); 17 | factory.setHost("localhost"); 18 | try (Connection connection = factory.newConnection(); 19 | Channel channel = connection.createChannel()) { 20 | channel.queueDeclare(TASK_QUEUE_NAME, true, false, false, null); 21 | 22 | String message = String.join(" ", argv); 23 | 24 | channel.basicPublish("", TASK_QUEUE_NAME, 25 | MessageProperties.PERSISTENT_TEXT_PLAIN, 26 | message.getBytes("UTF-8")); 27 | System.out.println(" [x] Sent '" + message + "'"); 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/mq/FanoutProducer.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.mq; 2 | 3 | import com.rabbitmq.client.Channel; 4 | import com.rabbitmq.client.Connection; 5 | import com.rabbitmq.client.ConnectionFactory; 6 | import java.util.Scanner; 7 | 8 | 9 | /** 10 | * @author leikooo 11 | */ 12 | public class FanoutProducer { 13 | 14 | private static final String EXCHANGE_NAME = "fanout-exchange"; 15 | 16 | public static void main(String[] argv) throws Exception { 17 | ConnectionFactory factory = new ConnectionFactory(); 18 | 19 | try (Connection connection = factory.newConnection(); 20 | Channel channel = connection.createChannel()) { 21 | channel.exchangeDeclare(EXCHANGE_NAME, "fanout"); 22 | Scanner sc = new Scanner(System.in); 23 | while (sc.hasNext()) { 24 | String message = sc.nextLine(); 25 | channel.basicPublish(EXCHANGE_NAME, "", null, message.getBytes("UTF-8")); 26 | System.out.println(" [x] Sent '" + message + "'"); 27 | 28 | } 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/config/CorsConfig.java: -------------------------------------------------------------------------------- 1 | //package com.leikooo.yubi.config; 2 | // 3 | //import org.springframework.context.annotation.Configuration; 4 | //import org.springframework.web.servlet.config.annotation.CorsRegistry; 5 | //import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; 6 | // 7 | ///** 8 | // * 全局跨域配置 9 | // * 10 | // * @author 程序员鱼皮 11 | // * @from 编程导航知识星球 12 | // */ 13 | //@Configuration 14 | //public class CorsConfig implements WebMvcConfigurer { 15 | // 16 | // @Override 17 | // public void addCorsMappings(CorsRegistry registry) { 18 | // // 覆盖所有请求 19 | // registry.addMapping("/**") 20 | // // 允许发送 Cookie 21 | // .allowCredentials(true) 22 | // // 放行哪些域名(必须用 patterns,否则 * 会和 allowCredentials 冲突) 23 | // .allowedOriginPatterns("https://bi.leikooo.cn", "https://www.leikooo.cn") 24 | // .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") 25 | // .allowedHeaders("*") 26 | // .exposedHeaders("*"); 27 | // } 28 | //} 29 | -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/exception/GlobalExceptionHandler.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.exception; 2 | 3 | import com.leikooo.yubi.common.BaseResponse; 4 | import com.leikooo.yubi.common.ErrorCode; 5 | import com.leikooo.yubi.common.ResultUtils; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.springframework.web.bind.annotation.ExceptionHandler; 8 | import org.springframework.web.bind.annotation.RestControllerAdvice; 9 | 10 | /** 11 | * 全局异常处理器 12 | * 13 | * @author 程序员鱼皮 14 | * @from 编程导航知识星球 15 | */ 16 | @RestControllerAdvice 17 | @Slf4j 18 | public class GlobalExceptionHandler { 19 | 20 | @ExceptionHandler(BusinessException.class) 21 | public BaseResponse businessExceptionHandler(BusinessException e) { 22 | log.error("BusinessException", e); 23 | return ResultUtils.error(e.getCode(), e.getMessage()); 24 | } 25 | 26 | @ExceptionHandler(RuntimeException.class) 27 | public BaseResponse runtimeExceptionHandler(RuntimeException e) { 28 | log.error("RuntimeException", e); 29 | return ResultUtils.error(ErrorCode.SYSTEM_ERROR, "系统错误"); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /sql/post_es_mapping.json: -------------------------------------------------------------------------------- 1 | { 2 | "aliases": { 3 | "post": {} 4 | }, 5 | "mappings": { 6 | "properties": { 7 | "title": { 8 | "type": "text", 9 | "analyzer": "ik_max_word", 10 | "search_analyzer": "ik_smart", 11 | "fields": { 12 | "keyword": { 13 | "type": "keyword", 14 | "ignore_above": 256 15 | } 16 | } 17 | }, 18 | "content": { 19 | "type": "text", 20 | "analyzer": "ik_max_word", 21 | "search_analyzer": "ik_smart", 22 | "fields": { 23 | "keyword": { 24 | "type": "keyword", 25 | "ignore_above": 256 26 | } 27 | } 28 | }, 29 | "tags": { 30 | "type": "keyword" 31 | }, 32 | "thumbNum": { 33 | "type": "long" 34 | }, 35 | "favourNum": { 36 | "type": "long" 37 | }, 38 | "userId": { 39 | "type": "keyword" 40 | }, 41 | "createTime": { 42 | "type": "date" 43 | }, 44 | "updateTime": { 45 | "type": "date" 46 | }, 47 | "isDelete": { 48 | "type": "keyword" 49 | } 50 | } 51 | } 52 | } -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/config/JsonConfig.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.config; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import com.fasterxml.jackson.databind.module.SimpleModule; 5 | import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; 6 | import org.springframework.boot.jackson.JsonComponent; 7 | import org.springframework.context.annotation.Bean; 8 | import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; 9 | 10 | /** 11 | * Spring MVC Json 配置 12 | * 13 | * @author 程序员鱼皮 14 | * @from 编程导航知识星球 15 | */ 16 | @JsonComponent 17 | public class JsonConfig { 18 | 19 | /** 20 | * 添加 Long 转 json 精度丢失的配置 21 | */ 22 | @Bean 23 | public ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) { 24 | ObjectMapper objectMapper = builder.createXmlMapper(false).build(); 25 | SimpleModule module = new SimpleModule(); 26 | module.addSerializer(Long.class, ToStringSerializer.instance); 27 | module.addSerializer(Long.TYPE, ToStringSerializer.instance); 28 | objectMapper.registerModule(module); 29 | return objectMapper; 30 | } 31 | } -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/config/RedissonConfig.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.config; 2 | 3 | import lombok.Data; 4 | import org.apache.commons.lang3.StringUtils; 5 | import org.redisson.Redisson; 6 | import org.redisson.api.RedissonClient; 7 | import org.redisson.config.Config; 8 | import org.springframework.boot.context.properties.ConfigurationProperties; 9 | import org.springframework.context.annotation.Bean; 10 | import org.springframework.context.annotation.Configuration; 11 | 12 | /** 13 | * @author leikooo 14 | * @Description 15 | */ 16 | @Configuration 17 | @ConfigurationProperties(prefix = "spring.redis") 18 | @Data 19 | public class RedissonConfig { 20 | 21 | private Integer database; 22 | 23 | private String host; 24 | 25 | private String password; 26 | 27 | private String port; 28 | @Bean 29 | public RedissonClient redissonClient() { 30 | Config config = new Config(); 31 | config.useReplicatedServers() 32 | .setDatabase(database) 33 | .setPassword(StringUtils.isEmpty(password) ? null : password) 34 | .addNodeAddress(String.format("redis://%s:%s", host, port)); 35 | return Redisson.create(config); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/exception/ThrowUtils.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.exception; 2 | 3 | import com.leikooo.yubi.common.ErrorCode; 4 | 5 | /** 6 | * 抛异常工具类 7 | * 8 | * @author 程序员鱼皮 9 | * @from 编程导航知识星球 10 | */ 11 | public class ThrowUtils { 12 | 13 | /** 14 | * 条件成立则抛异常 15 | * 16 | * @param condition 17 | * @param runtimeException 18 | */ 19 | public static void throwIf(boolean condition, RuntimeException runtimeException) { 20 | if (condition) { 21 | throw runtimeException; 22 | } 23 | } 24 | 25 | /** 26 | * 条件成立则抛异常 27 | * 28 | * @param condition 29 | * @param errorCode 30 | */ 31 | public static void throwIf(boolean condition, ErrorCode errorCode) { 32 | throwIf(condition, new BusinessException(errorCode)); 33 | } 34 | 35 | /** 36 | * 条件成立则抛异常 37 | * 38 | * @param condition 39 | * @param errorCode 40 | * @param message 41 | */ 42 | public static void throwIf(boolean condition, ErrorCode errorCode, String message) { 43 | throwIf(condition, new BusinessException(errorCode, message)); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/model/dto/chart/ChartQueryRequest.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.model.dto.chart; 2 | 3 | import com.leikooo.yubi.common.PageRequest; 4 | import lombok.AllArgsConstructor; 5 | import lombok.EqualsAndHashCode; 6 | import lombok.Getter; 7 | import lombok.NoArgsConstructor; 8 | import java.io.Serializable; 9 | import java.util.Date; 10 | 11 | /** 12 | * 用户查询请求 13 | * 14 | * @author 程序员鱼皮 15 | * @from 编程导航知识星球 16 | */ 17 | @EqualsAndHashCode(callSuper = true) 18 | @Getter 19 | @NoArgsConstructor 20 | @AllArgsConstructor 21 | public class ChartQueryRequest extends PageRequest implements Serializable { 22 | /** 23 | * id 24 | */ 25 | private Long id; 26 | 27 | /** 28 | * 分析目标 29 | */ 30 | private String goal; 31 | 32 | /** 33 | * 图表名称 34 | */ 35 | private String chartName; 36 | 37 | /** 38 | * 图表类型 39 | */ 40 | private String chartType; 41 | 42 | /** 43 | * 创建时间 44 | */ 45 | private Date createTime; 46 | 47 | /** 48 | * 更新时间 49 | */ 50 | private Date updateTime; 51 | 52 | private static final long serialVersionUID = -4138919418189603203L; 53 | } -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/manager/RedisLimiterManager.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.manager; 2 | 3 | import com.leikooo.yubi.common.ErrorCode; 4 | import com.leikooo.yubi.exception.BusinessException; 5 | import org.redisson.api.RRateLimiter; 6 | import org.redisson.api.RateIntervalUnit; 7 | import org.redisson.api.RateType; 8 | import org.redisson.api.RedissonClient; 9 | import org.springframework.stereotype.Service; 10 | 11 | import javax.annotation.Resource; 12 | 13 | /** 14 | * 专门提供 RedisLimiter 限流基础服务的(提供了通用的能力) 15 | * @author leikooo 16 | */ 17 | @Service 18 | public class RedisLimiterManager { 19 | 20 | @Resource 21 | private RedissonClient redissonClient; 22 | 23 | /** 24 | * 限流操作 25 | * 26 | * @param key 区分不同的限流器,比如不同的用户 id 应该分别统计 27 | */ 28 | public void doRateLimit(String key) { 29 | // 创建一个名称为user_limiter的限流器,每秒最多访问 2 次 30 | RRateLimiter rateLimiter = redissonClient.getRateLimiter(key); 31 | rateLimiter.trySetRate(RateType.OVERALL, 2, 1, RateIntervalUnit.SECONDS); 32 | // 每当一个操作来了后,请求一个令牌 33 | boolean canOp = rateLimiter.tryAcquire(1); 34 | if (!canOp) { 35 | throw new BusinessException(ErrorCode.TOO_MANY_REQUESTS_ERROR); 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/mq/SingleConsumer.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.mq; 2 | 3 | import com.rabbitmq.client.Channel; 4 | import com.rabbitmq.client.Connection; 5 | import com.rabbitmq.client.ConnectionFactory; 6 | import com.rabbitmq.client.DeliverCallback; 7 | import java.nio.charset.StandardCharsets; 8 | 9 | /** 10 | * @author leikooo 11 | * 消费者 12 | */ 13 | public class SingleConsumer { 14 | 15 | private final static String QUEUE_NAME = "hello"; 16 | 17 | public static void main(String[] argv) throws Exception { 18 | ConnectionFactory factory = new ConnectionFactory(); 19 | Connection connection = factory.newConnection(); 20 | Channel channel = connection.createChannel(); 21 | 22 | channel.queueDeclare(QUEUE_NAME, false, false, false, null); 23 | System.out.println(" [*] Waiting for messages. To exit press CTRL+C"); 24 | 25 | DeliverCallback deliverCallback = (consumerTag, delivery) -> { 26 | String message = new String(delivery.getBody(), StandardCharsets.UTF_8); 27 | System.out.println(" [x] Received '" + message + "'"); 28 | }; 29 | channel.basicConsume(QUEUE_NAME, true, deliverCallback, consumerTag -> { }); 30 | } 31 | } -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/common/ResultUtils.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.common; 2 | 3 | /** 4 | * 返回工具类 5 | * 6 | * @author 程序员鱼皮 7 | * @from 编程导航知识星球 8 | */ 9 | public class ResultUtils { 10 | 11 | /** 12 | * 成功 13 | * 14 | * @param data 15 | * @param 16 | * @return 17 | */ 18 | public static BaseResponse success(T data) { 19 | return new BaseResponse<>(0, data, "ok"); 20 | } 21 | 22 | /** 23 | * 失败 24 | * 25 | * @param errorCode 26 | * @return 27 | */ 28 | public static BaseResponse error(ErrorCode errorCode) { 29 | return new BaseResponse<>(errorCode); 30 | } 31 | 32 | /** 33 | * 失败 34 | * 35 | * @param code 36 | * @param message 37 | * @return 38 | */ 39 | public static BaseResponse error(int code, String message) { 40 | return new BaseResponse(code, null, message); 41 | } 42 | 43 | /** 44 | * 失败 45 | * 46 | * @param errorCode 47 | * @return 48 | */ 49 | public static BaseResponse error(ErrorCode errorCode, String message) { 50 | return new BaseResponse(errorCode.getCode(), null, message); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/mq/TTLProduct.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.mq; 2 | 3 | import com.rabbitmq.client.*; 4 | 5 | import java.io.IOException; 6 | import java.nio.charset.StandardCharsets; 7 | import java.util.HashMap; 8 | import java.util.Map; 9 | import java.util.Scanner; 10 | import java.util.concurrent.TimeoutException; 11 | 12 | /** 13 | * @author leikooo 14 | * @Description 15 | */ 16 | public class TTLProduct { 17 | private final static String QUEUE_NAME = "TTL"; 18 | 19 | public static void main(String[] args) throws IOException, TimeoutException { 20 | ConnectionFactory factory = new ConnectionFactory(); 21 | 22 | try (Connection connection = factory.newConnection(); 23 | Channel channel = connection.createChannel()) { 24 | Scanner sc = new Scanner(System.in); 25 | String message = sc.nextLine(); 26 | // 发送端指定每条消息的过期时间 27 | AMQP.BasicProperties properties = new AMQP.BasicProperties.Builder() 28 | .expiration("60000") 29 | .build(); 30 | channel.basicPublish("", QUEUE_NAME, properties, message.getBytes(StandardCharsets.UTF_8)); 31 | System.out.println(" [x] Sent '" + message + "'"); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/resources/mapper/UserMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | id,userAccount,userPassword, 21 | userName,userAvatar,userRole, 22 | createTime,updateTime,isDelete 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/model/dto/post/PostQueryRequest.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.model.dto.post; 2 | 3 | import com.leikooo.yubi.common.PageRequest; 4 | import java.io.Serializable; 5 | import java.util.List; 6 | import lombok.Data; 7 | import lombok.EqualsAndHashCode; 8 | 9 | /** 10 | * 查询请求 11 | * 12 | * @author 程序员鱼皮 13 | * @from 编程导航知识星球 14 | */ 15 | @EqualsAndHashCode(callSuper = true) 16 | @Data 17 | public class PostQueryRequest extends PageRequest implements Serializable { 18 | 19 | /** 20 | * id 21 | */ 22 | private Long id; 23 | 24 | /** 25 | * id 26 | */ 27 | private Long notId; 28 | 29 | /** 30 | * 搜索词 31 | */ 32 | private String searchText; 33 | 34 | /** 35 | * 标题 36 | */ 37 | private String title; 38 | 39 | /** 40 | * 内容 41 | */ 42 | private String content; 43 | 44 | /** 45 | * 标签列表 46 | */ 47 | private List tags; 48 | 49 | /** 50 | * 至少有一个标签 51 | */ 52 | private List orTags; 53 | 54 | /** 55 | * 创建用户 id 56 | */ 57 | private Long userId; 58 | 59 | /** 60 | * 收藏用户 id 61 | */ 62 | private Long favourUserId; 63 | 64 | private static final long serialVersionUID = 1L; 65 | } -------------------------------------------------------------------------------- /src/main/resources/mapper/PostFavourMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | id,postId,userId, 19 | createTime,updateTime 20 | 21 | 22 | 29 | 30 | -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/model/entity/PostThumb.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.model.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableField; 5 | import com.baomidou.mybatisplus.annotation.TableId; 6 | import com.baomidou.mybatisplus.annotation.TableName; 7 | import java.io.Serializable; 8 | import java.util.Date; 9 | 10 | import lombok.*; 11 | 12 | /** 13 | * 帖子点赞 14 | * 15 | * @author 程序员鱼皮 16 | * @from 编程导航知识星球 17 | */ 18 | @TableName(value = "post_thumb") 19 | @Getter 20 | @NoArgsConstructor 21 | @AllArgsConstructor 22 | @Setter 23 | public class PostThumb implements Serializable { 24 | 25 | /** 26 | * id 27 | */ 28 | @TableId(type = IdType.AUTO) 29 | private Long id; 30 | 31 | /** 32 | * 帖子 id 33 | */ 34 | private Long postId; 35 | 36 | /** 37 | * 创建用户 id 38 | */ 39 | private Long userId; 40 | 41 | /** 42 | * 创建时间 43 | */ 44 | private Date createTime; 45 | 46 | /** 47 | * 更新时间 48 | */ 49 | private Date updateTime; 50 | 51 | @TableField(exist = false) 52 | private static final long serialVersionUID = 1L; 53 | 54 | public PostThumb(Long postId, Long userId) { 55 | this.postId = postId; 56 | this.userId = userId; 57 | } 58 | } -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/model/entity/PostFavour.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.model.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableField; 5 | import com.baomidou.mybatisplus.annotation.TableId; 6 | import com.baomidou.mybatisplus.annotation.TableName; 7 | import java.io.Serializable; 8 | import java.util.Date; 9 | 10 | import lombok.*; 11 | 12 | /** 13 | * 帖子收藏 14 | * 15 | * @author 程序员鱼皮 16 | * @from 编程导航知识星球 17 | **/ 18 | @TableName(value = "post_favour") 19 | @Getter 20 | @Setter 21 | @NoArgsConstructor 22 | @AllArgsConstructor 23 | public class PostFavour implements Serializable { 24 | 25 | /** 26 | * id 27 | */ 28 | @TableId(type = IdType.AUTO) 29 | private Long id; 30 | 31 | /** 32 | * 帖子 id 33 | */ 34 | private Long postId; 35 | 36 | /** 37 | * 创建用户 id 38 | */ 39 | private Long userId; 40 | 41 | /** 42 | * 创建时间 43 | */ 44 | private Date createTime; 45 | 46 | /** 47 | * 更新时间 48 | */ 49 | private Date updateTime; 50 | 51 | @TableField(exist = false) 52 | private static final long serialVersionUID = 1L; 53 | 54 | public PostFavour(Long postId, Long userId) { 55 | this.postId = postId; 56 | this.userId = userId; 57 | } 58 | } -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/service/PostFavourService.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.service; 2 | 3 | import com.baomidou.mybatisplus.core.conditions.Wrapper; 4 | import com.baomidou.mybatisplus.core.metadata.IPage; 5 | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; 6 | import com.baomidou.mybatisplus.extension.service.IService; 7 | import com.leikooo.yubi.model.entity.Post; 8 | import com.leikooo.yubi.model.entity.PostFavour; 9 | import com.leikooo.yubi.model.entity.User; 10 | 11 | /** 12 | * 帖子收藏服务 13 | * 14 | * @author 程序员鱼皮 15 | * @from 编程导航知识星球 16 | */ 17 | public interface PostFavourService extends IService { 18 | 19 | /** 20 | * 帖子收藏 21 | * 22 | * @param postId 23 | * @param loginUser 24 | * @return 25 | */ 26 | int doPostFavour(long postId, User loginUser); 27 | 28 | /** 29 | * 分页获取用户收藏的帖子列表 30 | * 31 | * @param page 32 | * @param queryWrapper 33 | * @param favourUserId 34 | * @return 35 | */ 36 | Page listFavourPostByPage(IPage page, Wrapper queryWrapper, 37 | long favourUserId); 38 | 39 | /** 40 | * 帖子收藏(内部服务) 41 | * 42 | * @param userId 43 | * @param postId 44 | * @return 45 | */ 46 | int doPostFavourInner(long userId, long postId); 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/mq/DirectProducer.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.mq; 2 | 3 | import com.rabbitmq.client.Channel; 4 | import com.rabbitmq.client.Connection; 5 | import com.rabbitmq.client.ConnectionFactory; 6 | 7 | import java.util.Scanner; 8 | 9 | /** 10 | * @author leikooo 11 | */ 12 | public class DirectProducer { 13 | 14 | private static final String EXCHANGE_NAME = "direct-exchange"; 15 | 16 | public static void main(String[] argv) throws Exception { 17 | ConnectionFactory factory = new ConnectionFactory(); 18 | 19 | try (Connection connection = factory.newConnection(); 20 | Channel channel = connection.createChannel()) { 21 | channel.exchangeDeclare(EXCHANGE_NAME, "direct"); 22 | Scanner sc = new Scanner(System.in); 23 | while (sc.hasNext()) { 24 | String userInput = sc.nextLine(); 25 | String[] strings = userInput.split(" "); 26 | if (strings.length < 1) { 27 | continue; 28 | } 29 | String routingKey = strings[0]; 30 | String message = strings[1]; 31 | channel.basicPublish(EXCHANGE_NAME, routingKey, null, message.getBytes("UTF-8")); 32 | System.out.println(" [x] Sent routingKey'" + routingKey + "' message:'" + message + "'"); 33 | } 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/mq/TopicProducer.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.mq; 2 | 3 | import com.rabbitmq.client.Channel; 4 | import com.rabbitmq.client.Connection; 5 | import com.rabbitmq.client.ConnectionFactory; 6 | 7 | import java.util.Scanner; 8 | 9 | 10 | /** 11 | * @author leikooo 12 | */ 13 | public class TopicProducer { 14 | 15 | private static final String EXCHANGE_NAME = "topic-exchange"; 16 | 17 | public static void main(String[] argv) throws Exception { 18 | ConnectionFactory factory = new ConnectionFactory(); 19 | 20 | try (Connection connection = factory.newConnection(); 21 | Channel channel = connection.createChannel()) { 22 | 23 | channel.exchangeDeclare(EXCHANGE_NAME, "topic"); 24 | Scanner sc = new Scanner(System.in); 25 | while (sc.hasNext()) { 26 | String userInput = sc.nextLine(); 27 | String[] strings = userInput.split(" "); 28 | if (strings.length < 1) { 29 | continue; 30 | } 31 | String routingKey = strings[0]; 32 | String message = strings[1]; 33 | channel.basicPublish(EXCHANGE_NAME, routingKey, null, message.getBytes("UTF-8")); 34 | System.out.println(" [x] Sent routingKey'" + routingKey + "' message:'" + message + "'"); 35 | } 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/mq/TTLConsumer.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.mq; 2 | 3 | import com.rabbitmq.client.Channel; 4 | import com.rabbitmq.client.Connection; 5 | import com.rabbitmq.client.ConnectionFactory; 6 | import com.rabbitmq.client.DeliverCallback; 7 | 8 | import java.io.IOException; 9 | import java.util.HashMap; 10 | import java.util.Map; 11 | import java.util.concurrent.TimeoutException; 12 | 13 | /** 14 | * @author leikooo 15 | * @Description 16 | */ 17 | public class TTLConsumer { 18 | private final static String QUEUE_NAME = "TTL"; 19 | 20 | public static void main(String[] args) throws IOException, TimeoutException { 21 | ConnectionFactory factory = new ConnectionFactory(); 22 | 23 | try (Connection connection = factory.newConnection(); 24 | Channel channel = connection.createChannel()) { 25 | Map map = new HashMap(); 26 | map.put("x-message-ttl", 60000); 27 | channel.queueDeclare(QUEUE_NAME, true, false, false, map); 28 | 29 | DeliverCallback deliverCallback = (consumerTag, delivery) -> { 30 | String message = new String(delivery.getBody(), "UTF-8"); 31 | System.out.println(" [x] Received '" + message + "'"); 32 | }; 33 | channel.basicConsume(QUEUE_NAME, false, deliverCallback, consumerTag -> {}); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/manager/CosManager.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.manager; 2 | 3 | import com.qcloud.cos.COSClient; 4 | import com.qcloud.cos.model.PutObjectRequest; 5 | import com.qcloud.cos.model.PutObjectResult; 6 | import com.leikooo.yubi.config.CosClientConfig; 7 | import java.io.File; 8 | import javax.annotation.Resource; 9 | import org.springframework.stereotype.Component; 10 | 11 | /** 12 | * Cos 对象存储操作 13 | * 14 | * @author 程序员鱼皮 15 | * @from 编程导航知识星球 16 | */ 17 | @Component 18 | public class CosManager { 19 | 20 | @Resource 21 | private CosClientConfig cosClientConfig; 22 | 23 | @Resource 24 | private COSClient cosClient; 25 | 26 | /** 27 | * 上传对象 28 | * 29 | * @param key 唯一键 30 | * @param localFilePath 本地文件路径 31 | * @return 32 | */ 33 | public PutObjectResult putObject(String key, String localFilePath) { 34 | PutObjectRequest putObjectRequest = new PutObjectRequest(cosClientConfig.getBucket(), key, 35 | new File(localFilePath)); 36 | return cosClient.putObject(putObjectRequest); 37 | } 38 | 39 | /** 40 | * 上传对象 41 | * 42 | * @param key 唯一键 43 | * @param file 文件 44 | * @return 45 | */ 46 | public PutObjectResult putObject(String key, File file) { 47 | PutObjectRequest putObjectRequest = new PutObjectRequest(cosClientConfig.getBucket(), key, 48 | file); 49 | return cosClient.putObject(putObjectRequest); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/config/CosClientConfig.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.config; 2 | 3 | import com.qcloud.cos.COSClient; 4 | import com.qcloud.cos.ClientConfig; 5 | import com.qcloud.cos.auth.BasicCOSCredentials; 6 | import com.qcloud.cos.auth.COSCredentials; 7 | import com.qcloud.cos.region.Region; 8 | import lombok.Data; 9 | import org.springframework.boot.context.properties.ConfigurationProperties; 10 | import org.springframework.context.annotation.Bean; 11 | import org.springframework.context.annotation.Configuration; 12 | 13 | /** 14 | * 腾讯云对象存储客户端 15 | * 16 | * @author 程序员鱼皮 17 | * @from 编程导航知识星球 18 | */ 19 | @Configuration 20 | @ConfigurationProperties(prefix = "cos.client") 21 | @Data 22 | public class CosClientConfig { 23 | 24 | /** 25 | * accessKey 26 | */ 27 | private String accessKey; 28 | 29 | /** 30 | * secretKey 31 | */ 32 | private String secretKey; 33 | 34 | /** 35 | * 区域 36 | */ 37 | private String region; 38 | 39 | /** 40 | * 桶名 41 | */ 42 | private String bucket; 43 | 44 | @Bean 45 | public COSClient cosClient() { 46 | // 初始化用户身份信息(secretId, secretKey) 47 | COSCredentials cred = new BasicCOSCredentials(accessKey, secretKey); 48 | // 设置bucket的区域, COS地域的简称请参照 https://www.qcloud.com/document/product/436/6224 49 | ClientConfig clientConfig = new ClientConfig(new Region(region)); 50 | // 生成cos客户端 51 | return new COSClient(cred, clientConfig); 52 | } 53 | } -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/model/vo/ChartVO.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.model.vo; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | import lombok.NoArgsConstructor; 6 | 7 | import java.io.Serializable; 8 | import java.util.Date; 9 | 10 | /** 11 | * 用户视图(脱敏) 12 | * 13 | * @author 程序员鱼皮 14 | * @from 编程导航知识星球 15 | */ 16 | @Getter 17 | @NoArgsConstructor 18 | @AllArgsConstructor 19 | public class ChartVO implements Serializable { 20 | 21 | /** 22 | * id 23 | */ 24 | private long id; 25 | 26 | /** 27 | * 分析目标 28 | */ 29 | private String goal; 30 | 31 | /** 32 | * 图表数据 33 | */ 34 | private String chartData; 35 | 36 | /** 37 | * 图表类型 38 | */ 39 | private String chartType; 40 | 41 | /** 42 | * 生成的图表数据 43 | */ 44 | private String genChart; 45 | 46 | /** 47 | * 生成的分析结论 48 | */ 49 | private String genResult; 50 | 51 | /** 52 | * 创建用户 id 53 | */ 54 | private Long userId; 55 | 56 | /** 57 | * @see com.leikooo.yubi.model.enums.ResultEnum 58 | * wait,running,succeed,failed 59 | */ 60 | private String status; 61 | 62 | /** 63 | * 错误消息 64 | */ 65 | private String execMessage; 66 | 67 | /** 68 | * 创建时间 69 | */ 70 | private Date createTime; 71 | 72 | /** 73 | * 更新时间 74 | */ 75 | private Date updateTime; 76 | 77 | private static final long serialVersionUID = 4377216422203918656L; 78 | 79 | 80 | } -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/config/Knife4jConfig.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.config; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.context.annotation.Profile; 6 | import springfox.documentation.builders.ApiInfoBuilder; 7 | import springfox.documentation.builders.PathSelectors; 8 | import springfox.documentation.builders.RequestHandlerSelectors; 9 | import springfox.documentation.spi.DocumentationType; 10 | import springfox.documentation.spring.web.plugins.Docket; 11 | import springfox.documentation.swagger2.annotations.EnableSwagger2; 12 | 13 | /** 14 | * Knife4j 接口文档配置 15 | * https://doc.xiaominfo.com/knife4j/documentation/get_start.html 16 | * 17 | * @author 程序员鱼皮 18 | * @from 编程导航知识星球 19 | */ 20 | @Configuration 21 | @EnableSwagger2 22 | @Profile({"dev", "test"}) 23 | public class Knife4jConfig { 24 | 25 | @Bean 26 | public Docket defaultApi2() { 27 | return new Docket(DocumentationType.SWAGGER_2) 28 | .apiInfo(new ApiInfoBuilder() 29 | .title("接口文档") 30 | .description("yubi-backend") 31 | .version("1.0") 32 | .build()) 33 | .select() 34 | // 指定 Controller 扫描包路径 35 | .apis(RequestHandlerSelectors.basePackage("com.leikooo.yubi.controller")) 36 | .paths(PathSelectors.any()) 37 | .build(); 38 | } 39 | } -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/model/enums/FileUploadBizEnum.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.model.enums; 2 | 3 | import java.util.Arrays; 4 | import java.util.List; 5 | import java.util.stream.Collectors; 6 | import org.apache.commons.lang3.ObjectUtils; 7 | 8 | /** 9 | * 文件上传业务类型枚举 10 | * 11 | * @author 程序员鱼皮 12 | * @from 编程导航知识星球 13 | */ 14 | public enum FileUploadBizEnum { 15 | 16 | USER_AVATAR("用户头像", "user_avatar"); 17 | 18 | private final String text; 19 | 20 | private final String value; 21 | 22 | FileUploadBizEnum(String text, String value) { 23 | this.text = text; 24 | this.value = value; 25 | } 26 | 27 | /** 28 | * 获取值列表 29 | * 30 | * @return 31 | */ 32 | public static List getValues() { 33 | return Arrays.stream(values()).map(item -> item.value).collect(Collectors.toList()); 34 | } 35 | 36 | /** 37 | * 根据 value 获取枚举 38 | * 39 | * @param value 40 | * @return 41 | */ 42 | public static FileUploadBizEnum getEnumByValue(String value) { 43 | if (ObjectUtils.isEmpty(value)) { 44 | return null; 45 | } 46 | for (FileUploadBizEnum anEnum : FileUploadBizEnum.values()) { 47 | if (anEnum.value.equals(value)) { 48 | return anEnum; 49 | } 50 | } 51 | return null; 52 | } 53 | 54 | public String getValue() { 55 | return value; 56 | } 57 | 58 | public String getText() { 59 | return text; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/model/enums/UserRoleEnum.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.model.enums; 2 | 3 | import java.util.Arrays; 4 | import java.util.List; 5 | import java.util.stream.Collectors; 6 | import org.apache.commons.lang3.ObjectUtils; 7 | 8 | /** 9 | * 用户角色枚举 10 | * 11 | * @author 程序员鱼皮 12 | * @from 编程导航知识星球 13 | */ 14 | public enum UserRoleEnum { 15 | 16 | USER("用户", "user"), 17 | ADMIN("管理员", "admin"), 18 | BAN("被封号", "ban"); 19 | 20 | private final String text; 21 | 22 | private final String value; 23 | 24 | UserRoleEnum(String text, String value) { 25 | this.text = text; 26 | this.value = value; 27 | } 28 | 29 | /** 30 | * 获取值列表 31 | * 32 | * @return 33 | */ 34 | public static List getValues() { 35 | return Arrays.stream(values()).map(item -> item.value).collect(Collectors.toList()); 36 | } 37 | 38 | /** 39 | * 根据 value 获取枚举 40 | * 41 | * @param value 42 | * @return 43 | */ 44 | public static UserRoleEnum getEnumByValue(String value) { 45 | if (ObjectUtils.isEmpty(value)) { 46 | return null; 47 | } 48 | for (UserRoleEnum anEnum : UserRoleEnum.values()) { 49 | if (anEnum.value.equals(value)) { 50 | return anEnum; 51 | } 52 | } 53 | return null; 54 | } 55 | 56 | public String getValue() { 57 | return value; 58 | } 59 | 60 | public String getText() { 61 | return text; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/config/WxOpenConfig.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.config; 2 | 3 | import lombok.Data; 4 | import lombok.extern.slf4j.Slf4j; 5 | import me.chanjar.weixin.mp.api.WxMpService; 6 | import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl; 7 | import me.chanjar.weixin.mp.config.impl.WxMpDefaultConfigImpl; 8 | import org.springframework.boot.context.properties.ConfigurationProperties; 9 | import org.springframework.context.annotation.Configuration; 10 | 11 | /** 12 | * 微信开放平台配置 13 | * 14 | * @author 程序员鱼皮 15 | * @from 编程导航知识星球 16 | */ 17 | @Slf4j 18 | @Configuration 19 | @ConfigurationProperties(prefix = "wx.open") 20 | @Data 21 | public class WxOpenConfig { 22 | 23 | private String appId; 24 | 25 | private String appSecret; 26 | 27 | private WxMpService wxMpService; 28 | 29 | /** 30 | * 单例模式(不用 @Bean 是为了防止和公众号的 service 冲突) 31 | * 32 | * @return 33 | */ 34 | public WxMpService getWxMpService() { 35 | if (wxMpService != null) { 36 | return wxMpService; 37 | } 38 | synchronized (this) { 39 | if (wxMpService != null) { 40 | return wxMpService; 41 | } 42 | WxMpDefaultConfigImpl config = new WxMpDefaultConfigImpl(); 43 | config.setAppId(appId); 44 | config.setSecret(appSecret); 45 | WxMpService service = new WxMpServiceImpl(); 46 | service.setWxMpConfigStorage(config); 47 | wxMpService = service; 48 | return wxMpService; 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /src/main/resources/mapper/PostMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | id,title,content,tags, 24 | thumbNum,favourNum,userId, 25 | createTime,updateTime,isDelete 26 | 27 | 28 | 33 | 34 | -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/service/PostService.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.service; 2 | 3 | import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; 4 | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; 5 | import com.baomidou.mybatisplus.extension.service.IService; 6 | import com.leikooo.yubi.model.dto.post.PostQueryRequest; 7 | import com.leikooo.yubi.model.entity.Post; 8 | import com.leikooo.yubi.model.vo.PostVO; 9 | import javax.servlet.http.HttpServletRequest; 10 | 11 | /** 12 | * 帖子服务 13 | * 14 | * @author 程序员鱼皮 15 | * @from 编程导航知识星球 16 | */ 17 | public interface PostService extends IService { 18 | 19 | /** 20 | * 校验 21 | * 22 | * @param post 23 | * @param add 24 | */ 25 | void validPost(Post post, boolean add); 26 | 27 | /** 28 | * 获取查询条件 29 | * 30 | * @param postQueryRequest 31 | * @return 32 | */ 33 | QueryWrapper getQueryWrapper(PostQueryRequest postQueryRequest); 34 | 35 | /** 36 | * 从 ES 查询 37 | * 38 | * @param postQueryRequest 39 | * @return 40 | */ 41 | Page searchFromEs(PostQueryRequest postQueryRequest); 42 | 43 | /** 44 | * 获取帖子封装 45 | * 46 | * @param post 47 | * @param request 48 | * @return 49 | */ 50 | PostVO getPostVO(Post post, HttpServletRequest request); 51 | 52 | /** 53 | * 分页获取帖子封装 54 | * 55 | * @param postPage 56 | * @param request 57 | * @return 58 | */ 59 | Page getPostVOPage(Page postPage, HttpServletRequest request); 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/bizmq/BIMqInitMain.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.bizmq; 2 | 3 | import com.leikooo.yubi.constant.BIMQConstant; 4 | import com.rabbitmq.client.Channel; 5 | import com.rabbitmq.client.Connection; 6 | import com.rabbitmq.client.ConnectionFactory; 7 | 8 | import java.util.HashMap; 9 | import java.util.Map; 10 | 11 | /** 12 | * 用于创建测试程序用到的交换机和队列(只用在程序启动前执行一次) 13 | */ 14 | public class BIMqInitMain { 15 | public static void main(String[] args) { 16 | try { 17 | ConnectionFactory factory = new ConnectionFactory(); 18 | Connection connection = factory.newConnection(); 19 | Channel channel = connection.createChannel(); 20 | // 声明死信队列 21 | channel.exchangeDeclare(BIMQConstant.BI_DLX_EXCHANGE_NAME, "direct"); 22 | channel.queueDeclare(BIMQConstant.BI_DLX_QUEUE_NAME, true, false, false, null); 23 | channel.queueBind(BIMQConstant.BI_DLX_QUEUE_NAME, BIMQConstant.BI_DLX_EXCHANGE_NAME, BIMQConstant.BI_DLX_ROUTING_KEY); 24 | 25 | channel.exchangeDeclare(BIMQConstant.BI_EXCHANGE_NAME, "direct"); 26 | 27 | Map arg = new HashMap(); 28 | arg.put("x-dead-letter-exchange", BIMQConstant.BI_DLX_EXCHANGE_NAME); 29 | arg.put("x-dead-letter-routing-key", BIMQConstant.BI_DLX_ROUTING_KEY); 30 | channel.queueDeclare(BIMQConstant.BI_QUEUE_NAME, true, false, false, arg); 31 | channel.queueBind(BIMQConstant.BI_QUEUE_NAME, BIMQConstant.BI_EXCHANGE_NAME, BIMQConstant.BI_ROUTING_KEY); 32 | } catch (Exception e) { 33 | e.printStackTrace(); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/utils/SpringContextUtils.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.utils; 2 | 3 | import org.jetbrains.annotations.NotNull; 4 | import org.springframework.beans.BeansException; 5 | import org.springframework.context.ApplicationContext; 6 | import org.springframework.context.ApplicationContextAware; 7 | import org.springframework.stereotype.Component; 8 | 9 | /** 10 | * Spring 上下文获取工具 11 | * 12 | * @author 程序员鱼皮 13 | * @from 编程导航知识星球 14 | */ 15 | @Component 16 | public class SpringContextUtils implements ApplicationContextAware { 17 | 18 | private static ApplicationContext applicationContext; 19 | 20 | @Override 21 | public void setApplicationContext(@NotNull ApplicationContext applicationContext) throws BeansException { 22 | SpringContextUtils.applicationContext = applicationContext; 23 | } 24 | 25 | /** 26 | * 通过名称获取 Bean 27 | * 28 | * @param beanName 29 | * @return 30 | */ 31 | public static Object getBean(String beanName) { 32 | return applicationContext.getBean(beanName); 33 | } 34 | 35 | /** 36 | * 通过 class 获取 Bean 37 | * 38 | * @param beanClass 39 | * @param 40 | * @return 41 | */ 42 | public static T getBean(Class beanClass) { 43 | return applicationContext.getBean(beanClass); 44 | } 45 | 46 | /** 47 | * 通过名称和类型获取 Bean 48 | * 49 | * @param beanName 50 | * @param beanClass 51 | * @param 52 | * @return 53 | */ 54 | public static T getBean(String beanName, Class beanClass) { 55 | return applicationContext.getBean(beanName, beanClass); 56 | } 57 | } -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/job/once/FullSyncPostToEs.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.job.once; 2 | 3 | import com.leikooo.yubi.esdao.PostEsDao; 4 | import com.leikooo.yubi.model.dto.post.PostEsDTO; 5 | import com.leikooo.yubi.model.entity.Post; 6 | import com.leikooo.yubi.service.PostService; 7 | import java.util.List; 8 | import java.util.stream.Collectors; 9 | import javax.annotation.Resource; 10 | import lombok.extern.slf4j.Slf4j; 11 | import org.apache.commons.collections4.CollectionUtils; 12 | import org.springframework.boot.CommandLineRunner; 13 | 14 | /** 15 | * 全量同步帖子到 es 16 | * 17 | * @author 程序员鱼皮 18 | * @from 编程导航知识星球 19 | */ 20 | // todo 取消注释开启任务 21 | //@Component 22 | @Slf4j 23 | public class FullSyncPostToEs implements CommandLineRunner { 24 | 25 | @Resource 26 | private PostService postService; 27 | 28 | @Resource 29 | private PostEsDao postEsDao; 30 | 31 | @Override 32 | public void run(String... args) { 33 | List postList = postService.list(); 34 | if (CollectionUtils.isEmpty(postList)) { 35 | return; 36 | } 37 | List postEsDTOList = postList.stream().map(PostEsDTO::objToDto).collect(Collectors.toList()); 38 | final int pageSize = 500; 39 | int total = postEsDTOList.size(); 40 | log.info("FullSyncPostToEs start, total {}", total); 41 | for (int i = 0; i < total; i += pageSize) { 42 | int end = Math.min(i + pageSize, total); 43 | log.info("sync from {} to {}", i, end); 44 | postEsDao.saveAll(postEsDTOList.subList(i, end)); 45 | } 46 | log.info("FullSyncPostToEs end, total {}", total); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/additional-spring-configuration-metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "properties": [ 3 | { 4 | "name": "cos.client.accessKey", 5 | "type": "java.lang.String", 6 | "description": "Description for cos.client.accessKey." 7 | }, 8 | { 9 | "name": "cos.client.secretKey", 10 | "type": "java.lang.String", 11 | "description": "Description for cos.client.secretKey." 12 | }, 13 | { 14 | "name": "cos.client.region", 15 | "type": "java.lang.String", 16 | "description": "Description for cos.client.region." 17 | }, 18 | { 19 | "name": "cos.client.bucket", 20 | "type": "java.lang.String", 21 | "description": "Description for cos.client.bucket." 22 | }, 23 | { 24 | "name": "wx.open.appId", 25 | "type": "java.lang.String", 26 | "description": "Description for wx.open.appId." 27 | }, 28 | { 29 | "name": "wx.open.appSecret", 30 | "type": "java.lang.String", 31 | "description": "Description for wx.open.appSecret." 32 | }, 33 | { 34 | "name": "bai-lian.agentKey", 35 | "type": "java.lang.String" 36 | }, 37 | { 38 | "name": "bai-lian.accessKeySecret", 39 | "type": "java.lang.String" 40 | }, 41 | { 42 | "name": "bai-lian.accessKeyId", 43 | "type": "java.lang.String" 44 | }, 45 | { 46 | "name": "xun-fei.client.appId", 47 | "type": "java.lang.String" 48 | }, 49 | { 50 | "name": "xun-fei.client.apiSecret", 51 | "type": "java.lang.String" 52 | }, 53 | { 54 | "name": "xun-fei.client.apiKey", 55 | "type": "java.lang.String" 56 | }, 57 | { 58 | "name": "ai.apiKey", 59 | "type": "java.lang.String" 60 | } 61 | ] 62 | } -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/utils/NetUtils.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.utils; 2 | 3 | import java.net.InetAddress; 4 | import javax.servlet.http.HttpServletRequest; 5 | 6 | /** 7 | * 网络工具类 8 | * 9 | * @author 程序员鱼皮 10 | * @from 编程导航知识星球 11 | */ 12 | public class NetUtils { 13 | 14 | /** 15 | * 获取客户端 IP 地址 16 | * 17 | * @param request 18 | * @return 19 | */ 20 | public static String getIpAddress(HttpServletRequest request) { 21 | String ip = request.getHeader("x-forwarded-for"); 22 | if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { 23 | ip = request.getHeader("Proxy-Client-IP"); 24 | } 25 | if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { 26 | ip = request.getHeader("WL-Proxy-Client-IP"); 27 | } 28 | if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { 29 | ip = request.getRemoteAddr(); 30 | if (ip.equals("127.0.0.1")) { 31 | // 根据网卡取本机配置的 IP 32 | InetAddress inet = null; 33 | try { 34 | inet = InetAddress.getLocalHost(); 35 | } catch (Exception e) { 36 | e.printStackTrace(); 37 | } 38 | if (inet != null) { 39 | ip = inet.getHostAddress(); 40 | } 41 | } 42 | } 43 | // 多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割 44 | if (ip != null && ip.length() > 15) { 45 | if (ip.indexOf(",") > 0) { 46 | ip = ip.substring(0, ip.indexOf(",")); 47 | } 48 | } 49 | if (ip == null) { 50 | return "127.0.0.1"; 51 | } 52 | return ip; 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/mq/MultiConsumer.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.mq; 2 | 3 | import com.rabbitmq.client.Channel; 4 | import com.rabbitmq.client.Connection; 5 | import com.rabbitmq.client.ConnectionFactory; 6 | import com.rabbitmq.client.DeliverCallback; 7 | 8 | public class MultiConsumer { 9 | 10 | 11 | public static void main(String[] argv) throws Exception { 12 | String TASK_QUEUE_NAME = "multi_queue"; 13 | ConnectionFactory factory = new ConnectionFactory(); 14 | 15 | for (int i = 0; i < 2; i++) { 16 | final Connection connection = factory.newConnection(); 17 | final Channel channel = connection.createChannel(); 18 | 19 | channel.queueDeclare(TASK_QUEUE_NAME, true, false, false, null); 20 | System.out.println(" [*] Waiting for messages. To exit press CTRL+C"); 21 | channel.basicQos(2); 22 | int finalI = i; 23 | DeliverCallback deliverCallback = (consumerTag, delivery) -> { 24 | String message = new String(delivery.getBody(), "UTF-8"); 25 | 26 | System.out.println(" [x] 第 " + finalI + " Received '" + message + "'"); 27 | try { 28 | // 这个 multiType 如果是 true 那么表示这个消息是批量确认消息 29 | channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false); 30 | Thread.sleep(20000); 31 | } catch (Exception e) { 32 | e.printStackTrace(); 33 | channel.basicReject(delivery.getEnvelope().getDeliveryTag(), true); 34 | } finally { 35 | System.out.println(" [x] Done"); 36 | } 37 | }; 38 | // 这个 autoAck 一般建议默认 39 | channel.basicConsume(TASK_QUEUE_NAME, false, deliverCallback, consumerTag -> { 40 | }); 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/config/RabbitMQConfig.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.config; 2 | 3 | import org.springframework.amqp.core.*; 4 | import org.springframework.context.annotation.Bean; 5 | import org.springframework.context.annotation.Configuration; 6 | 7 | import java.util.HashMap; 8 | import java.util.Map; 9 | 10 | import static com.leikooo.yubi.constant.BIMQConstant.*; 11 | 12 | /** 13 | * @author leikooo 14 | */ 15 | @Configuration 16 | public class RabbitMQConfig { 17 | 18 | /** 19 | * 声明死信交换机 20 | */ 21 | @Bean 22 | public DirectExchange biDlxExchange() { 23 | return new DirectExchange(BI_DLX_EXCHANGE_NAME); 24 | } 25 | 26 | /** 27 | * 声明死信队列 28 | */ 29 | @Bean 30 | public Queue biDlxQueue() { 31 | return new Queue(BI_DLX_QUEUE_NAME, true, false, false); 32 | } 33 | 34 | /** 35 | * 将死信队列绑定到死信交换机 36 | */ 37 | @Bean 38 | public Binding biDlxBinding(Queue biDlxQueue, DirectExchange biDlxExchange) { 39 | return BindingBuilder.bind(biDlxQueue).to(biDlxExchange).with(BI_DLX_ROUTING_KEY); 40 | } 41 | 42 | /** 43 | * 声明主交换机 44 | */ 45 | @Bean 46 | public DirectExchange biExchange() { 47 | return new DirectExchange(BI_EXCHANGE_NAME); 48 | } 49 | 50 | /** 51 | * 声明主队列并指定其死信交换机配置 52 | */ 53 | @Bean 54 | public Queue biQueue() { 55 | Map args = new HashMap<>(); 56 | args.put("x-dead-letter-exchange", BI_DLX_EXCHANGE_NAME); 57 | args.put("x-dead-letter-routing-key", BI_DLX_ROUTING_KEY); 58 | return new Queue(BI_QUEUE_NAME, true, false, false, args); 59 | } 60 | 61 | /** 62 | * 将主队列绑定到主交换机 63 | */ 64 | @Bean 65 | public Binding biBinding(Queue biQueue, DirectExchange biExchange) { 66 | return BindingBuilder.bind(biQueue).to(biExchange).with(BI_ROUTING_KEY); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/service/ChartService.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.service; 2 | 3 | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; 4 | import com.baomidou.mybatisplus.extension.service.IService; 5 | import com.leikooo.yubi.model.dto.controller.ChartGenController; 6 | import com.leikooo.yubi.model.dto.controller.ChartQueryController; 7 | import com.leikooo.yubi.model.dto.controller.ChartRetryController; 8 | import com.leikooo.yubi.model.entity.Chart; 9 | import com.leikooo.yubi.model.vo.BiResponse; 10 | import com.leikooo.yubi.model.vo.ChartVO; 11 | import org.springframework.web.multipart.MultipartFile; 12 | 13 | import java.util.List; 14 | 15 | /** 16 | * @author liang 17 | * @description 针对表【chart(图表信息表)】的数据库操作Service 18 | * @createDate 2023-11-07 09:32:54 19 | */ 20 | public interface ChartService extends IService { 21 | /** 22 | * 返回 ChartVOList 23 | * 24 | * @param charts 25 | * @return 26 | */ 27 | List getChartVO(final List charts); 28 | 29 | /** 30 | * 返回 ChartVO 31 | * 32 | * @param chart 33 | * @return 34 | */ 35 | ChartVO getChartVO(Chart chart); 36 | 37 | /** 38 | * 返回对象列表 39 | * 40 | * @param chartQueryRequest 41 | * @return 42 | */ 43 | Page getChartVOList(final ChartQueryController chartQueryRequest); 44 | 45 | 46 | BiResponse getChart(final MultipartFile multipartFile, final ChartGenController chartGenController); 47 | 48 | BiResponse getChartASYNC(MultipartFile multipartFile, ChartGenController chartGenController); 49 | 50 | BiResponse getChartMQ(MultipartFile multipartFile, ChartGenController chartGenController); 51 | 52 | Page getMyChartList(ChartQueryController chartQueryController); 53 | 54 | BiResponse retryGenChart(ChartRetryController chartRetryController); 55 | 56 | boolean downloadCsvData(String chartId); 57 | 58 | } 59 | -------------------------------------------------------------------------------- /src/main/resources/mapper/ChartMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | id,goal,chartData, 25 | chartType,genChart,genResult, 26 | userId,createTime,updateTime, 27 | isDelete 28 | 29 | 30 | 31 | ${creatTableSQL} 32 | 33 | 34 | 35 | ${insertCVSData} 36 | 37 | 38 | 41 | 42 | -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/mq/DirectConsumer.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.mq; 2 | 3 | import com.rabbitmq.client.*; 4 | 5 | /** 6 | * @author leikooo 7 | */ 8 | public class DirectConsumer { 9 | 10 | private static final String EXCHANGE_NAME = "direct-exchange"; 11 | 12 | public static void main(String[] argv) throws Exception { 13 | ConnectionFactory factory = new ConnectionFactory(); 14 | Connection connection = factory.newConnection(); 15 | Channel channel = connection.createChannel(); 16 | // 声明一个 direct 交换机 17 | channel.exchangeDeclare(EXCHANGE_NAME, "direct"); 18 | 19 | String queueName1 = "yupi_queue"; 20 | channel.queueDeclare(queueName1, true, false, false, null); 21 | channel.queueBind(queueName1, EXCHANGE_NAME, "yupi"); 22 | 23 | String queueName2 = "leikooo_queue"; 24 | channel.queueDeclare(queueName2, true, false, false, null); 25 | channel.queueBind(queueName2, EXCHANGE_NAME, "leikooo"); 26 | 27 | System.out.println(" [*] Waiting for messages. To exit press CTRL+C"); 28 | 29 | DeliverCallback deliverCallback1 = (consumerTag, delivery) -> { 30 | String message = new String(delivery.getBody(), "UTF-8"); 31 | System.out.println(" [yupi] Received '" + 32 | delivery.getEnvelope().getRoutingKey() + "':'" + message + "'"); 33 | }; 34 | 35 | DeliverCallback deliverCallback2 = (consumerTag, delivery) -> { 36 | String message = new String(delivery.getBody(), "UTF-8"); 37 | System.out.println(" [leikooo] Received '" + 38 | delivery.getEnvelope().getRoutingKey() + "':'" + message + "'"); 39 | }; 40 | 41 | channel.basicConsume(queueName1, true, deliverCallback1, consumerTag -> { 42 | }); 43 | channel.basicConsume(queueName2, true, deliverCallback2, consumerTag -> { 44 | }); 45 | 46 | } 47 | } -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/mq/FanoutConsumer.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.mq; 2 | 3 | import com.rabbitmq.client.*; 4 | 5 | 6 | /** 7 | * @author leikooo 8 | */ 9 | public class FanoutConsumer { 10 | private static final String EXCHANGE_NAME = "fanout-exchange"; 11 | 12 | public static void main(String[] argv) throws Exception { 13 | ConnectionFactory factory = new ConnectionFactory(); 14 | 15 | Connection connection = factory.newConnection(); 16 | Channel channel1 = connection.createChannel(); 17 | Channel channel2 = connection.createChannel(); 18 | // 声明交换机 19 | channel1.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.FANOUT); 20 | // String name1 = "小王_queue"; 21 | // channel1.queueDeclare(name1, false, false, false, null); 22 | // 直接创建一个默认名称队列, 用完就走的队列 23 | String queueName = channel1.queueDeclare().getQueue(); 24 | channel1.queueBind(queueName, EXCHANGE_NAME, ""); 25 | 26 | // String name2 = "小李_queue"; 27 | // channel2.queueDeclare(name2, false, false, false, null); 28 | String queueName2 = channel2.queueDeclare().getQueue(); 29 | channel2.queueBind(queueName2, EXCHANGE_NAME, ""); 30 | 31 | 32 | System.out.println(" [*] Waiting for messages. To exit press CTRL+C"); 33 | 34 | DeliverCallback deliverCallback = (consumerTag, delivery) -> { 35 | String message = new String(delivery.getBody(), "UTF-8"); 36 | System.out.println(" [x] queue1 Received '" + message + "'"); 37 | }; 38 | DeliverCallback deliverCallback2 = (consumerTag, delivery) -> { 39 | String message = new String(delivery.getBody(), "UTF-8"); 40 | System.out.println(" [x] queue2 Received '" + message + "'"); 41 | }; 42 | 43 | channel1.basicConsume(queueName, true, deliverCallback, consumerTag -> { 44 | }); 45 | channel2.basicConsume(queueName2, true, deliverCallback2, consumerTag -> { 46 | }); 47 | } 48 | } -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/controller/QueueController.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.controller; 2 | 3 | import cn.hutool.json.JSONUtil; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.springframework.context.annotation.Profile; 6 | import org.springframework.web.bind.annotation.GetMapping; 7 | import org.springframework.web.bind.annotation.PostMapping; 8 | import org.springframework.web.bind.annotation.RequestMapping; 9 | import org.springframework.web.bind.annotation.RestController; 10 | 11 | import javax.annotation.Resource; 12 | import java.util.HashMap; 13 | import java.util.Map; 14 | import java.util.concurrent.CompletableFuture; 15 | import java.util.concurrent.ThreadPoolExecutor; 16 | 17 | /** 18 | * 队列测试类 19 | * 20 | * @author leikooo 21 | */ 22 | @RestController 23 | @RequestMapping("/queue") 24 | @Slf4j 25 | @Profile({"dev", "test"}) 26 | public class QueueController { 27 | @Resource 28 | private ThreadPoolExecutor threadPoolExecutor; 29 | 30 | @GetMapping("/add") 31 | public void add(String name) { 32 | CompletableFuture.runAsync(() -> { 33 | System.out.println("任务执行中:" + name + ", 执行人:" + Thread.currentThread().getName()); 34 | try { 35 | Thread.sleep(600000); 36 | } catch (InterruptedException e) { 37 | e.printStackTrace(); 38 | } 39 | }, threadPoolExecutor); 40 | } 41 | 42 | @GetMapping("/get") 43 | public String get() { 44 | Map map = new HashMap<>(); 45 | int size = threadPoolExecutor.getQueue().size(); 46 | map.put("队列长度", size); 47 | long taskCount = threadPoolExecutor.getTaskCount(); 48 | map.put("任务总数", taskCount); 49 | long completedTaskCount = threadPoolExecutor.getCompletedTaskCount(); 50 | map.put("已完成任务数", completedTaskCount); 51 | int activeCount = threadPoolExecutor.getActiveCount(); 52 | map.put("正在工作的线程数", activeCount); 53 | return JSONUtil.toJsonStr(map); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/job/cycle/IncSyncPostToEs.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.job.cycle; 2 | 3 | import com.leikooo.yubi.esdao.PostEsDao; 4 | import com.leikooo.yubi.mapper.PostMapper; 5 | import com.leikooo.yubi.model.dto.post.PostEsDTO; 6 | import com.leikooo.yubi.model.entity.Post; 7 | import java.util.Date; 8 | import java.util.List; 9 | import java.util.stream.Collectors; 10 | import javax.annotation.Resource; 11 | import lombok.extern.slf4j.Slf4j; 12 | import org.apache.commons.collections4.CollectionUtils; 13 | import org.springframework.scheduling.annotation.Scheduled; 14 | import org.springframework.stereotype.Component; 15 | 16 | /** 17 | * 增量同步帖子到 es 18 | * 19 | * @author 程序员鱼皮 20 | * @from 编程导航知识星球 21 | */ 22 | // @Component 23 | @Slf4j 24 | public class IncSyncPostToEs { 25 | 26 | @Resource 27 | private PostMapper postMapper; 28 | 29 | @Resource 30 | private PostEsDao postEsDao; 31 | 32 | /** 33 | * 每分钟执行一次 34 | */ 35 | @Scheduled(fixedRate = 60 * 1000) 36 | public void run() { 37 | // 查询近 5 分钟内的数据 38 | Date fiveMinutesAgoDate = new Date(new Date().getTime() - 5 * 60 * 1000L); 39 | List postList = postMapper.listPostWithDelete(fiveMinutesAgoDate); 40 | if (CollectionUtils.isEmpty(postList)) { 41 | log.info("no inc post"); 42 | return; 43 | } 44 | List postEsDTOList = postList.stream() 45 | .map(PostEsDTO::objToDto) 46 | .collect(Collectors.toList()); 47 | final int pageSize = 500; 48 | int total = postEsDTOList.size(); 49 | log.info("IncSyncPostToEs start, total {}", total); 50 | for (int i = 0; i < total; i += pageSize) { 51 | int end = Math.min(i + pageSize, total); 52 | log.info("sync from {} to {}", i, end); 53 | postEsDao.saveAll(postEsDTOList.subList(i, end)); 54 | } 55 | log.info("IncSyncPostToEs end, total {}", total); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/controller/PostThumbController.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.controller; 2 | 3 | import com.leikooo.yubi.common.BaseResponse; 4 | import com.leikooo.yubi.common.ErrorCode; 5 | import com.leikooo.yubi.common.ResultUtils; 6 | import com.leikooo.yubi.exception.BusinessException; 7 | import com.leikooo.yubi.model.dto.postthumb.PostThumbAddRequest; 8 | import com.leikooo.yubi.model.entity.User; 9 | import com.leikooo.yubi.service.PostThumbService; 10 | import com.leikooo.yubi.service.UserService; 11 | import javax.annotation.Resource; 12 | import javax.servlet.http.HttpServletRequest; 13 | import lombok.extern.slf4j.Slf4j; 14 | import org.springframework.web.bind.annotation.PostMapping; 15 | import org.springframework.web.bind.annotation.RequestBody; 16 | import org.springframework.web.bind.annotation.RequestMapping; 17 | import org.springframework.web.bind.annotation.RestController; 18 | 19 | /** 20 | * 帖子点赞接口 21 | * 22 | * @author 程序员鱼皮 23 | * @from 编程导航知识星球 24 | */ 25 | @RestController 26 | @RequestMapping("/post_thumb") 27 | @Slf4j 28 | public class PostThumbController { 29 | 30 | @Resource 31 | private PostThumbService postThumbService; 32 | 33 | @Resource 34 | private UserService userService; 35 | 36 | /** 37 | * 点赞 / 取消点赞 38 | * 39 | * @param postThumbAddRequest 40 | * @param request 41 | * @return resultNum 本次点赞变化数 42 | */ 43 | @PostMapping("/") 44 | public BaseResponse doThumb(@RequestBody PostThumbAddRequest postThumbAddRequest, 45 | HttpServletRequest request) { 46 | if (postThumbAddRequest == null || postThumbAddRequest.getPostId() <= 0) { 47 | throw new BusinessException(ErrorCode.PARAMS_ERROR); 48 | } 49 | // 登录才能点赞 50 | final User loginUser = userService.getLoginUser(request); 51 | long postId = postThumbAddRequest.getPostId(); 52 | int result = postThumbService.doPostThumb(postId, loginUser); 53 | return ResultUtils.success(result); 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/model/dto/controller/ChartQueryController.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.model.dto.controller; 2 | 3 | import com.leikooo.yubi.common.PageRequest; 4 | import com.leikooo.yubi.exception.ThrowUtils; 5 | import io.swagger.models.auth.In; 6 | import lombok.AllArgsConstructor; 7 | import lombok.EqualsAndHashCode; 8 | import lombok.Getter; 9 | import lombok.NoArgsConstructor; 10 | import org.apache.commons.lang3.StringUtils; 11 | 12 | import java.io.Serializable; 13 | import java.util.Date; 14 | 15 | /** 16 | * 用户查询请求 17 | * 18 | * @author 程序员鱼皮 19 | * @from 编程导航知识星球 20 | */ 21 | @Getter 22 | @NoArgsConstructor 23 | @AllArgsConstructor 24 | @EqualsAndHashCode(callSuper = true) 25 | public class ChartQueryController extends PageRequest implements Serializable { 26 | /** 27 | * id 28 | */ 29 | private Long id; 30 | 31 | /** 32 | * 分析目标 33 | */ 34 | private String goal; 35 | 36 | /** 37 | * 图标名称 38 | */ 39 | private String chartName; 40 | 41 | /** 42 | * 图表类型 43 | */ 44 | private String chartType; 45 | 46 | /** 47 | * 创建用户 id 48 | */ 49 | private Long userId; 50 | 51 | /** 52 | * current 53 | */ 54 | private long current; 55 | 56 | /** 57 | * pageSize 58 | */ 59 | private long pageSize; 60 | 61 | /** 62 | * 创建时间 63 | */ 64 | private Date createTime; 65 | 66 | /** 67 | * 更新时间 68 | */ 69 | private Date updateTime; 70 | 71 | private static final long serialVersionUID = -3389509881984782940L; 72 | 73 | public ChartQueryController(String chartName, String goal, String chartType, Long userId, Long current, Long pageSize, Date createTime, Date updateTime) { 74 | this.goal = goal; 75 | this.chartName = chartName; 76 | this.chartType = chartType; 77 | this.userId = userId; 78 | this.current = current; 79 | this.pageSize = pageSize; 80 | this.createTime = createTime; 81 | this.updateTime = updateTime; 82 | } 83 | } -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/utils/ExcelUtils.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.utils; 2 | 3 | import com.alibaba.excel.EasyExcel; 4 | import com.alibaba.excel.support.ExcelTypeEnum; 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.apache.commons.lang3.ObjectUtils; 7 | import org.springframework.web.multipart.MultipartFile; 8 | import java.io.InputStream; 9 | import java.util.LinkedHashMap; 10 | import java.util.List; 11 | import java.util.Map; 12 | import java.util.StringJoiner; 13 | import java.util.stream.Collectors; 14 | 15 | /** 16 | * @author leikooo 17 | * @Description 18 | */ 19 | @Slf4j 20 | public class ExcelUtils { 21 | 22 | public static String getExcelFileName(final MultipartFile multipartFile) { 23 | try (InputStream inputStream = multipartFile.getInputStream()) { 24 | List> excelData = EasyExcel.read(inputStream).excelType(ExcelTypeEnum.XLSX).sheet().headRowNumber(0).doReadSync(); 25 | // 读取表头 LinkedHashMap 是按照顺序读取的 26 | LinkedHashMap headerMap = (LinkedHashMap) excelData.get(0); 27 | List headerList = headerMap.values().stream() 28 | .filter(ObjectUtils::isNotEmpty) 29 | .collect(Collectors.toList()); 30 | // 读取数据 31 | List dataLines = excelData.stream() 32 | .skip(1) // 跳过表头 33 | .map(dataMap -> dataMap.values().stream() 34 | .filter(ObjectUtils::isNotEmpty) 35 | .collect(Collectors.joining(","))) // 拼接数据字段 36 | .collect(Collectors.toList()); 37 | // 使用 StringJoiner 来构建 CSV 字符串 38 | StringJoiner csvContent = new StringJoiner("\n"); 39 | csvContent.add(String.join(",", headerList)); // 添加表头 40 | dataLines.forEach(csvContent::add); // 添加数据行 41 | // System.out.println(csvContent); 42 | return csvContent.toString(); 43 | } catch (Exception e) { 44 | log.error("读取excel文件失败: ", e); 45 | throw new RuntimeException(e); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/model/entity/User.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.model.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableField; 5 | import com.baomidou.mybatisplus.annotation.TableId; 6 | import com.baomidou.mybatisplus.annotation.TableName; 7 | import lombok.*; 8 | 9 | import java.io.Serializable; 10 | import java.util.Date; 11 | 12 | /** 13 | * 用户 14 | * @author leikooo 15 | * @TableName user 16 | */ 17 | @TableName(value ="user") 18 | @Getter 19 | @Setter 20 | @Builder 21 | @NoArgsConstructor 22 | @AllArgsConstructor 23 | public class User implements Serializable { 24 | /** 25 | * id 26 | */ 27 | @TableId(type = IdType.AUTO) 28 | private Long id; 29 | 30 | /** 31 | * 账号 32 | */ 33 | private String userAccount; 34 | 35 | /** 36 | * 密码 37 | */ 38 | private String userPassword; 39 | 40 | /** 41 | * 用户昵称 42 | */ 43 | private String userName; 44 | 45 | /** 46 | * 用户头像 47 | */ 48 | private String userAvatar; 49 | 50 | /** 51 | * 用户角色:user/admin 52 | */ 53 | private String userRole; 54 | 55 | /** 56 | * 创建时间 57 | */ 58 | private Date createTime; 59 | 60 | /** 61 | * 更新时间 62 | */ 63 | private Date updateTime; 64 | 65 | /** 66 | * 是否删除 67 | */ 68 | private Integer isDelete; 69 | 70 | @TableField(exist = false) 71 | private static final long serialVersionUID = 1L; 72 | 73 | public User(String userAccount, String userPassword) { 74 | this.userAccount = userAccount; 75 | this.userPassword = userPassword; 76 | } 77 | 78 | public User(Long userId, String userName, String userAvatar) { 79 | this.id = userId; 80 | this.userName = userName; 81 | this.userAvatar = userAvatar; 82 | } 83 | 84 | public static User newUser(String userAccount, String userPassword) { 85 | return User.builder() 86 | .userAvatar("https://yupi.icu/logo.png") 87 | .userAccount(userAccount) 88 | .userPassword(userPassword) 89 | .build(); 90 | } 91 | } -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/model/entity/Post.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.model.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableField; 5 | import com.baomidou.mybatisplus.annotation.TableId; 6 | import com.baomidou.mybatisplus.annotation.TableLogic; 7 | import com.baomidou.mybatisplus.annotation.TableName; 8 | import java.io.Serializable; 9 | import java.util.Date; 10 | import lombok.*; 11 | 12 | /** 13 | * 帖子 14 | * 15 | * @author 程序员鱼皮 16 | * @from 编程导航知识星球 17 | */ 18 | @TableName(value = "post") 19 | @Getter 20 | @Setter 21 | @NoArgsConstructor 22 | @AllArgsConstructor 23 | public class Post implements Serializable { 24 | 25 | /** 26 | * id 27 | */ 28 | @TableId(type = IdType.ASSIGN_ID) 29 | private Long id; 30 | 31 | /** 32 | * 标题 33 | */ 34 | private String title; 35 | 36 | /** 37 | * 内容 38 | */ 39 | private String content; 40 | 41 | /** 42 | * 标签列表 json 43 | */ 44 | private String tags; 45 | 46 | /** 47 | * 点赞数 48 | */ 49 | private Integer thumbNum; 50 | 51 | /** 52 | * 收藏数 53 | */ 54 | private Integer favourNum; 55 | 56 | /** 57 | * 创建用户 id 58 | */ 59 | private Long userId; 60 | 61 | /** 62 | * 创建时间 63 | */ 64 | private Date createTime; 65 | 66 | /** 67 | * 更新时间 68 | */ 69 | private Date updateTime; 70 | 71 | /** 72 | * 是否删除 73 | */ 74 | @TableLogic 75 | private Integer isDelete; 76 | 77 | @TableField(exist = false) 78 | private static final long serialVersionUID = 1L; 79 | 80 | public Post(Long id, String title, String content, String tags) { 81 | this.id = id; 82 | this.title = title; 83 | this.content = content; 84 | this.tags = tags; 85 | } 86 | 87 | public Post(String title, String content, String tags, Integer thumbNum, Integer favourNum, Long userId) { 88 | this.title = title; 89 | this.content = content; 90 | this.tags = tags; 91 | this.thumbNum = thumbNum; 92 | this.favourNum = favourNum; 93 | this.userId = userId; 94 | } 95 | } -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/aop/LogInterceptor.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.aop; 2 | 3 | import java.util.UUID; 4 | import javax.servlet.http.HttpServletRequest; 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.apache.commons.lang3.StringUtils; 7 | import org.aspectj.lang.ProceedingJoinPoint; 8 | import org.aspectj.lang.annotation.Around; 9 | import org.aspectj.lang.annotation.Aspect; 10 | import org.springframework.beans.BeansException; 11 | import org.springframework.context.ApplicationContext; 12 | import org.springframework.context.ApplicationContextAware; 13 | import org.springframework.expression.ExpressionParser; 14 | import org.springframework.expression.spel.standard.SpelExpressionParser; 15 | import org.springframework.stereotype.Component; 16 | import org.springframework.util.StopWatch; 17 | import org.springframework.web.context.request.RequestAttributes; 18 | import org.springframework.web.context.request.RequestContextHolder; 19 | import org.springframework.web.context.request.ServletRequestAttributes; 20 | 21 | /** 22 | * 请求响应日志 AOP 23 | * 24 | * @author 程序员鱼皮 25 | * @from 编程导航知识星球 26 | **/ 27 | @Aspect 28 | @Component 29 | @Slf4j 30 | public class LogInterceptor { 31 | 32 | /** 33 | * 执行拦截 34 | */ 35 | @Around("execution(* com.leikooo.yubi.controller.*.*(..))") 36 | public Object doInterceptor(ProceedingJoinPoint point) throws Throwable { 37 | // 计时 38 | StopWatch stopWatch = new StopWatch(); 39 | stopWatch.start(); 40 | // 获取请求路径 41 | RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes(); 42 | HttpServletRequest httpServletRequest = ((ServletRequestAttributes) requestAttributes).getRequest(); 43 | // 生成请求唯一 id 44 | String requestId = UUID.randomUUID().toString(); 45 | String url = httpServletRequest.getRequestURI(); 46 | // 获取请求参数 47 | Object[] args = point.getArgs(); 48 | String reqParam = "[" + StringUtils.join(args, ", ") + "]"; 49 | // 输出请求日志 50 | log.info("request start,id: {}, path: {}, ip: {}, params: {}", requestId, url, 51 | httpServletRequest.getRemoteHost(), reqParam); 52 | // 执行原方法 53 | Object result = point.proceed(); 54 | // 输出响应日志 55 | stopWatch.stop(); 56 | long totalTimeMillis = stopWatch.getTotalTimeMillis(); 57 | log.info("request end, id: {}, cost: {}ms", requestId, totalTimeMillis); 58 | return result; 59 | } 60 | 61 | } 62 | 63 | -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/mq/DLXDirectConsumer.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.mq; 2 | 3 | import com.rabbitmq.client.Channel; 4 | import com.rabbitmq.client.Connection; 5 | import com.rabbitmq.client.ConnectionFactory; 6 | import com.rabbitmq.client.DeliverCallback; 7 | 8 | /** 9 | * @author leikooo 10 | */ 11 | public class DLXDirectConsumer { 12 | 13 | public static void main(String[] argv) throws Exception { 14 | ConnectionFactory factory = new ConnectionFactory(); 15 | Connection connection = factory.newConnection(); 16 | Channel channel = connection.createChannel(); 17 | System.out.println(" [*] Waiting for messages. To exit press CTRL+C"); 18 | 19 | DeliverCallback deliverCallback1 = (consumerTag, delivery) -> { 20 | String message = new String(delivery.getBody(), "UTF-8"); 21 | System.out.println(" [yupi] Received '" + 22 | delivery.getEnvelope().getRoutingKey() + "':'" + message + "'"); 23 | channel.basicNack(delivery.getEnvelope().getDeliveryTag(), false, false); 24 | }; 25 | 26 | DeliverCallback deliverCallback2 = (consumerTag, delivery) -> { 27 | String message = new String(delivery.getBody(), "UTF-8"); 28 | System.out.println(" [leikooo] Received '" + 29 | delivery.getEnvelope().getRoutingKey() + "':'" + message + "'"); 30 | channel.basicNack(delivery.getEnvelope().getDeliveryTag(), false, false); 31 | }; 32 | 33 | DeliverCallback deliverCallback3 = (consumerTag, delivery) -> { 34 | String message = new String(delivery.getBody(), "UTF-8"); 35 | System.out.println(" [boss] 死信队列 Received '" + 36 | delivery.getEnvelope().getRoutingKey() + "':'" + message + "'"); 37 | channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false); 38 | }; 39 | 40 | DeliverCallback deliverCallback4 = (consumerTag, delivery) -> { 41 | String message = new String(delivery.getBody(), "UTF-8"); 42 | System.out.println(" [outsourcing] 死信队列 Received '" + 43 | delivery.getEnvelope().getRoutingKey() + "':'" + message + "'"); 44 | channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false); 45 | }; 46 | 47 | channel.basicConsume("小王", false, deliverCallback1, consumerTag -> { 48 | }); 49 | channel.basicConsume("小李", false, deliverCallback2, consumerTag -> { 50 | }); 51 | channel.basicConsume("boss", false, deliverCallback3, consumerTag -> {}); 52 | 53 | channel.basicConsume("outsourcing", false, deliverCallback3, consumerTag -> {}); 54 | } 55 | } -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/mq/TopicConsumer.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.mq; 2 | 3 | import com.rabbitmq.client.Channel; 4 | import com.rabbitmq.client.Connection; 5 | import com.rabbitmq.client.ConnectionFactory; 6 | import com.rabbitmq.client.DeliverCallback; 7 | 8 | /** 9 | * @author leikooo 10 | */ 11 | public class TopicConsumer { 12 | 13 | private static final String EXCHANGE_NAME = "topic-exchange"; 14 | 15 | public static void main(String[] argv) throws Exception { 16 | ConnectionFactory factory = new ConnectionFactory(); 17 | 18 | Connection connection = factory.newConnection(); 19 | Channel channel = connection.createChannel(); 20 | 21 | channel.exchangeDeclare(EXCHANGE_NAME, "topic"); 22 | System.out.println(" [*] Waiting for messages. To exit press CTRL+C"); 23 | 24 | String backendQueue = "backend_queue"; 25 | channel.queueDeclare(backendQueue, true, false, false, null); 26 | // 绑定路由 routingKey 规则 27 | channel.queueBind(backendQueue, EXCHANGE_NAME, "#.后端.#"); 28 | 29 | String frontedQueue = "frontend_queue"; 30 | channel.queueDeclare(frontedQueue, true, false, false, null); 31 | channel.queueBind(frontedQueue, EXCHANGE_NAME, "#.前端.#"); 32 | 33 | String productQueue = "product_queue"; 34 | channel.queueDeclare(productQueue, true, false, false, null); 35 | channel.queueBind(productQueue, EXCHANGE_NAME, "#.产品.#"); 36 | 37 | DeliverCallback frontendDeliverCallback = (consumerTag, delivery) -> { 38 | String message = new String(delivery.getBody(), "UTF-8"); 39 | System.out.println(" [fronted] Received '" + 40 | delivery.getEnvelope().getRoutingKey() + "':'" + message + "'"); 41 | }; 42 | 43 | DeliverCallback backendDeliverCallback = (consumerTag, delivery) -> { 44 | String message = new String(delivery.getBody(), "UTF-8"); 45 | System.out.println(" [backend] Received '" + delivery.getEnvelope().getRoutingKey() + "':'" + message + "'"); 46 | }; 47 | 48 | DeliverCallback productDeliverCallback = (consumerTag, delivery) -> { 49 | String message = new String(delivery.getBody(), "UTF-8"); 50 | System.out.println(" [product] Received '" + delivery.getEnvelope().getRoutingKey() + "':'" + message + "'"); 51 | }; 52 | 53 | channel.basicConsume(backendQueue, true, backendDeliverCallback, consumerTag -> { 54 | }); 55 | 56 | channel.basicConsume(frontedQueue, true, frontendDeliverCallback, consumerTag -> { 57 | }); 58 | 59 | channel.basicConsume(productQueue, true, productDeliverCallback, consumerTag -> {}); 60 | 61 | } 62 | } -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/aop/AuthInterceptor.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.aop; 2 | 3 | import com.leikooo.yubi.annotation.AuthCheck; 4 | import com.leikooo.yubi.common.ErrorCode; 5 | import com.leikooo.yubi.exception.BusinessException; 6 | import com.leikooo.yubi.model.entity.User; 7 | import com.leikooo.yubi.model.enums.UserRoleEnum; 8 | import com.leikooo.yubi.service.UserService; 9 | import javax.annotation.Resource; 10 | import javax.servlet.http.HttpServletRequest; 11 | import org.apache.commons.lang3.StringUtils; 12 | import org.aspectj.lang.ProceedingJoinPoint; 13 | import org.aspectj.lang.annotation.Around; 14 | import org.aspectj.lang.annotation.Aspect; 15 | import org.aspectj.lang.annotation.Before; 16 | import org.springframework.stereotype.Component; 17 | import org.springframework.web.context.request.RequestAttributes; 18 | import org.springframework.web.context.request.RequestContextHolder; 19 | import org.springframework.web.context.request.ServletRequestAttributes; 20 | 21 | /** 22 | * 权限校验 AOP 23 | * 24 | * @author 程序员鱼皮 25 | * @from 编程导航知识星球 26 | */ 27 | @Aspect 28 | @Component 29 | public class AuthInterceptor { 30 | 31 | @Resource 32 | private UserService userService; 33 | 34 | /** 35 | * 执行拦截 36 | * 37 | * @param joinPoint 38 | * @param authCheck 39 | * @return 40 | */ 41 | @Around("@annotation(authCheck)") 42 | public Object doInterceptor(ProceedingJoinPoint joinPoint, AuthCheck authCheck) throws Throwable { 43 | String mustRole = authCheck.mustRole(); 44 | RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes(); 45 | HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest(); 46 | // 当前登录用户 47 | User loginUser = userService.getLoginUser(request); 48 | // 必须有该权限才通过 49 | if (StringUtils.isNotBlank(mustRole)) { 50 | UserRoleEnum mustUserRoleEnum = UserRoleEnum.getEnumByValue(mustRole); 51 | if (mustUserRoleEnum == null) { 52 | throw new BusinessException(ErrorCode.NO_AUTH_ERROR); 53 | } 54 | String userRole = loginUser.getUserRole(); 55 | // 如果被封号,直接拒绝 56 | if (UserRoleEnum.BAN.equals(mustUserRoleEnum)) { 57 | throw new BusinessException(ErrorCode.NO_AUTH_ERROR); 58 | } 59 | // 必须有管理员权限 60 | if (UserRoleEnum.ADMIN.equals(mustUserRoleEnum)) { 61 | if (!mustRole.equals(userRole)) { 62 | throw new BusinessException(ErrorCode.NO_AUTH_ERROR); 63 | } 64 | } 65 | } 66 | // 通过权限校验,放行 67 | return joinPoint.proceed(); 68 | } 69 | } 70 | 71 | -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/model/vo/PostVO.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.model.vo; 2 | 3 | import com.google.gson.Gson; 4 | import com.google.gson.reflect.TypeToken; 5 | import com.leikooo.yubi.model.entity.Post; 6 | import java.io.Serializable; 7 | import java.util.Collections; 8 | import java.util.Date; 9 | import java.util.List; 10 | import java.util.Optional; 11 | 12 | import lombok.Data; 13 | import org.apache.poi.openxml4j.opc.OPCPackage; 14 | import org.springframework.beans.BeanUtils; 15 | 16 | /** 17 | * 帖子视图 18 | * 19 | * @author 程序员鱼皮 20 | * @from 编程导航知识星球 21 | */ 22 | @Data 23 | public class PostVO implements Serializable { 24 | 25 | private final static Gson GSON = new Gson(); 26 | 27 | /** 28 | * id 29 | */ 30 | private Long id; 31 | 32 | /** 33 | * 标题 34 | */ 35 | private String title; 36 | 37 | /** 38 | * 内容 39 | */ 40 | private String content; 41 | 42 | /** 43 | * 点赞数 44 | */ 45 | private Integer thumbNum; 46 | 47 | /** 48 | * 收藏数 49 | */ 50 | private Integer favourNum; 51 | 52 | /** 53 | * 创建用户 id 54 | */ 55 | private Long userId; 56 | 57 | /** 58 | * 创建时间 59 | */ 60 | private Date createTime; 61 | 62 | /** 63 | * 更新时间 64 | */ 65 | private Date updateTime; 66 | 67 | /** 68 | * 标签列表 69 | */ 70 | private List tagList; 71 | 72 | /** 73 | * 创建人信息 74 | */ 75 | private UserVO user; 76 | 77 | /** 78 | * 是否已点赞 79 | */ 80 | private Boolean hasThumb; 81 | 82 | /** 83 | * 是否已收藏 84 | */ 85 | private Boolean hasFavour; 86 | 87 | /** 88 | * 包装类转对象 89 | * 90 | * @param postVO 91 | * @return 92 | */ 93 | public static Post voToObj(PostVO postVO) { 94 | if (postVO == null) { 95 | return null; 96 | } 97 | return new Post(postVO.getId(), postVO.getTitle(), postVO.getContent(), GSON.toJson(Optional.ofNullable(postVO.getTagList()).orElse(Collections.emptyList())), postVO.getThumbNum(), postVO.getFavourNum(), postVO.getUserId(), postVO.getCreateTime(), postVO.getUpdateTime(), 0); 98 | } 99 | 100 | /** 101 | * 对象转包装类 102 | * 103 | * @param post 104 | * @return 105 | */ 106 | public static PostVO objToVo(Post post) { 107 | if (post == null) { 108 | return null; 109 | } 110 | PostVO postVO = new PostVO(); 111 | BeanUtils.copyProperties(post, postVO); 112 | postVO.setTagList(GSON.fromJson(post.getTags(), new TypeToken>() { 113 | }.getType())); 114 | return postVO; 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/service/UserService.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.service; 2 | 3 | import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; 4 | import com.baomidou.mybatisplus.extension.service.IService; 5 | import com.leikooo.yubi.model.entity.User; 6 | import com.leikooo.yubi.model.user.UserQueryRequest; 7 | import com.leikooo.yubi.model.vo.LoginUserVO; 8 | import com.leikooo.yubi.model.vo.UserVO; 9 | import java.util.List; 10 | import javax.servlet.http.HttpServletRequest; 11 | 12 | /** 13 | * 用户服务 14 | * 15 | * @author 程序员鱼皮 16 | * @from 编程导航知识星球 17 | */ 18 | public interface UserService extends IService { 19 | 20 | /** 21 | * 用户注册 22 | * 23 | * @param userAccount 用户账户 24 | * @param userPassword 用户密码 25 | * @param checkPassword 校验密码 26 | * @return 新用户 id 27 | */ 28 | long userRegister(String userAccount, String userPassword, String checkPassword); 29 | 30 | /** 31 | * 用户登录 32 | * 33 | * @param userAccount 用户账户 34 | * @param userPassword 用户密码 35 | * @param request 36 | * @return 脱敏后的用户信息 37 | */ 38 | LoginUserVO userLogin(String userAccount, String userPassword, HttpServletRequest request); 39 | 40 | 41 | /** 42 | * 获取当前登录用户 43 | * 44 | * @param request 45 | * @return 46 | */ 47 | User getLoginUser(HttpServletRequest request); 48 | 49 | /** 50 | * 获取当前登录用户(允许未登录) 51 | * 52 | * @param request 53 | * @return 54 | */ 55 | User getLoginUserPermitNull(HttpServletRequest request); 56 | 57 | /** 58 | * 是否为管理员 59 | * 60 | * @param request 61 | * @return 62 | */ 63 | boolean isAdmin(HttpServletRequest request); 64 | 65 | /** 66 | * 是否为管理员 67 | * 68 | * @param user 69 | * @return 70 | */ 71 | boolean isAdmin(User user); 72 | 73 | /** 74 | * 用户注销 75 | * 76 | * @param request 77 | * @return 78 | */ 79 | boolean userLogout(HttpServletRequest request); 80 | 81 | /** 82 | * 获取脱敏的已登录用户信息 83 | * 84 | * @return 85 | */ 86 | LoginUserVO getLoginUserVO(User user); 87 | 88 | /** 89 | * 获取脱敏的用户信息 90 | * 91 | * @param user 92 | * @return 93 | */ 94 | UserVO getUserVO(User user); 95 | 96 | /** 97 | * 获取脱敏的用户信息 98 | * 99 | * @param userList 100 | * @return 101 | */ 102 | List getUserVO(List userList); 103 | 104 | /** 105 | * 获取查询条件 106 | * 107 | * @param userQueryRequest 108 | * @return 109 | */ 110 | QueryWrapper getQueryWrapper(UserQueryRequest userQueryRequest); 111 | 112 | } 113 | -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/job/cycle/ReGenChartData.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.job.cycle; 2 | 3 | import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; 4 | import com.leikooo.yubi.constant.ChartConstant; 5 | import com.leikooo.yubi.manager.AIManager; 6 | import com.leikooo.yubi.mapper.ChartMapper; 7 | import com.leikooo.yubi.model.dto.chart.ChartGenResult; 8 | import com.leikooo.yubi.model.entity.Chart; 9 | import com.leikooo.yubi.model.enums.ResultEnum; 10 | import com.leikooo.yubi.service.ChartService; 11 | import com.leikooo.yubi.utils.ChartDataUtil; 12 | import lombok.extern.slf4j.Slf4j; 13 | import org.springframework.scheduling.annotation.Scheduled; 14 | import org.springframework.stereotype.Component; 15 | 16 | import javax.annotation.Resource; 17 | import java.util.List; 18 | import java.util.Map; 19 | import java.util.concurrent.CompletableFuture; 20 | import java.util.concurrent.ThreadPoolExecutor; 21 | 22 | /** 23 | * @author leikooo 24 | * @description 向每隔 5分钟 执行一个在数据库中捞取数据的操作 25 | */ 26 | @Component 27 | @Slf4j 28 | public class ReGenChartData { 29 | @Resource 30 | private ChartMapper chartMapper; 31 | @Resource 32 | private AIManager aiManager; 33 | @Resource 34 | private ChartService chartService; 35 | @Resource 36 | private ThreadPoolExecutor threadPoolExecutor; 37 | 38 | @Scheduled(cron = "0 0/5 * * * ?") // Every 5 minutes 39 | public void doUpdateFailedChart() { 40 | QueryWrapper queryWrapper = new QueryWrapper<>(); 41 | queryWrapper.eq("status", ResultEnum.FAILED.getDes()); 42 | List failedCharts = chartMapper.selectList(queryWrapper); 43 | failedCharts.forEach(this::updateFailedChartAsync); 44 | } 45 | 46 | /** 47 | * 同步更新失败的图表 48 | * 49 | * @param chart 50 | */ 51 | private void updateFailedChart(final Chart chart) { 52 | Long chartId = chart.getId(); 53 | List> chartOriginalData = chartMapper.queryChartData(chartId); 54 | String cvsData = ChartDataUtil.changeDataToCSV(chartOriginalData); 55 | ChartGenResult result = ChartDataUtil.getGenResult(aiManager, chart.getGoal(), cvsData, chart.getChartType()); 56 | try { 57 | chartService.updateById(new Chart(chartId, result.getGenChart(), result.getGenResult(), ResultEnum.SUCCEED.getDes(), "")); 58 | } catch (Exception e) { 59 | chartService.updateById(new Chart(chartId, ResultEnum.FAILED.getDes(), e.getMessage())); 60 | log.error("更新图表数据失败,chartId:{}, error:{}", chartId, e.getMessage()); 61 | } 62 | } 63 | 64 | /** 65 | * 异步更新失败的图表 66 | * 67 | * @param chart 68 | */ 69 | private void updateFailedChartAsync(final Chart chart) { 70 | CompletableFuture.runAsync(() -> { 71 | updateFailedChart(chart); 72 | }, threadPoolExecutor); 73 | } 74 | } -------------------------------------------------------------------------------- /src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | config: 3 | name: application-pro 4 | application: 5 | name: yubi-backend 6 | profiles: 7 | active: dev 8 | # 支持 swagger3 9 | mvc: 10 | pathmatch: 11 | matching-strategy: ant_path_matcher 12 | # session 配置 13 | session: 14 | store-type: redis 15 | timeout: 2592000 16 | # 数据库配置 17 | datasource: 18 | driver-class-name: com.mysql.cj.jdbc.Driver 19 | url: jdbc:mysql://${yubi.mysql.ip}:${yubi.mysql.port}/${yubi.mysql.db}?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai 20 | username: ${yubi.mysql.username} 21 | password: ${yubi.mysql.password} 22 | # Redis 配置 23 | redis: 24 | database: ${yubi.redis.database} 25 | host: ${yubi.redis.host} 26 | port: ${yubi.redis.port} 27 | password: ${yubi.redis.password} 28 | timeout: 5000 29 | # Elasticsearch 配置 30 | # elasticsearch: 31 | # uris: http://localhost:9200 32 | # username: root 33 | # password: 123456 34 | # 文件上传 35 | servlet: 36 | multipart: 37 | # 大小限制 38 | max-file-size: 10MB 39 | rabbitmq: 40 | host: ${rabbitmq.host} 41 | port: ${rabbitmq.port} 42 | username: ${rabbitmq.username} 43 | password: ${rabbitmq.password} 44 | server: 45 | address: 0.0.0.0 46 | port: 9090 47 | # ssl: 48 | # key-store: classpath:_.leikooo.com.jks 49 | # key-store-type: JKS 50 | # key-store-password: ${ssl.key-password} 51 | servlet: 52 | context-path: /api 53 | # cookie 30 天过期 54 | session: 55 | cookie: 56 | max-age: 2592000 57 | # same-site: none 58 | # secure: true 59 | # domain: 60 | mybatis-plus: 61 | configuration: 62 | map-underscore-to-camel-case: false 63 | log-impl: org.apache.ibatis.logging.stdout.StdOutImpl 64 | global-config: 65 | db-config: 66 | logic-delete-field: isDelete # 全局逻辑删除的实体字段名 67 | logic-delete-value: 1 # 逻辑已删除值(默认为 1) 68 | logic-not-delete-value: 0 # 逻辑未删除值(默认为 0) 69 | # 微信相关 70 | wx: 71 | # 微信公众平台 72 | mp: 73 | token: xxx 74 | aesKey: xxx 75 | appId: xxx 76 | secret: xxx 77 | config-storage: 78 | http-client-type: HttpClient 79 | key-prefix: wx 80 | redis: 81 | host: 127.0.0.1 82 | port: 6379 83 | type: Memory 84 | # 微信开放平台 85 | # todo 需替换配置 86 | open: 87 | appId: xxx 88 | appSecret: xxx 89 | # 对象存储 90 | # todo 需替换配置 91 | cos: 92 | client: 93 | accessKey: xxx 94 | secretKey: xxx 95 | region: xxx 96 | bucket: xxx 97 | ## 阿里云百炼 98 | bai-lian: 99 | agentKey: ${bai-lian.agentKey} 100 | accessKeySecret: ${bai-lian.accessKeySecret} 101 | accessKeyId: ${bai-lian.accessKeyId} 102 | ## 讯飞 AI 配置 103 | xun-fei: 104 | client: 105 | appId: ${xun-fei.client.appId} 106 | apiSecret: ${xun-fei.client.apiSecret} 107 | apiKey: ${xun-fei.client.apiKey} 108 | -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/mq/DLXDirectProducer.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.mq; 2 | 3 | import com.rabbitmq.client.Channel; 4 | import com.rabbitmq.client.Connection; 5 | import com.rabbitmq.client.ConnectionFactory; 6 | 7 | import java.util.HashMap; 8 | import java.util.Map; 9 | import java.util.Scanner; 10 | 11 | /** 12 | * @author leikooo 13 | * 死信 14 | */ 15 | public class DLXDirectProducer { 16 | 17 | private static final String EXCHANGE_NAME = "direct-exchange"; 18 | private static final String DLX_EXCHANGE_NAME = "dlx_direct-exchange"; 19 | 20 | public static void main(String[] argv) throws Exception { 21 | ConnectionFactory factory = new ConnectionFactory(); 22 | 23 | try (Connection connection = factory.newConnection(); 24 | Channel channel = connection.createChannel()) { 25 | // 声明消息处理的交换机 26 | channel.exchangeDeclare(EXCHANGE_NAME, "direct"); 27 | // 声明死信交换机 28 | channel.exchangeDeclare(DLX_EXCHANGE_NAME, "direct"); 29 | // 声明死信队列 30 | String queueName3 = "boss"; 31 | channel.queueDeclare(queueName3, true, false, false, null); 32 | channel.queueBind(queueName3, DLX_EXCHANGE_NAME, "boss", null); 33 | 34 | String queueName4 = "outsourcing"; 35 | channel.queueDeclare(queueName4, true, false, false, null); 36 | channel.queueBind(queueName4, DLX_EXCHANGE_NAME, "outsourcing", null); 37 | 38 | // 正常的消息队列 39 | String queueName = "小王"; 40 | Map args = new HashMap<>(); 41 | args.put("x-dead-letter-exchange", DLX_EXCHANGE_NAME); 42 | args.put("x-dead-letter-routing-key", "outsourcing"); 43 | channel.queueDeclare(queueName, true, false, false, args); 44 | channel.queueBind(queueName, EXCHANGE_NAME, "小王", null); 45 | 46 | String queueName2 = "小李"; 47 | Map args2 = new HashMap<>(); 48 | args2.put("x-dead-letter-exchange", DLX_EXCHANGE_NAME); 49 | args2.put("x-dead-letter-routing-key", "boss"); 50 | channel.queueDeclare(queueName2, true, false, false, args2); 51 | channel.queueBind(queueName2, EXCHANGE_NAME, "小李", null); 52 | 53 | Scanner sc = new Scanner(System.in); 54 | while (sc.hasNext()) { 55 | String userInput = sc.nextLine(); 56 | String[] strings = userInput.split(" "); 57 | if (strings.length < 1) { 58 | continue; 59 | } 60 | String routingKey = strings[0]; 61 | String message = strings[1]; 62 | channel.basicPublish(EXCHANGE_NAME, routingKey, null, message.getBytes("UTF-8")); 63 | System.out.println(" [x] Sent routingKey'" + routingKey + "' message:'" + message + "'"); 64 | } 65 | } 66 | } 67 | } -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/utils/ChartDataUtil.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.utils; 2 | 3 | import cn.hutool.core.util.ObjectUtil; 4 | import com.leikooo.yubi.common.ErrorCode; 5 | import com.leikooo.yubi.exception.ThrowUtils; 6 | import com.leikooo.yubi.manager.AIManager; 7 | import com.leikooo.yubi.model.dto.chart.ChartGenResult; 8 | import lombok.extern.slf4j.Slf4j; 9 | import org.springframework.stereotype.Component; 10 | 11 | import java.util.List; 12 | import java.util.Map; 13 | import java.util.Set; 14 | import java.util.StringJoiner; 15 | import java.util.stream.Collectors; 16 | 17 | /** 18 | * @author leikooo 19 | * @description 把查询到的数据转换成字符串 20 | *

21 | * 用户id, 用户数据, 用户增量 22 | * 1, 200, 10 23 | * 2, 200, 20 24 | * 3, 800, 10 25 | */ 26 | @Component 27 | @Slf4j 28 | public class ChartDataUtil { 29 | 30 | public static String changeDataToCSV(List> chartOriginalData) { 31 | ThrowUtils.throwIf(chartOriginalData.size() == 0, ErrorCode.NOT_FOUND_ERROR); 32 | List> columnSets = chartOriginalData.stream() 33 | .map(Map::keySet) 34 | .collect(Collectors.toList()); 35 | List columnHeader = columnSets.stream() 36 | .map(column -> column.stream().filter(ObjectUtil::isNotNull).collect(Collectors.joining(","))) 37 | .collect(Collectors.toList()); 38 | // 拿到对应的 value 拼接上 39 | List columnDataList = chartOriginalData.stream().map(columnData -> { 40 | StringBuilder result = new StringBuilder(); 41 | String[] headers = columnHeader.get(0).split(","); 42 | for (int i = 0; i < headers.length; i++) { 43 | String data = (String) columnData.get(headers[i]); 44 | result.append(data); 45 | if (i != headers.length - 1) { 46 | result.append(","); 47 | } 48 | } 49 | result.append("\n"); 50 | return result.toString(); 51 | }).collect(Collectors.toList()); 52 | // 将 columnDataList 中的数据添加到 stringJoiner 53 | StringJoiner stringJoiner = new StringJoiner(""); 54 | stringJoiner.add(columnHeader.get(0)).add("\n"); 55 | columnDataList.forEach(stringJoiner::add); 56 | return stringJoiner.toString(); 57 | } 58 | 59 | /** 60 | * 获取 AI 生成结果 61 | * 62 | * @param aiManager AI 能力 63 | * @param goal 64 | * @param cvsData 65 | * @param chartType 66 | * @return 67 | */ 68 | public static ChartGenResult getGenResult(final AIManager aiManager, final String goal, final String cvsData, final String chartType) { 69 | String promote = "分析需求 " + goal + " \n原始数据如下: " + cvsData + "\n生成图标的类型是: " + chartType; 70 | String resultData = aiManager.sendMsgToXingHuo(true, promote); 71 | log.info("AI 生成的信息: {}", resultData); 72 | ThrowUtils.throwIf(resultData.split("'【【【【【'").length < 3, ErrorCode.SYSTEM_ERROR); 73 | String genChart = resultData.split("'【【【【【'")[1].trim(); 74 | String genResult = resultData.split("'【【【【【'")[2].trim(); 75 | return new ChartGenResult(genChart, genResult); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /.github/workflows/github-actions-demo.yml: -------------------------------------------------------------------------------- 1 | name: Docker Actions 2 | run-name: ${{ github.actor }} is use GitHub Actions 🚀 3 | on: [push] 4 | env: 5 | DOCKER_VERSION: ${{ secrets.DOCKER_VERSION }} 6 | jobs: 7 | build: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - name: check code 11 | uses: actions/checkout@v1 12 | - name: Set up JDK 17 13 | uses: actions/setup-java@v1 14 | with: 15 | java-version: 17 16 | - name: Decrypt configuration file 17 | run: | 18 | cd src/main/resources 19 | openssl aes-256-cbc -d -in application-dev.properties.enc -out application-dev.properties -k ${{ secrets.CONFIG_PASSWORD }} 20 | - name: Build with Maven 21 | run: | 22 | pwd 23 | ls -a 24 | mvn -B package -DskipTests=true -Dmaven.test.skip=true --file pom.xml 25 | # 26 | - name: Upload build artifact 27 | uses: actions/upload-artifact@v4.3.6 28 | with: 29 | name: build-artifact 30 | path: target/yubi-backend-0.0.1-SNAPSHOT.jar 31 | deploy: 32 | name: Deploy 33 | needs: [build] 34 | runs-on: ubuntu-latest 35 | steps: 36 | - name: Checkout 37 | uses: actions/checkout@v1 38 | - name: Download build artifact 39 | uses: actions/download-artifact@v4.1.7 40 | with: 41 | name: build-artifact 42 | path: ./target/ 43 | - name: login docker account 44 | uses: docker/login-action@v3.3.0 45 | with: 46 | registry: docker.io 47 | # Username used to log against the Docker registry 48 | username: ${{ secrets.DOCKER_USERNAME }} 49 | # Password or personal access token used to log against the Docker registry 50 | password: ${{ secrets.DOCKER_PASSWORD }} 51 | 52 | - name: build docker image 53 | run: | 54 | ls -a 55 | pwd 56 | docker build -t leikooo/leikoobi-backend:$DOCKER_VERSION . 57 | - name: push Image to Docker hub 58 | run: docker push leikooo/leikoobi-backend:$DOCKER_VERSION 59 | - name: login service pull docker image and run 60 | uses: appleboy/ssh-action@v1.0.3 61 | with: 62 | host: ${{ secrets.SERVER_HOST }} 63 | port: ${{ secrets.SERVER_PORT }} 64 | username: ${{ secrets.SERVER_USER }} 65 | password: ${{ secrets.SERVER_PASSWORD }} 66 | envs: DOCKER_VERSION 67 | script: | 68 | # 检查 9090 端口是否被占用 69 | if lsof -i:9090; then 70 | echo "Port 9090 is in use. Killing the process." 71 | # 获取占用端口的进程ID并终止它 72 | kill -9 $(lsof -ti:9090) 73 | fi 74 | 75 | # 拉取最新的 Docker 镜像 76 | docker pull leikooo/leikoobi-backend:$DOCKER_VERSION 77 | 78 | # 检查是否有已经运行的同名容器 79 | if docker ps -a --filter "name=leikoobi-backend" --format "{{.ID}}"; then 80 | echo "Stopping and removing existing container." 81 | docker stop leikoobi-backend || true 82 | docker rm leikoobi-backend || true 83 | fi 84 | 85 | # 运行新的容器 86 | docker run --name leikoobi-backend -p 9090:9090 -d leikooo/leikoobi-backend:$DOCKER_VERSION 87 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### @author 程序员鱼皮 ### 2 | ### @from 编程导航知识星球 ### 3 | 4 | HELP.md 5 | target/ 6 | !.mvn/wrapper/maven-wrapper.jar 7 | !**/src/main/**/target/ 8 | !**/src/test/**/target/ 9 | 10 | ### STS ### 11 | .apt_generated 12 | .classpath 13 | .factorypath 14 | .project 15 | .settings 16 | .springBeans 17 | .sts4-cache 18 | 19 | ### IntelliJ IDEA ### 20 | .idea 21 | *.iws 22 | *.iml 23 | *.ipr 24 | 25 | ### NetBeans ### 26 | /nbproject/private/ 27 | /nbbuild/ 28 | /dist/ 29 | /nbdist/ 30 | /.nb-gradle/ 31 | build/ 32 | !**/src/main/**/build/ 33 | !**/src/test/**/build/ 34 | 35 | ### VS Code ### 36 | .vscode/ 37 | ### Java template 38 | # Compiled class file 39 | *.class 40 | 41 | # Log file 42 | *.log 43 | 44 | # BlueJ files 45 | *.ctxt 46 | 47 | # Mobile Tools for Java (J2ME) 48 | .mtj.tmp/ 49 | 50 | # Package Files # 51 | *.jar 52 | *.war 53 | *.nar 54 | *.ear 55 | *.zip 56 | *.tar.gz 57 | *.rar 58 | 59 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 60 | hs_err_pid* 61 | 62 | ### Maven template 63 | target/ 64 | pom.xml.tag 65 | pom.xml.releaseBackup 66 | pom.xml.versionsBackup 67 | pom.xml.next 68 | release.properties 69 | dependency-reduced-pom.xml 70 | buildNumber.properties 71 | .mvn/timing.properties 72 | # https://github.com/takari/maven-wrapper#usage-without-binary-jar 73 | .mvn/wrapper/maven-wrapper.jar 74 | 75 | ### JetBrains template 76 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider 77 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 78 | 79 | # User-specific stuff 80 | .idea/**/workspace.xml 81 | .idea/**/tasks.xml 82 | .idea/**/usage.statistics.xml 83 | .idea/**/dictionaries 84 | .idea/**/shelf 85 | 86 | # Generated files 87 | .idea/**/contentModel.xml 88 | 89 | # Sensitive or high-churn files 90 | .idea/**/dataSources/ 91 | .idea/**/dataSources.ids 92 | .idea/**/dataSources.local.xml 93 | .idea/**/sqlDataSources.xml 94 | .idea/**/dynamic.xml 95 | .idea/**/uiDesigner.xml 96 | .idea/**/dbnavigator.xml 97 | 98 | # Gradle 99 | .idea/**/gradle.xml 100 | .idea/**/libraries 101 | 102 | # Gradle and Maven with auto-import 103 | # When using Gradle or Maven with auto-import, you should exclude module files, 104 | # since they will be recreated, and may cause churn. Uncomment if using 105 | # auto-import. 106 | # .idea/artifacts 107 | # .idea/compiler.xml 108 | # .idea/jarRepositories.xml 109 | # .idea/modules.xml 110 | # .idea/*.iml 111 | # .idea/modules 112 | # *.iml 113 | # *.ipr 114 | 115 | # CMake 116 | cmake-build-*/ 117 | 118 | # Mongo Explorer plugin 119 | .idea/**/mongoSettings.xml 120 | 121 | # File-based project format 122 | *.iws 123 | 124 | # IntelliJ 125 | out/ 126 | 127 | # mpeltonen/sbt-idea plugin 128 | .idea_modules/ 129 | 130 | # JIRA plugin 131 | atlassian-ide-plugin.xml 132 | 133 | # Cursive Clojure plugin 134 | .idea/replstate.xml 135 | 136 | # Crashlytics plugin (for Android Studio and IntelliJ) 137 | com_crashlytics_export_strings.xml 138 | crashlytics.properties 139 | crashlytics-build.properties 140 | fabric.properties 141 | 142 | # Editor-based Rest Client 143 | .idea/httpRequests 144 | 145 | # Android studio 3.1+ serialized cache file 146 | .idea/caches/build_file_checksums.ser 147 | 148 | /src/main/resources/application-dev.properties 149 | /src/main/resources/application-dev.properties 150 | -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/model/dto/post/PostEsDTO.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.model.dto.post; 2 | 3 | import com.google.common.reflect.TypeToken; 4 | import com.google.gson.Gson; 5 | import com.leikooo.yubi.model.entity.Post; 6 | import java.io.Serializable; 7 | import java.nio.file.OpenOption; 8 | import java.util.Collections; 9 | import java.util.Date; 10 | import java.util.List; 11 | import java.util.Optional; 12 | 13 | import lombok.Data; 14 | import org.apache.commons.collections4.CollectionUtils; 15 | import org.apache.commons.lang3.StringUtils; 16 | import org.springframework.beans.BeanUtils; 17 | import org.springframework.data.annotation.Id; 18 | import org.springframework.data.elasticsearch.annotations.Field; 19 | import org.springframework.data.elasticsearch.annotations.FieldType; 20 | 21 | import javax.swing.text.html.Option; 22 | 23 | /** 24 | * 帖子 ES 包装类 25 | * 26 | * @author 程序员鱼皮 27 | * @from 编程导航知识星球 28 | **/ 29 | // todo 取消注释开启 ES(须先配置 ES) 30 | //@Document(indexName = "post") 31 | @Data 32 | public class PostEsDTO implements Serializable { 33 | 34 | private static final String DATE_TIME_PATTERN = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"; 35 | 36 | /** 37 | * id 38 | */ 39 | @Id 40 | private Long id; 41 | 42 | /** 43 | * 标题 44 | */ 45 | private String title; 46 | 47 | /** 48 | * 内容 49 | */ 50 | private String content; 51 | 52 | /** 53 | * 标签列表 54 | */ 55 | private List tags; 56 | 57 | /** 58 | * 点赞数 59 | */ 60 | private Integer thumbNum; 61 | 62 | /** 63 | * 收藏数 64 | */ 65 | private Integer favourNum; 66 | 67 | /** 68 | * 创建用户 id 69 | */ 70 | private Long userId; 71 | 72 | /** 73 | * 创建时间 74 | */ 75 | @Field(index = false, store = true, type = FieldType.Date, format = {}, pattern = DATE_TIME_PATTERN) 76 | private Date createTime; 77 | 78 | /** 79 | * 更新时间 80 | */ 81 | @Field(index = false, store = true, type = FieldType.Date, format = {}, pattern = DATE_TIME_PATTERN) 82 | private Date updateTime; 83 | 84 | /** 85 | * 是否删除 86 | */ 87 | private Integer isDelete; 88 | 89 | private static final long serialVersionUID = 1L; 90 | 91 | private static final Gson GSON = new Gson(); 92 | 93 | /** 94 | * 对象转包装类 95 | * 96 | * @param post 97 | * @return 98 | */ 99 | public static PostEsDTO objToDto(Post post) { 100 | if (post == null) { 101 | return null; 102 | } 103 | PostEsDTO postEsDTO = new PostEsDTO(); 104 | BeanUtils.copyProperties(post, postEsDTO); 105 | String tagsStr = post.getTags(); 106 | if (StringUtils.isNotBlank(tagsStr)) { 107 | postEsDTO.setTags(GSON.fromJson(tagsStr, new TypeToken>() { 108 | }.getType())); 109 | } 110 | return postEsDTO; 111 | } 112 | 113 | /** 114 | * 包装类转对象 115 | * 116 | * @param postEsDTO 117 | * @return 118 | */ 119 | public static Post dtoToObj(PostEsDTO postEsDTO) { 120 | if (postEsDTO == null) { 121 | return null; 122 | } 123 | List tagList = postEsDTO.getTags(); 124 | return new Post(postEsDTO.getId(), postEsDTO.getTitle(), GSON.toJson(Optional.ofNullable(tagList).orElse(Collections.emptyList())), postEsDTO.getContent(), postEsDTO.getThumbNum(), postEsDTO.getFavourNum(), postEsDTO.getUserId(), postEsDTO.getCreateTime(), postEsDTO.getUpdateTime(), postEsDTO.getIsDelete()); 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/service/impl/PostThumbServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.service.impl; 2 | 3 | import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; 4 | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; 5 | import com.leikooo.yubi.common.ErrorCode; 6 | import com.leikooo.yubi.exception.BusinessException; 7 | import com.leikooo.yubi.mapper.PostThumbMapper; 8 | import com.leikooo.yubi.model.entity.Post; 9 | import com.leikooo.yubi.model.entity.PostThumb; 10 | import com.leikooo.yubi.model.entity.User; 11 | import com.leikooo.yubi.service.PostService; 12 | import com.leikooo.yubi.service.PostThumbService; 13 | import javax.annotation.Resource; 14 | import org.springframework.aop.framework.AopContext; 15 | import org.springframework.stereotype.Service; 16 | import org.springframework.transaction.annotation.Transactional; 17 | 18 | /** 19 | * 帖子点赞服务实现 20 | * 21 | * @author 程序员鱼皮 22 | * @from 编程导航知识星球 23 | */ 24 | @Service 25 | public class PostThumbServiceImpl extends ServiceImpl 26 | implements PostThumbService { 27 | 28 | @Resource 29 | private PostService postService; 30 | 31 | /** 32 | * 点赞 33 | * 34 | * @param postId 35 | * @param loginUser 36 | * @return 37 | */ 38 | @Override 39 | public int doPostThumb(long postId, User loginUser) { 40 | // 判断实体是否存在,根据类别获取实体 41 | Post post = postService.getById(postId); 42 | if (post == null) { 43 | throw new BusinessException(ErrorCode.NOT_FOUND_ERROR); 44 | } 45 | // 是否已点赞 46 | long userId = loginUser.getId(); 47 | // 每个用户串行点赞 48 | // 锁必须要包裹住事务方法 49 | PostThumbService postThumbService = (PostThumbService) AopContext.currentProxy(); 50 | synchronized (String.valueOf(userId).intern()) { 51 | return postThumbService.doPostThumbInner(userId, postId); 52 | } 53 | } 54 | 55 | /** 56 | * 封装了事务的方法 57 | * 58 | * @param userId 59 | * @param postId 60 | * @return 61 | */ 62 | @Override 63 | @Transactional(rollbackFor = Exception.class) 64 | public int doPostThumbInner(long userId, long postId) { 65 | PostThumb postThumb = new PostThumb(postId, userId); 66 | QueryWrapper thumbQueryWrapper = new QueryWrapper<>(postThumb); 67 | PostThumb oldPostThumb = this.getOne(thumbQueryWrapper); 68 | boolean result; 69 | // 已点赞 70 | if (oldPostThumb != null) { 71 | result = this.remove(thumbQueryWrapper); 72 | if (result) { 73 | // 点赞数 - 1 74 | result = postService.update() 75 | .eq("id", postId) 76 | .gt("thumbNum", 0) 77 | .setSql("thumbNum = thumbNum - 1") 78 | .update(); 79 | return result ? -1 : 0; 80 | } else { 81 | throw new BusinessException(ErrorCode.SYSTEM_ERROR); 82 | } 83 | } else { 84 | // 未点赞 85 | result = this.save(postThumb); 86 | if (result) { 87 | // 点赞数 + 1 88 | result = postService.update() 89 | .eq("id", postId) 90 | .setSql("thumbNum = thumbNum + 1") 91 | .update(); 92 | return result ? 1 : 0; 93 | } else { 94 | throw new BusinessException(ErrorCode.SYSTEM_ERROR); 95 | } 96 | } 97 | } 98 | 99 | } 100 | 101 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/service/impl/PostFavourServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.service.impl; 2 | 3 | import com.baomidou.mybatisplus.core.conditions.Wrapper; 4 | import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; 5 | import com.baomidou.mybatisplus.core.metadata.IPage; 6 | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; 7 | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; 8 | import com.leikooo.yubi.common.ErrorCode; 9 | import com.leikooo.yubi.exception.BusinessException; 10 | import com.leikooo.yubi.mapper.PostFavourMapper; 11 | import com.leikooo.yubi.model.entity.Post; 12 | import com.leikooo.yubi.model.entity.PostFavour; 13 | import com.leikooo.yubi.model.entity.User; 14 | import com.leikooo.yubi.service.PostFavourService; 15 | import com.leikooo.yubi.service.PostService; 16 | 17 | import javax.annotation.Resource; 18 | 19 | import org.springframework.aop.framework.AopContext; 20 | import org.springframework.stereotype.Service; 21 | import org.springframework.transaction.annotation.Transactional; 22 | 23 | /** 24 | * 帖子收藏服务实现 25 | * 26 | * @author 程序员鱼皮 27 | * @from 编程导航知识星球 28 | */ 29 | @Service 30 | public class PostFavourServiceImpl extends ServiceImpl 31 | implements PostFavourService { 32 | 33 | @Resource 34 | private PostService postService; 35 | 36 | /** 37 | * 帖子收藏 38 | * 39 | * @param postId 40 | * @param loginUser 41 | * @return 42 | */ 43 | @Override 44 | public int doPostFavour(long postId, User loginUser) { 45 | // 判断是否存在 46 | Post post = postService.getById(postId); 47 | if (post == null) { 48 | throw new BusinessException(ErrorCode.NOT_FOUND_ERROR); 49 | } 50 | // 是否已帖子收藏 51 | long userId = loginUser.getId(); 52 | // 每个用户串行帖子收藏 53 | // 锁必须要包裹住事务方法 54 | PostFavourService postFavourService = (PostFavourService) AopContext.currentProxy(); 55 | synchronized (String.valueOf(userId).intern()) { 56 | return postFavourService.doPostFavourInner(userId, postId); 57 | } 58 | } 59 | 60 | @Override 61 | public Page listFavourPostByPage(IPage page, Wrapper queryWrapper, long favourUserId) { 62 | if (favourUserId <= 0) { 63 | return new Page<>(); 64 | } 65 | return baseMapper.listFavourPostByPage(page, queryWrapper, favourUserId); 66 | } 67 | 68 | /** 69 | * 封装了事务的方法 70 | * 71 | * @param userId 72 | * @param postId 73 | * @return 74 | */ 75 | @Override 76 | @Transactional(rollbackFor = Exception.class) 77 | public int doPostFavourInner(long userId, long postId) { 78 | PostFavour postFavour = new PostFavour(userId, postId); 79 | QueryWrapper postFavourQueryWrapper = new QueryWrapper<>(postFavour); 80 | PostFavour oldPostFavour = this.getOne(postFavourQueryWrapper); 81 | boolean result; 82 | // 已收藏 83 | if (oldPostFavour != null) { 84 | result = this.remove(postFavourQueryWrapper); 85 | if (result) { 86 | // 帖子收藏数 - 1 87 | result = postService.update().eq("id", postId).gt("favourNum", 0).setSql("favourNum = favourNum - 1").update(); 88 | return result ? -1 : 0; 89 | } else { 90 | throw new BusinessException(ErrorCode.SYSTEM_ERROR); 91 | } 92 | } 93 | // 未帖子收藏 94 | result = this.save(postFavour); 95 | if (result) { 96 | // 帖子收藏数 + 1 97 | result = postService.update().eq("id", postId).setSql("favourNum = favourNum + 1").update(); 98 | return result ? 1 : 0; 99 | } else { 100 | throw new BusinessException(ErrorCode.SYSTEM_ERROR); 101 | } 102 | } 103 | 104 | } 105 | 106 | 107 | 108 | 109 | -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/controller/FileController.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.controller; 2 | 3 | import cn.hutool.core.io.FileUtil; 4 | import com.leikooo.yubi.common.BaseResponse; 5 | import com.leikooo.yubi.common.ErrorCode; 6 | import com.leikooo.yubi.common.ResultUtils; 7 | import com.leikooo.yubi.constant.FileConstant; 8 | import com.leikooo.yubi.exception.BusinessException; 9 | import com.leikooo.yubi.manager.CosManager; 10 | import com.leikooo.yubi.model.dto.file.UploadFileRequest; 11 | import com.leikooo.yubi.model.entity.User; 12 | import com.leikooo.yubi.model.enums.FileUploadBizEnum; 13 | import com.leikooo.yubi.service.UserService; 14 | import java.io.File; 15 | import java.util.Arrays; 16 | import javax.annotation.Resource; 17 | import javax.servlet.http.HttpServletRequest; 18 | import lombok.extern.slf4j.Slf4j; 19 | import org.apache.commons.lang3.RandomStringUtils; 20 | import org.springframework.web.bind.annotation.*; 21 | import org.springframework.web.multipart.MultipartFile; 22 | 23 | /** 24 | * 文件接口 25 | * 26 | * @author 程序员鱼皮 27 | * @from 编程导航知识星球 28 | */ 29 | @RestController 30 | @RequestMapping("/file") 31 | @Slf4j 32 | public class FileController { 33 | 34 | @Resource 35 | private UserService userService; 36 | 37 | @Resource 38 | private CosManager cosManager; 39 | 40 | /** 41 | * 文件上传 42 | * 43 | * @param multipartFile 44 | * @param uploadFileRequest 45 | * @param request 46 | * @return 47 | */ 48 | @PostMapping("/upload") 49 | public BaseResponse uploadFile(@RequestPart("file") MultipartFile multipartFile, 50 | UploadFileRequest uploadFileRequest, HttpServletRequest request) { 51 | String biz = uploadFileRequest.getBiz(); 52 | FileUploadBizEnum fileUploadBizEnum = FileUploadBizEnum.getEnumByValue(biz); 53 | if (fileUploadBizEnum == null) { 54 | throw new BusinessException(ErrorCode.PARAMS_ERROR); 55 | } 56 | validFile(multipartFile, fileUploadBizEnum); 57 | User loginUser = userService.getLoginUser(request); 58 | // 文件目录:根据业务、用户来划分 59 | String uuid = RandomStringUtils.randomAlphanumeric(8); 60 | String filename = uuid + "-" + multipartFile.getOriginalFilename(); 61 | String filepath = String.format("/%s/%s/%s", fileUploadBizEnum.getValue(), loginUser.getId(), filename); 62 | File file = null; 63 | try { 64 | // 上传文件 65 | file = File.createTempFile(filepath, null); 66 | multipartFile.transferTo(file); 67 | cosManager.putObject(filepath, file); 68 | // 返回可访问地址 69 | return ResultUtils.success(FileConstant.COS_HOST + filepath); 70 | } catch (Exception e) { 71 | log.error("file upload error, filepath = " + filepath, e); 72 | throw new BusinessException(ErrorCode.SYSTEM_ERROR, "上传失败"); 73 | } finally { 74 | if (file != null) { 75 | // 删除临时文件 76 | boolean delete = file.delete(); 77 | if (!delete) { 78 | log.error("file delete error, filepath = {}", filepath); 79 | } 80 | } 81 | } 82 | } 83 | 84 | /** 85 | * 校验文件 86 | * 87 | * @param multipartFile 88 | * @param fileUploadBizEnum 业务类型 89 | */ 90 | private void validFile(MultipartFile multipartFile, FileUploadBizEnum fileUploadBizEnum) { 91 | // 文件大小 92 | long fileSize = multipartFile.getSize(); 93 | // 文件后缀 94 | String fileSuffix = FileUtil.getSuffix(multipartFile.getOriginalFilename()); 95 | final long ONE_M = 1024 * 1024L; 96 | if (FileUploadBizEnum.USER_AVATAR.equals(fileUploadBizEnum)) { 97 | if (fileSize > ONE_M) { 98 | throw new BusinessException(ErrorCode.PARAMS_ERROR, "文件大小不能超过 1M"); 99 | } 100 | if (!Arrays.asList("jpeg", "jpg", "svg", "png", "webp").contains(fileSuffix)) { 101 | throw new BusinessException(ErrorCode.PARAMS_ERROR, "文件类型错误"); 102 | } 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/model/entity/Chart.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.model.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.*; 4 | import com.leikooo.yubi.common.ErrorCode; 5 | import com.leikooo.yubi.exception.ThrowUtils; 6 | import com.leikooo.yubi.model.enums.ResultEnum; 7 | import lombok.*; 8 | import org.apache.commons.lang3.StringUtils; 9 | import org.checkerframework.checker.units.qual.A; 10 | 11 | import javax.xml.transform.Result; 12 | import java.io.Serializable; 13 | import java.util.Date; 14 | 15 | /** 16 | * 图表信息表 17 | * 18 | * @author leikooo 19 | * @TableName chart 20 | */ 21 | @TableName(value = "chart") 22 | @Builder 23 | @NoArgsConstructor 24 | @AllArgsConstructor 25 | @Getter 26 | @Setter 27 | public class Chart implements Serializable { 28 | /** 29 | * id 30 | */ 31 | @TableId(type = IdType.ASSIGN_ID) 32 | private Long id; 33 | 34 | /** 35 | * 图标名称 36 | */ 37 | private String chartName; 38 | 39 | /** 40 | * 分析目标 41 | */ 42 | private String goal; 43 | 44 | /** 45 | * 图表数据 46 | */ 47 | private String chartData; 48 | 49 | /** 50 | * 图表类型 51 | */ 52 | private String chartType; 53 | 54 | /** 55 | * 生成的图表数据 56 | */ 57 | private String genChart; 58 | 59 | /** 60 | * 生成的分析结论 61 | */ 62 | private String genResult; 63 | 64 | /** 65 | * 创建用户 id 66 | */ 67 | private Long userId; 68 | 69 | /** 70 | * @see com.leikooo.yubi.model.enums.ResultEnum 71 | * wait,running,succeed,failed 72 | */ 73 | private String status; 74 | 75 | /** 76 | * 错误消息 77 | */ 78 | private String execMessage; 79 | 80 | /** 81 | * 创建时间 82 | */ 83 | private Date createTime; 84 | 85 | /** 86 | * 更新时间 87 | */ 88 | private Date updateTime; 89 | 90 | /** 91 | * 是否删除 92 | */ 93 | @TableLogic 94 | private Integer isDelete; 95 | 96 | @TableField(exist = false) 97 | private static final long serialVersionUID = 1L; 98 | 99 | public Chart(String chartName, String goal, String chartType, String genChart, String genResult, Long userId) { 100 | ThrowUtils.throwIf(StringUtils.isAnyBlank(goal, chartData, chartType, genChart, genResult) && (userId == null || userId < 0), ErrorCode.PARAMS_ERROR); 101 | this.chartName = chartName; 102 | this.goal = goal; 103 | this.chartType = chartType; 104 | this.genChart = genChart; 105 | this.genResult = genResult; 106 | this.userId = userId; 107 | } 108 | 109 | public Chart(Long id, String status, String execMessage) { 110 | ThrowUtils.throwIf((id == null || id < 0) || StringUtils.isAnyEmpty(status), ErrorCode.PARAMS_ERROR); 111 | this.id = id; 112 | this.status = status; 113 | this.execMessage = execMessage; 114 | } 115 | public Chart(String status, String execMessage, String genChart, String genResult, Long id) { 116 | ThrowUtils.throwIf((id == null || id < 0) || StringUtils.isAnyEmpty(status), ErrorCode.PARAMS_ERROR); 117 | this.id = id; 118 | this.status = status; 119 | this.execMessage = execMessage; 120 | this.genChart = genChart; 121 | this.genResult = genResult; 122 | } 123 | 124 | public Chart(String goal, String chartType, Long userId, String status) { 125 | this.goal = goal; 126 | this.chartType = chartType; 127 | this.userId = userId; 128 | this.status = status; 129 | } 130 | 131 | public Chart(Long id, String genChart, String genResult, String status, String execMessage) { 132 | ThrowUtils.throwIf((id == null || id < 0) || StringUtils.isAnyEmpty(genChart, genResult, status), ErrorCode.PARAMS_ERROR); 133 | this.id = id; 134 | this.genChart = genChart; 135 | this.genResult = genResult; 136 | this.status = status; 137 | this.execMessage = execMessage; 138 | } 139 | 140 | public Chart(String chartName, String goal, String chartType, Long userId) { 141 | this.chartName = chartName; 142 | this.goal = goal; 143 | this.chartType = chartType; 144 | this.userId = userId; 145 | } 146 | } -------------------------------------------------------------------------------- /sql/create_table.sql: -------------------------------------------------------------------------------- 1 | # 数据库初始化 2 | # @author 程序员鱼皮 3 | # @from 编程导航知识星球 4 | 5 | -- 创建库 6 | create database if not exists yubi; 7 | 8 | -- 切换库 9 | use yubi; 10 | 11 | -- 用户表 12 | create table if not exists user 13 | ( 14 | id bigint auto_increment comment 'id' primary key, 15 | userAccount varchar(256) not null comment '账号', 16 | userPassword varchar(512) not null comment '密码', 17 | userName varchar(256) null comment '用户昵称', 18 | userAvatar varchar(1024) null comment '用户头像', 19 | userRole varchar(256) default 'user' not null comment '用户角色:user/admin', 20 | createTime datetime default CURRENT_TIMESTAMP not null comment '创建时间', 21 | updateTime datetime default CURRENT_TIMESTAMP not null on update CURRENT_TIMESTAMP comment '更新时间', 22 | isDelete tinyint default 0 not null comment '是否删除', 23 | index idx_userAccount (userAccount) 24 | ) comment '用户' collate = utf8mb4_unicode_ci; 25 | 26 | -- 图表表 27 | create table if not exists chart 28 | ( 29 | id bigint auto_increment comment 'id' primary key, 30 | chartName varchar(124) null comment '图标名称', 31 | goal text null comment '分析目标', 32 | chartData text null comment '图表数据', 33 | chartType varchar(128) null comment '图表类型', 34 | genChart text null comment '生成的图表数据', 35 | genResult text null comment '生成的分析结论', 36 | userId bigint null comment '创建用户 id', 37 | status varchar(128) not null default 'wait' comment 'wait,running,succeed,failed', 38 | execMessage text null comment '执行信息', 39 | createTime datetime default CURRENT_TIMESTAMP not null comment '创建时间', 40 | updateTime datetime default CURRENT_TIMESTAMP not null on update CURRENT_TIMESTAMP comment '更新时间', 41 | isDelete tinyint default 0 not null comment '是否删除' 42 | ) comment '图表信息表' collate = utf8mb4_unicode_ci; 43 | 44 | -- 帖子表 45 | create table if not exists post 46 | ( 47 | id bigint auto_increment comment 'id' primary key, 48 | title varchar(512) null comment '标题', 49 | content text null comment '内容', 50 | tags varchar(1024) null comment '标签列表(json 数组)', 51 | thumbNum int default 0 not null comment '点赞数', 52 | favourNum int default 0 not null comment '收藏数', 53 | userId bigint not null comment '创建用户 id', 54 | createTime datetime default CURRENT_TIMESTAMP not null comment '创建时间', 55 | updateTime datetime default CURRENT_TIMESTAMP not null on update CURRENT_TIMESTAMP comment '更新时间', 56 | isDelete tinyint default 0 not null comment '是否删除', 57 | index idx_userId (userId) 58 | ) comment '帖子' collate = utf8mb4_unicode_ci; 59 | 60 | -- 帖子点赞表(硬删除) 61 | create table if not exists post_thumb 62 | ( 63 | id bigint auto_increment comment 'id' primary key, 64 | postId bigint not null comment '帖子 id', 65 | userId bigint not null comment '创建用户 id', 66 | createTime datetime default CURRENT_TIMESTAMP not null comment '创建时间', 67 | updateTime datetime default CURRENT_TIMESTAMP not null on update CURRENT_TIMESTAMP comment '更新时间', 68 | index idx_postId (postId), 69 | index idx_userId (userId) 70 | ) comment '帖子点赞'; 71 | 72 | -- 帖子收藏表(硬删除) 73 | create table if not exists post_favour 74 | ( 75 | id bigint auto_increment comment 'id' primary key, 76 | postId bigint not null comment '帖子 id', 77 | userId bigint not null comment '创建用户 id', 78 | createTime datetime default CURRENT_TIMESTAMP not null comment '创建时间', 79 | updateTime datetime default CURRENT_TIMESTAMP not null on update CURRENT_TIMESTAMP comment '更新时间', 80 | index idx_postId (postId), 81 | index idx_userId (userId) 82 | ) comment '帖子收藏'; 83 | -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/controller/PostFavourController.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.controller; 2 | 3 | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; 4 | import com.leikooo.yubi.common.BaseResponse; 5 | import com.leikooo.yubi.common.ErrorCode; 6 | import com.leikooo.yubi.common.ResultUtils; 7 | import com.leikooo.yubi.exception.BusinessException; 8 | import com.leikooo.yubi.exception.ThrowUtils; 9 | import com.leikooo.yubi.model.dto.post.PostQueryRequest; 10 | import com.leikooo.yubi.model.dto.postfavour.PostFavourAddRequest; 11 | import com.leikooo.yubi.model.dto.postfavour.PostFavourQueryRequest; 12 | import com.leikooo.yubi.model.entity.Post; 13 | import com.leikooo.yubi.model.entity.User; 14 | import com.leikooo.yubi.model.vo.PostVO; 15 | import com.leikooo.yubi.service.PostFavourService; 16 | import com.leikooo.yubi.service.PostService; 17 | import com.leikooo.yubi.service.UserService; 18 | import javax.annotation.Resource; 19 | import javax.servlet.http.HttpServletRequest; 20 | import lombok.extern.slf4j.Slf4j; 21 | import org.springframework.web.bind.annotation.PostMapping; 22 | import org.springframework.web.bind.annotation.RequestBody; 23 | import org.springframework.web.bind.annotation.RequestMapping; 24 | import org.springframework.web.bind.annotation.RestController; 25 | 26 | /** 27 | * 帖子收藏接口 28 | * 29 | * @author 程序员鱼皮 30 | * @from 编程导航知识星球 31 | */ 32 | @RestController 33 | @RequestMapping("/post_favour") 34 | @Slf4j 35 | public class PostFavourController { 36 | 37 | @Resource 38 | private PostFavourService postFavourService; 39 | 40 | @Resource 41 | private PostService postService; 42 | 43 | @Resource 44 | private UserService userService; 45 | 46 | /** 47 | * 收藏 / 取消收藏 48 | * 49 | * @param postFavourAddRequest 50 | * @param request 51 | * @return resultNum 收藏变化数 52 | */ 53 | @PostMapping("/") 54 | public BaseResponse doPostFavour(@RequestBody PostFavourAddRequest postFavourAddRequest, 55 | HttpServletRequest request) { 56 | if (postFavourAddRequest == null || postFavourAddRequest.getPostId() <= 0) { 57 | throw new BusinessException(ErrorCode.PARAMS_ERROR); 58 | } 59 | // 登录才能操作 60 | final User loginUser = userService.getLoginUser(request); 61 | long postId = postFavourAddRequest.getPostId(); 62 | int result = postFavourService.doPostFavour(postId, loginUser); 63 | return ResultUtils.success(result); 64 | } 65 | 66 | /** 67 | * 获取我收藏的帖子列表 68 | * 69 | * @param postQueryRequest 70 | * @param request 71 | */ 72 | @PostMapping("/my/list/page") 73 | public BaseResponse> listMyFavourPostByPage(@RequestBody PostQueryRequest postQueryRequest, 74 | HttpServletRequest request) { 75 | if (postQueryRequest == null) { 76 | throw new BusinessException(ErrorCode.PARAMS_ERROR); 77 | } 78 | User loginUser = userService.getLoginUser(request); 79 | long current = postQueryRequest.getCurrent(); 80 | long size = postQueryRequest.getPageSize(); 81 | // 限制爬虫 82 | ThrowUtils.throwIf(size > 20, ErrorCode.PARAMS_ERROR); 83 | Page postPage = postFavourService.listFavourPostByPage(new Page<>(current, size), 84 | postService.getQueryWrapper(postQueryRequest), loginUser.getId()); 85 | return ResultUtils.success(postService.getPostVOPage(postPage, request)); 86 | } 87 | 88 | /** 89 | * 获取用户收藏的帖子列表 90 | * 91 | * @param postFavourQueryRequest 92 | * @param request 93 | */ 94 | @PostMapping("/list/page") 95 | public BaseResponse> listFavourPostByPage(@RequestBody PostFavourQueryRequest postFavourQueryRequest, 96 | HttpServletRequest request) { 97 | if (postFavourQueryRequest == null) { 98 | throw new BusinessException(ErrorCode.PARAMS_ERROR); 99 | } 100 | long current = postFavourQueryRequest.getCurrent(); 101 | long size = postFavourQueryRequest.getPageSize(); 102 | Long userId = postFavourQueryRequest.getUserId(); 103 | // 限制爬虫 104 | ThrowUtils.throwIf(size > 20 || userId == null, ErrorCode.PARAMS_ERROR); 105 | Page postPage = postFavourService.listFavourPostByPage(new Page<>(current, size), 106 | postService.getQueryWrapper(postFavourQueryRequest.getPostQueryRequest()), userId); 107 | return ResultUtils.success(postService.getPostVOPage(postPage, request)); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/manager/AIManager.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.manager; 2 | 3 | import com.aliyun.broadscope.bailian.sdk.AccessTokenClient; 4 | import com.aliyun.broadscope.bailian.sdk.ApplicationClient; 5 | import com.aliyun.broadscope.bailian.sdk.models.BaiLianConfig; 6 | import com.aliyun.broadscope.bailian.sdk.models.CompletionsRequest; 7 | import com.aliyun.broadscope.bailian.sdk.models.CompletionsResponse; 8 | import io.github.briqt.spark4j.SparkClient; 9 | import io.github.briqt.spark4j.constant.SparkApiVersion; 10 | import io.github.briqt.spark4j.model.SparkMessage; 11 | import io.github.briqt.spark4j.model.SparkSyncChatResponse; 12 | import io.github.briqt.spark4j.model.request.SparkRequest; 13 | import lombok.extern.slf4j.Slf4j; 14 | import org.springframework.stereotype.Component; 15 | import org.springframework.transaction.support.TransactionTemplate; 16 | 17 | import javax.annotation.Resource; 18 | import java.util.ArrayList; 19 | import java.util.List; 20 | import java.util.Objects; 21 | 22 | /** 23 | * @author leikooo 24 | * @Description 接入 AI 能力的类 25 | */ 26 | @Component 27 | @Slf4j 28 | public class AIManager { 29 | @Resource 30 | private AccessTokenClient accessTokenClient; 31 | 32 | @Resource 33 | private SparkClient sparkClient; 34 | 35 | @Resource 36 | private TransactionTemplate transacTionTemplate; 37 | 38 | /** 39 | * 向 AI 发送请求 40 | * 41 | * @return 42 | */ 43 | public String sendMesToAI(final String content) { 44 | CompletionsResponse execute = transacTionTemplate.execute((simpleTransactionStatus) -> { 45 | String token = accessTokenClient.getToken(); 46 | BaiLianConfig config = new BaiLianConfig() 47 | .setApiKey(token); 48 | String appId = "2e9ff82a22d445bfb78b73effe8fa4cf"; 49 | CompletionsRequest request = new CompletionsRequest() 50 | .setAppId(appId) 51 | .setPrompt(content); 52 | 53 | ApplicationClient client = new ApplicationClient(config); 54 | return client.completions(request); 55 | }); 56 | return Objects.requireNonNull(execute).getData().getText(); 57 | } 58 | 59 | /** 60 | * 向 AI 发送请求 61 | * 62 | * @param isNeedTemplate 是否使用模板,进行 AI 生成; true 使用 、false 不使用 ,false 的情况是只想用 AI 不只是生成前端代码 63 | * @param content 内容 64 | * 分析需求: 65 | * 分析网站用户的增长情况 66 | * 原始数据: 67 | * 日期,用户数 68 | * 1号,10 69 | * 2号,20 70 | * 3号,30 71 | * @return AI 返回的内容 72 | * '【【【【【' 73 | *

74 | * '【【【【【' 75 | */ 76 | public String sendMsgToXingHuo(boolean isNeedTemplate, String content) { 77 | List messages = new ArrayList<>(); 78 | if (isNeedTemplate) { 79 | // AI 生成问题的预设条件 80 | String predefinedInformation = "你是一个数据分析师和前端开发专家,接下来我会按照以下固定格式给你提供内容:\n" + 81 | "分析需求:\n" + 82 | "{数据分析的需求或者目标}\n" + 83 | "原始数据:\n" + 84 | "{csv格式的原始数据,用,作为分隔符}\n" + 85 | "请根据这两部分内容,严格按照以下指定格式生成内容(此外不要输出任何多余的开头、结尾、注释)同时不要使用这个符号 '】'\n" + 86 | "'【【【【【'\n" + 87 | "{前端 Echarts V5 的 option 配置对象 JSON 代码, 不要生成任何多余的内容,比如注释和代码块标记}\n" + 88 | "'【【【【【'\n" + 89 | "{明确的数据分析结论、越详细越好,不要生成多余的注释} \n" 90 | + "下面是一个具体的例子的模板:" 91 | + "'【【【【【'" 92 | + "{" 93 | + " \"title\": {" 94 | + " \"text\": \"分析需求\"\n" 95 | + " }" 96 | + "}" 97 | + "'【【【【【'" + 98 | "结论:"; 99 | messages.add(SparkMessage.systemContent(predefinedInformation)); 100 | } 101 | messages.add(SparkMessage.userContent(content)); 102 | // 构造请求 103 | SparkRequest sparkRequest = SparkRequest.builder() 104 | // 消息列表 105 | .messages(messages) 106 | // 模型回答的tokens的最大长度,非必传,取值为[1,4096],默认为2048 107 | .maxTokens(2048) 108 | // 核采样阈值。用于决定结果随机性,取值越高随机性越强即相同的问题得到的不同答案的可能性越高 非必传,取值为[0,1],默认为0.5 109 | .temperature(0.2) 110 | // 指定请求版本,默认使用最新2.0版本 111 | .apiVersion(SparkApiVersion.V4_0) 112 | .build(); 113 | // 同步调用 114 | SparkSyncChatResponse chatResponse = sparkClient.chatSync(sparkRequest); 115 | String responseContent = chatResponse.getContent(); 116 | log.info("星火 AI 返回的结果 {}", responseContent); 117 | return responseContent; 118 | } 119 | 120 | } 121 | -------------------------------------------------------------------------------- /src/main/java/com/leikooo/yubi/bizmq/BIMessageConsumer.java: -------------------------------------------------------------------------------- 1 | package com.leikooo.yubi.bizmq; 2 | 3 | import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; 4 | import com.leikooo.yubi.common.ErrorCode; 5 | import com.leikooo.yubi.constant.BIMQConstant; 6 | import com.leikooo.yubi.constant.ChartConstant; 7 | import com.leikooo.yubi.exception.BusinessException; 8 | import com.leikooo.yubi.exception.ThrowUtils; 9 | import com.leikooo.yubi.manager.AIManager; 10 | import com.leikooo.yubi.mapper.ChartMapper; 11 | import com.leikooo.yubi.model.dto.chart.ChartGenResult; 12 | import com.leikooo.yubi.model.entity.Chart; 13 | import com.leikooo.yubi.model.enums.ResultEnum; 14 | import com.leikooo.yubi.service.ChartService; 15 | import com.leikooo.yubi.utils.ChartDataUtil; 16 | import com.rabbitmq.client.Channel; 17 | import lombok.extern.slf4j.Slf4j; 18 | import org.apache.commons.lang3.StringUtils; 19 | import org.springframework.amqp.rabbit.annotation.RabbitListener; 20 | import org.springframework.amqp.support.AmqpHeaders; 21 | import org.springframework.messaging.handler.annotation.Header; 22 | import org.springframework.stereotype.Component; 23 | 24 | import javax.annotation.Resource; 25 | import java.io.IOException; 26 | import java.util.Arrays; 27 | import java.util.Map; 28 | import java.util.concurrent.ConcurrentHashMap; 29 | import java.util.concurrent.atomic.AtomicInteger; 30 | 31 | /** 32 | * @author leikooo 33 | */ 34 | @Slf4j 35 | @Component 36 | public class BIMessageConsumer { 37 | 38 | @Resource 39 | private ChartService chartService; 40 | 41 | @Resource 42 | private AIManager aiManager; 43 | 44 | @Resource 45 | private ChartMapper chartMapper; 46 | 47 | /** 48 | * 消费正常消息 49 | * 50 | * @param message 51 | * @param channel 52 | * @param deliveryTag 53 | */ 54 | @RabbitListener(queues = {BIMQConstant.BI_QUEUE_NAME}, ackMode = "MANUAL") 55 | public void receiveMessage(String message, Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long deliveryTag) { 56 | if (StringUtils.isBlank(message)) { 57 | throwExceptionAndNackMessage(channel, deliveryTag); 58 | } 59 | log.info("receiveMessage message = {}", message); 60 | Chart chart = chartService.getById(message); 61 | if (chart == null) { 62 | throwExceptionAndNackMessage(channel, deliveryTag); 63 | } 64 | Long userId = chart.getUserId(); 65 | // 检查用户任务计数器 66 | int userTaskCount = (int) getRunningTaskCount(userId); 67 | try { 68 | if (userTaskCount <= BIMQConstant.MAX_CONCURRENT_CHARTS) { 69 | chartService.updateById(new Chart(Long.parseLong(message), ResultEnum.RUNNING.getDes(), "")); 70 | String csvData = ChartDataUtil.changeDataToCSV(chartMapper.queryChartData(Long.parseLong(message))); 71 | ThrowUtils.throwIf(StringUtils.isBlank(csvData), ErrorCode.PARAMS_ERROR); 72 | ChartGenResult genResult = ChartDataUtil.getGenResult(aiManager, chart.getGoal(), csvData, chart.getChartType()); 73 | boolean result = chartService.updateById(new Chart(chart.getId(), genResult.getGenChart(), genResult.getGenResult(), ResultEnum.SUCCEED.getDes(), "")); 74 | if (!result) { 75 | throwExceptionAndNackMessage(channel, deliveryTag); 76 | } 77 | channel.basicAck(deliveryTag, false); 78 | return; 79 | } 80 | channel.basicNack(deliveryTag, false, true); 81 | } catch (Exception e) { 82 | log.error(e.getMessage()); 83 | try { 84 | channel.basicNack(deliveryTag, false, false); 85 | } catch (IOException ex) { 86 | throw new RuntimeException(ex); 87 | } 88 | } 89 | } 90 | 91 | /** 92 | * 消费异常消息 93 | * 94 | * @param message 95 | * @param channel 96 | * @param deliveryTag 97 | */ 98 | @RabbitListener(queues = {BIMQConstant.BI_DLX_QUEUE_NAME}, ackMode = "MANUAL") 99 | public void receiveErrorMessage(String message, Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long deliveryTag) { 100 | if (StringUtils.isBlank(message)) { 101 | throwExceptionAndNackMessage(channel, deliveryTag); 102 | } 103 | log.info("receiveErrorMessage message = {}", message); 104 | Chart chart = chartService.getById(message); 105 | if (chart == null) { 106 | throwExceptionAndNackMessage(channel, deliveryTag); 107 | } 108 | chartService.updateById(new Chart(Long.parseLong(message), ResultEnum.FAILED.getDes(), "")); 109 | try { 110 | channel.basicAck(deliveryTag, false); 111 | } catch (IOException e) { 112 | log.error(e.getMessage()); 113 | } 114 | } 115 | 116 | /** 117 | * 抛异常同时拒绝消息 118 | * 119 | * @param channel 120 | * @param deliveryTag 121 | */ 122 | private void throwExceptionAndNackMessage(Channel channel, long deliveryTag) { 123 | try { 124 | channel.basicNack(deliveryTag, false, false); 125 | } catch (IOException e) { 126 | log.error(e.getMessage()); 127 | } 128 | throw new BusinessException(ErrorCode.SYSTEM_ERROR); 129 | } 130 | 131 | /** 132 | * 获取当前用户正在运行的任务数量,就算服务器出现问题,数据已经持久化到硬盘之中 133 | * 134 | * @param userId 135 | * @return 136 | */ 137 | private long getRunningTaskCount(Long userId) { 138 | QueryWrapper queryWrapper = new QueryWrapper<>(); 139 | queryWrapper.eq("userId", userId).eq("status", ResultEnum.RUNNING.getDes()); 140 | return chartService.count(queryWrapper); 141 | } 142 | } 143 | 144 | -------------------------------------------------------------------------------- /mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM https://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Maven Start Up Batch script 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM M2_HOME - location of maven2's installed home dir 28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending 30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | @REM e.g. to debug Maven itself, use 32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | @REM ---------------------------------------------------------------------------- 35 | 36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 37 | @echo off 38 | @REM set title of command window 39 | title %0 40 | @REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' 41 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 42 | 43 | @REM set %HOME% to equivalent of $HOME 44 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 45 | 46 | @REM Execute a user defined script before this one 47 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 48 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 49 | if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %* 50 | if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %* 51 | :skipRcPre 52 | 53 | @setlocal 54 | 55 | set ERROR_CODE=0 56 | 57 | @REM To isolate internal variables from possible post scripts, we use another setlocal 58 | @setlocal 59 | 60 | @REM ==== START VALIDATION ==== 61 | if not "%JAVA_HOME%" == "" goto OkJHome 62 | 63 | echo. 64 | echo Error: JAVA_HOME not found in your environment. >&2 65 | echo Please set the JAVA_HOME variable in your environment to match the >&2 66 | echo location of your Java installation. >&2 67 | echo. 68 | goto error 69 | 70 | :OkJHome 71 | if exist "%JAVA_HOME%\bin\java.exe" goto init 72 | 73 | echo. 74 | echo Error: JAVA_HOME is set to an invalid directory. >&2 75 | echo JAVA_HOME = "%JAVA_HOME%" >&2 76 | echo Please set the JAVA_HOME variable in your environment to match the >&2 77 | echo location of your Java installation. >&2 78 | echo. 79 | goto error 80 | 81 | @REM ==== END VALIDATION ==== 82 | 83 | :init 84 | 85 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 86 | @REM Fallback to current working directory if not found. 87 | 88 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 89 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 90 | 91 | set EXEC_DIR=%CD% 92 | set WDIR=%EXEC_DIR% 93 | :findBaseDir 94 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 95 | cd .. 96 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 97 | set WDIR=%CD% 98 | goto findBaseDir 99 | 100 | :baseDirFound 101 | set MAVEN_PROJECTBASEDIR=%WDIR% 102 | cd "%EXEC_DIR%" 103 | goto endDetectBaseDir 104 | 105 | :baseDirNotFound 106 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 107 | cd "%EXEC_DIR%" 108 | 109 | :endDetectBaseDir 110 | 111 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 112 | 113 | @setlocal EnableExtensions EnableDelayedExpansion 114 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 115 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 116 | 117 | :endReadAdditionalConfig 118 | 119 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 120 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 121 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 122 | 123 | set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" 124 | 125 | FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( 126 | IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B 127 | ) 128 | 129 | @REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 130 | @REM This allows using the maven wrapper in projects that prohibit checking in binary data. 131 | if exist %WRAPPER_JAR% ( 132 | if "%MVNW_VERBOSE%" == "true" ( 133 | echo Found %WRAPPER_JAR% 134 | ) 135 | ) else ( 136 | if not "%MVNW_REPOURL%" == "" ( 137 | SET DOWNLOAD_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" 138 | ) 139 | if "%MVNW_VERBOSE%" == "true" ( 140 | echo Couldn't find %WRAPPER_JAR%, downloading it ... 141 | echo Downloading from: %DOWNLOAD_URL% 142 | ) 143 | 144 | powershell -Command "&{"^ 145 | "$webclient = new-object System.Net.WebClient;"^ 146 | "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ 147 | "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ 148 | "}"^ 149 | "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ 150 | "}" 151 | if "%MVNW_VERBOSE%" == "true" ( 152 | echo Finished downloading %WRAPPER_JAR% 153 | ) 154 | ) 155 | @REM End of extension 156 | 157 | @REM Provide a "standardized" way to retrieve the CLI args that will 158 | @REM work with both Windows and non-Windows executions. 159 | set MAVEN_CMD_LINE_ARGS=%* 160 | 161 | %MAVEN_JAVA_EXE% ^ 162 | %JVM_CONFIG_MAVEN_PROPS% ^ 163 | %MAVEN_OPTS% ^ 164 | %MAVEN_DEBUG_OPTS% ^ 165 | -classpath %WRAPPER_JAR% ^ 166 | "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^ 167 | %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 168 | if ERRORLEVEL 1 goto error 169 | goto end 170 | 171 | :error 172 | set ERROR_CODE=1 173 | 174 | :end 175 | @endlocal & set ERROR_CODE=%ERROR_CODE% 176 | 177 | if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost 178 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 179 | if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat" 180 | if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd" 181 | :skipRcPost 182 | 183 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 184 | if "%MAVEN_BATCH_PAUSE%"=="on" pause 185 | 186 | if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE% 187 | 188 | cmd /C exit /B %ERROR_CODE% 189 | --------------------------------------------------------------------------------