From e73f60cc6b957051164fd6a9bab47e88466b4d93 Mon Sep 17 00:00:00 2001 From: JiyangTang <18010816106@163.com> Date: Fri, 16 Jun 2023 15:28:09 +0800 Subject: [PATCH] first commit --- README.md | 18 ++ baogutang-admin/Dockerfile | 7 + baogutang-admin/pom.xml | 153 +++++++++++++ .../admin/BaogutangAdminApplication.java | 23 ++ .../admin/domain/EdgeXAnnouncementsDto.java | 52 +++++ .../admin/schedule/EdgeXNoticeSchedule.java | 59 +++++ .../admin/services/IWxMsgPushService.java | 24 ++ .../services/impl/WxMsgPushServiceImpl.java | 81 +++++++ .../src/main/resources/bootstrap-local.yml | 19 ++ .../src/main/resources/bootstrap-prod.yml | 19 ++ .../src/main/resources/bootstrap-test.yml | 19 ++ .../src/main/resources/bootstrap.yml | 5 + .../src/main/resources/logback-spring.xml | 114 ++++++++++ .../admin/BaogutangAdminApplicationTests.java | 13 ++ baogutang-business/Dockerfile | 7 + baogutang-business/pom.xml | 208 ++++++++++++++++++ .../BaogutangBusinessApplication.java | 22 ++ .../controller/CallbackController.java | 23 ++ .../controller/WxMsgPushController.java | 35 +++ .../business/services/IWxMsgPushService.java | 18 ++ .../services/impl/WxMsgPushServiceImpl.java | 75 +++++++ .../src/main/resources/bootstrap-local.yml | 19 ++ .../src/main/resources/bootstrap-prod.yml | 19 ++ .../src/main/resources/bootstrap-test.yml | 19 ++ .../src/main/resources/bootstrap.yml | 5 + .../src/main/resources/logback-spring.xml | 114 ++++++++++ baogutang-common/pom.xml | 139 ++++++++++++ .../common/annotation/LoginRequired.java | 20 ++ .../common/annotation/RequestLimit.java | 37 ++++ .../common/annotation/VerifyRequired.java | 18 ++ .../baogutang/common/aspect/LogAspect.java | 65 ++++++ .../common/aspect/LoginRequiredAspect.java | 115 ++++++++++ .../common/aspect/RequestLimitAspect.java | 127 +++++++++++ .../common/aspect/VerifyRequiredAspect.java | 100 +++++++++ .../common/components/TokenComponent.java | 109 +++++++++ .../common/config/ExecutorConfig.java | 49 +++++ .../common/config/GlobalCorsConfig.java | 37 ++++ .../common/config/GlobalMDCTaskDecorator.java | 45 ++++ .../common/config/MdcRequestIdFilter.java | 44 ++++ .../common/config/MybatisPlusConfig.java | 27 +++ .../baogutang/common/config/RedisConfig.java | 85 +++++++ .../common/constants/CacheConstant.java | 21 ++ .../common/constants/ErrorCodeEnum.java | 80 +++++++ .../common/constants/JwtConstant.java | 20 ++ .../top/baogutang/common/domain/JwtBody.java | 66 ++++++ .../top/baogutang/common/domain/Page.java | 33 +++ .../top/baogutang/common/domain/Results.java | 192 ++++++++++++++++ .../common/domain/TokenCodeEnum.java | 25 +++ .../common/exceptions/BusinessException.java | 39 ++++ .../exceptions/GlobalExceptionHandler.java | 75 +++++++ .../properties/WxMsgPushProperties.java | 24 ++ .../top/baogutang/common/utils/HttpUtils.java | 161 ++++++++++++++ .../common/utils/UserThreadLocal.java | 22 ++ .../src/main/resources/script/accquire.lua | 10 + .../src/main/resources/script/ipLimit.lua | 25 +++ pom.xml | 155 +++++++++++++ 56 files changed, 3135 insertions(+) create mode 100644 README.md create mode 100644 baogutang-admin/Dockerfile create mode 100644 baogutang-admin/pom.xml create mode 100644 baogutang-admin/src/main/java/top/baogutang/admin/BaogutangAdminApplication.java create mode 100644 baogutang-admin/src/main/java/top/baogutang/admin/domain/EdgeXAnnouncementsDto.java create mode 100644 baogutang-admin/src/main/java/top/baogutang/admin/schedule/EdgeXNoticeSchedule.java create mode 100644 baogutang-admin/src/main/java/top/baogutang/admin/services/IWxMsgPushService.java create mode 100644 baogutang-admin/src/main/java/top/baogutang/admin/services/impl/WxMsgPushServiceImpl.java create mode 100644 baogutang-admin/src/main/resources/bootstrap-local.yml create mode 100644 baogutang-admin/src/main/resources/bootstrap-prod.yml create mode 100644 baogutang-admin/src/main/resources/bootstrap-test.yml create mode 100644 baogutang-admin/src/main/resources/bootstrap.yml create mode 100644 baogutang-admin/src/main/resources/logback-spring.xml create mode 100644 baogutang-admin/src/test/java/top/baogutang/business/admin/BaogutangAdminApplicationTests.java create mode 100644 baogutang-business/Dockerfile create mode 100644 baogutang-business/pom.xml create mode 100644 baogutang-business/src/main/java/top/baogutang/business/BaogutangBusinessApplication.java create mode 100644 baogutang-business/src/main/java/top/baogutang/business/controller/CallbackController.java create mode 100644 baogutang-business/src/main/java/top/baogutang/business/controller/WxMsgPushController.java create mode 100644 baogutang-business/src/main/java/top/baogutang/business/services/IWxMsgPushService.java create mode 100644 baogutang-business/src/main/java/top/baogutang/business/services/impl/WxMsgPushServiceImpl.java create mode 100644 baogutang-business/src/main/resources/bootstrap-local.yml create mode 100644 baogutang-business/src/main/resources/bootstrap-prod.yml create mode 100644 baogutang-business/src/main/resources/bootstrap-test.yml create mode 100644 baogutang-business/src/main/resources/bootstrap.yml create mode 100644 baogutang-business/src/main/resources/logback-spring.xml create mode 100644 baogutang-common/pom.xml create mode 100644 baogutang-common/src/main/java/top/baogutang/common/annotation/LoginRequired.java create mode 100644 baogutang-common/src/main/java/top/baogutang/common/annotation/RequestLimit.java create mode 100644 baogutang-common/src/main/java/top/baogutang/common/annotation/VerifyRequired.java create mode 100644 baogutang-common/src/main/java/top/baogutang/common/aspect/LogAspect.java create mode 100644 baogutang-common/src/main/java/top/baogutang/common/aspect/LoginRequiredAspect.java create mode 100644 baogutang-common/src/main/java/top/baogutang/common/aspect/RequestLimitAspect.java create mode 100644 baogutang-common/src/main/java/top/baogutang/common/aspect/VerifyRequiredAspect.java create mode 100644 baogutang-common/src/main/java/top/baogutang/common/components/TokenComponent.java create mode 100644 baogutang-common/src/main/java/top/baogutang/common/config/ExecutorConfig.java create mode 100644 baogutang-common/src/main/java/top/baogutang/common/config/GlobalCorsConfig.java create mode 100644 baogutang-common/src/main/java/top/baogutang/common/config/GlobalMDCTaskDecorator.java create mode 100644 baogutang-common/src/main/java/top/baogutang/common/config/MdcRequestIdFilter.java create mode 100644 baogutang-common/src/main/java/top/baogutang/common/config/MybatisPlusConfig.java create mode 100644 baogutang-common/src/main/java/top/baogutang/common/config/RedisConfig.java create mode 100644 baogutang-common/src/main/java/top/baogutang/common/constants/CacheConstant.java create mode 100644 baogutang-common/src/main/java/top/baogutang/common/constants/ErrorCodeEnum.java create mode 100644 baogutang-common/src/main/java/top/baogutang/common/constants/JwtConstant.java create mode 100644 baogutang-common/src/main/java/top/baogutang/common/domain/JwtBody.java create mode 100644 baogutang-common/src/main/java/top/baogutang/common/domain/Page.java create mode 100644 baogutang-common/src/main/java/top/baogutang/common/domain/Results.java create mode 100644 baogutang-common/src/main/java/top/baogutang/common/domain/TokenCodeEnum.java create mode 100644 baogutang-common/src/main/java/top/baogutang/common/exceptions/BusinessException.java create mode 100644 baogutang-common/src/main/java/top/baogutang/common/exceptions/GlobalExceptionHandler.java create mode 100644 baogutang-common/src/main/java/top/baogutang/common/properties/WxMsgPushProperties.java create mode 100644 baogutang-common/src/main/java/top/baogutang/common/utils/HttpUtils.java create mode 100644 baogutang-common/src/main/java/top/baogutang/common/utils/UserThreadLocal.java create mode 100644 baogutang-common/src/main/resources/script/accquire.lua create mode 100644 baogutang-common/src/main/resources/script/ipLimit.lua create mode 100644 pom.xml diff --git a/README.md b/README.md new file mode 100644 index 0000000..486fe44 --- /dev/null +++ b/README.md @@ -0,0 +1,18 @@ +# Read Me First + +The following was discovered as part of building this project: + +* The JVM level was changed from '1.8' to '17', review + the [JDK Version Range](https://github.com/spring-projects/spring-framework/wiki/Spring-Framework-Versions#jdk-version-range) + on the wiki for more details. + +# Getting Started + +### Reference Documentation + +For further reference, please consider the following sections: + +* [Official Apache Maven documentation](https://maven.apache.org/guides/index.html) +* [Spring Boot Maven Plugin Reference Guide](https://docs.spring.io/spring-boot/docs/3.1.0/maven-plugin/reference/html/) +* [Create an OCI image](https://docs.spring.io/spring-boot/docs/3.1.0/maven-plugin/reference/html/#build-image) + diff --git a/baogutang-admin/Dockerfile b/baogutang-admin/Dockerfile new file mode 100644 index 0000000..04cae7b --- /dev/null +++ b/baogutang-admin/Dockerfile @@ -0,0 +1,7 @@ +FROM openjdk:8 +ENV TZ=Asia/Shanghai +RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone +COPY ./baogutang-admin/target/baogutang-admin-1.0.0-SNAPSHOT.jar /apps/baogutang-admin.jar +WORKDIR /apps +RUN bash -c 'touch /baogutang-admin.jar' +CMD exec java $JAVA_OPTS -jar baogutang-admin.jar diff --git a/baogutang-admin/pom.xml b/baogutang-admin/pom.xml new file mode 100644 index 0000000..2f1d7f1 --- /dev/null +++ b/baogutang-admin/pom.xml @@ -0,0 +1,153 @@ + + + 4.0.0 + + top.baogutang + baogutang-parent + 1.0.0-SNAPSHOT + + baogutang-admin + + 8 + 8 + + + + org.springframework.boot + spring-boot-starter-web + + + top.baogutang + baogutang-common + 1.0.0-SNAPSHOT + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + + + com.aliyun.openservices + aliyun-log-logback-appender + + + com.google.protobuf + protobuf-java + + + com.alibaba + fastjson + + + + mysql + mysql-connector-java + + + org.projectlombok + lombok + + + com.baomidou + mybatis-plus-boot-starter + + + io.springfox + springfox-boot-starter + + + org.junit.jupiter + junit-jupiter + test + + + org.junit.jupiter + junit-jupiter-params + test + + + org.junit.jupiter + junit-jupiter-engine + test + + + org.junit.jupiter + junit-jupiter-api + test + + + com.aliyun + dingtalk + 2.0.20 + + + + + + + src/main/resources + true + + + + + org.springframework.boot + spring-boot-maven-plugin + 2.2.5.RELEASE + + + + org.projectlombok + lombok + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.19 + + true + + + + + + + + local + + local + + + true + + + + test + + test + + + false + + + + prod + + prod + + + false + + + + + + diff --git a/baogutang-admin/src/main/java/top/baogutang/admin/BaogutangAdminApplication.java b/baogutang-admin/src/main/java/top/baogutang/admin/BaogutangAdminApplication.java new file mode 100644 index 0000000..199e4fa --- /dev/null +++ b/baogutang-admin/src/main/java/top/baogutang/admin/BaogutangAdminApplication.java @@ -0,0 +1,23 @@ +package top.baogutang.admin; + +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.scheduling.annotation.EnableScheduling; + +/** + * @author nikooh + */ +@Slf4j +@EnableScheduling +@SpringBootApplication(scanBasePackages = "top.baogutang.*") +@MapperScan(basePackages = {"top.baogutang.admin.dao.mapper", "top.baogutang.common.dao.mapper"}) +public class BaogutangAdminApplication { + + public static void main(String[] args) { + SpringApplication.run(BaogutangAdminApplication.class, args); + log.info("<<<<<<<<<<<<<<<==BAO_GU_TANG后台管理服务启动成功!!!==>>>>>>>>>>>>>>>"); + } + +} diff --git a/baogutang-admin/src/main/java/top/baogutang/admin/domain/EdgeXAnnouncementsDto.java b/baogutang-admin/src/main/java/top/baogutang/admin/domain/EdgeXAnnouncementsDto.java new file mode 100644 index 0000000..98816c4 --- /dev/null +++ b/baogutang-admin/src/main/java/top/baogutang/admin/domain/EdgeXAnnouncementsDto.java @@ -0,0 +1,52 @@ +package top.baogutang.admin.domain; + +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; + +/** + * @description: + * @author: nikooh + * @date: 2023/06/16 : 12:09 + */ +@Data +public class EdgeXAnnouncementsDto implements Serializable { + + private static final long serialVersionUID = 2069054276884655439L; + + /** + * id + */ + private Long id; + + /** + * 标题 + */ + private String title; + + /** + * 类型 + */ + private String type; + + /** + * 封面 + */ + private String cover; + + /** + * 创作者 + */ + private String creator; + + /** + * 内容 + */ + private String content; + + /** + * 创建时间 + */ + private Date createdAt; +} diff --git a/baogutang-admin/src/main/java/top/baogutang/admin/schedule/EdgeXNoticeSchedule.java b/baogutang-admin/src/main/java/top/baogutang/admin/schedule/EdgeXNoticeSchedule.java new file mode 100644 index 0000000..3a28028 --- /dev/null +++ b/baogutang-admin/src/main/java/top/baogutang/admin/schedule/EdgeXNoticeSchedule.java @@ -0,0 +1,59 @@ +package top.baogutang.admin.schedule; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.TypeReference; +import com.zjiecode.wxpusher.client.bean.Message; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections.CollectionUtils; +import org.springframework.cloud.context.config.annotation.RefreshScope; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; +import top.baogutang.admin.domain.EdgeXAnnouncementsDto; +import top.baogutang.admin.services.IWxMsgPushService; +import top.baogutang.common.domain.Page; +import top.baogutang.common.domain.Results; +import top.baogutang.common.properties.WxMsgPushProperties; +import top.baogutang.common.utils.HttpUtils; + +import javax.annotation.Resource; +import java.util.Objects; + +/** + * @description: EDGEX公告 + * @author: nikooh + * @date: 2023/06/16 : 11:41 + */ +@Slf4j +@Component +@RefreshScope +public class EdgeXNoticeSchedule { + + @Resource + private IWxMsgPushService wxMsgPushService; + + @Resource + private WxMsgPushProperties wxMsgPushProperties; + + /** + * 每分钟查询edgeX公告 + */ + @Scheduled(cron = "0 0/1 * * * ? ") + public void edgeXNotice() { + Results> results = HttpUtils.get("https://art-api.edge-x.cn/api/v1/art/announcements?pageNum=1&pageSize=1", new TypeReference>>() { + }); + log.info(">>>>>>>>>>请求获取edgeX公告返回数据:{}<<<<<<<<<<", JSON.toJSONString(results)); + if (Objects.isNull(results)) { + return; + } + if (!Boolean.TRUE.equals(results.isSuccess())) { + return; + } + if (Objects.isNull(results.getData()) || CollectionUtils.isEmpty(results.getData().getList())) { + return; + } + EdgeXAnnouncementsDto announcementsDto = results.getData().getList().get(0); + if (Math.abs(announcementsDto.getCreatedAt().getTime() - System.currentTimeMillis()) <= 60000) { + wxMsgPushService.msgPush(Message.CONTENT_TYPE_HTML, announcementsDto.getTitle(), announcementsDto.getContent(), wxMsgPushProperties.getTopicIds()); + } + } +} diff --git a/baogutang-admin/src/main/java/top/baogutang/admin/services/IWxMsgPushService.java b/baogutang-admin/src/main/java/top/baogutang/admin/services/IWxMsgPushService.java new file mode 100644 index 0000000..485c9b1 --- /dev/null +++ b/baogutang-admin/src/main/java/top/baogutang/admin/services/IWxMsgPushService.java @@ -0,0 +1,24 @@ +package top.baogutang.admin.services; + + +import java.util.Set; + +/** + * @description: 微信消息推送service + * @author: nikooh + * @date: 2023/06/16 : 10:27 + */ +public interface IWxMsgPushService { + + /** + * 微信消息推送 + * + * @param msgType 消息类型 + * @param summary 消息主题 + * @param msgContent 消息内容 + * @param topicIdSet 消息模版id + * @return 推送结果 + */ + Boolean msgPush(Integer msgType, String summary, String msgContent, Set topicIdSet); + +} diff --git a/baogutang-admin/src/main/java/top/baogutang/admin/services/impl/WxMsgPushServiceImpl.java b/baogutang-admin/src/main/java/top/baogutang/admin/services/impl/WxMsgPushServiceImpl.java new file mode 100644 index 0000000..69096cc --- /dev/null +++ b/baogutang-admin/src/main/java/top/baogutang/admin/services/impl/WxMsgPushServiceImpl.java @@ -0,0 +1,81 @@ +package top.baogutang.admin.services.impl; + +import com.alibaba.fastjson.JSON; +import com.zjiecode.wxpusher.client.WxPusher; +import com.zjiecode.wxpusher.client.bean.Message; +import com.zjiecode.wxpusher.client.bean.MessageResult; +import com.zjiecode.wxpusher.client.bean.Result; +import com.zjiecode.wxpusher.client.bean.WxUser; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections.CollectionUtils; +import org.springframework.cloud.context.config.annotation.RefreshScope; +import org.springframework.stereotype.Service; +import top.baogutang.admin.services.IWxMsgPushService; +import com.zjiecode.wxpusher.client.bean.Page; +import top.baogutang.common.exceptions.BusinessException; +import top.baogutang.common.properties.WxMsgPushProperties; + +import javax.annotation.Resource; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * @description: + * @author: nikooh + * @date: 2023/06/16 : 10:27 + */ +@Slf4j +@RefreshScope +@Service +public class WxMsgPushServiceImpl implements IWxMsgPushService { + + @Resource + private WxMsgPushProperties wxMsgPushProperties; + + @Override + public Boolean msgPush(Integer msgType, String summary, String msgContent, Set topicIdSet) { + Message message = new Message(); + Set userUidSet = this.queryUids(wxMsgPushProperties.getAppToken()); + if (CollectionUtils.isEmpty(userUidSet)) { + return Boolean.TRUE; + } + message.setUids(userUidSet); + message.setAppToken(wxMsgPushProperties.getAppToken()); + message.setSummary(summary); + message.setContentType(msgType); + message.setContent(msgContent); + Result> result = null; + try { + result = WxPusher.send(message); + } catch (Exception e) { + log.error(">>>>>>>>>>推送消息异常:{}<<<<<<<<<<", e.getMessage(), e); + return Boolean.FALSE; + } + log.info(">>>>>>>>>>消息推送结果:{}<<<<<<<<<<", JSON.toJSONString(result)); + if (Objects.nonNull(result) && result.isSuccess()) { + return Boolean.TRUE; + } + return Boolean.FALSE; + } + + private Set queryUids(String appToken) { + //分页查询全部用户 + Result> wxUsersResult = null; + try { + wxUsersResult = WxPusher.queryWxUser(appToken, 1, Integer.MAX_VALUE); + } catch (Exception e) { + log.error(">>>>>>>>>>查询用户信息失败:{}<<<<<<<<<<", e.getMessage(), e); + throw new BusinessException("查询用户信息失败"); + } + log.info(">>>>>>>>>>查询用户列表结果:{}<<<<<<<<<<", JSON.toJSONString(wxUsersResult)); + if (Objects.nonNull(wxUsersResult) && Objects.nonNull(wxUsersResult.getData()) && CollectionUtils.isNotEmpty(wxUsersResult.getData().getRecords())) { + return wxUsersResult.getData().getRecords().stream() + .map(WxUser::getUid) + .collect(Collectors.toSet()); + } + return Collections.emptySet(); + } +} diff --git a/baogutang-admin/src/main/resources/bootstrap-local.yml b/baogutang-admin/src/main/resources/bootstrap-local.yml new file mode 100644 index 0000000..152de4e --- /dev/null +++ b/baogutang-admin/src/main/resources/bootstrap-local.yml @@ -0,0 +1,19 @@ +spring: + application: + name: baogutang-admin + cloud: + nacos: + discovery: + server-addr: 42.51.4.235:8848 + config: + server-addr: 42.51.4.235:8848 + namespace: adcc486c-d6fb-465a-a4f6-3fea7234433b + file-extension: yml + refresh-enabled: true + group: DEFAULT_GROUP + username: nacos + password: 199312 +# shared-configs: +# - data-id: goods-blindbox.yml +# refresh: true + diff --git a/baogutang-admin/src/main/resources/bootstrap-prod.yml b/baogutang-admin/src/main/resources/bootstrap-prod.yml new file mode 100644 index 0000000..c74d3b2 --- /dev/null +++ b/baogutang-admin/src/main/resources/bootstrap-prod.yml @@ -0,0 +1,19 @@ +spring: + application: + name: baogutang-admin + cloud: + nacos: + discovery: + server-addr: 42.51.4.235:8848 + config: + server-addr: 42.51.4.235:8848 + namespace: 50b370f0-6fc9-4e1d-9a19-df276cff9eac + file-extension: yml + refresh-enabled: true + group: DEFAULT_GROUP + username: nacos + password: 199312 +# shared-configs: +# - data-id: goods-blindbox.yml +# refresh: true + diff --git a/baogutang-admin/src/main/resources/bootstrap-test.yml b/baogutang-admin/src/main/resources/bootstrap-test.yml new file mode 100644 index 0000000..a636c66 --- /dev/null +++ b/baogutang-admin/src/main/resources/bootstrap-test.yml @@ -0,0 +1,19 @@ +spring: + application: + name: baogutang-admin + cloud: + nacos: + discovery: + server-addr: 42.51.4.235:8848 + config: + server-addr: 42.51.4.235:8848 + namespace: 7e44b734-781d-4c21-a524-4bad1557d95f + file-extension: yml + refresh-enabled: true + group: DEFAULT_GROUP + username: nacos + password: 199312 +# shared-configs: +# - data-id: goods-blindbox.yml +# refresh: true + diff --git a/baogutang-admin/src/main/resources/bootstrap.yml b/baogutang-admin/src/main/resources/bootstrap.yml new file mode 100644 index 0000000..6b429df --- /dev/null +++ b/baogutang-admin/src/main/resources/bootstrap.yml @@ -0,0 +1,5 @@ +spring: + profiles: + active: @profileActive@ + main: + allow-bean-definition-overriding: true \ No newline at end of file diff --git a/baogutang-admin/src/main/resources/logback-spring.xml b/baogutang-admin/src/main/resources/logback-spring.xml new file mode 100644 index 0000000..e040eb0 --- /dev/null +++ b/baogutang-admin/src/main/resources/logback-spring.xml @@ -0,0 +1,114 @@ + + + + logback + + + + + + %date{yyyy-MM-dd HH:mm:ss.SSS} [%X{X-Request-Id}] [%thread] %-5level %logger - %msg%n + + + + + ${log.path}/app.log + + ${log.path}/%d{yyyy-MM-dd,aux}/app-%d{yyyy-MM-dd}.%i.log + + + 50MB + + + 60 + + + %date{HH:mm:ss.SSS} [%X{X-Request-Id}] [%thread] %-5level %logger - %msg%n + + + + + 10000 + 0 + + + + + + + + + + + + + + + + + cn-shanghai.log.aliyuncs.com + LTAI5tRN9T5Tz1QExcSpUaBc + LniIMK15XEOc6Nn5mOrtX399FkVfQd + baogutang + ${appEnv} + + + ${appId} + + + + 3000 + 4096 + 3145728 + 104857600 + 3 + 8 + + + Asia/Shanghai + + yyyy-MM-dd HH:mm:ss.SSS + + + + %d{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%X{X-Request-Id}] [%thread] %logger{0}: %msg + + + + + INFO + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/baogutang-admin/src/test/java/top/baogutang/business/admin/BaogutangAdminApplicationTests.java b/baogutang-admin/src/test/java/top/baogutang/business/admin/BaogutangAdminApplicationTests.java new file mode 100644 index 0000000..df1dcbe --- /dev/null +++ b/baogutang-admin/src/test/java/top/baogutang/business/admin/BaogutangAdminApplicationTests.java @@ -0,0 +1,13 @@ +package top.baogutang.business.admin; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class BaogutangAdminApplicationTests { + + @Test + void contextLoads() { + } + +} diff --git a/baogutang-business/Dockerfile b/baogutang-business/Dockerfile new file mode 100644 index 0000000..cbc3dec --- /dev/null +++ b/baogutang-business/Dockerfile @@ -0,0 +1,7 @@ +FROM openjdk:8 +ENV TZ=Asia/Shanghai +RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone +COPY ./baogutang-business/target/baogutang-business-1.0.0-SNAPSHOT.jar /apps/baogutang-business.jar +WORKDIR /apps +RUN bash -c 'touch /baogutang-business.jar' +CMD exec java $JAVA_OPTS -jar baogutang-business.jar diff --git a/baogutang-business/pom.xml b/baogutang-business/pom.xml new file mode 100644 index 0000000..3837c4c --- /dev/null +++ b/baogutang-business/pom.xml @@ -0,0 +1,208 @@ + + + 4.0.0 + + top.baogutang + baogutang-parent + 1.0.0-SNAPSHOT + + + baogutang-business + + + 8 + 8 + 8 + + + + top.baogutang + baogutang-common + 1.0.0-SNAPSHOT + + + com.squareup.okhttp3 + okhttp + + + org.springframework.boot + spring-boot-starter-web + + + org.yaml + snakeyaml + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + + + com.aliyun.openservices + aliyun-log-logback-appender + + + com.google.protobuf + protobuf-java + + + com.alibaba + fastjson + + + + mysql + mysql-connector-java + + + + org.projectlombok + lombok + compile + + + com.baomidou + mybatis-plus-boot-starter + + + + io.springfox + springfox-boot-starter + + + + + org.postgresql + postgresql + + + + org.junit.jupiter + junit-jupiter + test + + + org.junit.jupiter + junit-jupiter-params + test + + + org.junit.jupiter + junit-jupiter-engine + test + + + org.junit.jupiter + junit-jupiter-api + test + + + + + + + + src/main/resources + true + + **/*.pfx + **/*.cer + + + + src/main/resources + false + + **/*.pfx + **/*.cer + + + + + + org.springframework.boot + spring-boot-maven-plugin + 2.2.5.RELEASE + + + + org.projectlombok + lombok + + + + + + org.apache.maven.plugins + maven-resources-plugin + + UTF-8 + + + p12 + crt + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.19 + + true + + + + org.mybatis.generator + mybatis-generator-maven-plugin + 1.4.0 + + + io.github.javthon + mybatis-generator-yml-maven-plugin + 0.0.1 + + src/main/resources/generatorConfig.yml + + + + + + + + local + + local + + + true + + + + test + + test + + + false + + + + prod + + prod + + + false + + + + + \ No newline at end of file diff --git a/baogutang-business/src/main/java/top/baogutang/business/BaogutangBusinessApplication.java b/baogutang-business/src/main/java/top/baogutang/business/BaogutangBusinessApplication.java new file mode 100644 index 0000000..5621958 --- /dev/null +++ b/baogutang-business/src/main/java/top/baogutang/business/BaogutangBusinessApplication.java @@ -0,0 +1,22 @@ +package top.baogutang.business; + +import lombok.extern.slf4j.Slf4j; +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * @description: + * @author: nikooh + * @date: 2023/06/15 : 11:22 + */ +@Slf4j +@SpringBootApplication(scanBasePackages = "top.baogutang.*") +@MapperScan(basePackages = {"top.baogutang.business.dao.mapper", "top.baogutang.common.dao.mapper"}) +public class BaogutangBusinessApplication { + + public static void main(String[] args) { + SpringApplication.run(BaogutangBusinessApplication.class, args); + log.info("<<<<<<<<<<<<<<<==BAO_GU_TANG客户端服务启动成功!!!==>>>>>>>>>>>>>>>"); + } +} diff --git a/baogutang-business/src/main/java/top/baogutang/business/controller/CallbackController.java b/baogutang-business/src/main/java/top/baogutang/business/controller/CallbackController.java new file mode 100644 index 0000000..6aaad28 --- /dev/null +++ b/baogutang-business/src/main/java/top/baogutang/business/controller/CallbackController.java @@ -0,0 +1,23 @@ +package top.baogutang.business.controller; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import top.baogutang.common.domain.Results; + +/** + * @description: 回调控制器 + * @author: nikooh + * @date: 2023/06/16 : 10:10 + */ +@Slf4j +@RestController +@RequestMapping("/api/v1/callback") +public class CallbackController { + + + @RequestMapping("/wx/msgPush") + public Results wxMsgPushCallback() { + return Results.ok(null); + } +} diff --git a/baogutang-business/src/main/java/top/baogutang/business/controller/WxMsgPushController.java b/baogutang-business/src/main/java/top/baogutang/business/controller/WxMsgPushController.java new file mode 100644 index 0000000..34f4ae2 --- /dev/null +++ b/baogutang-business/src/main/java/top/baogutang/business/controller/WxMsgPushController.java @@ -0,0 +1,35 @@ +package top.baogutang.business.controller; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.servlet.view.RedirectView; +import top.baogutang.business.services.IWxMsgPushService; + +import javax.annotation.Resource; + +/** + * @description: 微信消息推送控制器 + * @author: nikooh + * @date: 2023/06/16 : 10:24 + */ +@Slf4j +@Controller +@RequestMapping("/api/v1/wx/msgPush") +public class WxMsgPushController { + + @Resource + private IWxMsgPushService wxMsgPushService; + + + /** + * 创建一个带参数的二维码,用户扫码的时候,回调里面会携带二维码的参数. + * + * @return 二维码 + */ + @GetMapping("/qrCode") + public RedirectView getQrCode() { + return new RedirectView(wxMsgPushService.getOrCode()); + } +} diff --git a/baogutang-business/src/main/java/top/baogutang/business/services/IWxMsgPushService.java b/baogutang-business/src/main/java/top/baogutang/business/services/IWxMsgPushService.java new file mode 100644 index 0000000..7020d54 --- /dev/null +++ b/baogutang-business/src/main/java/top/baogutang/business/services/IWxMsgPushService.java @@ -0,0 +1,18 @@ +package top.baogutang.business.services; + + +/** + * @description: 微信消息推送service + * @author: nikooh + * @date: 2023/06/16 : 10:27 + */ +public interface IWxMsgPushService { + + /** + * 创建一个带参数的二维码,用户扫码的时候,回调里面会携带二维码的参数. + * + * @return 二维码 + */ + String getOrCode(); + +} diff --git a/baogutang-business/src/main/java/top/baogutang/business/services/impl/WxMsgPushServiceImpl.java b/baogutang-business/src/main/java/top/baogutang/business/services/impl/WxMsgPushServiceImpl.java new file mode 100644 index 0000000..640f7d1 --- /dev/null +++ b/baogutang-business/src/main/java/top/baogutang/business/services/impl/WxMsgPushServiceImpl.java @@ -0,0 +1,75 @@ +package top.baogutang.business.services.impl; + +import com.alibaba.fastjson.JSON; +import com.zjiecode.wxpusher.client.WxPusher; +import com.zjiecode.wxpusher.client.bean.CreateQrcodeReq; +import com.zjiecode.wxpusher.client.bean.CreateQrcodeResp; +import com.zjiecode.wxpusher.client.bean.Result; +import lombok.extern.slf4j.Slf4j; +import org.springframework.cloud.context.config.annotation.RefreshScope; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Service; +import top.baogutang.business.services.IWxMsgPushService; +import top.baogutang.common.constants.ErrorCodeEnum; +import top.baogutang.common.exceptions.BusinessException; +import top.baogutang.common.properties.WxMsgPushProperties; + +import javax.annotation.Resource; + +import java.util.Objects; +import java.util.concurrent.TimeUnit; + +import static top.baogutang.common.constants.CacheConstant.WX_MSG_PUSH_QR_CODE_PREFIX; + +/** + * @description: + * @author: nikooh + * @date: 2023/06/16 : 10:27 + */ +@Slf4j +@RefreshScope +@Service +public class WxMsgPushServiceImpl implements IWxMsgPushService { + + /** + * 二维码有效时间 + */ + private static final Integer QR_CODE_EXPIRE_TIME = 10 * 24 * 60 * 60; + + @Resource + private WxMsgPushProperties wxMsgPushProperties; + + @Resource + private RedisTemplate redisTemplate; + + @Override + public String getOrCode() { + String cacheKey = String.format(WX_MSG_PUSH_QR_CODE_PREFIX, wxMsgPushProperties.getAppToken()); + Object cacheUrlObj = redisTemplate.opsForValue().get(cacheKey); + if (Objects.nonNull(cacheUrlObj)) { + return (String) cacheUrlObj; + } + CreateQrcodeReq createQrcodeReq = new CreateQrcodeReq(); + //必填,应用的appToken + createQrcodeReq.setAppToken(wxMsgPushProperties.getAppToken()); + //必填,携带的参数 + createQrcodeReq.setExtra("Niko's MP"); + //可选,二维码有效时间,默认1800 s,最大30天,单位是s + createQrcodeReq.setValidTime(QR_CODE_EXPIRE_TIME); + Result respResult = null; + try { + respResult = WxPusher.createAppTempQrcode(createQrcodeReq); + } catch (Exception e) { + log.error(">>>>>>>>>>创建二维码异常:{}<<<<<<<<<<", e.getMessage(), e); + throw new BusinessException(ErrorCodeEnum.E_BIZ_ERROR); + } + log.info(">>>>>>>>>>创建临时二维码请求参数:{},响应参数:{}<<<<<<<<<<", JSON.toJSONString(createQrcodeReq), JSON.toJSONString(respResult)); + if (respResult.isSuccess()) { + //创建成功 + CreateQrcodeResp createQrcodeResp = respResult.getData(); + redisTemplate.opsForValue().set(cacheKey, createQrcodeResp.getUrl(), QR_CODE_EXPIRE_TIME, TimeUnit.SECONDS); + return createQrcodeResp.getUrl(); + } + throw new BusinessException(ErrorCodeEnum.E_BIZ_ERROR); + } +} diff --git a/baogutang-business/src/main/resources/bootstrap-local.yml b/baogutang-business/src/main/resources/bootstrap-local.yml new file mode 100644 index 0000000..034519c --- /dev/null +++ b/baogutang-business/src/main/resources/bootstrap-local.yml @@ -0,0 +1,19 @@ +spring: + application: + name: baogutang-business + cloud: + nacos: + discovery: + server-addr: 42.51.4.235:8848 + config: + server-addr: 42.51.4.235:8848 + namespace: adcc486c-d6fb-465a-a4f6-3fea7234433b + file-extension: yml + refresh-enabled: true + group: DEFAULT_GROUP + username: nacos + password: 199312 +# shared-configs: +# - data-id: goods-blindbox.yml +# refresh: true + diff --git a/baogutang-business/src/main/resources/bootstrap-prod.yml b/baogutang-business/src/main/resources/bootstrap-prod.yml new file mode 100644 index 0000000..25bbba7 --- /dev/null +++ b/baogutang-business/src/main/resources/bootstrap-prod.yml @@ -0,0 +1,19 @@ +spring: + application: + name: baogutang-business + cloud: + nacos: + discovery: + server-addr: 42.51.4.235:8848 + config: + server-addr: 42.51.4.235:8848 + namespace: 50b370f0-6fc9-4e1d-9a19-df276cff9eac + file-extension: yml + refresh-enabled: true + group: DEFAULT_GROUP + username: nacos + password: 199312 +# shared-configs: +# - data-id: goods-blindbox.yml +# refresh: true + diff --git a/baogutang-business/src/main/resources/bootstrap-test.yml b/baogutang-business/src/main/resources/bootstrap-test.yml new file mode 100644 index 0000000..80c633a --- /dev/null +++ b/baogutang-business/src/main/resources/bootstrap-test.yml @@ -0,0 +1,19 @@ +spring: + application: + name: baogutang-business + cloud: + nacos: + discovery: + server-addr: 42.51.4.235:8848 + config: + server-addr: 42.51.4.235:8848 + namespace: 7e44b734-781d-4c21-a524-4bad1557d95f + file-extension: yml + refresh-enabled: true + group: DEFAULT_GROUP + username: nacos + password: 199312 +# shared-configs: +# - data-id: goods-blindbox.yml +# refresh: true + diff --git a/baogutang-business/src/main/resources/bootstrap.yml b/baogutang-business/src/main/resources/bootstrap.yml new file mode 100644 index 0000000..6b429df --- /dev/null +++ b/baogutang-business/src/main/resources/bootstrap.yml @@ -0,0 +1,5 @@ +spring: + profiles: + active: @profileActive@ + main: + allow-bean-definition-overriding: true \ No newline at end of file diff --git a/baogutang-business/src/main/resources/logback-spring.xml b/baogutang-business/src/main/resources/logback-spring.xml new file mode 100644 index 0000000..e040eb0 --- /dev/null +++ b/baogutang-business/src/main/resources/logback-spring.xml @@ -0,0 +1,114 @@ + + + + logback + + + + + + %date{yyyy-MM-dd HH:mm:ss.SSS} [%X{X-Request-Id}] [%thread] %-5level %logger - %msg%n + + + + + ${log.path}/app.log + + ${log.path}/%d{yyyy-MM-dd,aux}/app-%d{yyyy-MM-dd}.%i.log + + + 50MB + + + 60 + + + %date{HH:mm:ss.SSS} [%X{X-Request-Id}] [%thread] %-5level %logger - %msg%n + + + + + 10000 + 0 + + + + + + + + + + + + + + + + + cn-shanghai.log.aliyuncs.com + LTAI5tRN9T5Tz1QExcSpUaBc + LniIMK15XEOc6Nn5mOrtX399FkVfQd + baogutang + ${appEnv} + + + ${appId} + + + + 3000 + 4096 + 3145728 + 104857600 + 3 + 8 + + + Asia/Shanghai + + yyyy-MM-dd HH:mm:ss.SSS + + + + %d{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%X{X-Request-Id}] [%thread] %logger{0}: %msg + + + + + INFO + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/baogutang-common/pom.xml b/baogutang-common/pom.xml new file mode 100644 index 0000000..912d22d --- /dev/null +++ b/baogutang-common/pom.xml @@ -0,0 +1,139 @@ + + + 4.0.0 + + top.baogutang + baogutang-parent + 1.0.0-SNAPSHOT + + + baogutang-common + + + 8 + 8 + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-aop + + + org.springframework.boot + spring-boot-starter-test + + + org.springframework.boot + spring-boot-starter-integration + + + org.springframework.boot + spring-boot-starter-data-redis + + + org.springframework.cloud + spring-cloud-context + + + org.springframework.integration + spring-integration-redis + + + org.springframework.boot + spring-boot-starter-validation + + + com.alibaba + fastjson + + + org.projectlombok + lombok + + + io.jsonwebtoken + jjwt + + + org.apache.commons + commons-lang3 + + + com.google.guava + guava + + + org.apache.commons + commons-pool2 + + + org.apache.httpcomponents + httpclient + + + com.baomidou + mybatis-plus-boot-starter + + + commons-lang + commons-lang + + + io.swagger + swagger-annotations + 1.5.22 + + + + com.squareup.okhttp3 + okhttp + + + com.aliyun.openservices + ons-client + + + com.aliyun + aliyun-java-sdk-core + + + com.aliyun + aliyun-java-sdk-green + + + com.aliyun.oss + aliyun-sdk-oss + + + + com.aliyun + tea-openapi + 0.2.2 + + + + io.searchbox + jest + 6.3.1 + + + + io.searchbox + jest-common + 6.3.1 + + + com.zjiecode + wxpusher-java-sdk + 2.1.4 + + + + \ No newline at end of file diff --git a/baogutang-common/src/main/java/top/baogutang/common/annotation/LoginRequired.java b/baogutang-common/src/main/java/top/baogutang/common/annotation/LoginRequired.java new file mode 100644 index 0000000..178ed97 --- /dev/null +++ b/baogutang-common/src/main/java/top/baogutang/common/annotation/LoginRequired.java @@ -0,0 +1,20 @@ +package top.baogutang.common.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * @description: + * @author: nikooh + * @date: 2023/06/15 : 11:31 + */ +@Documented +@Target({ElementType.TYPE, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +public @interface LoginRequired { + + boolean required() default true; +} diff --git a/baogutang-common/src/main/java/top/baogutang/common/annotation/RequestLimit.java b/baogutang-common/src/main/java/top/baogutang/common/annotation/RequestLimit.java new file mode 100644 index 0000000..49dfd73 --- /dev/null +++ b/baogutang-common/src/main/java/top/baogutang/common/annotation/RequestLimit.java @@ -0,0 +1,37 @@ +package top.baogutang.common.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.util.concurrent.TimeUnit; + +/** + * @description: + * @author: nikooh + * @date: 2023/06/15 : 12:04 + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface RequestLimit { + + /** + * 指定时间内不可重复提交,单位秒 + */ + long timeout() default 1; + + /** + * 时间单位 + */ + TimeUnit timeUnit() default TimeUnit.SECONDS; + + /** + * 指定key + */ + String key() default ""; + + /** + * 是否限制单个人的, 包含token + */ + boolean includeToken() default true; +} diff --git a/baogutang-common/src/main/java/top/baogutang/common/annotation/VerifyRequired.java b/baogutang-common/src/main/java/top/baogutang/common/annotation/VerifyRequired.java new file mode 100644 index 0000000..d72fd2d --- /dev/null +++ b/baogutang-common/src/main/java/top/baogutang/common/annotation/VerifyRequired.java @@ -0,0 +1,18 @@ +package top.baogutang.common.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * @description: + * @author: nikooh + * @date: 2023/06/15 : 12:07 + */ +@Documented +@Target({ElementType.TYPE, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +public @interface VerifyRequired { +} diff --git a/baogutang-common/src/main/java/top/baogutang/common/aspect/LogAspect.java b/baogutang-common/src/main/java/top/baogutang/common/aspect/LogAspect.java new file mode 100644 index 0000000..5e73c6d --- /dev/null +++ b/baogutang-common/src/main/java/top/baogutang/common/aspect/LogAspect.java @@ -0,0 +1,65 @@ +package top.baogutang.common.aspect; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.serializer.SerializerFeature; +import lombok.extern.slf4j.Slf4j; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Pointcut; +import org.slf4j.MDC; +import org.springframework.stereotype.Component; +import org.springframework.web.context.request.RequestAttributes; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; +import top.baogutang.common.domain.Results; + +import javax.servlet.http.HttpServletRequest; + +/** + * @description: + * @author: nikooh + * @date: 2023/06/15 : 12:16 + */ +@Slf4j +@Aspect +@Component +public class LogAspect { + + + @Pointcut("@within(org.springframework.web.bind.annotation.RestController)") + public void logPointCut() { + //pointCut + + } + + @Around("logPointCut()") + public Object doAround(ProceedingJoinPoint pjp) throws Throwable { + Object result; + RequestAttributes ra = RequestContextHolder.getRequestAttributes(); + ServletRequestAttributes sra = (ServletRequestAttributes) ra; + assert sra != null; + HttpServletRequest request = sra.getRequest(); + String requestId = MDC.get("X-Request-Id"); + long startMills = System.currentTimeMillis(); + long cost = 0; + try { + result = pjp.proceed(); + cost = System.currentTimeMillis() - startMills; + log.info("请求结束!本次请求耗时:{},url: {}, method: {}, params: {},user:{}, token: {}, 响应结果:{}", + cost, request.getRequestURL().toString(), + request.getMethod(), pjp.getArgs(), JSON.toJSONString(request.getAttribute("user")), request.getHeader("authorization"), + JSON.toJSONString(result, SerializerFeature.DisableCircularReferenceDetect)); + if (result instanceof Results) { + Results r = (Results) result; + r.setRid(requestId); + } + } catch (Exception e) { + log.error("请求异常!!!本次请求耗时:{},error:{},url: {}, method: {}, params: {},user:{}, token: {}", cost, e, request.getRequestURL().toString(), + request.getMethod(), pjp.getArgs(), JSON.toJSONString(request.getAttribute("user")), request.getHeader("token")); + throw e; + } + return result; + } + +} diff --git a/baogutang-common/src/main/java/top/baogutang/common/aspect/LoginRequiredAspect.java b/baogutang-common/src/main/java/top/baogutang/common/aspect/LoginRequiredAspect.java new file mode 100644 index 0000000..a9b3fb3 --- /dev/null +++ b/baogutang-common/src/main/java/top/baogutang/common/aspect/LoginRequiredAspect.java @@ -0,0 +1,115 @@ +package top.baogutang.common.aspect; + +import com.alibaba.fastjson.JSON; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.aspectj.lang.annotation.After; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.aspectj.lang.annotation.Pointcut; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Component; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; +import top.baogutang.common.annotation.LoginRequired; +import top.baogutang.common.components.TokenComponent; +import top.baogutang.common.constants.CacheConstant; +import top.baogutang.common.domain.JwtBody; +import top.baogutang.common.domain.TokenCodeEnum; +import top.baogutang.common.exceptions.BusinessException; +import top.baogutang.common.utils.UserThreadLocal; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletRequest; +import java.util.Objects; + +import static top.baogutang.common.constants.JwtConstant.AUTHORIZATION; + +/** + * @description: + * @author: nikooh + * @date: 2023/06/15 : 11:32 + */ +@Slf4j +@Aspect +@Component +public class LoginRequiredAspect { + + @Resource + private TokenComponent tokenComponent; + + @Resource + private RedisTemplate redisTemplate; + + @Pointcut("@annotation(top.baogutang.common.annotation.LoginRequired)") + public void point() { + } + + @Before(value = "point() && @annotation(loginRequired)") + public void verifyTokenForClass(LoginRequired loginRequired) { + if (loginRequired.required()) { + checkToken(); + } else { + checkTokenWithOutRequired(); + } + } + + @After("point()") + public void after() { + UserThreadLocal.remove(); + } + + private void checkToken() { + + ServletRequestAttributes requestAttributes = + (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); + if (Objects.isNull(requestAttributes)) { + return; + } + HttpServletRequest request = requestAttributes.getRequest(); + String token = request.getHeader(AUTHORIZATION); + if (StringUtils.isEmpty(token)) { + throw new BusinessException( + TokenCodeEnum.AUTH_TOKEN_EMPTY.getCode(), TokenCodeEnum.AUTH_TOKEN_EMPTY.getMessage()); + } + + log.info("request url:{},method:{}", request.getRequestURL(), request.getMethod()); + JwtBody body = tokenComponent.parseToken(token); + Long userId = body.getId(); + Object val = redisTemplate.opsForValue().get(CacheConstant.TOKEN + userId); + if (Objects.isNull(val)) { + redisTemplate.opsForValue().set(CacheConstant.TOKEN + userId, token); + } else { + //比对token、不相同则判断token生产时间,取最新的覆盖 + String redisToken = String.valueOf(val); + if (!token.equals(redisToken)) { + JwtBody redisBody = tokenComponent.parseToken(redisToken); + if (body.getIat() > redisBody.getIat()) { + log.info("newTokenCover,currentBody:{} redisBody:{}", JSON.toJSONString(body), JSON.toJSONString(redisBody)); + redisTemplate.opsForValue().set(CacheConstant.TOKEN + userId, token); + } else { + log.info("FoundOldToken,currentBody:{} redisBody:{}", JSON.toJSONString(body), JSON.toJSONString(redisBody)); + throw new BusinessException( + TokenCodeEnum.AUTH_FAILED.getCode(), TokenCodeEnum.AUTH_FAILED.getMessage()); + } + } + } + UserThreadLocal.set(body.getId()); + } + + private void checkTokenWithOutRequired() { + ServletRequestAttributes requestAttributes = + (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); + if (Objects.isNull(requestAttributes)) { + return; + } + HttpServletRequest request = requestAttributes.getRequest(); + String token = request.getHeader(AUTHORIZATION); + if (StringUtils.isNotEmpty(token)) { + log.info("request url:{},method:{}", request.getRequestURL(), request.getMethod()); + JwtBody body = tokenComponent.parseToken(token); + UserThreadLocal.set(body.getId()); + } + } + +} diff --git a/baogutang-common/src/main/java/top/baogutang/common/aspect/RequestLimitAspect.java b/baogutang-common/src/main/java/top/baogutang/common/aspect/RequestLimitAspect.java new file mode 100644 index 0000000..ff9cc76 --- /dev/null +++ b/baogutang-common/src/main/java/top/baogutang/common/aspect/RequestLimitAspect.java @@ -0,0 +1,127 @@ +package top.baogutang.common.aspect; + +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.codec.digest.DigestUtils; +import org.apache.commons.lang3.StringUtils; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Pointcut; +import org.aspectj.lang.reflect.MethodSignature; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Component; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; +import top.baogutang.common.annotation.RequestLimit; +import top.baogutang.common.domain.Results; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletRequest; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.Objects; +import java.util.concurrent.TimeUnit; + +/** + * @description: + * @author: nikooh + * @date: 2023/06/15 : 12:05 + */ +@Slf4j +@Aspect +@Component +public class RequestLimitAspect { + + + @Resource + private RedisTemplate redisTemplate; + + @Pointcut("@annotation(top.baogutang.common.annotation.RequestLimit)") + public void limit() { + } + + @Around("limit()") + public Object around(ProceedingJoinPoint point) throws Throwable { + HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest(); + String ip = getIpAddress(request); + MethodSignature signature = (MethodSignature) point.getSignature(); + Method method = signature.getMethod(); + RequestLimit requestLimit = method.getAnnotation(RequestLimit.class); + if (requestLimit != null) { + String key = requestLimit.key(); + if (StringUtils.isEmpty(key)) { + String className = method.getDeclaringClass().getName(); + String name = method.getName(); + String args = Arrays.toString(point.getArgs()); + String ipKey = String.format("%s#%s#%s", className, name, args); + int hashCode = Math.abs(ipKey.hashCode()); + key = String.format("%s_%d", ip, hashCode); + } + if (requestLimit.includeToken()) { + String token = request.getHeader("authorization"); + if (token != null) { + key = key.concat("_").concat(DigestUtils.md5Hex(token)); + } + } + long timeout = requestLimit.timeout(); + TimeUnit timeUnit = requestLimit.timeUnit(); + if (timeout < 0) { + //过期时间5s + timeout = 5; + } + Boolean lock = redisTemplate.opsForValue().setIfAbsent(key, "1", timeout, timeUnit); + if (!Boolean.TRUE.equals(lock)) { + return Results.failed("正在赶来中,请稍后再试~"); + } + } + return point.proceed(); + } + + /** + * 获取请求用户的IP地址 + * + * @param request request + * @return return + */ + public String getIpAddress(HttpServletRequest request) { + + String ipAddresses = request.getHeader("x-forwarded-for"); + + ipAddresses = getString(request, ipAddresses); + + if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) { + //X-Real-IP:nginx服务代理 + ipAddresses = request.getHeader("X-Real-IP"); + } + + //有些网络通过多层代理,那么获取到的ip就会有多个,一般都是通过逗号(,)分割开来,并且第一个ip为客户端的真实IP + if (ipAddresses != null && ipAddresses.length() != 0) { + ipAddresses = ipAddresses.split(",")[0]; + } + + //还是不能获取到,最后再通过request.getRemoteAddr();获取 + if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) { + ipAddresses = request.getRemoteAddr(); + } + + return ipAddresses; + } + + public static String getString(HttpServletRequest request, String ipAddresses) { + if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) { + //Proxy-Client-IP:apache 服务代理 + ipAddresses = request.getHeader("Proxy-Client-IP"); + } + + if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) { + //WL-Proxy-Client-IP:weblogic 服务代理 + ipAddresses = request.getHeader("WL-Proxy-Client-IP"); + } + + if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) { + //HTTP_CLIENT_IP:有些代理服务器 + ipAddresses = request.getHeader("HTTP_CLIENT_IP"); + } + return ipAddresses; + } +} diff --git a/baogutang-common/src/main/java/top/baogutang/common/aspect/VerifyRequiredAspect.java b/baogutang-common/src/main/java/top/baogutang/common/aspect/VerifyRequiredAspect.java new file mode 100644 index 0000000..b2feb61 --- /dev/null +++ b/baogutang-common/src/main/java/top/baogutang/common/aspect/VerifyRequiredAspect.java @@ -0,0 +1,100 @@ +package top.baogutang.common.aspect; + +import com.alibaba.fastjson.JSON; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.After; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Pointcut; +import org.aspectj.lang.reflect.MethodSignature; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Component; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; +import top.baogutang.common.annotation.VerifyRequired; +import top.baogutang.common.components.TokenComponent; +import top.baogutang.common.constants.CacheConstant; +import top.baogutang.common.domain.JwtBody; +import top.baogutang.common.domain.Results; +import top.baogutang.common.domain.TokenCodeEnum; +import top.baogutang.common.exceptions.BusinessException; +import top.baogutang.common.utils.UserThreadLocal; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletRequest; +import java.lang.reflect.Method; +import java.util.Objects; + +import static top.baogutang.common.constants.ErrorCodeEnum.E_BIZ_NEED_VERIFY; +import static top.baogutang.common.constants.JwtConstant.AUTHORIZATION; + +/** + * @description: + * @author: nikooh + * @date: 2023/06/15 : 12:07 + */ +@Slf4j +@Aspect +@Component +public class VerifyRequiredAspect { + + + @Resource + private TokenComponent tokenComponent; + + @Resource + private RedisTemplate redisTemplate; + + @Pointcut("@annotation(top.baogutang.common.annotation.VerifyRequired)") + public void verify() { + } + + @Around("verify()") + public Object around(ProceedingJoinPoint point) throws Throwable { + HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest(); + MethodSignature signature = (MethodSignature) point.getSignature(); + Method method = signature.getMethod(); + VerifyRequired verifyRequired = method.getAnnotation(VerifyRequired.class); + if (verifyRequired != null) { + String token = request.getHeader(AUTHORIZATION); + if (StringUtils.isBlank(token)) { + throw new BusinessException( + TokenCodeEnum.AUTH_TOKEN_EMPTY.getCode(), TokenCodeEnum.AUTH_TOKEN_EMPTY.getMessage()); + } + JwtBody body = tokenComponent.parseToken(token); + Long userId = body.getId(); + UserThreadLocal.set(userId); + if (Objects.isNull(redisTemplate.opsForValue().get(CacheConstant.USER_VERIFY_KEY_PREFIX + userId))) { + return Results.failed(E_BIZ_NEED_VERIFY.getCode(), E_BIZ_NEED_VERIFY.getMsg()); + } + Object val = redisTemplate.opsForValue().get(CacheConstant.TOKEN + userId); + if (Objects.isNull(val)) { + redisTemplate.opsForValue().set(CacheConstant.TOKEN + userId, token); + } else { + //比对token、不相同则判断token生产时间,取最新的覆盖 + String redisToken = String.valueOf(val); + if (!token.equals(redisToken)) { + JwtBody redisBody = tokenComponent.parseToken(redisToken); + if (body.getIat() > redisBody.getIat()) { + log.info("VerifyNewTokenCover,currentBody:{} redisBody:{}", JSON.toJSONString(body), JSON.toJSONString(redisBody)); + redisTemplate.opsForValue().set(CacheConstant.TOKEN + userId, token); + } else { + log.info("FoundOldToken,currentBody:{} redisBody:{}", JSON.toJSONString(body), JSON.toJSONString(redisBody)); + throw new BusinessException( + TokenCodeEnum.AUTH_FAILED.getCode(), TokenCodeEnum.AUTH_FAILED.getMessage()); + } + } + } + + } + return point.proceed(); + } + + @After("verify()") + public void after() { + UserThreadLocal.remove(); + } + +} diff --git a/baogutang-common/src/main/java/top/baogutang/common/components/TokenComponent.java b/baogutang-common/src/main/java/top/baogutang/common/components/TokenComponent.java new file mode 100644 index 0000000..ccd1174 --- /dev/null +++ b/baogutang-common/src/main/java/top/baogutang/common/components/TokenComponent.java @@ -0,0 +1,109 @@ +package top.baogutang.common.components; + +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.ExpiredJwtException; +import io.jsonwebtoken.Jws; +import io.jsonwebtoken.JwtBuilder; +import io.jsonwebtoken.JwtException; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SignatureAlgorithm; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import org.springframework.util.Base64Utils; +import top.baogutang.common.domain.JwtBody; +import top.baogutang.common.domain.TokenCodeEnum; +import top.baogutang.common.exceptions.BusinessException; + +import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +import static top.baogutang.common.constants.JwtConstant.ALGORITHM_FAMILY_NAME; + +/** + * @description: + * @author: nikooh + * @date: 2023/06/15 : 11:45 + */ +@Slf4j +@Component +public class TokenComponent { + @Value("${jwt.publicKey:1}") + private String publicKey; + + public JwtBody parseToken(String token) { + log.info("parse token:{}", token); + Jws claimsJws; + try { + claimsJws = Jwts.parser().setSigningKey(publicKeyFromBase64()).parseClaimsJws(token); + } catch (ExpiredJwtException expiredJwtException) { + return new JwtBody(expiredJwtException.getClaims()); + + } catch (JwtException e) { + if (e.getMessage().contains("expired")) { + throw new BusinessException( + TokenCodeEnum.AUTH_TIME_OUT.getCode(), TokenCodeEnum.AUTH_TIME_OUT.getMessage()); + } + throw new BusinessException( + TokenCodeEnum.AUTH_FAILED.getCode(), TokenCodeEnum.AUTH_FAILED.getMessage()); + } + return new JwtBody(claimsJws.getBody()); + } + + public static String createToken(String data, long secondTime, String subject) { + JwtBuilder jwtBuilder = Jwts.builder(); + subject = subject != null ? subject : UUID.randomUUID().toString(); + Map map = new HashMap<>(); + if (StringUtils.isNotEmpty(data)) { + map.put("data", data); + } + jwtBuilder.setClaims(map).setSubject(subject); + long currentTime = System.currentTimeMillis(); + if (secondTime == 0) { + currentTime += 30 * 60 * 1000; + } else { + currentTime += secondTime * 1000; + } + Date newDate = new Date(currentTime); + jwtBuilder.setExpiration(newDate); + return jwtBuilder.signWith(SignatureAlgorithm.ES256, privateKeyFromBase64()).compact(); + } + + private static PrivateKey privateKeyFromBase64() { + try { + byte[] keyBytes = Base64Utils.decodeFromString("MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgn9N3dA834ctlm7jkBXcRvR4+/hnYjZvYO1s5hisWG0yhRANCAAR91zvPpA/9Mc4DtxAWwWsnhj1rGk0XmTNqdnfPQJmLazYXaUvEuYuR+SNf6pl0OH2jLoAsXUFYtIFUzV/qv9ph"); + + PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes); + KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM_FAMILY_NAME); + return keyFactory.generatePrivate(keySpec); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * 公钥64位序列化 + * + * @return PublicKey + */ + private PublicKey publicKeyFromBase64() { + try { + byte[] keyBytes = Base64Utils.decodeFromString(publicKey); + X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes); + KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM_FAMILY_NAME); + return keyFactory.generatePublic(keySpec); + } catch (NoSuchAlgorithmException | InvalidKeySpecException ex) { + throw new IllegalArgumentException(ex); + } + } +} diff --git a/baogutang-common/src/main/java/top/baogutang/common/config/ExecutorConfig.java b/baogutang-common/src/main/java/top/baogutang/common/config/ExecutorConfig.java new file mode 100644 index 0000000..ddf797a --- /dev/null +++ b/baogutang-common/src/main/java/top/baogutang/common/config/ExecutorConfig.java @@ -0,0 +1,49 @@ +package top.baogutang.common.config; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; + +import java.util.concurrent.ThreadPoolExecutor; + +/** + * @description: 线程池配置 + * @author: nikooh + * @date: 2023/06/15 : 12:18 + */ +@Slf4j +@Configuration +public class ExecutorConfig { + + @Value("${spring.application.name}") + private String appName; + + @Value("${thread.pool.core.pool.size:10}") + private Integer corePoolSize; + + @Value("${thread.pool.max.pool.size:20}") + private Integer maxPoolSize; + + @Value("${thread.pool.keep.alive.second:10}") + private Integer keepAliveSecond; + + @Value("${thread.pool.queue.capacity:200}") + private Integer queueCapacity; + + @Bean("commonExecutor") + public ThreadPoolTaskExecutor threadPoolTaskExecutor() { + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + executor.setThreadNamePrefix(appName); + executor.setCorePoolSize(corePoolSize); + executor.setMaxPoolSize(maxPoolSize); + executor.setKeepAliveSeconds(keepAliveSecond); + executor.setQueueCapacity(queueCapacity); + executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); + executor.setWaitForTasksToCompleteOnShutdown(true); + executor.initialize(); + return executor; + } + +} diff --git a/baogutang-common/src/main/java/top/baogutang/common/config/GlobalCorsConfig.java b/baogutang-common/src/main/java/top/baogutang/common/config/GlobalCorsConfig.java new file mode 100644 index 0000000..b89c0e1 --- /dev/null +++ b/baogutang-common/src/main/java/top/baogutang/common/config/GlobalCorsConfig.java @@ -0,0 +1,37 @@ +package top.baogutang.common.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.CorsRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +/** + * @description: 全局跨域配置 + * @author: nikooh + * @date: 2023/06/15 : 12:19 + */ +@Configuration +public class GlobalCorsConfig { + + @Bean + public WebMvcConfigurer corsConfigurer() { + return new WebMvcConfigurer() { + @Override + //重写父类提供的跨域请求处理的接口 + public void addCorsMappings(CorsRegistry registry) { + //添加映射路径 + registry.addMapping("/**") + //放行哪些原始域 + .allowedOrigins("*") + //是否发送Cookie信息 + .allowCredentials(true) + //放行哪些原始域(请求方式) + .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") + //放行哪些原始域(头部信息) + .allowedHeaders("*") + //暴露哪些头部信息(因为跨域访问默认不能获取全部头部信息) + .exposedHeaders("Header1", "Header2"); + } + }; + } +} diff --git a/baogutang-common/src/main/java/top/baogutang/common/config/GlobalMDCTaskDecorator.java b/baogutang-common/src/main/java/top/baogutang/common/config/GlobalMDCTaskDecorator.java new file mode 100644 index 0000000..7390929 --- /dev/null +++ b/baogutang-common/src/main/java/top/baogutang/common/config/GlobalMDCTaskDecorator.java @@ -0,0 +1,45 @@ +package top.baogutang.common.config; + +import org.slf4j.MDC; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.config.BeanPostProcessor; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.task.TaskDecorator; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; + +import java.util.Map; + +/** + * @description: 多线程链路ID + * @author: nikooh + * @date: 2023/06/15 : 12:20 + */ +@Configuration +public class GlobalMDCTaskDecorator implements TaskDecorator, BeanPostProcessor { + + + @Override + public Runnable decorate(Runnable runnable) { + Map mdcContext = MDC.getCopyOfContextMap(); + return () -> { + try { + if (mdcContext != null) { + MDC.setContextMap(mdcContext); + } + runnable.run(); + } finally { + MDC.clear(); + } + }; + } + + + @Override + public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { + if (bean instanceof ThreadPoolTaskExecutor) { + ((ThreadPoolTaskExecutor) bean).setTaskDecorator(this); + } + return bean; + } + +} diff --git a/baogutang-common/src/main/java/top/baogutang/common/config/MdcRequestIdFilter.java b/baogutang-common/src/main/java/top/baogutang/common/config/MdcRequestIdFilter.java new file mode 100644 index 0000000..5f6516f --- /dev/null +++ b/baogutang-common/src/main/java/top/baogutang/common/config/MdcRequestIdFilter.java @@ -0,0 +1,44 @@ +package top.baogutang.common.config; + +import lombok.extern.slf4j.Slf4j; +import org.slf4j.MDC; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.UUID; + +/** + * @description: + * @author: nikooh + * @date: 2023/06/15 : 12:25 + */ +@Slf4j +@Component +@Order(1) +public class MdcRequestIdFilter extends OncePerRequestFilter { + + private static final String REQUEST_ID_KEY = "X-Request-Id"; + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { + try { + String traceId = request.getHeader(REQUEST_ID_KEY); + if (traceId == null) { + traceId = UUID.randomUUID().toString().replace("-", ""); + log.info("requestId为空,自动生成 {}", traceId); + request.setAttribute(REQUEST_ID_KEY, traceId); + } + MDC.put(REQUEST_ID_KEY, traceId); + filterChain.doFilter(request, response); + } finally { + MDC.remove(REQUEST_ID_KEY); + } + + } +} diff --git a/baogutang-common/src/main/java/top/baogutang/common/config/MybatisPlusConfig.java b/baogutang-common/src/main/java/top/baogutang/common/config/MybatisPlusConfig.java new file mode 100644 index 0000000..16f5b7a --- /dev/null +++ b/baogutang-common/src/main/java/top/baogutang/common/config/MybatisPlusConfig.java @@ -0,0 +1,27 @@ +package top.baogutang.common.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import com.baomidou.mybatisplus.annotation.DbType; +import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; +import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor; +import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; + +/** + * @description: + * @author: developer + * @date: 2022/05/26 : 15:51 + */ +@Configuration +public class MybatisPlusConfig { + @Bean + public MybatisPlusInterceptor mybatisPlusInterceptor() { + MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); + //分页插件 + interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); + //乐观锁插件 + interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); + return interceptor; + } +} diff --git a/baogutang-common/src/main/java/top/baogutang/common/config/RedisConfig.java b/baogutang-common/src/main/java/top/baogutang/common/config/RedisConfig.java new file mode 100644 index 0000000..d47831f --- /dev/null +++ b/baogutang-common/src/main/java/top/baogutang/common/config/RedisConfig.java @@ -0,0 +1,85 @@ +package top.baogutang.common.config; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.PropertyAccessor; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator; +import lombok.extern.slf4j.Slf4j; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.io.ClassPathResource; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.script.DefaultRedisScript; +import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; +import org.springframework.data.redis.serializer.StringRedisSerializer; +import org.springframework.integration.redis.util.RedisLockRegistry; +import org.springframework.scripting.support.ResourceScriptSource; + +import java.net.UnknownHostException; + +/** + * @description: 缓存配置 + * @author: nikooh + * @date: 2023/06/15 : 12:21 + */ +@Slf4j +@EnableCaching +@Configuration +public class RedisConfig { + + + @Bean + @SuppressWarnings("all") + public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException { + // 为了开发方便,直接使用 + RedisTemplate template = new RedisTemplate(); + template.setConnectionFactory(redisConnectionFactory); + + // Json 配置序列化 + // 使用 jackson 解析任意的对象 + Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class); + // 使用 objectMapper 进行转义 + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); + objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL); + jackson2JsonRedisSerializer.setObjectMapper(objectMapper); + // String 的序列化 + StringRedisSerializer stringRedisSerializer = new StringRedisSerializer(); + + // key 采用 String 的序列化方式 + template.setKeySerializer(stringRedisSerializer); + // Hash 的 key 采用 String 的序列化方式 + template.setHashKeySerializer(stringRedisSerializer); + // value 采用 jackson 的序列化方式 + template.setValueSerializer(jackson2JsonRedisSerializer); + // Hash 的 value 采用 String 的序列化方式 + template.setHashValueSerializer(jackson2JsonRedisSerializer); + // 把所有的配置 set 进 template + template.afterPropertiesSet(); + + return template; + } + + @Bean(destroyMethod = "destroy") + public RedisLockRegistry redisLockRegistry(RedisConnectionFactory redisConnectionFactory) { + return new RedisLockRegistry(redisConnectionFactory, "lock"); + } + + @Bean("accquireRedisScript") + public DefaultRedisScript accquireRedisScript() { + DefaultRedisScript redisScript = new DefaultRedisScript<>(); + redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("script/accquire.lua"))); + redisScript.setResultType(Boolean.class); + return redisScript; + } + + @Bean("ipLimitRedisScript") + public DefaultRedisScript ipLimitRedisScript() { + DefaultRedisScript redisScript = new DefaultRedisScript<>(); + redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("script/ipLimit.lua"))); + redisScript.setResultType(Boolean.class); + return redisScript; + } +} diff --git a/baogutang-common/src/main/java/top/baogutang/common/constants/CacheConstant.java b/baogutang-common/src/main/java/top/baogutang/common/constants/CacheConstant.java new file mode 100644 index 0000000..c94abe8 --- /dev/null +++ b/baogutang-common/src/main/java/top/baogutang/common/constants/CacheConstant.java @@ -0,0 +1,21 @@ +package top.baogutang.common.constants; + +/** + * @description: 缓存常量 + * @author: nikooh + * @date: 2023/06/15 : 11:28 + */ +public class CacheConstant { + + /** + * 实名认证完成 + */ + public static final String USER_VERIFY_KEY_PREFIX = "top:baogutang:user:verify:"; + + public static final String TOKEN = "top:baogutang:user:token:"; + + /** + * 微信消息推送获取临时二维码缓存key + */ + public static final String WX_MSG_PUSH_QR_CODE_PREFIX = "top:baogutang:wx:msg_push:%s:"; +} diff --git a/baogutang-common/src/main/java/top/baogutang/common/constants/ErrorCodeEnum.java b/baogutang-common/src/main/java/top/baogutang/common/constants/ErrorCodeEnum.java new file mode 100644 index 0000000..012ebba --- /dev/null +++ b/baogutang-common/src/main/java/top/baogutang/common/constants/ErrorCodeEnum.java @@ -0,0 +1,80 @@ +package top.baogutang.common.constants; + +/** + * @description: + * @author: nikooh + * @date: 2023/06/15 : 11:48 + */ +public enum ErrorCodeEnum { + /** + * 数据不存在 + */ + E_BIZ_ERROR(80800000, "请稍后重试~"), + E_BIZ_ALREADY_VERIFIED(81010001, "您已经完成认证了哦~"), + E_BIZ_ID_NO_ALREADY_VERIFIED(81010002, "该身份证已被认证哦~"), + E_PARAM_ID_NO_INCORRECT(81010003, "您输入的身份证有误哦~"), + E_PARAM_MOBILE_INCORRECT(81010004, "您输入的手机号有误哦~"), + E_PARAM_MSG_INCORRECT(81010005, "短信验证码不正确哟~"), + E_BIZ_MSG_SEND_FAIL(81010006, "请求频繁哟~"), + E_BIZ_NEED_VERIFY(81010007, "请完成实名认证~"), + + E_BIZ_DIGITAL_ART_DELAY_ORDER_PROCESS_ERROR(81010015, "订单已超时哦~"), + E_BIZ_DIGITAL_ART_CREATE_ORDER_ERROR(81010016, "创建订单失败"), + E_BIZ_DIGITAL_ART_CREATE_PAYMENT_UPDATE_ORDER_ERROR(81010017, "创建支付数据更新订单失败"), + E_BIZ_DIGITAL_ART_STORAGE_DATA_NOT_EXISTS(81010018, "库存不存在哦~"), + E_BIZ_DIGITAL_ART_STORAGE_EXISTS_PENDING_ORDER(81010019, "当前库存存在进行中订单哦~"), + E_BIZ_DIGITAL_ART_STORAGE_EXISTS_NICKNAME(81010020, "当前昵称已存在"), + E_BIZ_DIGITAL_ART_ORDER_NOT_EXISTS(81010022, "订单不存在哦~"), + E_BIZ_DIGITAL_ART_ORDER_ALREADY_PAID(81010023, "订单已支付哦~"), + E_BIZ_DIGITAL_ART_ACCOUNT_NOT_EXISTS(81010015, "账户信息不存在,请先绑定支付宝~"), + E_BIZ_DIGITAL_ART_ACCOUNT_STATE_ERROR(81010016, "账户状态异常,请联系官方~"), + E_BIZ_DIGITAL_ART_USER_NOT_EXISTS(81010017, "用户信息不存在哦~"), + E_BIZ_DIGITAL_ART_MSG_CODE_NOT_EXISTS(81010018, "验证码已失效,请重新发送~"), + E_BIZ_DIGITAL_ART_MSG_CODE_NOT_VALID(81010019, "验证码错误,请重新输入~"), + E_BIZ_DIGITAL_ART_MOBILE_EMAIL_NOT_AVAILABLE(81010020, "仅能填写手机号或邮箱哦~"), + E_BIZ_DIGITAL_ART_PASSWORD_NOT_VALID(81010022, "支付密码只能是数字哦~"), + E_BIZ_DIGITAL_ART_PAY_PASSWORD_NOT_SET(81010023, "支付密码还未设置哦~"), + E_BIZ_DIGITAL_ART_PAY_PASSWORD_FAIL(81010024, "支付密码错误,请重新输入~"), + E_BIZ_DIGITAL_ART_PAY_PASSWORD_FAILS(81010025, "输入错误次数过多,请稍后再试~"), + E_BIZ_DIGITAL_ART_PAY_ACCOUNT_NOT_BOUND(81010026, "请先绑定支付宝~"), + E_BIZ_DIGITAL_ART_ACCOUNT_BALANCE_NOT_ENOUGH(81010027, "账户余额不足~"), + E_BIZ_DIGITAL_ART_CASH_TOO_MANY_TIMES(81010028, "超过最大提现次数,请明天再来~"), + E_BIZ_DIGITAL_ART_CASH_TOO_MANY_AMOUNT(81010029, "当日累计提现已超过最大提现金额~"), + E_BIZ_DIGITAL_ART_CASH_ERROR(81010030, "提现失败,请稍后重试~"), + E_BIZ_DIGITAL_ART_TRANS_ERROR(81010031, "交易失败,请稍后重试~"), + E_BIZ_DIGITAL_ART_PASSWORD_LENGTH_NOT_VALID(81010033, "请输入六位支付密码~"), + E_BIZ_DIGITAL_ART_EXCHANGE_MALL_OUT_OF_STOCK(81010040, "好遗憾,抢光了"), + E_BIZ_DIGITAL_ART_GOODS_NOT_ENOUGH_STORAGE(81010048, "库存不足,请稍后重试~"), + E_BIZ_DIGITAL_ART_VOUCHER_NOT_EXISTS(81010050, "兑换码不存在~"), + E_BIZ_DIGITAL_ART_VOUCHER_ALREADY_USED(81010051, "兑换码已兑换~"), + E_BIZ_DIGITAL_ART_VOUCHER_USER_HOLD_GOODS_NOT_ENOUGH(81010052, "暂无兑换资格"), + E_BIZ_DIGITAL_ART_GOODS_LIMIT_BUY(81010053, "您已经达到购买上限"), + + USER_NOT_EXITS(81010015, "用户不存在"), + PASSWORD_ERROR(81010016, "密码错误"), + NO_PERMISSION(81010017, "账号无权限"), + E_BIZ_SIGN_ERROR(81011023, "签名错误~"), + E_BIZ_NOT_COLLAB_GOODS_ERROR(81011024, "该商品不支持此操作~"), + E_BIZ_MSG_SELL_LOCK(81011032, "请勿频繁操作~"), + UNAUTHORIZED(401, "暂未登录或token已经过期"), + FORBIDDEN(403, "没有相关权限"), + AUTHORIZATION_HEADER_IS_EMPTY(600, "请求头中的token为空"), + + DATA_IS_NULL(81011033, "数据为空");; + + private final int code; + private final String msg; + + ErrorCodeEnum(int code, String msg) { + this.code = code; + this.msg = msg; + } + + public int getCode() { + return code; + } + + public String getMsg() { + return msg; + } +} diff --git a/baogutang-common/src/main/java/top/baogutang/common/constants/JwtConstant.java b/baogutang-common/src/main/java/top/baogutang/common/constants/JwtConstant.java new file mode 100644 index 0000000..7654dfd --- /dev/null +++ b/baogutang-common/src/main/java/top/baogutang/common/constants/JwtConstant.java @@ -0,0 +1,20 @@ +package top.baogutang.common.constants; + +/** + * @description: + * @author: nikooh + * @date: 2023/06/15 : 12:02 + */ +public class JwtConstant { + + /** + * token名称 + */ + public static final String AUTHORIZATION = "authorization"; + + /** + * 加密方式 + */ + public static final String ALGORITHM_FAMILY_NAME = "EC"; + +} diff --git a/baogutang-common/src/main/java/top/baogutang/common/domain/JwtBody.java b/baogutang-common/src/main/java/top/baogutang/common/domain/JwtBody.java new file mode 100644 index 0000000..02c4bba --- /dev/null +++ b/baogutang-common/src/main/java/top/baogutang/common/domain/JwtBody.java @@ -0,0 +1,66 @@ +package top.baogutang.common.domain; + +import io.jsonwebtoken.Claims; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +/** + * @description: + * @author: nikooh + * @date: 2023/06/15 : 11:47 + */ +public class JwtBody { + + private String subject; + + private Long id; + + /** + * 签发时间 + */ + private Long iat; + + public JwtBody(Claims claims) { + if (Objects.nonNull(claims)) { + this.subject = claims.getSubject(); + Map map = new HashMap<>(claims); + this.iat = Long.valueOf(String.valueOf(map.get("iat"))); + this.id = Long.valueOf(String.valueOf(map.get("id"))); + } + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Long getIat() { + return iat; + } + + public void setIat(Long iat) { + this.iat = iat; + } + + public String getSubject() { + return subject; + } + + public void setSubject(String subject) { + this.subject = subject; + } + + @Override + public String toString() { + return "JwtBody{" + + "subject='" + subject + '\'' + + ", id=" + id + + ", iat=" + iat + + '}'; + } +} diff --git a/baogutang-common/src/main/java/top/baogutang/common/domain/Page.java b/baogutang-common/src/main/java/top/baogutang/common/domain/Page.java new file mode 100644 index 0000000..e80db23 --- /dev/null +++ b/baogutang-common/src/main/java/top/baogutang/common/domain/Page.java @@ -0,0 +1,33 @@ +package top.baogutang.common.domain; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.util.List; + +/** + * @description: + * @author: nikooh + * @date: 2023/06/16 : 12:28 + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class Page implements Serializable { + + private static final long serialVersionUID = 7521438130006292396L; + + private Integer totalCount; + + private Integer pageSize; + + private Integer totalPage; + + private Integer currPage; + + private List list; +} diff --git a/baogutang-common/src/main/java/top/baogutang/common/domain/Results.java b/baogutang-common/src/main/java/top/baogutang/common/domain/Results.java new file mode 100644 index 0000000..f48c5da --- /dev/null +++ b/baogutang-common/src/main/java/top/baogutang/common/domain/Results.java @@ -0,0 +1,192 @@ +package top.baogutang.common.domain; + +import io.swagger.annotations.ApiModelProperty; +import org.slf4j.MDC; +import top.baogutang.common.constants.ErrorCodeEnum; + +import java.io.Serializable; + +/** + * @description: + * @author: nikooh + * @date: 2023/06/15 : 11:54 + */ +public class Results implements Serializable { + private static final long serialVersionUID = 1L; + public static final int SUCCESS_CODE = 200; + public static final int FAIL_CODE = 300; + public static final String SUCCESS_MSG = "success"; + public static final int PARAM_ILLEGAL_CODE = 301; + + @ApiModelProperty("响应码,200为请求成功;其他异常均为业务异常") + private int code; + @ApiModelProperty("响应信息") + private String msg; + @ApiModelProperty("业务数据") + private T data; + @ApiModelProperty("全局链路请求id") + private String rid; + + public Results() { + } + + public Results(int code, String msg) { + this.code = code; + this.msg = msg; + } + + public Results(int code, String msg, T data) { + this.code = code; + this.msg = msg; + this.data = data; + } + + public static Results ok() { + return restResult(null, 200, (String) null); + } + + public static Results ok(T data) { + return restResult(data, SUCCESS_CODE, SUCCESS_MSG); + } + + public static Results ok(T data, int i, String msg) { + return restResult(data, 200, (String) null); + } + + + public static Results ok(T data, String msg) { + return restResult(data, 200, msg); + } + + public static Results result(T data, int code, String msg) { + return restResult(data, code, msg); + } + + public static Results failed() { + return restResult(null, 300, null); + } + + public static Results failed(String msg) { + return restResult(null, 300, msg); + } + + public static Results failed(int code, String msg) { + return restResult(null, code, msg); + } + + + public static Results failed(T data) { + return restResult(data, 300, (String) null); + } + + public static Results failed(T data, String msg) { + return restResult(data, 300, msg); + } + + public static Results restResult(T data, int code, String msg) { + Results r = new Results<>(); + r.setCode(code); + r.setData(data); + r.setMsg(msg); + String rid = MDC.get("X-Request-Id"); + r.setRid(rid); + return r; + } + + public static Results failed(ErrorCodeEnum errorCodeEnum) { + return restResult(null, errorCodeEnum.getCode(), errorCodeEnum.getMsg()); + } + + public Boolean isSuccess() { + return this.code == SUCCESS_CODE; + } + + public static boolean isSuccess(Results response) { + return response != null && response.code == SUCCESS_CODE; + } + + + public void setErrorCodeEnum(int code, String msg) { + this.code = code; + this.msg = msg; + } + + public static Results.ResultsBuilder builder() { + return new Results.ResultsBuilder(); + } + + public int getCode() { + return this.code; + } + + public String getMsg() { + return this.msg; + } + + public T getData() { + return this.data; + } + + public String getRid() { + return rid; + } + + public void setRid(String rid) { + this.rid = rid; + } + + public void setCode(final int code) { + this.code = code; + } + + public void setMsg(final String msg) { + this.msg = msg; + } + + public void setData(final T data) { + this.data = data; + } + + protected boolean canEqual(final Object other) { + return other instanceof Results; + } + + + @Override + public String toString() { + return "Results(code=" + this.getCode() + ", msg=" + this.getMsg() + ", data=" + this.getData() + ")"; + } + + public static class ResultsBuilder { + private int code; + private String msg; + private T data; + + ResultsBuilder() { + } + + public Results.ResultsBuilder code(final int code) { + this.code = code; + return this; + } + + public Results.ResultsBuilder msg(final String msg) { + this.msg = msg; + return this; + } + + public Results.ResultsBuilder data(final T data) { + this.data = data; + return this; + } + + public Results build() { + return new Results(this.code, this.msg, this.data); + } + + @Override + public String toString() { + return "Results.ResultsBuilder(code=" + this.code + ", msg=" + this.msg + ", data=" + this.data + ")"; + } + } +} diff --git a/baogutang-common/src/main/java/top/baogutang/common/domain/TokenCodeEnum.java b/baogutang-common/src/main/java/top/baogutang/common/domain/TokenCodeEnum.java new file mode 100644 index 0000000..46c0277 --- /dev/null +++ b/baogutang-common/src/main/java/top/baogutang/common/domain/TokenCodeEnum.java @@ -0,0 +1,25 @@ +package top.baogutang.common.domain; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * @description: + * @author: nikooh + * @date: 2023/06/15 : 12:00 + */ +@Getter +@AllArgsConstructor +public enum TokenCodeEnum { + /** + * 未获取到token + */ + AUTH_TOKEN_EMPTY(-200, "未获取到token"), + AUTH_FAILED(-200, "无权限"), + AUTH_TIME_OUT(-200, "token已过期"), + AUTH_TOKEN_ILLEGAL(-200, "Token不合法"); + + public int code; + + public String message; +} diff --git a/baogutang-common/src/main/java/top/baogutang/common/exceptions/BusinessException.java b/baogutang-common/src/main/java/top/baogutang/common/exceptions/BusinessException.java new file mode 100644 index 0000000..24b5bbf --- /dev/null +++ b/baogutang-common/src/main/java/top/baogutang/common/exceptions/BusinessException.java @@ -0,0 +1,39 @@ +package top.baogutang.common.exceptions; + +import top.baogutang.common.constants.ErrorCodeEnum; +import top.baogutang.common.domain.Results; + +/** + * @description: 业务异常 + * @author: nikooh + * @date: 2023/06/15 : 11:48 + */ +public class BusinessException extends RuntimeException { + + private static final long serialVersionUID = -8485514816508612287L; + + private int code = Results.FAIL_CODE; + + public BusinessException(int code, String message) { + super(message); + this.code = code; + } + + + public BusinessException(String message) { + super(message); + } + + public BusinessException(ErrorCodeEnum errorCodeEnum) { + super(errorCodeEnum.getMsg()); + this.code = errorCodeEnum.getCode(); + } + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } +} diff --git a/baogutang-common/src/main/java/top/baogutang/common/exceptions/GlobalExceptionHandler.java b/baogutang-common/src/main/java/top/baogutang/common/exceptions/GlobalExceptionHandler.java new file mode 100644 index 0000000..f3cf789 --- /dev/null +++ b/baogutang-common/src/main/java/top/baogutang/common/exceptions/GlobalExceptionHandler.java @@ -0,0 +1,75 @@ +package top.baogutang.common.exceptions; + +import lombok.extern.slf4j.Slf4j; +import org.apache.catalina.connector.ClientAbortException; +import org.springframework.validation.BindingResult; +import org.springframework.validation.FieldError; +import org.springframework.validation.ObjectError; +import org.springframework.web.HttpMediaTypeNotSupportedException; +import org.springframework.web.HttpRequestMethodNotSupportedException; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.MissingServletRequestParameterException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import top.baogutang.common.domain.Results; + +import java.util.List; + +/** + * @description: 全局异常处理 + * @author: nikooh + * @date: 2023/06/15 : 12:23 + */ +@Slf4j +@RestControllerAdvice +public class GlobalExceptionHandler { + + + public GlobalExceptionHandler() { + // + } + + @ExceptionHandler({Throwable.class}) + public Results handleException(Throwable e) { + log.error("请求发生错误,错误信息:{}", e.getMessage(), e); + return Results.failed("出错啦,请稍后再试哟~"); + } + + + @ExceptionHandler({Exception.class}) + public Results handleException(Exception e) { + log.error("请求发生错误,错误信息:", e); + return Results.failed("服务器出错啦,请稍后再试哟~"); + } + + @ExceptionHandler({ClientAbortException.class}) + public Results handleClientAbortException() { + log.error("ClientAbortException: java.io.IOException: Broken pipe: {}", "客户端链接断开"); + return Results.failed(300, "服务器出错啦,请稍后再试哟~"); + } + + @ExceptionHandler({IllegalArgumentException.class, MissingServletRequestParameterException.class, HttpMediaTypeNotSupportedException.class, HttpRequestMethodNotSupportedException.class}) + public Results handleMultiException(Exception e) { + log.error("请求发生错误,错误信息:{}", e.getMessage(), e); + return Results.failed(300, e.getMessage()); + } + + @ExceptionHandler({BusinessException.class}) + public Results businessException(BusinessException e) { + log.error("请求发生错误,code:{},message:{}", e.getCode(), e.getMessage()); + return Results.failed(e.getCode(), e.getMessage()); + } + + @ExceptionHandler({MethodArgumentNotValidException.class}) + public Results parameterExceptionHandler(MethodArgumentNotValidException e) { + BindingResult exceptions = e.getBindingResult(); + if (exceptions.hasErrors()) { + List errors = exceptions.getAllErrors(); + if (!errors.isEmpty()) { + FieldError fieldError = (FieldError) errors.get(0); + return Results.failed(301, fieldError.getDefaultMessage()); + } + } + return Results.failed(301, e.getMessage()); + } +} diff --git a/baogutang-common/src/main/java/top/baogutang/common/properties/WxMsgPushProperties.java b/baogutang-common/src/main/java/top/baogutang/common/properties/WxMsgPushProperties.java new file mode 100644 index 0000000..78399bd --- /dev/null +++ b/baogutang-common/src/main/java/top/baogutang/common/properties/WxMsgPushProperties.java @@ -0,0 +1,24 @@ +package top.baogutang.common.properties; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.cloud.context.config.annotation.RefreshScope; +import org.springframework.stereotype.Component; + +import java.util.Set; + +/** + * @description: + * @author: nikooh + * @date: 2023/06/16 : 12:50 + */ +@Data +@Component +@RefreshScope +@ConfigurationProperties(prefix = "wx.msg-push") +public class WxMsgPushProperties { + + private String appToken; + + private Set topicIds; +} diff --git a/baogutang-common/src/main/java/top/baogutang/common/utils/HttpUtils.java b/baogutang-common/src/main/java/top/baogutang/common/utils/HttpUtils.java new file mode 100644 index 0000000..be5c4ff --- /dev/null +++ b/baogutang-common/src/main/java/top/baogutang/common/utils/HttpUtils.java @@ -0,0 +1,161 @@ +package top.baogutang.common.utils; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.TypeReference; +import lombok.extern.slf4j.Slf4j; +import top.baogutang.common.constants.ErrorCodeEnum; +import top.baogutang.common.exceptions.BusinessException; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.util.Map; +import java.util.Set; + +/** + * 说明:网络请求工具 + * + * @author nikooh + */ +@Slf4j +public final class HttpUtils { + private static final String CHARSET_NAME = "UTF-8"; + + private HttpUtils() { + + } + + /** + * 发送post请求 + * + * @param data 发送的数据 + * @param url 请求后台的url + * @return 发送的result结果 + */ + public static T post(Object data, String url, TypeReference type) { + try { + if (data == null) { + return null; + } + String dataStr = JSON.toJSONString(data); + + URL cUrl = new URL(url); + HttpURLConnection urlConnection = (HttpURLConnection) cUrl.openConnection(); + urlConnection.setConnectTimeout(60000); + urlConnection.setReadTimeout(60000); + urlConnection.setUseCaches(false); + urlConnection.setRequestMethod("POST"); + //设置请求属性 + urlConnection.setRequestProperty("Content-Type", "application/json"); + urlConnection.setRequestProperty("Charset", CHARSET_NAME); + urlConnection.setDoOutput(true); + urlConnection.connect(); + OutputStream outputStream = urlConnection.getOutputStream(); + outputStream.write(dataStr.getBytes(StandardCharsets.UTF_8)); + outputStream.flush(); + return dealConnect(urlConnection, type); + } catch (Exception e) { + log.error(">>>>>>>>>>请求异常:{}<<<<<<<<<<", e.getMessage(), e); + throw new BusinessException(ErrorCodeEnum.E_BIZ_ERROR); + } + } + + public static T get(String path, TypeReference type) { + return get(null, path, type); + } + + /** + * 发送get请求 + */ + public static T get(Map data, String url, TypeReference type) { + try { + String query = parseMap2Query(data); + if (!query.isEmpty()) { + url = url + "?" + query; + } + URL cUrl = new URL(url); + HttpURLConnection urlConnection = (HttpURLConnection) cUrl.openConnection(); + urlConnection.setConnectTimeout(60000); + urlConnection.setReadTimeout(60000); + urlConnection.setUseCaches(false); + urlConnection.setRequestMethod("GET"); + //设置请求属性 + urlConnection.setRequestProperty("Charset", CHARSET_NAME); + urlConnection.setDoOutput(true); + urlConnection.connect(); + return dealConnect(urlConnection, type); + } catch (Exception e) { + log.error(">>>>>>>>>>请求异常:{}<<<<<<<<<<", e.getMessage(), e); + throw new BusinessException(ErrorCodeEnum.E_BIZ_ERROR); + } + } + + /** + * 把map转成query查询字符串 + */ + private static String parseMap2Query(Map data) { + if (data == null || data.size() <= 0) { + return ""; + } + Set> entries = data.entrySet(); + StringBuilder stringBuilder = new StringBuilder(); + for (Map.Entry entry : entries) { + if (stringBuilder.length() > 0) { + stringBuilder.append("&"); + } + stringBuilder.append(entry.getKey()).append("=").append(entry.getValue()); + } + return stringBuilder.toString(); + } + + + /** + * 处理连接以后的状态信息 + * + * @param urlConnection 打开的连接 + * @return 返回发送结果 + */ + private static T dealConnect(HttpURLConnection urlConnection, TypeReference type) { + try { + int responseCode = urlConnection.getResponseCode(); + if (responseCode != 200) { + throw new BusinessException(ErrorCodeEnum.E_BIZ_ERROR); + } + InputStream inputStream = urlConnection.getInputStream(); + String res = inputStream2String(inputStream); + if (res == null || res.isEmpty()) { + return null; + } + return JSON.parseObject(res, type); + + } catch (Exception e) { + log.error(">>>>>>>>>>请求异常:{}<<<<<<<<<<", e.getMessage(), e); + throw new BusinessException(ErrorCodeEnum.E_BIZ_ERROR); + } + } + + /** + * 从输入流中读取内容到字符串 + * + * @param inputStream 输入路 + * @return 返回字符串 + */ + private static String inputStream2String(InputStream inputStream) { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + int len = 0; + byte[] bytes = new byte[4096]; + try { + while ((len = inputStream.read(bytes)) != -1) { + outputStream.write(bytes, 0, len); + } + return new String(outputStream.toByteArray(), StandardCharsets.UTF_8); + } catch (IOException e) { + log.error(">>>>>>>>>>请求异常:{}<<<<<<<<<<", e.getMessage(), e); + throw new BusinessException(ErrorCodeEnum.E_BIZ_ERROR); + } + } +} diff --git a/baogutang-common/src/main/java/top/baogutang/common/utils/UserThreadLocal.java b/baogutang-common/src/main/java/top/baogutang/common/utils/UserThreadLocal.java new file mode 100644 index 0000000..9bc75d0 --- /dev/null +++ b/baogutang-common/src/main/java/top/baogutang/common/utils/UserThreadLocal.java @@ -0,0 +1,22 @@ +package top.baogutang.common.utils; + +/** + * @description: + * @author: nikooh + * @date: 2023/06/15 : 12:03 + */ +public class UserThreadLocal { + private static final ThreadLocal userThread = new ThreadLocal<>(); + + public static void set(Long userId) { + userThread.set(userId); + } + + public static Long get() { + return userThread.get(); + } + + public static void remove() { + userThread.remove(); + } +} diff --git a/baogutang-common/src/main/resources/script/accquire.lua b/baogutang-common/src/main/resources/script/accquire.lua new file mode 100644 index 0000000..eb77d8a --- /dev/null +++ b/baogutang-common/src/main/resources/script/accquire.lua @@ -0,0 +1,10 @@ +local limit = tonumber(ARGV[1]) +local current = tonumber(redis.call('get', KEYS[1]) or '0') +if current + 1 > limit +then + return false +else + redis.call('INCRBY', KEYS[1], '1') + redis.call('expire', KEYS[1], '2') +end +return true \ No newline at end of file diff --git a/baogutang-common/src/main/resources/script/ipLimit.lua b/baogutang-common/src/main/resources/script/ipLimit.lua new file mode 100644 index 0000000..8dcb796 --- /dev/null +++ b/baogutang-common/src/main/resources/script/ipLimit.lua @@ -0,0 +1,25 @@ +--- +--- Generated by EmmyLua(https://github.com/EmmyLua) +--- Created by developer. +--- DateTime: 2022/6/21 14:26 +--- + +local val = redis.call('incr', KEYS[1]) +local ttl = redis.call('ttl', KEYS[1]) + +--获取ARGV内的参数并打印 +local expire = ARGV[1] +local times = ARGV[2] + +if val == 1 then + redis.call('expire', KEYS[1], tonumber(expire)) +else + if ttl == -1 then + redis.call('expire', KEYS[1], tonumber(expire)) + end +end + +if val > tonumber(times) then + return false +end +return true \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..7f9bcb2 --- /dev/null +++ b/pom.xml @@ -0,0 +1,155 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.3.2.RELEASE + + top.baogutang + baogutang-parent + pom + 1.0.0-SNAPSHOT + + + baogutang-admin + baogutang-business + baogutang-common + + + + 2.3.2.RELEASE + Hoxton.SR9 + 2.2.6.RELEASE + 0.1.13 + 2.5.0 + 1.2.83 + 0.9.0 + 1.18.12 + 29.0-jre + 2.10.0 + 4.5.3 + 3.0.0 + 3.4.2 + 2.6 + 1.8.8.1.Final + 4.9.3 + 3.8.1 + 4.1.1 + 3.6.5 + + + + + + org.springframework.cloud + spring-cloud-dependencies + ${spring-cloud.version} + pom + import + + + com.alibaba.cloud + spring-cloud-alibaba-dependencies + ${spring.cloud.alibaba.version} + pom + import + + + com.aliyun.openservices + aliyun-log-logback-appender + ${aliyun.log.version} + + + com.google.protobuf + protobuf-java + ${protobuf.java.version} + + + com.alibaba + fastjson + ${fastjson.version} + + + io.jsonwebtoken + jjwt + ${jwt.token.version} + + + org.projectlombok + lombok + ${lombok.version} + provided + + + org.apache.commons + commons-lang3 + ${commons-lang3.version} + + + + com.google.guava + guava + ${guava.vsersion} + + + + org.apache.commons + commons-pool2 + ${commons.pool2.version} + + + commons-lang + commons-lang + ${commons.lang.version} + + + org.apache.httpcomponents + httpclient + ${httpClient.version} + + + com.baomidou + mybatis-plus-boot-starter + ${mybatis.plus.boot.version} + + + + io.springfox + springfox-boot-starter + ${springfox-boot.version} + + + + com.aliyun.openservices + ons-client + + ${ons.version} + + + com.squareup.okhttp3 + okhttp + ${okhttp3.version} + + + + com.aliyun.oss + aliyun-sdk-oss + ${aliyun.oss.verson} + + + + com.aliyun + aliyun-java-sdk-core + ${aliyun.sdk.core.verson} + + + com.aliyun + aliyun-java-sdk-green + ${aliyun.sdk.green.verson} + + + + +