保证消息能发送

main
夜灬瞬 2 years ago
parent e60fd40ec3
commit 9a63a4524a

@ -17,4 +17,11 @@
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
</dependencies>
</project>

@ -0,0 +1,57 @@
package com.shun.business.config;
import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author
* @since 2023/6/21 21:08
*/
@Configuration
public class RabbitMQConfig {
/**
*
*/
public static final String PLACE_ORDER_EXCHANGE = "place_order_exchange";
/**
* Queue
*/
public static final String COUPON_QUEUE = "coupon_queue";
public static final String USER_POINTS_QUEUE = "user_points_queue";
public static final String BUSINESS_QUEUE = "business_queue";
@Bean
public Exchange placeOrderExchange(){
return ExchangeBuilder.fanoutExchange(PLACE_ORDER_EXCHANGE).build();
}
@Bean
public Queue couponQueue(){
return QueueBuilder.durable(COUPON_QUEUE).build();
}
@Bean
public Queue userPointsQueue(){
return QueueBuilder.durable(USER_POINTS_QUEUE).build();
}
@Bean
public Queue businessQueue(){
return QueueBuilder.durable(BUSINESS_QUEUE).build();
}
@Bean
public Binding couponBinding(Exchange placeOrderExchange, Queue couponQueue){
return BindingBuilder.bind(couponQueue).to(placeOrderExchange).with("").noargs();
}
@Bean
public Binding userPointsBinding(Exchange placeOrderExchange,Queue userPointsQueue){
return BindingBuilder.bind(userPointsQueue).to(placeOrderExchange).with("").noargs();
}
@Bean
public Binding businessBinding(Exchange placeOrderExchange,Queue businessQueue){
return BindingBuilder.bind(businessQueue).to(placeOrderExchange).with("").noargs();
}
}

@ -0,0 +1,28 @@
package com.shun.business.listener;
import com.rabbitmq.client.Channel;
import com.shun.business.config.RabbitMQConfig;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
/**
*
*
* @author
* @since 2023/6/21 21:43
*/
@Component
@Slf4j
public class BusinessListener {
@RabbitListener(queues = {RabbitMQConfig.BUSINESS_QUEUE})
public void consume(String msg, Channel channel, Message message) throws Exception {
// 预扣除优惠券
Thread.sleep(400);
log.info("优惠券预扣除成功! msg:{}",msg);
// 手动ACK
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
}
}

@ -7,3 +7,13 @@ spring:
nacos:
discovery:
server-addr: 127.0.0.1:8848
rabbitmq:
host: 192.168.48.128
port: 5672
username: admin
password: admin
virtual-host: /
listener:
simple:
# 手动 ack
acknowledge-mode: manual

@ -18,4 +18,11 @@
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
</dependencies>
</project>

@ -0,0 +1,57 @@
package com.shun.integral.config;
import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author
* @since 2023/6/21 21:08
*/
@Configuration
public class RabbitMQConfig {
/**
*
*/
public static final String PLACE_ORDER_EXCHANGE = "place_order_exchange";
/**
* Queue
*/
public static final String COUPON_QUEUE = "coupon_queue";
public static final String USER_POINTS_QUEUE = "user_points_queue";
public static final String BUSINESS_QUEUE = "business_queue";
@Bean
public Exchange placeOrderExchange(){
return ExchangeBuilder.fanoutExchange(PLACE_ORDER_EXCHANGE).build();
}
@Bean
public Queue couponQueue(){
return QueueBuilder.durable(COUPON_QUEUE).build();
}
@Bean
public Queue userPointsQueue(){
return QueueBuilder.durable(USER_POINTS_QUEUE).build();
}
@Bean
public Queue businessQueue(){
return QueueBuilder.durable(BUSINESS_QUEUE).build();
}
@Bean
public Binding couponBinding(Exchange placeOrderExchange, Queue couponQueue){
return BindingBuilder.bind(couponQueue).to(placeOrderExchange).with("").noargs();
}
@Bean
public Binding userPointsBinding(Exchange placeOrderExchange,Queue userPointsQueue){
return BindingBuilder.bind(userPointsQueue).to(placeOrderExchange).with("").noargs();
}
@Bean
public Binding businessBinding(Exchange placeOrderExchange,Queue businessQueue){
return BindingBuilder.bind(businessQueue).to(placeOrderExchange).with("").noargs();
}
}

@ -0,0 +1,28 @@
package com.shun.integral.listener;
import com.rabbitmq.client.Channel;
import com.shun.integral.config.RabbitMQConfig;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
/**
*
*
* @author
* @since 2023/6/21 21:43
*/
@Component
@Slf4j
public class IntegralListener {
@RabbitListener(queues = {RabbitMQConfig.USER_POINTS_QUEUE})
public void consume(String msg, Channel channel, Message message) throws Exception {
// 预扣除优惠券
Thread.sleep(400);
log.info("优惠券预扣除成功! msg:{}",msg);
// 手动ACK
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
}
}

@ -7,3 +7,13 @@ spring:
nacos:
discovery:
server-addr: 127.0.0.1:8848
rabbitmq:
host: 192.168.48.128
port: 5672
username: admin
password: admin
virtual-host: /
listener:
simple:
# 手动 ack
acknowledge-mode: manual

@ -0,0 +1,93 @@
package com.shun.placeOrder.config;
import com.shun.placeOrder.util.GlobalCache;
import jakarta.annotation.Nullable;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.ReturnedMessage;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import java.util.Arrays;
/**
* @author
* @since 2023/6/24 14:35
*/
@Component
@Slf4j
public class RabbitTemplateConfig {
@Bean
public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory){
//1、new出RabbitTemplate对象
RabbitTemplate rabbitTemplate = new RabbitTemplate();
//2、将connectionFactory设置到RabbitTemplate对象中
rabbitTemplate.setConnectionFactory(connectionFactory);
//3、设置confirm回调
rabbitTemplate.setConfirmCallback(confirmCallback());
//4、设置return回调
/*
old
rabbitTemplate.setReturnCallback(returnCallback());
*/
rabbitTemplate.setReturnsCallback(returnCallback());
//5、设置mandatory为true
rabbitTemplate.setMandatory(true);
//6、返回RabbitTemplate对象即可
return rabbitTemplate;
}
public RabbitTemplate.ConfirmCallback confirmCallback(){
return (correlationData, ack, cause) -> {
// 如果消息 ack 成功
if (correlationData == null) {
return;
}
String msgId = correlationData.getId();
if(ack){
log.info("\n消息发送到Exchange成功!! msgId = " + msgId);
GlobalCache.remove(msgId);
}else{
log.info("\n消息发送到Exchange失败!! msgId = " + msgId);
}
};
}
public RabbitTemplate.ReturnsCallback returnCallback(){
return new RabbitTemplate.ReturnsCallback(){
/**
* Returned message callback.
*
* @param returned the returned message and metadata.
*/
@Override
public void returnedMessage(@Nullable ReturnedMessage returned) {
log.info("\n消息未路由到队列");
assert returned != null;
log.info("\nreturn消息为" + Arrays.toString(returned.getMessage().getBody()));
log.info("\nreturn交换机为" + returned.getExchange());
log.info("\nreturn路由为" + returned.getRoutingKey());
}
};
}
/**
* old
* @return
*/
// public RabbitTemplate.ReturnCallback returnCallback(){
// return new RabbitTemplate.ReturnCallback(){
// @Override
// public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
// System.out.println("消息未路由到队列");
// System.out.println("return消息为" + new String(message.getBody()));
// System.out.println("return交换机为" + exchange);
// System.out.println("return路由为" + routingKey);
// }
// };
// }
}

@ -2,11 +2,14 @@ package com.shun.placeOrder.contoller;
import com.shun.placeOrder.client.*;
import com.shun.placeOrder.config.RabbitMQConfig;
import com.shun.placeOrder.util.GlobalCache;
import jakarta.annotation.Resource;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.*;
/**
* @author
* @since 2023/6/19 15:46
@ -35,10 +38,9 @@ public class PlaceOrderController {
stockClient.delStock();
// 调用订单服务 创建订单
orderClient.create();
//feginToBu();
mqToBo();
//mqToBo();
mqToBuMessageBz();
long end = System.currentTimeMillis();
System.out.println("耗时:" + (end - start));
@ -46,6 +48,25 @@ public class PlaceOrderController {
}
/**
* MQ
*/
private void mqToBuMessageBz() {
String userAndOrderInfo = "用户信息&订单信息&优惠券信息等等…………";
// 生成当前消息的唯一标识
String id = UUID.randomUUID().toString();
// 封装消息信息
Map<String, Object> map = new HashMap<>();
map.put("message",userAndOrderInfo);
map.put("exchange",RabbitMQConfig.PLACE_ORDER_EXCHANGE);
map.put("routingKey","");
map.put("sendTime",new Date());
// 将id标识和消息存储到全局缓存中
GlobalCache.set(id,map);
// 将同步方式修改为基于RabbitMQ的异步方式
rabbitTemplate.convertAndSend(RabbitMQConfig.PLACE_ORDER_EXCHANGE,"",userAndOrderInfo);
}
private void mqToBo() {
String userAndOrderInfo = "用户信息&订单信息&优惠券信息等等…………";
// 将同步方式修改为基于RabbitMQ的异步方式

@ -0,0 +1,25 @@
package com.shun.placeOrder.util;
import java.util.HashMap;
import java.util.Map;
/**
* cache
* @author
* @since 2023/6/24 15:07
*/
public class GlobalCache {
private static final Map<String,Object> MAP = new HashMap<>();
public static void set(String key,Object value){
MAP.put(key,value);
}
public static Object get(String key){
return MAP.get(key);
}
public static void remove(String key){
MAP.remove(key);
}
}

@ -13,3 +13,7 @@ spring:
username: admin
password: admin
virtual-host: /
# 设置发布者确认类型 使用 with CorrelationData 将确认与发送的消息相关联 也就是指定发送模式
publisher-confirm-type: correlated
# 开启回调处理
publisher-returns: true

@ -1,11 +0,0 @@
FROM openjdk:11.0-jre-buster
LABEL maintainer="研究院研发组 <research-maint@itcast.cn>"
ENV JAVA_OPTS=""
# 设定时区
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
WORKDIR /app
ADD app.jar /app/app.jar
ENTRYPOINT ["sh","-c","java -jar $JAVA_OPTS /app/app.jar"]

@ -1,195 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.tianji</groupId>
<artifactId>tjxt</artifactId>
<version>1.0.0</version>
<modules>
<module>tj-common</module>
<module>tj-auth</module>
<module>tj-api</module>
<module>tj-gateway</module>
<module>tj-user</module>
<module>tj-message</module>
<module>tj-media</module>
<module>tj-course</module>
<module>tj-search</module>
<module>tj-learning</module>
<module>tj-pay</module>
<module>tj-trade</module>
<module>tj-exam</module>
<module>tj-promotion</module>
<module>tj-data</module>
<module>tj-remark</module>
</modules>
<packaging>pom</packaging>
<!-- 继承 Spring Boot 父工程 -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.2</version>
</parent>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<tjxt.version>1.0</tjxt.version>
<org.projectlombok.version>1.18.20</org.projectlombok.version>
<spring-cloud.version>2021.0.3</spring-cloud.version>
<spring-cloud-alibaba.version>2021.0.1.0</spring-cloud-alibaba.version>
<mybatis-plus.version>3.4.3</mybatis-plus.version>
<hutool.version>5.7.17</hutool.version>
<swagger.version>3.0.3</swagger.version>
<mysql.version>8.0.23</mysql.version>
<ali.sdk.core.version>4.6.0</ali.sdk.core.version>
<ali.sdk.kms.version>2.10.1</ali.sdk.kms.version>
<ali.sdk.oss.version>3.10.2</ali.sdk.oss.version>
<ali.sdk.pay.version>4.33.12.ALL</ali.sdk.pay.version>
<tencent.cloud.version>3.1.515</tencent.cloud.version>
<redisson.version>3.13.6</redisson.version>
<elasticsearch.version>7.12.1</elasticsearch.version>
<tencent.sdk.cos.version>5.6.89</tencent.sdk.cos.version>
<tencent.sdk.vod.version>2.1.5</tencent.sdk.vod.version>
<xxl-job-version>2.3.1</xxl-job-version>
<seata-version>1.5.1</seata-version>
</properties>
<!-- 对依赖包进行管理 -->
<dependencyManagement>
<dependencies>
<!-- Spring Cloud 依赖包管理 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring-cloud-alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- 数据库驱动包管理 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<!-- mybatis plus 管理 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<!--knife4j-->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
<version>${swagger.version}</version>
</dependency>
<!--腾讯云-->
<dependency>
<groupId>com.tencentcloudapi</groupId>
<artifactId>tencentcloud-sdk-java</artifactId>
<version>${tencent.cloud.version}</version>
</dependency>
<!--ali sdk-->
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-core</artifactId>
<version>${ali.sdk.core.version}</version>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-kms</artifactId>
<version>${ali.sdk.kms.version}</version>
</dependency>
<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-sdk-java</artifactId>
<version>${ali.sdk.pay.version}</version>
</dependency>
<!--阿里云OSS的SDK-->
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>${ali.sdk.oss.version}</version>
</dependency>
<!--redisson-->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>${redisson.version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>${aspectj.version}</version>
</dependency>
<!--腾讯cos-->
<dependency>
<groupId>com.qcloud</groupId>
<artifactId>cos_api</artifactId>
<version>${tencent.sdk.cos.version}</version>
</dependency>
<!--腾讯vod-->
<dependency>
<groupId>com.qcloud</groupId>
<artifactId>vod_api</artifactId>
<version>${tencent.sdk.vod.version}</version>
</dependency>
<dependency>
<groupId>com.xuxueli</groupId>
<artifactId>xxl-job-core</artifactId>
<version>${xxl-job-version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- lombok 管理 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${org.projectlombok.version}</version>
</dependency>
<!--单元测试-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--开启bootstrap文件读取-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
</dependencies>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>11</source> <!-- depending on your project -->
<target>11</target> <!-- depending on your project -->
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>

@ -1,74 +0,0 @@
#! /bin/sh
cd /usr/local/src/script || exit 1
BASE_PATH='/usr/local/src/jenkins/workspace/tjxt-dev-build'
PROJECT_NAME=""
PROJECT_PATH=''
CONTAINER_NAME=""
JAVA_OPTS="-Xms300m -Xmx300m"
PORT=8080
DEBUG_PORT=0
while getopts "c:n:d:p:o:a:" opt; do
case $opt in
c)
CONTAINER_NAME=$OPTARG
;;
n)
PROJECT_NAME=$OPTARG
;;
d)
PROJECT_PATH=$OPTARG
;;
p)
PORT=$OPTARG
;;
o)
[ -n "$OPTARG" ] && JAVA_OPTS=$OPTARG
;;
a)
[ -n "$OPTARG" ] && DEBUG_PORT=$OPTARG
;;
?)
echo "unkonw argument"
exit 1
;;
esac
done
if [ "$DEBUG_PORT" = "0" ]; then
JAVA_OPTS=$JAVA_OPTS
else
JAVA_OPTS="-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005"
fi
IMAGE_NAME="${CONTAINER_NAME}:latest"
echo "copy xx.jar from ${BASE_PATH}/${PROJECT_PATH}"
rm -f app.jar
cp ${BASE_PATH}/${PROJECT_PATH}/target/${PROJECT_NAME}.jar ./app.jar || exit 1
echo "begin to build ${PROJECT_NAME} image "
[ -n "`docker ps -a | grep ${CONTAINER_NAME}`" ] && docker rm -f ${CONTAINER_NAME}
[ -n "`docker images | grep ${CONTAINER_NAME}`" ] && docker rmi ${IMAGE_NAME}
docker build -t ${IMAGE_NAME} . || exit 1
echo "${PROJECT_NAME} image build successjava_opts = $JAVA_OPTS ^_^"
echo "begin to create container ${CONTAINER_NAME}port: ${PORT} "
if [ "$DEBUG_PORT" = "0" ]; then
echo "run in normal mode"
docker run -d --name ${CONTAINER_NAME} \
-p "${PORT}:${PORT}" \
-e JAVA_OPTS="${JAVA_OPTS}" \
--memory 300m --memory-swap -1 \
--network heima-net ${IMAGE_NAME} \
|| exit 1
else
echo "run in debug mode"
docker run -d --name ${CONTAINER_NAME} \
-p "${PORT}:${PORT}" \
-p ${DEBUG_PORT}:5005 \
-e JAVA_OPTS="${JAVA_OPTS}" \
--network heima-net ${IMAGE_NAME} \
|| exit 1
fi
echo "container is running now !! ^_^"
exit 0

@ -1,48 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>tjxt</artifactId>
<groupId>com.tianji</groupId>
<version>1.0.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>tj-api</artifactId>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!--httpClient的依赖 -->
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
</dependency>
<!--common-->
<dependency>
<groupId>com.tianji</groupId>
<artifactId>tj-common</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
</dependency>
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
</dependency>
<!--sentinel-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
</dependencies>
</project>

@ -1,4 +0,0 @@
package com.tianji.api.annotations;
public @interface EnableCategoryCache {
}

@ -1,92 +0,0 @@
package com.tianji.api.cache;
import com.github.benmanes.caffeine.cache.Cache;
import com.tianji.api.client.course.CategoryClient;
import com.tianji.api.dto.course.CategoryBasicDTO;
import com.tianji.common.utils.CollUtils;
import lombok.RequiredArgsConstructor;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@RequiredArgsConstructor
public class CategoryCache {
private final Cache<String, Map<Long, CategoryBasicDTO>> categoryCaches;
private final CategoryClient categoryClient;
public Map<Long, CategoryBasicDTO> getCategoryMap() {
return categoryCaches.get("CATEGORY", key -> {
// 1.从CategoryClient查询
List<CategoryBasicDTO> list = categoryClient.getAllOfOneLevel();
if(list == null || list.isEmpty()){
return CollUtils.emptyMap();
}
// 2.转换数据
return list.stream().collect(Collectors.toMap(CategoryBasicDTO::getId, c -> c));
});
}
public String getCategoryNames(List<Long> ids) {
if (ids == null || ids.size() == 0) {
return "";
}
// 1.读取分类缓存
Map<Long, CategoryBasicDTO> map = getCategoryMap();
// 2.根据id查询分类名称并组装
StringBuilder sb = new StringBuilder();
for (Long id : ids) {
sb.append(map.get(id).getName()).append("/");
}
// 3.返回结果
return sb.deleteCharAt(sb.length() - 1).toString();
}
public List<String> getCategoryNameList(List<Long> ids) {
if (ids == null || ids.size() == 0) {
return CollUtils.emptyList();
}
// 1.读取分类缓存
Map<Long, CategoryBasicDTO> map = getCategoryMap();
// 2.根据id查询分类名称并组装
List<String> list = new ArrayList<>(ids.size());
for (Long id : ids) {
list.add(map.get(id).getName());
}
// 3.返回结果
return list;
}
public List<CategoryBasicDTO> queryCategoryByIds(List<Long> ids) {
if (ids == null || ids.size() == 0) {
return CollUtils.emptyList();
}
Map<Long, CategoryBasicDTO> map = getCategoryMap();
return ids.stream()
.map(map::get)
.collect(Collectors.toList());
}
public List<String> getNameByLv3Ids(List<Long> lv3Ids) {
Map<Long, CategoryBasicDTO> map = getCategoryMap();
List<String> list = new ArrayList<>(lv3Ids.size());
for (Long lv3Id : lv3Ids) {
CategoryBasicDTO lv3 = map.get(lv3Id);
CategoryBasicDTO lv2 = map.get(lv3.getParentId());
CategoryBasicDTO lv1 = map.get(lv2.getParentId());
list.add(lv1.getName() + lv2.getName() + lv3.getName());
}
return list;
}
public String getNameByLv3Id(Long lv3Id) {
Map<Long, CategoryBasicDTO> map = getCategoryMap();
CategoryBasicDTO lv3 = map.get(lv3Id);
CategoryBasicDTO lv2 = map.get(lv3.getParentId());
CategoryBasicDTO lv1 = map.get(lv2.getParentId());
return lv1.getName() + lv2.getName() + lv3.getName();
}
}

@ -1,36 +0,0 @@
package com.tianji.api.cache;
import com.github.benmanes.caffeine.cache.Cache;
import com.tianji.api.client.auth.AuthClient;
import com.tianji.api.dto.auth.RoleDTO;
import com.tianji.api.dto.user.UserDTO;
import com.tianji.common.enums.UserType;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
public class RoleCache {
private final Cache<Long, RoleDTO> roleCaches;
private final AuthClient authClient;
public String getRoleName(Long roleId) {
RoleDTO roleDTO = roleCaches.get(roleId, authClient::queryRoleById);
if (roleDTO == null) {
return null;
}
return roleDTO.getName();
}
public String exchangeRoleName(UserDTO u) {
if (u == null) {
return "--";
}
if (UserType.STUDENT.equalsValue(u.getType())) {
// 学生,直接返回角色名称
return u.getName();
} else {
// 管理员需要拼接角色名称
return getRoleName(u.getRoleId()) + "-" + u.getName();
}
}
}

@ -1,18 +0,0 @@
package com.tianji.api.client.auth;
import com.tianji.api.dto.auth.RoleDTO;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import java.util.List;
@FeignClient("auth-service")
public interface AuthClient {
@GetMapping("/roles/{id}")
RoleDTO queryRoleById(@PathVariable("id") Long id);
@GetMapping("/roles/list")
List<RoleDTO> listAllRoles();
}

@ -1,23 +0,0 @@
package com.tianji.api.client.course;
import com.tianji.api.dto.course.CataSimpleInfoDTO;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.List;
@FeignClient(contextId = "catalogue", value = "course-service",path = "catalogues")
public interface CatalogueClient {
/**
* id
*
* @param ids id
* @return id
*/
@GetMapping("/batchQuery")
List<CataSimpleInfoDTO> batchQueryCatalogue(@RequestParam("ids") Iterable<Long> ids);
}

@ -1,18 +0,0 @@
package com.tianji.api.client.course;
import com.tianji.api.dto.course.CategoryBasicDTO;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import java.util.List;
@FeignClient(contextId = "category",value = "course-service",path = "categorys")
public interface CategoryClient {
/**
*
* @return
*/
@GetMapping("getAllOfOneLevel")
List<CategoryBasicDTO> getAllOfOneLevel();
}

@ -1,68 +0,0 @@
package com.tianji.api.client.course;
import com.tianji.api.dto.course.*;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.List;
@FeignClient(contextId = "course", value = "course-service")
public interface CourseClient {
/**
* id
* @param teacherIds id
* @return id
*/
@GetMapping("/course/infoByTeacherIds")
List<SubNumAndCourseNumDTO> infoByTeacherIds(@RequestParam("teacherIds") Iterable<Long> teacherIds);
/**
* idmediaIdid
*
* @param sectionId id
* @return mediaIdid
*/
@GetMapping("/course/section/{id}")
SectionInfoDTO sectionInfo(@PathVariable("id") Long sectionId);
/**
* Id
*
* @param mediaIds id
* @return id
*/
@GetMapping("/course/media/useInfo")
List<MediaQuoteDTO> mediaUserInfo(@RequestParam("mediaIds") Iterable<Long> mediaIds);
/**
* id
*
* @param id id
* @return
*/
@GetMapping("/course/{id}/searchInfo")
CourseSearchDTO getSearchInfo(@PathVariable("id") Long id);
/**
* id
* @param ids id
* @return
*/
@GetMapping("/courses/simpleInfo/list")
List<CourseSimpleInfoDTO> getSimpleInfoList(@RequestParam("ids") Iterable<Long> ids);
/**
* id
* @param id id
* @return
*/
@GetMapping("/course/{id}")
CourseFullInfoDTO getCourseInfoById(
@PathVariable("id") Long id,
@RequestParam(value = "withCatalogue", required = false) boolean withCatalogue,
@RequestParam(value = "withTeachers", required = false) boolean withTeachers
);
}

@ -1,15 +0,0 @@
package com.tianji.api.client.course;
import com.tianji.api.dto.course.SubjectDTO;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.List;
@FeignClient(value = "course-service", path = "subjects")
public interface SubjectClient {
@GetMapping("list")
List<SubjectDTO> queryByIds(@RequestParam("ids") Iterable<Long> ids);
}

@ -1,36 +0,0 @@
package com.tianji.api.client.exam;
import com.tianji.api.dto.exam.QuestionBizDTO;
import com.tianji.api.dto.exam.QuestionDTO;
import io.swagger.annotations.ApiParam;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.List;
import java.util.Map;
@FeignClient("exam-service")
public interface ExamClient {
@PostMapping("/question-biz/list")
void saveQuestionBizInfoBatch(@RequestBody Iterable<QuestionBizDTO> qbs);
@GetMapping("/question-biz/biz/list")
List<QuestionBizDTO> queryQuestionIdsByBizIds(@RequestParam("ids") Iterable<Long> bizIds);
@GetMapping("/question-biz/scores")
Map<Long, Integer> queryQuestionScoresByBizIds(@RequestParam("ids") Iterable<Long> bizIds);
@GetMapping("/questions/list")
List<QuestionDTO> queryQuestionByIds(@ApiParam("要查询的题目的id集合") @RequestParam("ids") Iterable<Long> ids);
@GetMapping("/questions/numOfTeacher")
Map<Long, Integer> countSubjectNumOfTeacher(@RequestParam("ids") Iterable<Long> createrIds);
@GetMapping("/questions//scores")
Map<Long, Integer> queryQuestionScores(
@ApiParam("要查询的题目的id集合") @RequestParam("ids") Iterable<Long> ids);
}

@ -1,36 +0,0 @@
package com.tianji.api.client.learning;
import com.tianji.api.client.learning.fallback.LearningClientFallback;
import com.tianji.api.dto.leanring.LearningLessonDTO;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@FeignClient(value = "learning-service", fallbackFactory = LearningClientFallback.class)
public interface LearningClient {
/**
*
* @param courseId id
* @return
*/
@GetMapping("/lessons/{courseId}/count")
Integer countLearningLessonByCourse(@PathVariable("courseId") Long courseId);
/**
*
* @param courseId id
* @return truefalse
*/
@GetMapping("/lessons/{courseId}/valid")
Long isLessonValid(@PathVariable("courseId") Long courseId);
/**
*
* @param courseId id
* @return
*/
@GetMapping("/learning-records/course/{courseId}")
LearningLessonDTO queryLearningRecordByCourse(@PathVariable("courseId") Long courseId);
}

@ -1,31 +0,0 @@
package com.tianji.api.client.learning.fallback;
import com.tianji.api.client.learning.LearningClient;
import com.tianji.api.dto.leanring.LearningLessonDTO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.openfeign.FallbackFactory;
@Slf4j
public class LearningClientFallback implements FallbackFactory<LearningClient> {
@Override
public LearningClient create(Throwable cause) {
log.error("查询学习服务异常", cause);
return new LearningClient() {
@Override
public Integer countLearningLessonByCourse(Long courseId) {
return 0;
}
@Override
public Long isLessonValid(Long courseId) {
return null;
}
@Override
public LearningLessonDTO queryLearningRecordByCourse(Long courseId) {
return null;
}
};
}
}

@ -1,14 +0,0 @@
package com.tianji.api.client.remark;
import com.tianji.api.client.remark.fallback.RemarkClientFallback;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.Set;
@FeignClient(value = "remark-service", fallbackFactory = RemarkClientFallback.class)
public interface RemarkClient {
@GetMapping("/likes/list")
Set<Long> isBizLiked(@RequestParam("bizIds") Iterable<Long> bizIds);
}

@ -1,24 +0,0 @@
package com.tianji.api.client.remark.fallback;
import com.tianji.api.client.remark.RemarkClient;
import com.tianji.common.utils.CollUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.openfeign.FallbackFactory;
import java.util.Set;
@Slf4j
public class RemarkClientFallback implements FallbackFactory<RemarkClient> {
@Override
public RemarkClient create(Throwable cause) {
log.error("查询remark-service服务异常", cause);
return new RemarkClient() {
@Override
public Set<Long> isBizLiked(Iterable<Long> bizIds) {
return CollUtils.emptySet();
}
};
}
}

@ -1,15 +0,0 @@
package com.tianji.api.client.search;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.List;
@FeignClient("search-service")
public interface SearchClient {
@GetMapping("/courses/name")
List<Long> queryCoursesIdByName(
@RequestParam(value = "keyword", required = false) String keyword);
}

@ -1,46 +0,0 @@
package com.tianji.api.client.trade;
import com.tianji.api.client.trade.fallback.TradeClientFallback;
import com.tianji.api.dto.course.CoursePurchaseInfoDTO;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.List;
import java.util.Map;
@FeignClient(value = "trade-service", fallbackFactory = TradeClientFallback.class)
public interface TradeClient {
/**
*
* @param courseIdList id
* @return
*/
@GetMapping("/order-details/enrollNum")
Map<Long, Integer> countEnrollNumOfCourse(@RequestParam("courseIdList") List<Long> courseIdList);
/**
*
* @param studentIds id
* @return
*/
@GetMapping("/order-details/enrollCourse")
Map<Long, Integer> countEnrollCourseOfStudent(@RequestParam("studentIds") List<Long> studentIds);
/**
*
* @param id id
* @return
*/
@GetMapping("/order-details/course/{id}")
Boolean checkMyLesson(@PathVariable("id") Long id);
/**
* 退
* @param courseId id
* @return
*/
@GetMapping("/order-details/purchaseInfo")
CoursePurchaseInfoDTO getPurchaseInfoOfCourse(@RequestParam("courseId") Long courseId);
}

@ -1,41 +0,0 @@
package com.tianji.api.client.trade.fallback;
import com.tianji.api.client.trade.TradeClient;
import com.tianji.api.dto.course.CoursePurchaseInfoDTO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.openfeign.FallbackFactory;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Slf4j
public class TradeClientFallback implements FallbackFactory<TradeClient> {
@Override
public TradeClient create(Throwable cause) {
log.error("查询交易服务异常", cause);
return new TradeClient() {
@Override
public Map<Long, Integer> countEnrollNumOfCourse(List<Long> courseIdList) {
return new HashMap<>();
}
@Override
public Map<Long, Integer> countEnrollCourseOfStudent(List<Long> studentIds) {
return new HashMap<>();
}
@Override
public Boolean checkMyLesson(Long id) {
return false;
}
@Override
public CoursePurchaseInfoDTO getPurchaseInfoOfCourse(Long courseId) {
return new CoursePurchaseInfoDTO();
}
};
}
}

@ -1,57 +0,0 @@
package com.tianji.api.client.user;
import com.tianji.api.client.user.fallback.UserClientFallback;
import com.tianji.api.dto.user.LoginFormDTO;
import com.tianji.api.dto.user.UserDTO;
import com.tianji.common.domain.dto.LoginUserDTO;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@FeignClient(value = "user-service", fallbackFactory = UserClientFallback.class)
public interface UserClient {
/**
* id
* @param phone
* @return id
*/
@GetMapping("/users/ids")
Long exchangeUserIdWithPhone(@RequestParam("phone") String phone);
/**
*
* @param loginDTO
* @param isStaff
* @return
*/
@PostMapping("/users/detail/{isStaff}")
LoginUserDTO queryUserDetail(@RequestBody LoginFormDTO loginDTO, @PathVariable("isStaff") boolean isStaff);
/**
*
* @param id id
* @return 0-1-2-
*/
@GetMapping("/users/{id}/type")
Integer queryUserType(@PathVariable("id") Long id);
/**
* <h1>id</h1>
* @param ids id
* @return
*/
@GetMapping("/users/list")
List<UserDTO> queryUserByIds(@RequestParam("ids") Iterable<Long> ids);
/**
* id
* @param id id
* @return
*/
@GetMapping("/users/{id}")
UserDTO queryUserById(@PathVariable("id") Long id);
}

@ -1,45 +0,0 @@
package com.tianji.api.client.user.fallback;
import com.tianji.api.client.user.UserClient;
import com.tianji.api.dto.user.LoginFormDTO;
import com.tianji.api.dto.user.UserDTO;
import com.tianji.common.domain.dto.LoginUserDTO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.openfeign.FallbackFactory;
import java.util.Collections;
import java.util.List;
@Slf4j
public class UserClientFallback implements FallbackFactory<UserClient> {
@Override
public UserClient create(Throwable cause) {
log.error("查询用户服务出现异常", cause);
return new UserClient() {
@Override
public Long exchangeUserIdWithPhone(String phone) {
return null;
}
@Override
public LoginUserDTO queryUserDetail(LoginFormDTO loginDTO, boolean isStaff) {
return null;
}
@Override
public Integer queryUserType(Long id) {
return null;
}
@Override
public List<UserDTO> queryUserByIds(Iterable<Long> ids) {
return Collections.emptyList();
}
@Override
public UserDTO queryUserById(Long id) {
return null;
}
};
}
}

@ -1,33 +0,0 @@
package com.tianji.api.config;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.tianji.api.cache.CategoryCache;
import com.tianji.api.client.course.CategoryClient;
import com.tianji.api.dto.course.CategoryBasicDTO;
import org.springframework.context.annotation.Bean;
import java.time.Duration;
import java.util.Map;
public class CategoryCacheConfig {
/**
* caffeine
*/
@Bean
public Cache<String, Map<Long, CategoryBasicDTO>> categoryCaches(){
return Caffeine.newBuilder()
.initialCapacity(1) // 容量限制
.maximumSize(10_000) // 最大内存限制
.expireAfterWrite(Duration.ofMinutes(30)) // 有效期
.build();
}
/**
*
*/
@Bean
public CategoryCache categoryCache(
Cache<String, Map<Long, CategoryBasicDTO>> categoryCaches, CategoryClient categoryClient){
return new CategoryCache(categoryCaches, categoryClient);
}
}

@ -1,31 +0,0 @@
package com.tianji.api.config;
import com.tianji.api.client.learning.fallback.LearningClientFallback;
import com.tianji.api.client.remark.fallback.RemarkClientFallback;
import com.tianji.api.client.trade.fallback.TradeClientFallback;
import com.tianji.api.client.user.fallback.UserClientFallback;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class FallbackConfig {
@Bean
public LearningClientFallback learningClientFallback(){
return new LearningClientFallback();
}
@Bean
public TradeClientFallback tradeClientFallback(){
return new TradeClientFallback();
}
@Bean
public UserClientFallback userClientFallback(){
return new UserClientFallback();
}
@Bean
public RemarkClientFallback remarkClientFallback(){
return new RemarkClientFallback();
}
}

@ -1,22 +0,0 @@
package com.tianji.api.config;
import feign.RequestInterceptor;
import org.slf4j.MDC;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import static com.tianji.common.constants.Constant.*;
@Configuration
@EnableFeignClients(basePackages = "com.tianji.api.client")
public class RequestIdRelayConfiguration {
@Bean
public RequestInterceptor requestIdInterceptor(){
return template -> template
.header(REQUEST_ID_HEADER, MDC.get(REQUEST_ID_HEADER))
.header(REQUEST_FROM_HEADER, FEIGN_ORIGIN_NAME);
}
}

@ -1,31 +0,0 @@
package com.tianji.api.config;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.tianji.api.cache.RoleCache;
import com.tianji.api.client.auth.AuthClient;
import com.tianji.api.dto.auth.RoleDTO;
import org.springframework.context.annotation.Bean;
import java.time.Duration;
public class RoleCacheConfig {
/**
* caffeine
*/
@Bean
public Cache<Long, RoleDTO> roleCaches(){
return Caffeine.newBuilder()
.initialCapacity(1)
.maximumSize(10_000)
.expireAfterWrite(Duration.ofMinutes(30))
.build();
}
/**
*
*/
@Bean
public RoleCache roleCache(Cache<Long, RoleDTO> roleCaches, AuthClient authClient){
return new RoleCache(roleCaches, authClient);
}
}

@ -1,34 +0,0 @@
package com.tianji.api.constants;
import com.tianji.common.enums.BaseEnum;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* @author wusongsong
* @since 2022/7/18 16:07
* @version 1.0.0
**/
@Getter
@AllArgsConstructor
public enum CourseStatus implements BaseEnum {
NO_UP_SHELF(1, "待上架"),
SHELF(2, "已上架"),
DOWN_SHELF(3, "下架"),
FINISHED(4, "已完结");
private final int value;
private final String desc;
public static String desc(Integer status) {
if (status == null) {
return "";
}
for (CourseStatus courseStatus : values()) {
if (courseStatus.getValue() == status) {
return courseStatus.getDesc();
}
}
return null;
}
}

@ -1,5 +0,0 @@
package com.tianji.api.constants;
public interface SmsConstants {
String VERIFY_CODE_PARAM_NAME = "code";
}

@ -1,27 +0,0 @@
package com.tianji.api.dto;
import com.tianji.common.utils.CollUtils;
import lombok.Data;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* idnunididnum
* @author wusongsong
* @since 2022/8/3 9:27
* @version 1.0.0
**/
@Data
public class IdAndNumDTO {
private Long id;
private Integer num;
public static Map<Long, Integer> toMap(List<IdAndNumDTO> list){
if (CollUtils.isEmpty(list)) {
return CollUtils.emptyMap();
}
return list.stream().collect(Collectors.toMap(IdAndNumDTO::getId, IdAndNumDTO::getNum));
}
}

@ -1,44 +0,0 @@
package com.tianji.api.dto.auth;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.io.Serializable;
/**
* <p>
*
* </p>
*
* @author
* @since 2022-06-16
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@ApiModel(description = "角色实体")
public class RoleDTO implements Serializable {
private static final long serialVersionUID = 1L;
/**
*
*/
@ApiModelProperty(value = "主键", example = "1")
private Long id;
/**
* admin
*/
@ApiModelProperty(value = "角色代号", example = "admin")
private String code;
/**
*
*/
@ApiModelProperty(value = "角色名称", example = "教师")
private String name;
}

@ -1,19 +0,0 @@
package com.tianji.api.dto.course;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* @author wusongsong
* @since 2022/7/27 14:22
* @version 1.0.0
**/
@Data
public class CataSimpleInfoDTO {
@ApiModelProperty("目录id")
private Long id;
@ApiModelProperty("目录名称")
private String name;
@ApiModelProperty("数字序号,不包含章序号")
private Integer cIndex;
}

@ -1,41 +0,0 @@
package com.tianji.api.dto.course;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.List;
/**
* @author wusongsong
* @since 2022/7/11 16:42
* @version 1.0.0
**/
@Data
@ApiModel(description = "课程目录")
public class CatalogueDTO {
@ApiModelProperty("章、节、练习id")
private Long id;
@ApiModelProperty("序号")
private Integer index;
@ApiModelProperty("章节练习名称")
private String name;
@ApiModelProperty("课程总时长,单位秒")
private Integer mediaDuration;
@ApiModelProperty("是否支持免费试看")
private Boolean trailer;
@ApiModelProperty("媒资名称")
private String mediaName;
@ApiModelProperty("媒资id")
private Long mediaId;
@ApiModelProperty("目录类型123测试")
private Integer type;
@ApiModelProperty("题目数量")
private Integer subjectNum;
@ApiModelProperty("题目总分")
private Integer totalScore;
@ApiModelProperty("是否可以修改,默认不能修改")
private Boolean canUpdate = false;
@ApiModelProperty("该章的所有小节和练习")
private List<CatalogueDTO> sections;
}

@ -1,16 +0,0 @@
package com.tianji.api.dto.course;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
@ApiModel(description = "分类id和名称信息")
public class CategoryBasicDTO {
@ApiModelProperty(value = "分类id", example = "1")
private Long id;
@ApiModelProperty(value = "分类名称", example = "Java")
private String name;
@ApiModelProperty(value = "父分类id", example = "0")
private Long parentId;
}

@ -1,40 +0,0 @@
package com.tianji.api.dto.course;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.time.LocalDateTime;
/**
* @ClassName CategoryDTO
* @author wusongsong
* @since 2022/7/21 14:51
* @version 1.0.0
**/
@Data
@ApiModel("课程分类")
public class CategoryDTO {
@ApiModelProperty("课程分类id")
private Long id;
@ApiModelProperty("课程分类名称")
private String name;
@ApiModelProperty("三级分类数量")
private Integer thirdCategoryNum;
@ApiModelProperty("课程数量")
private Integer courseNum;
@ApiModelProperty("状态1正常2禁用")
private Integer status;
@ApiModelProperty("状态描述")
private String statusDesc;
@ApiModelProperty("创建时间")
private LocalDateTime createTime;
@ApiModelProperty("更新时间")
private LocalDateTime updateTime;
@ApiModelProperty("排序")
private Integer index;
@ApiModelProperty("父id")
private Long parentId;
@ApiModelProperty("级别")
private Integer level;
}

@ -1,56 +0,0 @@
package com.tianji.api.dto.course;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.time.LocalDateTime;
@ApiModel("课程信息")
@Data
public class CourseDTO {
@ApiModelProperty("课程id")
private Long id;
@ApiModelProperty("课程名称")
private String name;
@ApiModelProperty("一级课程分类id")
private Long categoryIdLv1;
@ApiModelProperty("二级课程分类id")
private Long categoryIdLv2;
@ApiModelProperty("三级课程分类id")
private Long categoryIdLv3;
@ApiModelProperty("课程封面")
private String coverUrl;
@ApiModelProperty("创建时间")
private LocalDateTime createTime;
@ApiModelProperty("更新时间")
private LocalDateTime updateTime;
@ApiModelProperty("价格")
private Integer price;
@ApiModelProperty("视频播放时长")
private Integer duration;
@ApiModelProperty("课程有效期天数")
private Integer validDuration;
@ApiModelProperty("是否免费")
private Boolean free;
@ApiModelProperty("发布时间")
private LocalDateTime publishTime;
@ApiModelProperty("章节数")
private Integer sections;
@ApiModelProperty("课程状态")
private Byte status;
@ApiModelProperty("老师id")
private Long teacher;
@ApiModelProperty("课程类型1直播课程2录播课程")
private Integer courseType;
@ApiModelProperty("更新时间")
private Long updater;
@ApiModelProperty("课程进行到的步骤1基本信息2目录3课程视频4课程题目5课程老师")
private Integer step;
@ApiModelProperty(value = "课程报名人数(销量)", example = "3920")
private Integer sold = 0;
@ApiModelProperty(value = "课程评价得分45代表4.5星", example = "35")
private Integer score = 0;
@ApiModelProperty("课程是否禁用,0:禁用1启用")
private Integer enable;
}

@ -1,49 +0,0 @@
package com.tianji.api.dto.course;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.List;
/**
*
*
* @author wusongsong
* @since 2022/8/5 16:54
* @version 1.0.0
**/
@Data
@ApiModel(description = "课程详细信息,包含课程、目录、教师")
public class CourseFullInfoDTO {
@ApiModelProperty("课程id")
private Long id;
@ApiModelProperty("课程名称")
private String name;
@ApiModelProperty("封面链接")
private String coverUrl;
@ApiModelProperty("价格")
private Integer price;
@ApiModelProperty("一级课程分类id")
private Long firstCateId;
@ApiModelProperty("二级课程分类id")
private Long secondCateId;
@ApiModelProperty("三级课程分类id")
private Long thirdCateId;
@ApiModelProperty("课程总节数")
private Integer sectionNum;
@ApiModelProperty("课程购买有效期结束时间")
private LocalDateTime purchaseEndTime;
@ApiModelProperty("课程学习有效期")
private Integer validDuration;
@ApiModelProperty("课程章信息")
private List<CatalogueDTO> chapters;
@ApiModelProperty("老师列表")
private List<Long> teacherIds;
@JsonIgnore
public List<Long> getCategoryIds(){
return List.of(firstCateId, secondCateId, thirdCateId);
}
}

@ -1,26 +0,0 @@
package com.tianji.api.dto.course;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
*
* @author wusongsong
* @since 2022/7/26 20:41
* @version 1.0.0
**/
@Data
@ApiModel("课程购买信息")
@NoArgsConstructor
@AllArgsConstructor
public class CoursePurchaseInfoDTO {
@ApiModelProperty("报名人数")
private Integer enrollNum;
@ApiModelProperty("退款人数")
private Integer refundNum;
@ApiModelProperty("实付总金额")
private Integer realPayAmount;
}

@ -1,49 +0,0 @@
package com.tianji.api.dto.course;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.time.LocalDateTime;
/**
*
* @ClassName CourseDTO
* @author wusongsong
* @since 2022/7/18 13:12
* @version 1.0.0
**/
@ApiModel(description = "课程信息")
@Data
public class CourseSearchDTO {
@ApiModelProperty("课程id")
private Long id;
@ApiModelProperty("课程名称")
private String name;
@ApiModelProperty("一级课程分类id")
private Long categoryIdLv1;
@ApiModelProperty("二级课程分类id")
private Long categoryIdLv2;
@ApiModelProperty("三级课程分类id")
private Long categoryIdLv3;
@ApiModelProperty("课程封面")
private String coverUrl;
@ApiModelProperty("价格")
private Integer price;
@ApiModelProperty("是否免费")
private Boolean free;
@ApiModelProperty("发布时间")
private LocalDateTime publishTime;
@ApiModelProperty("章节数")
private Integer sections;
@ApiModelProperty("课程时长")
private Integer duration;
@ApiModelProperty("老师id")
private Long teacher;
@ApiModelProperty("课程类型1直播课程2录播课程")
private Integer courseType;
@ApiModelProperty(value = "课程报名人数(销量)", example = "3920")
private Integer sold = 0;
@ApiModelProperty(value = "课程评价得分45代表4.5星", example = "35")
private Integer score = 0;
}

@ -1,45 +0,0 @@
package com.tianji.api.dto.course;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.List;
/**
* @author wusongsong
* @since 2022/7/27 14:32
* @version 1.0.0
**/
@Data
public class CourseSimpleInfoDTO {
@ApiModelProperty("课程id")
private Long id;
@ApiModelProperty("课程名称")
private String name;
@ApiModelProperty("封面url")
private String coverUrl;
@ApiModelProperty("价格")
private Integer price;
@ApiModelProperty("课程状态")
private Integer status;
@ApiModelProperty("是否是免费课程")
private Boolean free;
@ApiModelProperty("一级分类id")
private Long firstCateId;
@ApiModelProperty("二级分类id")
private Long secondCateId;
@ApiModelProperty("三级分类id")
private Long thirdCateId;
@ApiModelProperty("小节数量")
private Integer sectionNum;
@ApiModelProperty("课程购买有效期结束时间")
private LocalDateTime purchaseEndTime;
@ApiModelProperty("课程学习有效期,单位:月")
private Integer validDuration;
@JsonIgnore
public List<Long> getCategoryIds(){
return List.of(firstCateId, secondCateId, thirdCateId);
}
}

@ -1,24 +0,0 @@
package com.tianji.api.dto.course;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @ClassName MediaQuoteDTO
* @author wusongsong
* @since 2022/7/18 17:43
* @version 1.0.0
**/
@ApiModel("媒资被引用情况")
@Data
@AllArgsConstructor
@NoArgsConstructor
public class MediaQuoteDTO {
@ApiModelProperty("媒资id")
private Long mediaId;
@ApiModelProperty("引用数")
private Integer quoteNum;
}

@ -1,22 +0,0 @@
package com.tianji.api.dto.course;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@ApiModel("小节信息包含课程id和媒资id")
@AllArgsConstructor
@NoArgsConstructor
public class SectionInfoDTO {
@ApiModelProperty("课程id")
private Long courseId;
@ApiModelProperty("媒资id")
private Long mediaId;
@ApiModelProperty("是否支持免费试看")
private Boolean trailer;
@ApiModelProperty("免费时长不免费为0单位分钟")
private Integer freeDuration;
}

@ -1,28 +0,0 @@
package com.tianji.api.dto.course;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import javax.validation.constraints.NotNull;
/**
*
* @ClassName SubNumAndCourseNumDTO
* @author wusongsong
* @since 2022/7/18 15:12
* @version 1.0.0
**/
@Data
@AllArgsConstructor
@NotNull
@ApiModel("老师id和老师对应的课程数出题数")
public class SubNumAndCourseNumDTO {
@ApiModelProperty("老师id")
private Long teacherId;
@ApiModelProperty("老师负责的课程数")
private Integer courseNum;
@ApiModelProperty("老师出题数")
private Integer subjectNum;
}

@ -1,47 +0,0 @@
package com.tianji.api.dto.course;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
/**
* <p>
*
* </p>
*
* @author
* @since 2022-07-18
*/
@Data
@ApiModel(description = "考试问题详情")
public class SubjectDTO implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty("问题id")
private Long id;
@ApiModelProperty("题干")
private String name;
@ApiModelProperty("选择题的选项")
private List<String> options;
@ApiModelProperty("分值")
private Integer score;
@ApiModelProperty("问题类型1单选题2多选题3不定向选择题4判断题5主观题")
private Integer subjectType;
@ApiModelProperty("难易度1简单2中等3困难")
private Integer difficulty;
@ApiModelProperty("解析")
private String analysis;
@ApiModelProperty("选择题答案0对应A1对应B可填多个")
private List<Integer> answers;
}

@ -1,23 +0,0 @@
package com.tianji.api.dto.exam;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
@Data
@ApiModel(description = "题目与业务关联信息")
@Accessors(chain = true)
@AllArgsConstructor(staticName = "of")
@NoArgsConstructor
public class QuestionBizDTO{
@ApiModelProperty("业务id要关联问题的某业务id例如小节id")
private Long bizId;
@ApiModelProperty("题目id")
private Long questionId;
}

@ -1,40 +0,0 @@
package com.tianji.api.dto.exam;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.util.List;
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@ApiModel(description = "题目数据")
public class QuestionDTO {
@ApiModelProperty("题目id")
private Long id;
@ApiModelProperty("题目名称,题干")
private String name;
@ApiModelProperty("题目类型1单选题2多选题3不定向选择题4判断题5主观题")
private String type;
@ApiModelProperty("难易度1简单2中等3困难")
private Integer difficulty;
@ApiModelProperty("分值")
private Integer score;
@ApiModelProperty("选择题选项json数组格式")
private List<String> options;
@ApiModelProperty("选择题正确答案1到10如果有多个答案中间使用逗号隔开如果是判断题1代表正确其他代表错误")
private String answer;
@ApiModelProperty("答案解析")
private String analysis;
}

@ -1,18 +0,0 @@
package com.tianji.api.dto.leanring;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.List;
@Data
@ApiModel(description = "学习课表进度信息")
public class LearningLessonDTO {
@ApiModelProperty("课表id")
private Long id;
@ApiModelProperty("最近学习的小节id")
private Long latestSectionId;
@ApiModelProperty("学习过的小节的记录")
private List<LearningRecordDTO> records;
}

@ -1,16 +0,0 @@
package com.tianji.api.dto.leanring;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
@ApiModel(description = "小节信息及学习进度")
public class LearningRecordDTO {
@ApiModelProperty("对应节的id")
private Long sectionId;
@ApiModelProperty("视频的当前观看时长,单位秒")
private Integer moment;
@ApiModelProperty("是否完成学习默认false")
private Boolean finished;
}

@ -1,30 +0,0 @@
package com.tianji.api.dto.leanring;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.time.LocalDateTime;
@Data
@ApiModel(description = "学习记录表单数据")
public class LearningRecordFormDTO {
@ApiModelProperty("小节类型1-视频2-考试")
private Integer sectionType;
@ApiModelProperty("课表id")
private Long lessonId;
@ApiModelProperty("对应节的id")
private Long sectionId;
@ApiModelProperty("视频总时长,单位秒")
private Integer duration;
@ApiModelProperty("视频的当前观看时长单位秒第一次提交填0")
private Integer moment;
@ApiModelProperty("提交时间")
private LocalDateTime commitTime;
}

@ -1,22 +0,0 @@
package com.tianji.api.dto.promotion;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@ApiModel(description = "订单的可用优惠券及折扣信息")
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class CouponDiscountDTO {
@ApiModelProperty("优惠券id")
private Long id;
@ApiModelProperty("优惠券规则")
private String rule;
@ApiModelProperty("本订单最大优惠金额")
private Integer discountAmount;
}

@ -1,17 +0,0 @@
package com.tianji.api.dto.promotion;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.List;
@Data
@ApiModel(description = "订单中课程及优惠券信息")
public class OrderCouponDTO {
@ApiModelProperty("用户优惠券id")
private Long userCouponId;
@ApiModelProperty("订单中的课程列表")
private List<OrderCourseDTO> courseList;
}

@ -1,16 +0,0 @@
package com.tianji.api.dto.promotion;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
@ApiModel(description = "订单中的课程信息")
public class OrderCourseDTO {
@ApiModelProperty("课id")
private Long id;
@ApiModelProperty("课程的三级分类id")
private Long cateId;
@ApiModelProperty("课程价格")
private Integer price;
}

@ -1,13 +0,0 @@
package com.tianji.api.dto.remark;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor(staticName = "of")
public class LikedTimesDTO {
private Long bizId;
private Integer likedTimes;
}

@ -1,14 +0,0 @@
package com.tianji.api.dto.sms;
import io.swagger.annotations.ApiModel;
import lombok.Data;
import java.util.Map;
@Data
@ApiModel(description = "短信发送参数")
public class SmsInfoDTO {
private String templateCode;
private Iterable<String> phones;
private Map<String, String> templateParams;
}

@ -1,28 +0,0 @@
package com.tianji.api.dto.trade;
import lombok.Builder;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.List;
@Data
@Builder
public class OrderBasicDTO {
/**
* id
*/
private Long orderId;
/**
* id
*/
private Long userId;
/**
* id
*/
private List<Long> courseIds;
/**
*
*/
private LocalDateTime finishTime;
}

@ -1,24 +0,0 @@
package com.tianji.api.dto.user;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotNull;
@Data
@ApiModel(description = "登录表单实体")
public class LoginFormDTO {
@ApiModelProperty(value = "登录方式1-密码登录; 2-验证码登录", example = "1", required = true)
@NotNull
private Integer type;
@ApiModelProperty(value = "用户名", example = "jack")
private String username;
@ApiModelProperty(value = "手机号", example = "13800010001")
private String cellPhone;
@ApiModelProperty(value = "密码", example = "123", required = true)
@NotNull
private String password;
@ApiModelProperty(value = "7天免密登录", example = "true")
private Boolean rememberMe = false;
}

@ -1,53 +0,0 @@
package com.tianji.api.dto.user;
import com.tianji.common.constants.RegexConstants;
import com.tianji.common.validate.annotations.EnumValid;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
@Data
@ApiModel(description = "用户详情")
public class UserDTO {
@ApiModelProperty(value = "用户id", example = "1")
private Long id;
@ApiModelProperty(value = "手机", example = "13890011009")
@Pattern(regexp = RegexConstants.PHONE_PATTERN, message = "手机号格式错误")
private String cellPhone;
@ApiModelProperty(value = "用户名称/昵称", example = "李四")
private String name;
@ApiModelProperty(value = "用户类型1-其他员工,2-普通学员3-老师", example = "2")
@EnumValid(enumeration = {1,2,3}, message = "用户类型错误")
@NotNull
private Integer type;
@ApiModelProperty(value = "角色id老师和学生不用填", example = "5")
private Long roleId;
@ApiModelProperty(value = "头像", example = "default-user-icon.jpg")
private String icon;
@ApiModelProperty(value = "岗位", example = "讲师")
private String job;
@ApiModelProperty(value = "个人介绍", example = "黑马高级Java讲师")
private String intro;
@ApiModelProperty(value = "形象照地址", example = "default-teacher-photo.jpg")
private String photo;
@ApiModelProperty(value = "用户名", example = "13800010004")
private String username;
@ApiModelProperty(value = "邮箱")
@Email
private String email;
@ApiModelProperty(value = "QQ号码")
private String qq;
@ApiModelProperty(value = "省")
private String province;
@ApiModelProperty(value = "市")
private String city;
@ApiModelProperty(value = "区")
private String district;
@ApiModelProperty(value = "性别0-男性1-女性", example = "0")
@EnumValid(enumeration = {0, 1}, message = "性别格式不正确")
private Integer gender;
}

@ -1,5 +0,0 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.tianji.api.config.RequestIdRelayConfiguration, \
com.tianji.api.config.RoleCacheConfig, \
com.tianji.api.config.FallbackConfig, \
com.tianji.api.config.CategoryCacheConfig

@ -1,26 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>tjxt</artifactId>
<groupId>com.tianji</groupId>
<version>1.0.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>tj-auth</artifactId>
<packaging>pom</packaging>
<modules>
<module>tj-auth-common</module>
<module>tj-auth-service</module>
<module>tj-auth-resource-sdk</module>
<module>tj-auth-gateway-sdk</module>
</modules>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
</project>

@ -1,26 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>tj-auth</artifactId>
<groupId>com.tianji</groupId>
<version>1.0.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>tj-auth-common</artifactId>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<dependencies>
<!--domain-->
<dependency>
<groupId>com.tianji</groupId>
<artifactId>tj-common</artifactId>
<version>1.0.0</version>
</dependency>
</dependencies>
</project>

@ -1,27 +0,0 @@
package com.tianji.auth.common.constants;
public interface AuthErrorInfo {
interface Msg {
String INVALID_STAFF_TYPE = "无效的账户类型";
String INVALID_ROLE_ID = "绑定的角色不存在";
String PRIVILEGE_EXISTS = "权限信息已存在";
String PRIVILEGE_NOT_FOUND = "权限数据不存在";
String ROLE_NOT_FOUND = "角色数据不存在";
String MENU_NOT_FOUND = "菜单数据不存在";
String UNAUTHORIZED = "未登录";
String FORBIDDEN = "无访问权限";
String INVALID_TOKEN = "无效的token";
String EXPIRED_TOKEN = "token已过期";
String INVALID_TOKEN_PAYLOAD = "token参数格式错误";
}
interface Code {
// 过期token
int EXPIRED_TOKEN_CODE = 40101;
// 无效token
int INVALID_TOKEN_CODE = 40102;
}
}

@ -1,29 +0,0 @@
package com.tianji.auth.common.constants;
import java.time.Duration;
public class JwtConstants {
public static final String PAYLOAD_USER_KEY = "user";
public static final String PAYLOAD_JTI_KEY = "jti";
public static final String JWT_REDIS_KEY_PREFIX = "jwt:uid:";
// token过期时间测试期间改为 1天正常是5分钟
public static final Duration JWT_TOKEN_TTL = Duration.ofMinutes(5);
// public static final Duration JWT_TOKEN_TTL = Duration.ofMinutes(60 * 24);
public static final Duration JWT_REFRESH_TTL = Duration.ofMinutes(30);
public static final Duration JWT_REMEMBER_ME_TTL = Duration.ofDays(7);
public static final String JWT_ALGORITHM = "rs256";
public static final String AUTHORIZATION_HEADER = "authorization";
public static final String REFRESH_HEADER = "refresh";
public static final String USER_HEADER = "user-info";
/* 权限缓存 KEY begin */
public static final String AUTH_PRIVILEGE_KEY = "auth:privileges";
public static final String AUTH_PRIVILEGE_VERSION_KEY = "version";
public static final String LOCK_AUTH_PRIVILEGE_KEY = "lock:auth:privileges";
/* 权限缓存 KEY end */
}

@ -1,13 +0,0 @@
package com.tianji.auth.common.domain;
import lombok.Data;
import java.util.Set;
@Data
public class PrivilegeRoleDTO {
private Long id;
private String antPath;
private Boolean internal;
private Set<Long> roles;
}

@ -1,34 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>tj-auth</artifactId>
<groupId>com.tianji</groupId>
<version>1.0.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>tj-auth-gateway-sdk</artifactId>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>com.tianji</groupId>
<artifactId>tj-auth-common</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-commons</artifactId>
</dependency>
</dependencies>
</project>

@ -1,24 +0,0 @@
package com.tianji.authsdk.gateway.config;
import com.tianji.authsdk.gateway.util.AuthUtil;
import com.tianji.authsdk.gateway.util.JwtSignerHolder;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.StringRedisTemplate;
@Configuration
public class AuthAutoConfiguration {
@Bean
@ConditionalOnClass(DiscoveryClient.class)
public JwtSignerHolder jwtSignerHolder(DiscoveryClient discoveryClient){
return new JwtSignerHolder(discoveryClient);
}
@Bean
public AuthUtil authUtil(JwtSignerHolder jwtSignerHolder, StringRedisTemplate stringRedisTemplate){
return new AuthUtil(jwtSignerHolder, stringRedisTemplate);
}
}

@ -1,176 +0,0 @@
package com.tianji.authsdk.gateway.util;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.exceptions.ValidateException;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import cn.hutool.jwt.JWT;
import cn.hutool.jwt.JWTValidator;
import com.tianji.auth.common.domain.PrivilegeRoleDTO;
import com.tianji.common.domain.R;
import com.tianji.common.domain.dto.LoginUserDTO;
import com.tianji.common.exceptions.ForbiddenException;
import com.tianji.common.exceptions.UnauthorizedException;
import com.tianji.common.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.util.AntPathMatcher;
import java.util.*;
import java.util.stream.Collectors;
import static com.tianji.auth.common.constants.AuthErrorInfo.Code.EXPIRED_TOKEN_CODE;
import static com.tianji.auth.common.constants.AuthErrorInfo.Code.INVALID_TOKEN_CODE;
import static com.tianji.auth.common.constants.AuthErrorInfo.Msg.*;
import static com.tianji.auth.common.constants.JwtConstants.*;
@Slf4j
public class AuthUtil {
// 缓存权限信息
private Map<String, PrivilegeRoleDTO> privileges = new HashMap<>();
// 要拦截的路径匹配符的集合
private Set<String> paths = new HashSet<>();
// 权限版本信息,减少不必要的缓存处理
private int privilegeVersion;
private final AntPathMatcher antPathMatcher = new AntPathMatcher();
private final JwtSignerHolder jwtSignerHolder;
private final StringRedisTemplate stringRedisTemplate;
private final BoundHashOperations<String, String, String> hashOps;
public AuthUtil(JwtSignerHolder jwtSignerHolder, StringRedisTemplate stringRedisTemplate) {
this.jwtSignerHolder = jwtSignerHolder;
this.stringRedisTemplate = stringRedisTemplate;
this.hashOps = stringRedisTemplate.boundHashOps(AUTH_PRIVILEGE_KEY);
}
public R<LoginUserDTO> parseToken(String token) {
// 1.校验token是否为空
if(StringUtils.isBlank(token)){
return R.error(INVALID_TOKEN_CODE, INVALID_TOKEN);
}
JWT jwt = null;
try {
jwt = JWT.of(token).setSigner(jwtSignerHolder.getJwtSigner());
} catch (Exception e) {
return R.error(INVALID_TOKEN_CODE, INVALID_TOKEN);
}
// 2.校验jwt是否有效
if (!jwt.verify()) {
// 验证失败,返回空
return R.error(INVALID_TOKEN_CODE, INVALID_TOKEN);
}
// 3.校验是否过期
try {
JWTValidator.of(jwt).validateDate();
} catch (ValidateException e) {
return R.error(EXPIRED_TOKEN_CODE, EXPIRED_TOKEN);
}
// 4.数据格式校验
Object userPayload = jwt.getPayload(PAYLOAD_USER_KEY);
if (userPayload == null) {
// 数据为空
return R.error(INVALID_TOKEN_CODE, INVALID_TOKEN_PAYLOAD);
}
// 5.数据解析
LoginUserDTO userDTO;
try {
userDTO = ((JSONObject)userPayload).toBean(LoginUserDTO.class);
} catch (RuntimeException e) {
// token格式有误
return R.error(INVALID_TOKEN_CODE, INVALID_TOKEN_PAYLOAD);
}
// 6.返回
return R.ok(userDTO);
}
public void checkAuth(String antPath, R<LoginUserDTO> r){
// 1.判断是否是需要权限的路径
String matchPath = findMatchPath(antPath);
if(matchPath == null){
// 没有权限限制,直接放行
return;
}
// 2.判断是否登录成功
if(!r.success()){
// 未登录,直接报错
throw new UnauthorizedException(r.getCode(), r.getMsg());
}
// 3.获取当前路径所需权限
PrivilegeRoleDTO pathPrivilege = findPathPrivilege(matchPath);
// 4.权限判断
Set<Long> requiredRoles = pathPrivilege.getRoles();
if (!CollectionUtil.contains(requiredRoles, r.getData().getRoleId())) {
// 没有访问权限
throw new ForbiddenException(FORBIDDEN);
}
}
private String findMatchPath(String antPath){
String matchPath = null;
for (String pathPattern : paths) {
if(antPathMatcher.match(pathPattern, antPath)){
matchPath = pathPattern;
break;
}
}
return matchPath;
}
private PrivilegeRoleDTO findPathPrivilege(String path){
return privileges.get(path);
}
private List<PrivilegeRoleDTO> loadPrivileges(){
List<String> values = hashOps.values();
if(CollUtil.isEmpty(values)){
return Collections.emptyList();
}
return values.stream()
.map(json -> JSONUtil.toBean(json, PrivilegeRoleDTO.class))
.collect(Collectors.toList());
}
private int currentVersion() {
String version = stringRedisTemplate.opsForValue().get(AUTH_PRIVILEGE_VERSION_KEY);
if(StrUtil.isEmpty(version)){
return 0;
}
return Integer.parseInt(version);
}
@Scheduled(fixedDelay = 20000)
public void refreshTask(){
// 1.获取版本号
int currentVersion = currentVersion();
if (currentVersion == this.privilegeVersion) {
// 版本一致,说明数据没有更新,直接结束任务
return;
}
// 2.获取最新权限信息
List<PrivilegeRoleDTO> privilegeRoleDTOS = loadPrivileges();
if(CollUtil.isEmpty(privilegeRoleDTOS)){
// 更新版本
this.privilegeVersion = currentVersion;
return;
}
// 3.数据处理
Map<String, PrivilegeRoleDTO> map = new HashMap<>();
for (PrivilegeRoleDTO p : privilegeRoleDTOS) {
map.put(p.getAntPath(), p);
this.privileges = map;
}
this.paths = map.keySet();
// 4.更新版本
this.privilegeVersion = currentVersion;
}
}

@ -1,110 +0,0 @@
package com.tianji.authsdk.gateway.util;
import cn.hutool.crypto.KeyUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.asymmetric.AsymmetricAlgorithm;
import cn.hutool.http.HttpUtil;
import cn.hutool.jwt.signers.JWTSigner;
import cn.hutool.jwt.signers.JWTSignerUtil;
import com.tianji.auth.common.constants.JwtConstants;
import com.tianji.common.utils.CollUtils;
import com.tianji.common.utils.MarkedRunnable;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import javax.annotation.PostConstruct;
import java.nio.charset.StandardCharsets;
import java.security.PublicKey;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
@Data
@Slf4j
public class JwtSignerHolder {
private volatile JWTSigner jwtSigner;
private DiscoveryClient discoveryClient;
public JwtSignerHolder(DiscoveryClient discoveryClient) {
this.discoveryClient = discoveryClient;
}
private final ExecutorService ses = new ThreadPoolExecutor(
1,
1,
10,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(1),
r -> new Thread(r, "AuthFetchJwkThread")
);
@PostConstruct
public void init(){
// 尝试获取jwk秘钥
ses.submit(new MarkedRunnable(new JwkTask(discoveryClient)));
}
public void shutdown(){
ses.shutdown();
log.debug("销毁加载秘钥线程 AuthFetchJwkThread");
}
public static void sleep(long time){
try {
Thread.sleep(time);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
class JwkTask implements Runnable{
private final DiscoveryClient discoveryClient;
public JwkTask(DiscoveryClient discoveryClient) {
this.discoveryClient = discoveryClient;
}
@Override
public void run() {
while (jwtSigner == null) {
try {
log.info("尝试加载auth服务地址");
List<ServiceInstance> instances = discoveryClient.getInstances("auth-service");
if(CollUtils.isEmpty(instances)){
log.error("加载auth服务地址失败原因数据为空");
sleep(10000);
continue;
}
ServiceInstance instance = instances.get(0);
String jwkUri = String.format("http://%s:%d/jwks", instance.getHost(), instance.getPort());
log.info("加载auth服务地址成功{}", jwkUri);
log.info("尝试加载jwk秘钥");
// 请求获取jwk
String result = HttpUtil.get(jwkUri, StandardCharsets.UTF_8);
if(result == null){
log.error("加载jwk秘钥失败原因数据为空");
sleep(10000);
continue;
}
// 解析
PublicKey publicKey = KeyUtil.generatePublicKey(
AsymmetricAlgorithm.RSA_ECB_PKCS1.getValue(),
SecureUtil.decode(result)
);
jwtSigner = JWTSignerUtil.createSigner(JwtConstants.JWT_ALGORITHM, publicKey);
log.info("加载jwk秘钥成功");
} catch (Exception e) {
log.error("加载jwk秘钥失败原因{}", e.getMessage());
sleep(10000);
}
}
// 关闭线程池
shutdown();
}
}
}

@ -1,19 +0,0 @@
{
"groups": [
{
"name": "tj.auth.gateway",
"type": "com.tianji.authsdk.gateway.config.AuthAutoConfiguration",
"sourceType": "com.tianji.authsdk.gateway.config.AuthProperties"
}
],
"properties": [
{
"name": "tj.auth.excludePath",
"type": "java.util.Set",
"description": "不用登录就能访问的路径ant风格通配符",
"sourceType": "com.tianji.authsdk.gateway.config.AuthProperties",
"defaultValue": ""
}
],
"hints": []
}

@ -1,2 +0,0 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.tianji.authsdk.gateway.config.AuthAutoConfiguration

@ -1,41 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>tj-auth</artifactId>
<groupId>com.tianji</groupId>
<version>1.0.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>tj-auth-resource-sdk</artifactId>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.tianji</groupId>
<artifactId>tj-auth-common</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-core</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

@ -1,17 +0,0 @@
package com.tianji.authsdk.resource.config;
import feign.Feign;
import com.tianji.authsdk.resource.interceptors.FeignRelayUserInterceptor;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@ConditionalOnClass(Feign.class)
public class FeignRelayUserAutoConfiguration {
@Bean
public FeignRelayUserInterceptor feignRelayUserInterceptor(){
return new FeignRelayUserInterceptor();
}
}

@ -1,14 +0,0 @@
package com.tianji.authsdk.resource.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.util.List;
@Data
@ConfigurationProperties(prefix = "tj.auth.resource")
public class ResourceAuthProperties {
private Boolean enable = false;
private List<String> includeLoginPaths;
private List<String> excludeLoginPaths;
}

@ -1,52 +0,0 @@
package com.tianji.authsdk.resource.config;
import cn.hutool.core.collection.CollUtil;
import com.tianji.authsdk.resource.interceptors.LoginAuthInterceptor;
import com.tianji.authsdk.resource.interceptors.UserInfoInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
@EnableConfigurationProperties(ResourceAuthProperties.class)
public class ResourceInterceptorConfiguration implements WebMvcConfigurer {
private final ResourceAuthProperties authProperties;
@Autowired
public ResourceInterceptorConfiguration(ResourceAuthProperties resourceAuthProperties) {
this.authProperties = resourceAuthProperties;
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 1.添加用户信息拦截器
registry.addInterceptor(new UserInfoInterceptor()).order(0);
// 2.是否需要做登录拦截
if(!authProperties.getEnable()){
// 无需登录拦截
return;
}
// 2.添加登录拦截器
InterceptorRegistration registration = registry.addInterceptor(new LoginAuthInterceptor()).order(1);
// 2.1.添加拦截器路径
if(CollUtil.isNotEmpty(authProperties.getIncludeLoginPaths())){
registration.addPathPatterns(authProperties.getIncludeLoginPaths());
}
// 2.2.添加排除路径
if(CollUtil.isNotEmpty(authProperties.getExcludeLoginPaths())){
registration.excludePathPatterns(authProperties.getExcludeLoginPaths());
}
// 2.3.排除swagger路径
registration.excludePathPatterns(
"/v2/**",
"/v3/**",
"/swagger-resources/**",
"/webjars/**",
"/doc.html"
);
}
}

@ -1,17 +0,0 @@
package com.tianji.authsdk.resource.interceptors;
import com.tianji.auth.common.constants.JwtConstants;
import com.tianji.common.utils.UserContext;
import feign.RequestInterceptor;
import feign.RequestTemplate;
public class FeignRelayUserInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate template) {
Long userId = UserContext.getUser();
if (userId == null) {
return;
}
template.header(JwtConstants.USER_HEADER, userId.toString());
}
}

@ -1,27 +0,0 @@
package com.tianji.authsdk.resource.interceptors;
import com.tianji.common.utils.UserContext;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Slf4j
public class LoginAuthInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 1.尝试获取用户信息
Long userId = UserContext.getUser();
// 2.判断是否登录
if (userId == null) {
response.setStatus(401);
response.sendError(401, "未登录用户无法访问!");
// 2.3.未登录,直接拦截
return false;
}
// 3.登录则放行
return true;
}
}

@ -1,38 +0,0 @@
package com.tianji.authsdk.resource.interceptors;
import com.tianji.auth.common.constants.JwtConstants;
import com.tianji.common.utils.UserContext;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Slf4j
public class UserInfoInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 1.尝试获取头信息中的用户信息
String authorization = request.getHeader(JwtConstants.USER_HEADER);
// 2.判断是否为空
if (authorization == null) {
return true;
}
// 3.转为用户id并保存
try {
Long userId = Long.valueOf(authorization);
UserContext.setUser(userId);
return true;
} catch (NumberFormatException e) {
log.error("用户身份信息格式不正确,{}, 原因:{}", authorization, e.getMessage());
return true;
}
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
// 清理用户信息
UserContext.removeUser();
}
}

@ -1,31 +0,0 @@
{
"groups": [
{
"name": "tj.auth.resource",
"type": "com.tianji.authsdk.resource.config.ResourceInterceptorConfiguration",
"sourceType": "com.tianji.authsdk.resource.config.ResourceAuthProperties"
}
],
"properties": [
{
"name": "tj.auth.resource.enable",
"type": "java.lang.Boolean",
"description": "是否开启登录拦截功能,如果开启则需要指定拦截路径,默认拦截所有",
"sourceType": "com.tianji.authsdk.resource.config.ResourceAuthProperties",
"defaultValue": false
},
{
"name": "tj.auth.resource.includeLoginPaths",
"type": "java.util.List",
"description": "要拦截的路径,例如:/user/**",
"sourceType": "com.tianji.authsdk.resource.config.ResourceAuthProperties"
},
{
"name": "tj.auth.resource.excludeLoginPaths",
"type": "java.util.List",
"description": "不拦截的路径,例如:/user/**",
"sourceType": "com.tianji.authsdk.resource.config.ResourceAuthProperties"
}
],
"hints": []
}

@ -1,3 +0,0 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.tianji.authsdk.resource.config.ResourceInterceptorConfiguration, \
com.tianji.authsdk.resource.config.FeignRelayUserAutoConfiguration

@ -1,92 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>tj-auth</artifactId>
<groupId>com.tianji</groupId>
<version>1.0.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>tj-auth-service</artifactId>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>com.tianji</groupId>
<artifactId>tj-api</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--Redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--redisson-->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
</dependency>
<!--commons-pool2-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<!--discovery-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--config-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!--loadbalancer-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<!--sdk-->
<dependency>
<groupId>com.tianji</groupId>
<artifactId>tj-auth-resource-sdk</artifactId>
<version>1.0.0</version>
</dependency>
</dependencies>
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>build-info</goal>
</goals>
</execution>
</executions>
<configuration>
<mainClass>com.tianji.auth.AuthApplication</mainClass>
</configuration>
</plugin>
</plugins>
</build>
</project>

@ -1,41 +0,0 @@
package com.tianji.auth;
import lombok.extern.slf4j.Slf4j;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.core.env.Environment;
import org.springframework.scheduling.annotation.EnableScheduling;
import java.net.InetAddress;
import java.net.UnknownHostException;
@MapperScan("com.tianji.auth.mapper")
@SpringBootApplication
@EnableScheduling
@Slf4j
public class AuthApplication {
public static void main(String[] args) throws UnknownHostException {
SpringApplication app = new SpringApplicationBuilder(AuthApplication.class).build(args);
Environment env = app.run(args).getEnvironment();
String protocol = "http";
if (env.getProperty("server.ssl.key-store") != null) {
protocol = "https";
}
log.info("--/\n---------------------------------------------------------------------------------------\n\t" +
"Application '{}' is running! Access URLs:\n\t" +
"Local: \t\t{}://localhost:{}\n\t" +
"External: \t{}://{}:{}\n\t" +
"Profile(s): \t{}" +
"\n---------------------------------------------------------------------------------------",
env.getProperty("spring.application.name"),
protocol,
env.getProperty("server.port"),
protocol,
InetAddress.getLocalHost().getHostAddress(),
env.getProperty("server.port"),
env.getActiveProfiles());
log.info("--/\n------------------------------------------------------------------------------\n\t" );
}
}

@ -1,41 +0,0 @@
package com.tianji.auth.config;
import org.apache.tomcat.util.http.LegacyCookieProcessor;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.embedded.tomcat.TomcatContextCustomizer;
import org.springframework.cloud.bootstrap.encrypt.KeyProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.rsa.crypto.KeyStoreKeyFactory;
import java.security.KeyPair;
@Configuration
@EnableConfigurationProperties(KeyProperties.class)
public class AuthConfig {
@Bean
@ConfigurationProperties(prefix = "encrypt")
public KeyProperties keyProperties(){
return new KeyProperties();
}
@Bean
public KeyPair keyPair(KeyProperties keyProperties){
// 获取秘钥工厂
KeyStoreKeyFactory keyStoreKeyFactory =
new KeyStoreKeyFactory(
keyProperties.getKeyStore().getLocation(),
keyProperties.getKeyStore().getPassword().toCharArray());
//读取钥匙对
return keyStoreKeyFactory.getKeyPair(
keyProperties.getKeyStore().getAlias(),
keyProperties.getKeyStore().getSecret().toCharArray());
}
@Bean
public TomcatContextCustomizer cookieTomcatContextCustomizer(){
return context -> context.setCookieProcessor(new LegacyCookieProcessor());
}
}

@ -1,6 +0,0 @@
package com.tianji.auth.constants;
public abstract class AuthConstants {
/*管理员的角色ID*/
public static final Long ADMIN_ROLE_ID = 1L;
}

@ -1,47 +0,0 @@
package com.tianji.auth.controller;
import com.tianji.api.dto.user.LoginFormDTO;
import com.tianji.auth.common.constants.JwtConstants;
import com.tianji.auth.service.IAccountService;
import com.tianji.common.utils.WebUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
/**
*
*/
@RestController
@RequestMapping("/accounts")
@Api(tags = "账户管理")
@RequiredArgsConstructor
public class AccountController {
private final IAccountService accountService;
@ApiOperation("登录并获取token")
@PostMapping(value = "/login")
public String loginByPw(@RequestBody LoginFormDTO loginFormDTO){
return accountService.login(loginFormDTO, false);
}
@ApiOperation("管理端登录并获取token")
@PostMapping(value = "/admin/login")
public String adminLoginByPw(@RequestBody LoginFormDTO loginFormDTO){
return accountService.login(loginFormDTO, true);
}
@ApiOperation("退出登录")
@PostMapping(value = "/logout")
public void logout(){
accountService.logout();
}
@ApiOperation("刷新token")
@GetMapping(value = "/refresh")
public String refreshToken(@CookieValue(JwtConstants.REFRESH_HEADER) String token){
return accountService.refreshToken(WebUtils.cookieBuilder().decode(token));
}
}

@ -1,30 +0,0 @@
package com.tianji.auth.controller;
import cn.hutool.core.codec.Base64;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import springfox.documentation.annotations.ApiIgnore;
import java.security.KeyPair;
@RestController
@RequestMapping("jwks")
@ApiIgnore
public class JwkController {
private final KeyPair keyPair;
@Autowired
public JwkController(KeyPair keyPair) {
this.keyPair = keyPair;
}
@GetMapping
public String getJwk(){
// TODO 可以加入clientId和clientSecret校验
// 获取公钥并转码
return Base64.encode(keyPair.getPublic().getEncoded());
}
}

@ -1,152 +0,0 @@
package com.tianji.auth.controller;
import cn.hutool.core.collection.CollectionUtil;
import com.tianji.auth.domain.dto.MenuDTO;
import com.tianji.auth.domain.po.Menu;
import com.tianji.auth.domain.vo.MenuOptionVO;
import com.tianji.auth.service.IMenuService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* <p>
* 访
* </p>
*
* @author
* @since 2022-06-16
*/
@RestController
@RequestMapping("/menus")
@Api(tags = "菜单管理")
@RequiredArgsConstructor
public class MenuController {
private final IMenuService menuService;
/**
* id
* @param pid id 0 1
* @return
*/
@GetMapping("/parent/{pid}")
@ApiOperation("根据父菜单id查询子菜单")
public List<MenuOptionVO> listMenusByParent(
@ApiParam(value = "父菜单id", example = "0") @PathVariable("pid") Long pid){
// 1.根据父id查询
List<Menu> list = menuService.lambdaQuery().eq(Menu::getParentId, pid).list();
// 2.非空判断
if (CollectionUtil.isEmpty(list)) {
return Collections.emptyList();
}
// 3.数据转换
return list.stream().map(MenuOptionVO::new).collect(Collectors.toList());
}
@GetMapping("{id}")
@ApiOperation("根据id查询菜单")
public MenuOptionVO getMenuById(@ApiParam(value = "菜单id", example = "1") @PathVariable("id") Long id) {
Menu menu = menuService.getById(id);
if (menu == null) {
return null;
}
return new MenuOptionVO(menu);
}
/**
*
* @return
*/
@GetMapping
@ApiOperation("查询菜单,按照多级菜单组成树结构")
public List<MenuOptionVO> listMenuTree(){
// 1.查询所有菜单
List<Menu> menus = menuService.list();
return convert2MenuDTOs(menus);
}
private List<MenuOptionVO> convert2MenuDTOs(List<Menu> menus) {
if (CollectionUtil.isEmpty(menus)) {
return Collections.emptyList();
}
// 2.按照父菜单id分组
Map<Long, List<MenuOptionVO>> menuMap = menus.stream()
.map(MenuOptionVO::new)
.collect(Collectors.groupingBy(MenuOptionVO::getParentId));
// 3.组合
// 3.1.获取1级菜单
List<MenuOptionVO> parents = menuMap.get(0L);
// 3.2.获取2级菜单
for (MenuOptionVO parent : parents) {
List<MenuOptionVO> subMenus = menuMap.get(parent.getId());
subMenus.sort(Comparator.comparingInt(MenuOptionVO::getPriority));
parent.setSubMenus(subMenus);
}
// 3.3.排序
parents.sort(Comparator.comparingInt(MenuOptionVO::getPriority));
return parents;
}
/**
*
* @return
*/
@GetMapping("me")
@ApiOperation("查询我的菜单,按照多级菜单组成树结构")
public List<MenuOptionVO> listMenuTreeByUser(){
// 1.查询所有菜单
List<Menu> menus = menuService.listMenuByUser();
return convert2MenuDTOs(menus);
}
@PostMapping
@ApiOperation("新增菜单")
public void saveMenu(@RequestBody MenuDTO menuDTO){
// 1.数据转换
Menu menu = new Menu(menuDTO);
// 2.保存
menuService.saveMenu(menu);
}
@PutMapping("{id}")
@ApiOperation("更新菜单")
public void updateMenu(
@RequestBody MenuDTO menuDTO,
@ApiParam(value = "菜单id", example = "1")@PathVariable("id") Long id) {
menuDTO.setId(id);
menuService.updateById(new Menu(menuDTO));
}
@DeleteMapping("{id}")
@ApiOperation("根据id删除菜单")
public void deleteMenu(
@ApiParam(value = "菜单id", example = "1") @PathVariable("id") Long id) {
menuService.deleteMenu(id);
}
@PostMapping("/role/{roleId}")
@ApiOperation("绑定角色与菜单权限")
public void bindRoleMenus(
@ApiParam(value = "角色id", example = "1") @PathVariable("roleId") Long roleId,
@ApiParam(value = "菜单id集合") List<Long> menuIds){
menuService.bindRoleMenus(roleId, menuIds);
}
@DeleteMapping("/role/{roleId}")
@ApiOperation("解除角色的菜单权限")
public void deleteRoleMenus(
@ApiParam(value = "角色id", example = "1") @PathVariable("roleId") Long roleId,
@ApiParam(value = "菜单id集合") List<Long> menuIds){
menuService.deleteRoleMenus(roleId, menuIds);
}
}

@ -1,192 +0,0 @@
package com.tianji.auth.controller;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.tianji.auth.domain.dto.PrivilegeDTO;
import com.tianji.auth.domain.po.Privilege;
import com.tianji.auth.domain.vo.PrivilegeOptionVO;
import com.tianji.auth.service.IPrivilegeService;
import com.tianji.common.domain.dto.PageDTO;
import com.tianji.common.domain.query.PageQuery;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
/**
* <p>
* 访
* </p>
*
* @author
* @since 2022-06-15
*/
@RestController
@RequestMapping("/privileges")
@Api(tags = "权限管理接口")
@RequiredArgsConstructor
public class PrivilegeController {
private final IPrivilegeService privilegesService;
/**
*
*
* @param pageQuery
* @return
*/
@ApiOperation("分页查询所有权限")
@GetMapping
public PageDTO<PrivilegeDTO> listAllPrivileges(PageQuery pageQuery) {
// 1.分页查询
Page<Privilege> page = privilegesService.listPrivilegesByPage(pageQuery);
// 2.非空判断
List<Privilege> list = page.getRecords();
if (CollectionUtil.isEmpty(list)) {
// 结果为空,返回空结果 添加总页码数
return new PageDTO<>(page.getTotal(), page.getPages(), Collections.emptyList());
}
// 3.数据转换
List<PrivilegeDTO> dtoList = list.stream().map(Privilege::toDTO).collect(Collectors.toList());
// 4.封装返回
return new PageDTO<>(page.getTotal(), page.getPages(), dtoList);
}
/**
*
*
* @return
*/
@ApiOperation("查询菜单下的所有权限,作为下拉选框菜单")
@GetMapping("options/{menuId}")
public List<PrivilegeOptionVO> listAllPrivilegesOptionsByMenuId(
@ApiParam(value = "菜单id", example = "1") @PathVariable("menuId") Long menuId
) {
// 1.查询菜单下的权限
List<Privilege> list = privilegesService.lambdaQuery()
.eq(Privilege::getMenuId, menuId)
.eq(Privilege::getInternal, false)
.list();
// 2.非空判断
if (CollectionUtil.isEmpty(list)) {
// 结果为空,返回空结果
return Collections.emptyList();
}
// 3.数据转换
return list.stream()
.map(PrivilegeOptionVO::new).collect(Collectors.toList());
}
/**
*
*
* @return
*/
@ApiOperation("查询菜单下的权限列表,某个角色的权限")
@GetMapping("/roles/{roleId}/{menuId}")
public List<PrivilegeOptionVO> listPrivilegeByRoleId(
@ApiParam(value = "角色id", required = true, example = "1") @PathVariable("roleId") Long roleId,
@ApiParam(value = "菜单id", required = true, example = "1") @PathVariable("menuId") Long menuId
) {
// 1.查询角色对应的权限id
Set<Long> privilegeIds = privilegesService.listPrivilegeByRoleId(roleId);
if (CollectionUtil.isEmpty(privilegeIds)) {
return Collections.emptyList();
}
// 2.查询菜单下所有权限
List<PrivilegeOptionVO> vos = listAllPrivilegesOptionsByMenuId(menuId);
// 3.标记
for (PrivilegeOptionVO vo : vos) {
vo.setChecked(privilegeIds.contains(vo.getId()));
}
return vos;
}
/**
*
*
* @param privilegeDTO
* @return
*/
@ApiOperation("新增权限")
@PostMapping
public PrivilegeDTO savePrivilege(@Validated @RequestBody PrivilegeDTO privilegeDTO) {
// 域对象转换
Privilege privilege = new Privilege(privilegeDTO);
// 新增
privilegesService.savePrivilege(privilege);
// 返回
return privilege.toDTO();
}
/**
*
*
* @param privilegeDTO
* @param id id
* @return
*/
@ApiOperation("修改权限")
@PutMapping("{id}")
public PrivilegeDTO updatePrivilege(
@RequestBody PrivilegeDTO privilegeDTO,
@ApiParam(value = "要修改的权限id", required = true, example = "1") @PathVariable("id") Long id) {
// 域对象转换
Privilege privilege = new Privilege(privilegeDTO);
privilege.setId(id);
// 修改
privilegesService.updateById(privilege);
// 返回
return privilege.toDTO();
}
/**
*
*
* @param id id
*/
@ApiOperation("删除权限")
@DeleteMapping("{id}")
public void removePrivilegeById(
@ApiParam(value = "要删除的权限id", required = true, example = "1") @PathVariable("id") Long id) {
privilegesService.removePrivilegeById(id);
}
/**
* API
*
* @param roleId id
* @param privilegeIds id
*/
@PostMapping("/role/{roleId}")
@ApiOperation("绑定角色与API权限")
public void bindRolePrivileges(
@ApiParam(value = "角色id", example = "1") @PathVariable("roleId") Long roleId,
@ApiParam(value = "API权限的id集合") List<Long> privilegeIds) {
privilegesService.bindRolePrivileges(roleId, privilegeIds);
}
/**
* API
*
* @param roleId id
* @param privilegeIds id
*/
@DeleteMapping("/role/{roleId}")
@ApiOperation("解除角色的API权限")
public void deleteRolePrivileges(
@ApiParam(value = "角色id", example = "1") @PathVariable("roleId") Long roleId,
@ApiParam(value = "API权限的id集合") List<Long> privilegeIds) {
privilegesService.deleteRolePrivileges(roleId, privilegeIds);
}
}

@ -1,99 +0,0 @@
package com.tianji.auth.controller;
import cn.hutool.core.collection.CollectionUtil;
import com.tianji.api.dto.auth.RoleDTO;
import com.tianji.auth.domain.po.Role;
import com.tianji.auth.service.IRoleService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
/**
* @author
* @since 2022-06-16
*/
@Api(tags = "角色管理")
@RestController
@RequestMapping("/roles")
@RequiredArgsConstructor
public class RoleController {
private final IRoleService roleService;
@ApiOperation("查询员工角色列表")
@GetMapping("/list")
public List<RoleDTO> listAllRoles(){
// 1.查询
List<Role> list = roleService.list();
if (CollectionUtil.isEmpty(list)) {
return Collections.emptyList();
}
// 3.数据转换
return list.stream().map(Role::toDTO).collect(Collectors.toList());
}
@ApiOperation("查询员工角色列表")
@GetMapping
public List<RoleDTO> listStaffRoles(){
// 1.查询
List<Role> list = roleService.lambdaQuery().eq(Role::getType, Role.RoleType.CUSTOM).list();
if (CollectionUtil.isEmpty(list)) {
return Collections.emptyList();
}
// 3.数据转换
return list.stream().map(Role::toDTO).collect(Collectors.toList());
}
@ApiOperation("根据id查询角色")
@GetMapping("/{id}")
public RoleDTO queryRoleById(@PathVariable("id") Long id){
// 1.查询
Role role = roleService.getById(id);
if (role == null) {
return null;
}
// 2.数据转换
return role.toDTO();
}
@ApiOperation("新增角色")
@PostMapping
public RoleDTO saveRole(@RequestBody RoleDTO roleDTO) {
Role role = new Role(roleDTO);
role.setType(Role.RoleType.CUSTOM);
// 1.新增
roleService.save(role);
// 2.返回
roleDTO.setId(role.getId());
return roleDTO;
}
@ApiOperation("修改角色信息")
@PutMapping("{id}")
public void updateRole(
@RequestBody RoleDTO roleDTO,
@ApiParam(value = "角色id", example = "1") @PathVariable("id") Long id
) {
// 1.数据转换
Role role = new Role(roleDTO);
role.setId(id);
// 2.修改
roleService.updateById(role);
}
@ApiOperation("删除角色信息")
@DeleteMapping("{id}")
public void deleteRole(@ApiParam(value = "角色id", example = "1") @PathVariable("id") Long id) {
roleService.deleteRole(id);
}
}

@ -1,27 +0,0 @@
package com.tianji.auth.domain.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
@ApiModel(description = "菜单表单实体")
public class MenuDTO {
@ApiModelProperty(value = "菜单id", example = "1")
private Long id;
@ApiModelProperty(value = "父菜单id", example = "0")
private Long parentId;
@ApiModelProperty(value = "菜单文本", example = "系统管理")
private String label;
@ApiModelProperty(value = "菜单路径", example = "/sys/index")
private String path;
@ApiModelProperty(value = "菜单图标", example = "el-icon-sys")
private String icon;
@ApiModelProperty(value = "菜单顺序", example = "1")
private Integer priority;
}

@ -1,32 +0,0 @@
package com.tianji.auth.domain.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
import java.io.Serializable;
@Data
@ApiModel(description = "API权限")
public class PrivilegeDTO implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "权限id", example = "1")
private Long id;
@ApiModelProperty(value = "权限所属菜单id", example = "1")
private Long menuId;
@ApiModelProperty(value = "权限说明", example = "新增员工")
@NotNull(message = "权限说明不能为空")
private String intro;
@ApiModelProperty(value = "API请求方式", example = "GET")
@Pattern(regexp = "^GET|POST|PUT|DELETE$", message = "请求方式必须是大写")
private String method;
@ApiModelProperty(value = "API请求路径", example = "/account/staff")
@NotNull(message = "uri不能为空")
private String uri;
@ApiModelProperty("是否是内部权限")
private Boolean internal;
}

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

Loading…
Cancel
Save