├── .gitignore ├── chatgpt-data.iml ├── chatgpt-data-app ├── src │ ├── main │ │ ├── resources │ │ │ ├── application.yml │ │ │ ├── mybatis │ │ │ │ ├── config │ │ │ │ │ └── mybatis-config.xml │ │ │ │ └── mapper │ │ │ │ │ ├── openai_product_mapper.xml │ │ │ │ │ └── user_account_mapper.xml │ │ │ ├── application-prod.yml │ │ │ └── application-test.yml │ │ └── java │ │ │ └── com │ │ │ └── luckysj │ │ │ └── chatgpt │ │ │ └── data │ │ │ ├── config │ │ │ ├── RateLimiterAopConfig.java │ │ │ ├── ChatGLMSDKConfigProperties.java │ │ │ ├── ChatGPTSDKConfigProperties.java │ │ │ ├── AliPayClientConfig.java │ │ │ ├── AliPayConfigProperties.java │ │ │ ├── ThreadPoolConfigProperties.java │ │ │ ├── RedisCientConfigProperties.java │ │ │ ├── RabbitDeliveryConfig.java │ │ │ ├── PrometheusConfiguration.java │ │ │ ├── ChatGLMSDKConfig.java │ │ │ ├── GoogleGuavaCodeCacheConfig.java │ │ │ ├── SensitiveWordConfig.java │ │ │ ├── ChatGPTSDKConfig.java │ │ │ ├── ThreadPoolConfig.java │ │ │ └── RedisClientConfig.java │ │ │ └── Application.java │ └── test │ │ └── java │ │ └── com │ │ └── luckysj │ │ └── chatgpt │ │ └── data │ │ ├── UtilsTest.java │ │ ├── TongYiQianWenTest.java │ │ └── AliPayTest.java └── data │ └── log │ └── log-info-2023-12-16.0.log ├── .idea ├── codeStyles │ └── codeStyleConfig.xml ├── .gitignore ├── vcs.xml ├── plugin.xml ├── inspectionProfiles │ └── Project_Default.xml ├── misc.xml ├── jarRepositories.xml ├── compiler.xml └── encodings.xml ├── chatgpt-data-domain ├── src │ └── main │ │ └── java │ │ └── com │ │ └── luckysj │ │ └── chatgpt │ │ └── data │ │ └── domain │ │ ├── weixin │ │ ├── repository │ │ │ └── IWeiXinRepository.java │ │ ├── service │ │ │ ├── IWeiXinValidateService.java │ │ │ ├── IWeiXinBehaviorService.java │ │ │ ├── validate │ │ │ │ └── WeiXinValidateService.java │ │ │ └── message │ │ │ │ └── WeiXinBehaviorService.java │ │ └── model │ │ │ ├── valobj │ │ │ ├── ContentCodeVO.java │ │ │ └── MsgTypeVO.java │ │ │ └── entity │ │ │ ├── UserBehaviorMessageEntity.java │ │ │ └── MessageTextEntity.java │ │ ├── openai │ │ ├── model │ │ │ ├── valobj │ │ │ │ ├── LogicCheckTypeVO.java │ │ │ │ ├── GenerativeModelVO.java │ │ │ │ └── UserAccountStatusVO.java │ │ │ ├── entity │ │ │ │ ├── ChoiceEntity.java │ │ │ │ ├── MessageEntity.java │ │ │ │ ├── RuleLogicEntity.java │ │ │ │ └── UserAccountQuotaEntity.java │ │ │ └── aggregates │ │ │ │ └── ChatProcessAggregate.java │ │ ├── service │ │ │ ├── IChatService.java │ │ │ ├── rule │ │ │ │ ├── ILogicFilter.java │ │ │ │ ├── impl │ │ │ │ │ ├── ModelTypeFilter.java │ │ │ │ │ ├── AccountStatusFilter.java │ │ │ │ │ ├── UserQuotaFilter.java │ │ │ │ │ ├── AccessLimitFilter.java │ │ │ │ │ └── SensitiveWordFilter.java │ │ │ │ └── factory │ │ │ │ │ └── DefaultLogicFactory.java │ │ │ ├── channel │ │ │ │ ├── service │ │ │ │ │ ├── IGenerativeModelService.java │ │ │ │ │ └── impl │ │ │ │ │ │ ├── ImageGenerativeModelServiceImpl.java │ │ │ │ │ │ └── TextGenerativeModelServiceImpl.java │ │ │ │ ├── OpenAiGroupService.java │ │ │ │ └── impl │ │ │ │ │ ├── ChatGPTService.java │ │ │ │ │ └── ChatGLMService.java │ │ │ ├── ChatService.java │ │ │ └── AbstractChatService.java │ │ ├── repository │ │ │ └── IOpenAiRepository.java │ │ └── annotation │ │ │ └── LogicStrategy.java │ │ ├── auth │ │ ├── model │ │ │ ├── valobj │ │ │ │ └── AuthTYpeVo.java │ │ │ └── entity │ │ │ │ └── AuthStateEntity.java │ │ ├── repository │ │ │ └── IAuthRepository.java │ │ └── service │ │ │ ├── IAuthService.java │ │ │ └── AuthService.java │ │ └── order │ │ ├── model │ │ ├── valobj │ │ │ ├── AliPayTradeTypeVo.java │ │ │ ├── PayTypeVO.java │ │ │ ├── PayStatusVO.java │ │ │ └── OrderStatusVO.java │ │ ├── aggregates │ │ │ └── CreateOrderAggregate.java │ │ └── entity │ │ │ ├── ShopCartEntity.java │ │ │ ├── OrderEntity.java │ │ │ ├── UnpaidOrderEntity.java │ │ │ ├── ProductEntity.java │ │ │ └── PayOrderEntity.java │ │ ├── config │ │ └── AliPayConfig.java │ │ ├── repository │ │ └── IOrderRepository.java │ │ └── service │ │ ├── IOrderService.java │ │ └── AbstractOrderService.java └── pom.xml ├── chatgpt-data-types ├── src │ └── main │ │ └── java │ │ └── com │ │ └── luckysj │ │ └── chatgpt │ │ └── data │ │ └── types │ │ ├── annotation │ │ ├── RedisTopic.java │ │ └── AccessInterceptor.java │ │ ├── enums │ │ ├── ChatGPTModel.java │ │ ├── OpenAiChannel.java │ │ ├── OpenAIProductEnableModel.java │ │ └── ChatGLMModel.java │ │ ├── model │ │ └── Response.java │ │ ├── exception │ │ └── ChatGPTException.java │ │ ├── sdk │ │ └── weixin │ │ │ ├── Article.java │ │ │ ├── SignUtil.java │ │ │ └── SignatureUtil.java │ │ ├── common │ │ └── Constants.java │ │ └── util │ │ ├── QRCodeUtil.java │ │ ├── EncryptUtil.java │ │ ├── IPUtils.java │ │ └── IdWorkerUtils.java └── pom.xml ├── README.md ├── chatgpt-data-trigger ├── src │ └── main │ │ └── java │ │ └── com │ │ └── luckysj │ │ └── chatgpt │ │ └── data │ │ └── trigger │ │ ├── http │ │ ├── dto │ │ │ ├── ChoiceEntity.java │ │ │ ├── MessageEntity.java │ │ │ ├── ChatGPTRequestDTO.java │ │ │ └── SaleProductDTO.java │ │ ├── AuthController.java │ │ └── ChatGPTAIServiceController.java │ │ ├── mq │ │ ├── OrderPaySuccessListener.java │ │ ├── OrderPaySuccessRedisListener.java │ │ └── OrderPaySuccessMqListener.java │ │ └── job │ │ ├── TimeoutCloseOrderJob.java │ │ ├── OrderReplenishmentJob.java │ │ └── NoPayNotifyOrderJob.java └── pom.xml ├── chatgpt-data-infrastructure ├── src │ └── main │ │ └── java │ │ └── com │ │ └── luckysj │ │ └── chatgpt │ │ └── data │ │ └── infrastructure │ │ ├── dao │ │ ├── IOpenAIProductDao.java │ │ ├── IOpenAIOrderDao.java │ │ └── IUserAccountDao.java │ │ ├── po │ │ ├── UserAccountPO.java │ │ ├── OpenAIProductPO.java │ │ └── OpenAIOrderPO.java │ │ ├── repository │ │ ├── WeiXinRepository.java │ │ ├── OpenAiRepository.java │ │ └── AuthRepository.java │ │ └── redis │ │ └── RedisService.java └── pom.xml └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | /chatgpt-data-app/src/main/resources/application-local.yml 2 | -------------------------------------------------------------------------------- /chatgpt-data.iml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /chatgpt-data-app/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | 2 | spring: 3 | config: 4 | name: chatgpt 5 | profiles: 6 | active: dev 7 | -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Datasource local storage ignored files 5 | /dataSources/ 6 | /dataSources.local.xml 7 | # 基于编辑器的 HTTP 客户端请求 8 | /httpRequests/ 9 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/plugin.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /chatgpt-data-domain/src/main/java/com/luckysj/chatgpt/data/domain/weixin/repository/IWeiXinRepository.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.domain.weixin.repository; 2 | 3 | /** 4 | * @author www.luckysj.top 刘仕杰 5 | * @description 微信服务仓储 6 | * @create 2023/12/17 20:59:07 7 | */ 8 | public interface IWeiXinRepository { 9 | String genCode(String openid); 10 | } 11 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /chatgpt-data-domain/src/main/java/com/luckysj/chatgpt/data/domain/openai/model/valobj/LogicCheckTypeVO.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.domain.openai.model.valobj; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | 6 | @Getter 7 | @AllArgsConstructor 8 | public enum LogicCheckTypeVO { 9 | SUCCESS("0000", "校验通过"), 10 | REFUSE("0001","校验拒绝"), 11 | ; 12 | 13 | private final String code; 14 | private final String info; 15 | } 16 | -------------------------------------------------------------------------------- /chatgpt-data-types/src/main/java/com/luckysj/chatgpt/data/types/annotation/RedisTopic.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.types.annotation; 2 | 3 | import java.lang.annotation.*; 4 | 5 | /** 6 | * @description redisson 消息主题注解 7 | * @create 2023/12/17 21:56:10 8 | */ 9 | @Retention(RetentionPolicy.RUNTIME) 10 | @Target({ElementType.TYPE}) 11 | @Documented 12 | public @interface RedisTopic { 13 | 14 | // 主题名称 15 | String topic() default ""; 16 | 17 | } 18 | -------------------------------------------------------------------------------- /chatgpt-data-domain/src/main/java/com/luckysj/chatgpt/data/domain/openai/service/IChatService.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.domain.openai.service; 2 | 3 | import com.luckysj.chatgpt.data.domain.openai.model.aggregates.ChatProcessAggregate; 4 | import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyEmitter; 5 | 6 | public interface IChatService { 7 | ResponseBodyEmitter completions(ResponseBodyEmitter emitter, ChatProcessAggregate chatProcess); 8 | } 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 大语言模型服务平台 - By 刘仕杰 2 | 3 | 基于DDD架构设计的大语言模型服务平台,使用了个人开发的chatgpt-sdk-java与chatglm-sdk-java服务会话SDK进行请求应答,整体项目分层清晰,综合运用策略,工厂,模板等设计模式编写代码,代码逻辑清晰 4 | 5 | --- 6 | 7 | >**作者**:LuckySJ-刘仕杰 - 在线体验地址[**www.luckysj.online**](https://www.luckysj.online/) 8 | 9 | ## 主要功能 10 | 11 | 1. AIGC服务(大模型生成式服务),支持消费额度限制,模型使用限制等功能 12 | 2. 对接微信公众号,转发公众号消息完成鉴权 13 | 3. 对接支付宝沙箱完成支付系统,自由购买对话额度 14 | 15 | ## 技术栈 16 | 17 | SpringBoot、MyBatis、MySQL、Redis、Guava、OKHttp3、RabbitMQ。 18 | -------------------------------------------------------------------------------- /chatgpt-data-app/src/main/java/com/luckysj/chatgpt/data/config/RateLimiterAopConfig.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.config; 2 | 3 | import com.luckysj.chatgpt.data.aop.RateLimiterAOP; 4 | import org.springframework.context.annotation.Bean; 5 | import org.springframework.context.annotation.Configuration; 6 | 7 | @Configuration 8 | public class RateLimiterAopConfig { 9 | @Bean 10 | public RateLimiterAOP rateLimiterAOP(){ 11 | return new RateLimiterAOP(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /chatgpt-data-app/src/main/resources/mybatis/config/mybatis-config.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /chatgpt-data-domain/src/main/java/com/luckysj/chatgpt/data/domain/openai/service/rule/ILogicFilter.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.domain.openai.service.rule; 2 | 3 | import com.luckysj.chatgpt.data.domain.openai.model.aggregates.ChatProcessAggregate; 4 | import com.luckysj.chatgpt.data.domain.openai.model.entity.RuleLogicEntity; 5 | 6 | public interface ILogicFilter { 7 | 8 | RuleLogicEntity fileter(ChatProcessAggregate chatProcessAggregate, T data) throws Exception; 9 | 10 | } 11 | -------------------------------------------------------------------------------- /chatgpt-data-trigger/src/main/java/com/luckysj/chatgpt/data/trigger/http/dto/ChoiceEntity.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.trigger.http.dto; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * @author www.luckysj.top 刘仕杰 7 | * @description 8 | * @create 2023/12/04 19:29:22 9 | */ 10 | @Data 11 | public class ChoiceEntity { 12 | 13 | /** stream = true 请求参数里返回的属性是 delta */ 14 | private MessageEntity delta; 15 | /** stream = false 请求参数里返回的属性是 delta */ 16 | private MessageEntity message; 17 | 18 | } 19 | -------------------------------------------------------------------------------- /chatgpt-data-domain/src/main/java/com/luckysj/chatgpt/data/domain/openai/model/entity/ChoiceEntity.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.domain.openai.model.entity; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * @author www.luckysj.top 刘仕杰 7 | * @description 8 | * @create 2023/12/04 19:48:27 9 | */ 10 | @Data 11 | public class ChoiceEntity { 12 | 13 | /** stream = true 请求参数里返回的属性是 delta */ 14 | private MessageEntity delta; 15 | /** stream = false 请求参数里返回的属性是 delta */ 16 | private MessageEntity message; 17 | 18 | } 19 | -------------------------------------------------------------------------------- /chatgpt-data-types/src/main/java/com/luckysj/chatgpt/data/types/enums/ChatGPTModel.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.types.enums; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | import lombok.ToString; 6 | 7 | @Getter 8 | @ToString 9 | @AllArgsConstructor 10 | public enum ChatGPTModel { 11 | 12 | /** gpt-3.5-turbo */ 13 | GPT_3_5_TURBO("gpt-3.5-turbo"), 14 | /** 文生图 */ 15 | DALL_E_2("dall-e-2"), 16 | DALL_E_3("dall-e-3"), 17 | ; 18 | 19 | private final String code; 20 | 21 | } 22 | -------------------------------------------------------------------------------- /chatgpt-data-domain/src/main/java/com/luckysj/chatgpt/data/domain/auth/model/valobj/AuthTYpeVo.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.domain.auth.model.valobj; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | import lombok.NoArgsConstructor; 6 | 7 | @Getter 8 | @NoArgsConstructor 9 | @AllArgsConstructor 10 | public enum AuthTypeVo { 11 | CODE_SUCCESS("0000","验证成功"), 12 | CODE_NOT_EXIST("0001","验证码不存在"), 13 | CODE_INVALIDATION("0002","验证码无效"); 14 | private String code; 15 | private String info; 16 | } 17 | -------------------------------------------------------------------------------- /chatgpt-data-domain/src/main/java/com/luckysj/chatgpt/data/domain/weixin/service/IWeiXinValidateService.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.domain.weixin.service; 2 | 3 | /** 4 | * @author www.luckysj.top 刘仕杰 5 | * @description 验签接口 6 | * @create 2023/12/05 15:45:55 7 | */ 8 | public interface IWeiXinValidateService { 9 | 10 | /** 11 | * @description 签名校验 12 | * @param 13 | * @return boolean 验签结果 14 | * @date 2023/12/05 15:47:18 15 | */ 16 | boolean checkSign(String signature, String timestamp, String nonce); 17 | 18 | } 19 | -------------------------------------------------------------------------------- /chatgpt-data-domain/src/main/java/com/luckysj/chatgpt/data/domain/openai/model/valobj/GenerativeModelVO.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.domain.openai.model.valobj; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | 6 | /** 7 | * @author www.luckysj.top 刘仕杰 8 | * @description 模型生成类型 9 | * @create 2023/12/18 17:55:30 10 | */ 11 | @Getter 12 | @AllArgsConstructor 13 | public enum GenerativeModelVO { 14 | 15 | TEXT("TEXT","文本"), 16 | IMAGES("IMAGES","图片"), 17 | ; 18 | 19 | private final String code; 20 | private final String info; 21 | 22 | } 23 | -------------------------------------------------------------------------------- /chatgpt-data-trigger/src/main/java/com/luckysj/chatgpt/data/trigger/http/dto/MessageEntity.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.trigger.http.dto; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | /** 9 | * @author www.luckysj.top 刘仕杰 10 | * @description 11 | * @create 2023/12/04 19:29:22 12 | */ 13 | @Data 14 | @Builder 15 | @AllArgsConstructor 16 | @NoArgsConstructor 17 | public class MessageEntity { 18 | 19 | private String role; 20 | private String content; 21 | private String name; 22 | 23 | } 24 | -------------------------------------------------------------------------------- /chatgpt-data-app/src/main/resources/application-prod.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 80 3 | 4 | # 应用配置 5 | app: 6 | config: 7 | # 版本,方便通过接口版本升级 8 | api-version: v1 9 | # 跨域,开发阶段可以设置为 * 不限制 10 | cross-origin: https://itedus.cn 11 | 12 | # ChatGPT SDK Config 13 | chatgpt: 14 | sdk: 15 | config: 16 | # 官网地址 https://api.openai.com/ 17 | api-host: https://api.openai.com/ 18 | # 官网申请 https://platform.openai.com/account/api-keys 19 | api-key: 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 20 | 21 | # 日志 22 | logging: 23 | level: 24 | root: info 25 | config: classpath:logback-spring.xml -------------------------------------------------------------------------------- /chatgpt-data-domain/src/main/java/com/luckysj/chatgpt/data/domain/openai/model/entity/MessageEntity.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.domain.openai.model.entity; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | /** 9 | * @author www.luckysj.top 刘仕杰 10 | * @description 消息实体 11 | * @create 2023/12/04 19:48:07 12 | */ 13 | @Data 14 | @Builder 15 | @AllArgsConstructor 16 | @NoArgsConstructor 17 | public class MessageEntity { 18 | 19 | private String role; 20 | private String content; 21 | private String name; 22 | 23 | } 24 | -------------------------------------------------------------------------------- /chatgpt-data-domain/src/main/java/com/luckysj/chatgpt/data/domain/weixin/model/valobj/ContentCodeVO.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.domain.weixin.model.valobj; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | import lombok.NoArgsConstructor; 6 | 7 | /** 8 | * @author www.luckysj.top 刘仕杰 9 | * @description 微信公众号用户消息行为特征码 10 | * @create 2023/12/17 21:17:38 11 | */ 12 | @Getter 13 | @NoArgsConstructor 14 | @AllArgsConstructor 15 | public enum ContentCodeVO { 16 | GENCODE("403","获取验证码"), 17 | GETOPENID("404","获取当前账户OpenId"); 18 | 19 | private String code; 20 | private String desc; 21 | } 22 | -------------------------------------------------------------------------------- /chatgpt-data-domain/src/main/java/com/luckysj/chatgpt/data/domain/weixin/model/valobj/MsgTypeVO.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.domain.weixin.model.valobj; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | import lombok.NoArgsConstructor; 6 | 7 | /** 8 | * @author www.luckysj.top 刘仕杰 9 | * @description 微信公众号消息类型值对象,用于描述对象属性的值,为值对象。 10 | * @create 2023/12/17 21:17:21 11 | */ 12 | @Getter 13 | @NoArgsConstructor 14 | @AllArgsConstructor 15 | public enum MsgTypeVO { 16 | 17 | EVENT("event","事件消息"), 18 | TEXT("text","文本消息"); 19 | 20 | private String code; 21 | private String desc; 22 | 23 | } 24 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 10 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /chatgpt-data-types/src/main/java/com/luckysj/chatgpt/data/types/model/Response.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.types.model; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | import java.io.Serializable; 9 | 10 | /** 11 | * @author www.luckysj.top 刘仕杰 12 | * @description 通用结果返回类 13 | * @create 2023/12/05 11:18:13 14 | */ 15 | @Data 16 | @Builder 17 | @NoArgsConstructor 18 | @AllArgsConstructor 19 | public class Response implements Serializable { 20 | 21 | private String code; 22 | private String info; 23 | private T data; 24 | 25 | } 26 | -------------------------------------------------------------------------------- /chatgpt-data-app/src/main/java/com/luckysj/chatgpt/data/Application.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.boot.context.properties.EnableConfigurationProperties; 6 | import org.springframework.scheduling.annotation.EnableScheduling; 7 | 8 | // 全局启动类 9 | @SpringBootApplication 10 | @EnableConfigurationProperties 11 | @EnableScheduling //启用定时任务 12 | public class Application { 13 | public static void main(String[] args) { 14 | SpringApplication.run(Application.class); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /chatgpt-data-domain/src/main/java/com/luckysj/chatgpt/data/domain/auth/model/entity/AuthStateEntity.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.domain.auth.model.entity; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | /** 9 | * @author www.luckysj.top 刘仕杰 10 | * @description 鉴权结果 11 | * @create 2023/12/05 11:26:32 12 | */ 13 | @Data 14 | @Builder 15 | @NoArgsConstructor 16 | @AllArgsConstructor 17 | public class AuthStateEntity { 18 | 19 | private String code; 20 | private String info; 21 | private String openId; 22 | private String token; 23 | 24 | } 25 | -------------------------------------------------------------------------------- /chatgpt-data-domain/src/main/java/com/luckysj/chatgpt/data/domain/weixin/service/IWeiXinBehaviorService.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.domain.weixin.service; 2 | 3 | 4 | import com.luckysj.chatgpt.data.domain.weixin.model.entity.UserBehaviorMessageEntity; 5 | 6 | /** 7 | * @author www.luckysj.top 刘仕杰 8 | * @description 受理用户行为接口 9 | * @create 2023/12/05 15:46:14 10 | */ 11 | public interface IWeiXinBehaviorService { 12 | 13 | /** 14 | * @description 受理公众号用户行为 15 | * @param 16 | * @return 17 | * @date 2023/12/05 15:49:34 18 | */ 19 | String acceptUserBehavior(UserBehaviorMessageEntity userBehaviorMessageEntity); 20 | 21 | } 22 | -------------------------------------------------------------------------------- /chatgpt-data-domain/src/main/java/com/luckysj/chatgpt/data/domain/openai/repository/IOpenAiRepository.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.domain.openai.repository; 2 | 3 | import com.luckysj.chatgpt.data.domain.openai.model.entity.UserAccountQuotaEntity; 4 | 5 | /** 6 | * @author www.luckysj.top 刘仕杰 7 | * @description openai仓储接口 8 | * @create 2023/12/09 17:10:50 9 | */ 10 | public interface IOpenAiRepository { 11 | 12 | int subAccountQuota(String openai); 13 | 14 | UserAccountQuotaEntity queryUserAccount(String openid); 15 | 16 | void putRedisVisitCount(String key, Integer value, Long time); 17 | 18 | int getRedisVisitCount(String key); 19 | 20 | } 21 | -------------------------------------------------------------------------------- /chatgpt-data-app/src/main/java/com/luckysj/chatgpt/data/config/ChatGLMSDKConfigProperties.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.config; 2 | 3 | import lombok.Data; 4 | import org.springframework.boot.context.properties.ConfigurationProperties; 5 | 6 | /** 7 | * @author www.luckysj.top 刘仕杰 8 | * @description ChatGLM会话工厂配置 9 | * @create 2023/12/15 18:35:48 10 | */ 11 | @Data 12 | @ConfigurationProperties(prefix = "chatglm.sdk.config", ignoreInvalidFields = true) 13 | public class ChatGLMSDKConfigProperties { 14 | 15 | /** 状态;open = 开启、close 关闭 */ 16 | private boolean enable; 17 | /** 转发地址 */ 18 | private String apiHost; 19 | /** 可以申请 sk-*** */ 20 | private String apiSecretKey; 21 | 22 | } 23 | -------------------------------------------------------------------------------- /chatgpt-data-domain/src/main/java/com/luckysj/chatgpt/data/domain/openai/model/entity/RuleLogicEntity.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.domain.openai.model.entity; 2 | 3 | import com.luckysj.chatgpt.data.domain.openai.model.valobj.LogicCheckTypeVO; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Builder; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | 9 | /** 10 | * @author www.luckysj.top 刘仕杰 11 | * @description 规则校验结果类 12 | * @create 2023/12/07 19:49:34 13 | */ 14 | @Data 15 | @NoArgsConstructor 16 | @AllArgsConstructor 17 | @Builder 18 | public class RuleLogicEntity { 19 | 20 | private LogicCheckTypeVO type; 21 | private String info; 22 | private T data; 23 | 24 | } 25 | -------------------------------------------------------------------------------- /chatgpt-data-domain/src/main/java/com/luckysj/chatgpt/data/domain/openai/annotation/LogicStrategy.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.domain.openai.annotation; 2 | 3 | import com.luckysj.chatgpt.data.domain.openai.service.rule.factory.DefaultLogicFactory; 4 | 5 | import java.lang.annotation.ElementType; 6 | import java.lang.annotation.Retention; 7 | import java.lang.annotation.RetentionPolicy; 8 | import java.lang.annotation.Target; 9 | 10 | @Target({ElementType.TYPE}) //指定注解可以应用的地方,ElementType.TYPE 表示该注解可以应用在类、接口(包括注解类型)或枚举声明 11 | @Retention(RetentionPolicy.RUNTIME) //指定注解的生命周期,即它在什么时候可用,RetentionPolicy.RUNTIME 表示在运行时可用 12 | public @interface LogicStrategy { 13 | DefaultLogicFactory.LogicModel logicMode(); 14 | 15 | } 16 | -------------------------------------------------------------------------------- /chatgpt-data-domain/src/main/java/com/luckysj/chatgpt/data/domain/openai/service/channel/service/IGenerativeModelService.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.domain.openai.service.channel.service; 2 | 3 | import com.fasterxml.jackson.core.JsonProcessingException; 4 | import com.luckysj.chatgpt.data.domain.openai.model.aggregates.ChatProcessAggregate; 5 | import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyEmitter; 6 | 7 | /** 8 | * @author www.luckysj.top 刘仕杰 9 | * @description 模型生成文字/图片服务 接口 10 | * @create 2023/12/18 17:48:24 11 | */ 12 | public interface IGenerativeModelService { 13 | void doMessageResponse(ChatProcessAggregate chatProcess, ResponseBodyEmitter emitter) throws Exception; 14 | } 15 | -------------------------------------------------------------------------------- /chatgpt-data-types/src/main/java/com/luckysj/chatgpt/data/types/annotation/AccessInterceptor.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.types.annotation; 2 | 3 | import java.lang.annotation.*; 4 | 5 | @Documented 6 | @Retention(RetentionPolicy.RUNTIME) 7 | @Target({ElementType.TYPE, ElementType.METHOD}) 8 | public @interface AccessInterceptor { 9 | 10 | /** 用哪个字段作为拦截标识符,配置all则是对整个接口限流,配置request_ip, 11 | * 则是对访问ip限流,配置其他str,则会到参数中寻找对应名称的属性值(包括对象内部属性) */ 12 | String key() default "all"; 13 | 14 | /** 限制频次(每秒请求次数) */ 15 | long permitsPerSecond(); 16 | 17 | /** 黑名单拦截(多少次限制后加入黑名单)0 不限制 */ 18 | double blacklistCount() default 0; 19 | 20 | /** 拦截后的执行方法 */ 21 | String fallbackMethod(); 22 | 23 | } 24 | -------------------------------------------------------------------------------- /chatgpt-data-domain/src/main/java/com/luckysj/chatgpt/data/domain/openai/service/channel/OpenAiGroupService.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.domain.openai.service.channel; 2 | 3 | import com.fasterxml.jackson.core.JsonProcessingException; 4 | import com.luckysj.chatgpt.data.domain.openai.model.aggregates.ChatProcessAggregate; 5 | import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyEmitter; 6 | 7 | /** 8 | * @author www.luckysj.top 刘仕杰 9 | * @description 渠道组,这里我们目前有两个渠道,分别是chatgpt和chatglm 10 | * @create 2023/12/15 17:35:37 11 | */ 12 | public interface OpenAiGroupService { 13 | 14 | void doMessageResponse(ChatProcessAggregate chatProcess, ResponseBodyEmitter emitter) throws Exception; 15 | 16 | } 17 | -------------------------------------------------------------------------------- /chatgpt-data-domain/src/main/java/com/luckysj/chatgpt/data/domain/order/model/valobj/AliPayTradeTypeVo.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.domain.order.model.valobj; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | 6 | /** 7 | * @author www.luckysj.top 刘仕杰 8 | * @description 支付通知结果交易状态,默认通知只有TRADE_SUCCESS会触发 9 | * @create 2023/12/15 12:30:19 10 | */ 11 | @Getter 12 | @AllArgsConstructor 13 | public enum AliPayTradeTypeVo { 14 | WAIT_BUYER_PAY("WAIT_BUYER_PAY", "交易创建,等待买家付款。"), 15 | TRADE_CLOSED("TRADE_CLOSED","未付款交易超时关闭,或支付完成后全额退款。"), 16 | TRADE_SUCCESS("TRADE_SUCCESS","交易支付成功。"), 17 | TRADE_FINISHED("TRADE_FINISHED","交易结束,不可退款。"), 18 | ; 19 | 20 | private final String code; 21 | private final String desc; 22 | } 23 | -------------------------------------------------------------------------------- /chatgpt-data-app/src/main/java/com/luckysj/chatgpt/data/config/ChatGPTSDKConfigProperties.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.config; 2 | 3 | import lombok.Data; 4 | import org.springframework.boot.context.properties.ConfigurationProperties; 5 | import org.springframework.context.annotation.Configuration; 6 | 7 | /** 8 | * @author www.luckysj.top 刘仕杰 9 | * @description ChatGPT会话工厂配置 10 | * @create 2023-12-04 18:53 11 | */ 12 | @Data 13 | @ConfigurationProperties(prefix = "chatgpt.sdk.config", ignoreInvalidFields = true) 14 | public class ChatGPTSDKConfigProperties { 15 | /** openai 代理地址 */ 16 | private String apiHost; 17 | /** apikey,*/ 18 | private String apiKey; 19 | /** 验证tokne(这里暂时用不到,openai不需要,如果需要中转服务进行会话前认证可以使用该字段) */ 20 | private String authToken; 21 | } 22 | -------------------------------------------------------------------------------- /chatgpt-data-trigger/src/main/java/com/luckysj/chatgpt/data/trigger/http/dto/ChatGPTRequestDTO.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.trigger.http.dto; 2 | 3 | import com.luckysj.chatgpt.data.types.enums.ChatGPTModel; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Builder; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | 9 | import java.util.List; 10 | 11 | /** 12 | * @author www.luckysj.top 刘仕杰 13 | * @description gpt服务请求dto 14 | * @create 2023/12/04 19:29:22 15 | */ 16 | @Data 17 | @Builder 18 | @NoArgsConstructor 19 | @AllArgsConstructor 20 | public class ChatGPTRequestDTO { 21 | 22 | /** 默认模型 */ 23 | private String model = ChatGPTModel.GPT_3_5_TURBO.getCode(); 24 | 25 | /** 问题描述 */ 26 | private List messages; 27 | 28 | } 29 | -------------------------------------------------------------------------------- /chatgpt-data-domain/src/main/java/com/luckysj/chatgpt/data/domain/weixin/model/entity/UserBehaviorMessageEntity.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.domain.weixin.model.entity; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | import java.util.Date; 9 | 10 | /** 11 | * @author Fuzhengwei bugstack.cn @小傅哥 12 | * @description 用户行为信息 13 | * @create 2023-08-05 17:11 14 | */ 15 | @Data 16 | @Builder 17 | @NoArgsConstructor 18 | @AllArgsConstructor 19 | public class UserBehaviorMessageEntity { 20 | 21 | private String openId; 22 | private String fromUserName; 23 | private String msgType; 24 | private String content; 25 | private String event; 26 | private Date createTime; 27 | 28 | } 29 | -------------------------------------------------------------------------------- /chatgpt-data-domain/src/main/java/com/luckysj/chatgpt/data/domain/order/model/valobj/PayTypeVO.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.domain.order.model.valobj; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | 6 | /** 7 | * @author www.luckysj.top 刘仕杰 8 | * @description 支付类型 9 | * @create 2023/12/10 17:31:28 10 | */ 11 | @Getter 12 | @AllArgsConstructor 13 | public enum PayTypeVO { 14 | 15 | ZHIFUBAO_SHAHE(0, "支付宝支付"), 16 | ; 17 | 18 | private final Integer code; 19 | private final String desc; 20 | 21 | public static PayTypeVO get(Integer code){ 22 | switch (code){ 23 | case 0: 24 | return PayTypeVO.ZHIFUBAO_SHAHE; 25 | default: 26 | return PayTypeVO.ZHIFUBAO_SHAHE; 27 | } 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /chatgpt-data-types/src/main/java/com/luckysj/chatgpt/data/types/enums/OpenAiChannel.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.types.enums; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | 6 | /** 7 | * @author Fuzhengwei bugstack.cn @小傅哥 8 | * @description OepnAi 渠道 9 | * @create 2023-10-15 14:01 10 | */ 11 | @Getter 12 | @AllArgsConstructor 13 | public enum OpenAiChannel { 14 | 15 | ChatGLM("ChatGLM"), 16 | ChatGPT("ChatGPT"), 17 | 18 | ; 19 | private final String code; 20 | 21 | public static OpenAiChannel getChannel(String model) { 22 | if (model.toLowerCase().contains("gpt") || model.toLowerCase().contains("dall")) return OpenAiChannel.ChatGPT; 23 | if (model.toLowerCase().contains("glm")) return OpenAiChannel.ChatGLM; 24 | return null; 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /chatgpt-data-domain/src/main/java/com/luckysj/chatgpt/data/domain/order/model/aggregates/CreateOrderAggregate.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.domain.order.model.aggregates; 2 | 3 | import com.luckysj.chatgpt.data.domain.order.model.entity.OrderEntity; 4 | import com.luckysj.chatgpt.data.domain.order.model.entity.ProductEntity; 5 | import lombok.AllArgsConstructor; 6 | import lombok.Builder; 7 | import lombok.Data; 8 | import lombok.NoArgsConstructor; 9 | 10 | /** 11 | * @author www.luckysj.top 刘仕杰 12 | * @description 下单聚合对象 13 | * @create 2023/12/10 17:28:42 14 | */ 15 | @Data 16 | @Builder 17 | @AllArgsConstructor 18 | @NoArgsConstructor 19 | public class CreateOrderAggregate { 20 | 21 | /** 用户ID;微信用户唯一标识 */ 22 | private String openid; 23 | /** 商品 */ 24 | private ProductEntity product; 25 | /** 订单 */ 26 | private OrderEntity order; 27 | 28 | } 29 | -------------------------------------------------------------------------------- /chatgpt-data-infrastructure/src/main/java/com/luckysj/chatgpt/data/infrastructure/dao/IOpenAIProductDao.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.infrastructure.dao; 2 | 3 | import com.luckysj.chatgpt.data.infrastructure.po.OpenAIProductPO; 4 | import org.apache.ibatis.annotations.Mapper; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * @author www.luckysj.top 刘仕杰 10 | * @description 商品dao 11 | * @create 2023/12/10 11:15:09 12 | */ 13 | @Mapper 14 | public interface IOpenAIProductDao { 15 | 16 | /** 17 | * @description 根据id查询商品 18 | * @param productId 19 | * @return OpenAIProductPO 商品实体类 20 | * @date 2023/12/10 16:38:27 21 | */ 22 | OpenAIProductPO queryProductByProductId(Integer productId); 23 | 24 | /** 25 | * @description 获取商品列表 26 | * @param 27 | * @return 商品数组信息 28 | * @date 2023/12/10 16:38:57 29 | */ 30 | List queryProductList(); 31 | } 32 | -------------------------------------------------------------------------------- /chatgpt-data-types/src/main/java/com/luckysj/chatgpt/data/types/exception/ChatGPTException.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.types.exception; 2 | /** 3 | * @author www.luckysj.top 刘仕杰 4 | * @description chatgpt会话异常 5 | * @create 2023/12/04 19:31:16 6 | */ 7 | public class ChatGPTException extends RuntimeException{ 8 | // 异常编码 9 | private String code; 10 | // 异常信息 11 | private String message; 12 | 13 | public ChatGPTException(String code) { 14 | this.code = code; 15 | } 16 | 17 | public ChatGPTException(String code, String message) { 18 | super(message); 19 | this.code = code; 20 | this.message = message; 21 | } 22 | 23 | public ChatGPTException(String code, String message, Throwable cause) { 24 | super(message); 25 | super.initCause(cause); 26 | this.code = code; 27 | this.message = message; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /chatgpt-data-domain/src/main/java/com/luckysj/chatgpt/data/domain/weixin/service/validate/WeiXinValidateService.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.domain.weixin.service.validate; 2 | 3 | import com.luckysj.chatgpt.data.domain.weixin.service.IWeiXinValidateService; 4 | import com.luckysj.chatgpt.data.types.sdk.weixin.SignatureUtil; 5 | import org.springframework.beans.factory.annotation.Value; 6 | import org.springframework.stereotype.Service; 7 | 8 | /** 9 | * @author www.luckysj.top 刘仕杰 10 | * @description 验签接口实现类 11 | * @create 2023/12/05 15:46:56 12 | */ 13 | @Service 14 | public class WeiXinValidateService implements IWeiXinValidateService { 15 | 16 | @Value("${wx.config.token}") 17 | private String token; 18 | 19 | @Override 20 | public boolean checkSign(String signature, String timestamp, String nonce) { 21 | return SignatureUtil.check(token, signature, timestamp, nonce); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /.idea/jarRepositories.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | 19 | 20 | -------------------------------------------------------------------------------- /chatgpt-data-trigger/src/main/java/com/luckysj/chatgpt/data/trigger/http/dto/SaleProductDTO.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.trigger.http.dto; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | import java.math.BigDecimal; 9 | 10 | /** 11 | * @author www.luckysj.top 刘仕杰 12 | * @description 商品对象dto 13 | * @create 2023/12/10 14:24:14 14 | */ 15 | @Data 16 | @Builder 17 | @AllArgsConstructor 18 | @NoArgsConstructor 19 | public class SaleProductDTO { 20 | 21 | /** 22 | * 商品ID 23 | */ 24 | private Integer productId; 25 | /** 26 | * 商品名称 27 | */ 28 | private String productName; 29 | /** 30 | * 商品描述 31 | */ 32 | private String productDesc; 33 | /** 34 | * 额度次数 35 | */ 36 | private Integer quota; 37 | /** 38 | * 商品价格 39 | */ 40 | private BigDecimal price; 41 | 42 | } 43 | -------------------------------------------------------------------------------- /chatgpt-data-domain/src/main/java/com/luckysj/chatgpt/data/domain/auth/repository/IAuthRepository.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.domain.auth.repository; 2 | 3 | /** 4 | * @author www.luckysj.top 刘仕杰 5 | * @description 用户账户仓储服务 6 | * @create 2023/12/10 16:48:59 7 | */ 8 | public interface IAuthRepository { 9 | /** 10 | * @description 新增账户 11 | * @param 12 | * @return 13 | * @date 2023/12/10 16:50:38 14 | */ 15 | boolean insertUserIfNotExist(String openid); 16 | 17 | /** 18 | * @description 获取验证码 19 | * @date 2023/12/17 21:30:03 20 | */ 21 | String getCodeUserOpenId(String code); 22 | 23 | /** 24 | * @description 通过openid删除验证码 25 | * @date 2023/12/17 21:30:03 26 | */ 27 | void removeCodeByOpenId(String code, String openId); 28 | 29 | /** 30 | * @description 本地测试生成验证码 31 | * @date 2023/12/17 21:38:32 32 | */ 33 | String genCodeTest(String openid); 34 | } 35 | -------------------------------------------------------------------------------- /chatgpt-data-domain/src/main/java/com/luckysj/chatgpt/data/domain/openai/model/valobj/UserAccountStatusVO.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.domain.openai.model.valobj; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | 6 | /** 7 | * @author www.luckysj.top 刘仕杰 8 | * @description 账户状态 9 | * @create 2023/12/09 17:10:09 10 | */ 11 | @Getter 12 | @AllArgsConstructor 13 | public enum UserAccountStatusVO { 14 | 15 | AVAILABLE(0, "可用"), 16 | FREEZE(1,"冻结"), 17 | ; 18 | 19 | private final Integer code; 20 | private final String info; 21 | 22 | public static UserAccountStatusVO get(Integer code){ 23 | switch (code){ 24 | case 0: 25 | return UserAccountStatusVO.AVAILABLE; 26 | case 1: 27 | return UserAccountStatusVO.FREEZE; 28 | default: 29 | return UserAccountStatusVO.AVAILABLE; 30 | } 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /chatgpt-data-domain/src/main/java/com/luckysj/chatgpt/data/domain/order/model/entity/ShopCartEntity.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.domain.order.model.entity; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | /** 9 | * @author www.luckysj.top 刘仕杰 10 | * @description 简单购物车实体对象;大型电商中,购物车还会包括;商品类型【实物/虚拟】、商品数量、优惠信息、配送信息、增值服务 11 | * @create 2023/12/10 17:30:43 12 | */ 13 | @Data 14 | @Builder 15 | @AllArgsConstructor 16 | @NoArgsConstructor 17 | public class ShopCartEntity { 18 | 19 | /** 20 | * 用户微信唯一ID 21 | */ 22 | private String openid; 23 | 24 | /** 25 | * 商品ID 26 | */ 27 | private Integer productId; 28 | 29 | @Override 30 | public String toString() { 31 | return "ShopCartEntity{" + 32 | "openid='" + openid + '\'' + 33 | ", productId=" + productId + 34 | '}'; 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /chatgpt-data-types/src/main/java/com/luckysj/chatgpt/data/types/sdk/weixin/Article.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.types.sdk.weixin; 2 | 3 | public class Article { 4 | 5 | private String title; 6 | private String description; 7 | private String picUrl; 8 | private String url; 9 | 10 | public String getTitle() { 11 | return title; 12 | } 13 | 14 | public void setTitle(String title) { 15 | this.title = title; 16 | } 17 | 18 | public String getDescription() { 19 | return description; 20 | } 21 | 22 | public void setDescription(String description) { 23 | this.description = description; 24 | } 25 | 26 | public String getPicUrl() { 27 | return picUrl; 28 | } 29 | 30 | public void setPicUrl(String picUrl) { 31 | this.picUrl = picUrl; 32 | } 33 | 34 | public String getUrl() { 35 | return url; 36 | } 37 | 38 | public void setUrl(String url) { 39 | this.url = url; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /chatgpt-data-app/src/main/java/com/luckysj/chatgpt/data/config/AliPayClientConfig.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.config; 2 | 3 | import com.alipay.api.AlipayClient; 4 | import com.alipay.api.DefaultAlipayClient; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | 8 | /** 9 | * @author www.luckysj.top 刘仕杰 10 | * @description 支付宝支付配置 11 | * @create 2023/12/15 11:04:06 12 | */ 13 | @Configuration 14 | public class AliPayClientConfig { 15 | 16 | /** 17 | * @description 支付宝请求客户端 18 | * @param aliPayConfig 支付配置 19 | * @date 2023/12/15 11:22:14 20 | */ 21 | @Bean 22 | public AlipayClient alipayClient(AliPayConfigProperties aliPayConfig){ 23 | return new DefaultAlipayClient(aliPayConfig.getUrl(), aliPayConfig.getAppId(), 24 | aliPayConfig.getApp_private_key(), aliPayConfig.FORMAT, aliPayConfig.CHARSET, 25 | aliPayConfig.getAlipay_public_key(), aliPayConfig.SIGNTYPE); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /chatgpt-data-domain/src/main/java/com/luckysj/chatgpt/data/domain/order/model/entity/OrderEntity.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.domain.order.model.entity; 2 | 3 | import com.luckysj.chatgpt.data.domain.order.model.valobj.OrderStatusVO; 4 | import com.luckysj.chatgpt.data.domain.order.model.valobj.PayTypeVO; 5 | import lombok.AllArgsConstructor; 6 | import lombok.Builder; 7 | import lombok.Data; 8 | import lombok.NoArgsConstructor; 9 | 10 | import java.math.BigDecimal; 11 | import java.util.Date; 12 | 13 | /** 14 | * @author www.luckysj.top 刘仕杰 15 | * @description 订单实体对象 16 | * @create 2023/12/10 17:29:42 17 | */ 18 | @Data 19 | @Builder 20 | @AllArgsConstructor 21 | @NoArgsConstructor 22 | public class OrderEntity { 23 | 24 | /** 订单编号 */ 25 | private String orderId; 26 | /** 下单时间 */ 27 | private Date orderTime; 28 | /** 订单状态;0-创建完成、1-等待发货、2-发货完成、3-系统关单 */ 29 | private OrderStatusVO orderStatus; 30 | /** 订单金额 */ 31 | private BigDecimal totalAmount; 32 | /** 支付类型 */ 33 | private PayTypeVO payTypeVO; 34 | 35 | } 36 | -------------------------------------------------------------------------------- /chatgpt-data-infrastructure/src/main/java/com/luckysj/chatgpt/data/infrastructure/dao/IOpenAIOrderDao.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.infrastructure.dao; 2 | 3 | import com.luckysj.chatgpt.data.infrastructure.po.OpenAIOrderPO; 4 | import org.apache.ibatis.annotations.Mapper; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * @author www.luckysj.top 刘仕杰 10 | * @description 订单dao 11 | * @create 2023/12/10 11:17:19 12 | */ 13 | @Mapper 14 | public interface IOpenAIOrderDao { 15 | OpenAIOrderPO queryUnpaidOrder(OpenAIOrderPO openAIOrderPOReq); 16 | 17 | void insert(OpenAIOrderPO order); 18 | 19 | void updateOrderPayInfo(OpenAIOrderPO openAIOrderPO); 20 | 21 | int changeOrderPaySuccess(OpenAIOrderPO openAIOrderPO); 22 | 23 | OpenAIOrderPO queryOrder(String orderId); 24 | 25 | int updateOrderStatusDeliverGoods(String orderId); 26 | 27 | List queryReplenishmentOrder(); 28 | 29 | List queryNoPayNotifyOrder(); 30 | 31 | List queryTimeoutCloseOrderList(); 32 | 33 | boolean changeOrderClose(String orderId); 34 | } 35 | -------------------------------------------------------------------------------- /chatgpt-data-domain/src/main/java/com/luckysj/chatgpt/data/domain/order/model/valobj/PayStatusVO.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.domain.order.model.valobj; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | 6 | /** 7 | * @author www.luckysj.top 刘仕杰 8 | * @description 支付状态 9 | * @create 2023/12/10 17:31:20 10 | */ 11 | @Getter 12 | @AllArgsConstructor 13 | public enum PayStatusVO { 14 | 15 | WAIT(0, "等待支付"), 16 | SUCCESS(1,"支付完成"), 17 | FAIL(2,"支付失败"), 18 | ABANDON(3,"放弃支付"), 19 | ; 20 | 21 | private final Integer code; 22 | private final String desc; 23 | 24 | public static PayStatusVO get(Integer code){ 25 | switch (code){ 26 | case 0: 27 | return PayStatusVO.WAIT; 28 | case 1: 29 | return PayStatusVO.SUCCESS; 30 | case 2: 31 | return PayStatusVO.FAIL; 32 | case 3: 33 | return PayStatusVO.ABANDON; 34 | default: 35 | return PayStatusVO.WAIT; 36 | } 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /chatgpt-data-types/src/main/java/com/luckysj/chatgpt/data/types/enums/OpenAIProductEnableModel.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.types.enums; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | 6 | /** 7 | * @author Fuzhengwei bugstack.cn @小傅哥 8 | * @description 9 | * @create 2023-09-28 20:58 10 | */ 11 | @Getter 12 | @AllArgsConstructor 13 | public enum OpenAIProductEnableModel { 14 | 15 | CLOSE(0, "无效,已关闭"), 16 | OPEN(1,"有效,使用中"), 17 | ; 18 | 19 | private final Integer code; 20 | 21 | private final String info; 22 | 23 | public static com.luckysj.chatgpt.data.types.enums.OpenAIProductEnableModel get(Integer code){ 24 | switch (code){ 25 | case 0: 26 | return com.luckysj.chatgpt.data.types.enums.OpenAIProductEnableModel.CLOSE; 27 | case 1: 28 | return com.luckysj.chatgpt.data.types.enums.OpenAIProductEnableModel.OPEN; 29 | default: 30 | return com.luckysj.chatgpt.data.types.enums.OpenAIProductEnableModel.CLOSE; 31 | } 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /chatgpt-data-domain/src/main/java/com/luckysj/chatgpt/data/domain/order/model/valobj/OrderStatusVO.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.domain.order.model.valobj; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | 6 | /** 7 | * @author www.luckysj.top 刘仕杰 8 | * @description 订单状态 9 | * @create 2023/12/10 17:31:10 10 | */ 11 | @Getter 12 | @AllArgsConstructor 13 | public enum OrderStatusVO { 14 | 15 | CREATE(0, "创建完成"), 16 | WAIT(1,"等待发货"), 17 | COMPLETED(2,"发货完成"), 18 | CLOSE(3,"系统关单"), 19 | ; 20 | 21 | private final Integer code; 22 | private final String desc; 23 | 24 | public static OrderStatusVO get(Integer code){ 25 | switch (code){ 26 | case 0: 27 | return OrderStatusVO.CREATE; 28 | case 1: 29 | return OrderStatusVO.WAIT; 30 | case 2: 31 | return OrderStatusVO.COMPLETED; 32 | case 3: 33 | return OrderStatusVO.CLOSE; 34 | default: 35 | return OrderStatusVO.CREATE; 36 | } 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /chatgpt-data-types/src/main/java/com/luckysj/chatgpt/data/types/common/Constants.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.types.common; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | import lombok.NoArgsConstructor; 6 | 7 | public class Constants { 8 | // 白名单分隔符 9 | public final static String SPLIT = ","; 10 | 11 | @AllArgsConstructor 12 | @NoArgsConstructor 13 | @Getter 14 | public enum ResponseCode { 15 | SUCCESS("0000", "成功"), 16 | UN_ERROR("0001", "未知错误"), 17 | ILLEGAL_PARAMETER("0002", "非法参数"), 18 | TOKEN_ERROR("0003", "权限拦截"), 19 | ORDER_PRODUCT_ERR("OE001", "所购商品已下线,请重新选择下单商品"), 20 | FREQUENCY_LIMITED("O004", "访问频率过高,请稍后重试"); 21 | 22 | private String code; 23 | private String info; 24 | 25 | } 26 | 27 | public static class MessageQueueKey { 28 | public static String DeliveryExchange = "chatgpt.quate.delivery.exchange"; 29 | public static String DeliveryQueue = "chatgpt.quate.delivery.queue"; 30 | public static String DeliveryKey = "chatgpt.quate.deliverye.key"; 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /chatgpt-data-domain/src/main/java/com/luckysj/chatgpt/data/domain/auth/service/IAuthService.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.domain.auth.service; 2 | 3 | import com.luckysj.chatgpt.data.domain.auth.model.entity.AuthStateEntity; 4 | 5 | /** 6 | * @author www.luckysj.top 刘仕杰 7 | * @description 登录授权服务接口 8 | * @create 2023/12/05 14:55:39 9 | */ 10 | public interface IAuthService { 11 | /** 12 | * @description 用户登录校验 13 | * @param code 验证码 14 | * @return AuthStateEntity 校验状态 15 | * @date 2023/12/05 14:59:15 16 | */ 17 | AuthStateEntity doLogin(String code); 18 | 19 | /** 20 | * @description token校验 21 | * @param token 22 | * @return Boolean 23 | * @date 2023/12/05 15:00:10 24 | */ 25 | boolean checkToken(String token); 26 | 27 | /** 28 | * @description 解析token获取openid 29 | * @param token 30 | * @return String openid 31 | * @date 2023/12/09 19:37:18 32 | */ 33 | String parseOpenid(String token); 34 | 35 | /** 36 | * @description 本地获取验证码 37 | * @return 38 | * @date 2023/12/05 15:00:10 39 | */ 40 | String getAuthCode(String openid); 41 | } 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 刘仕杰 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /chatgpt-data-app/src/main/resources/application-test.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8090 3 | 4 | # 应用配置 5 | app: 6 | config: 7 | # 版本,方便通过接口版本升级 8 | api-version: v1 9 | # 跨域,开发阶段可以设置为 * 不限制 10 | cross-origin: * 11 | 12 | # 线程池配置 13 | thread: 14 | pool: 15 | executor: 16 | config: 17 | core-pool-size: 20 18 | max-pool-size: 50 19 | keep-alive-time: 5000 20 | block-queue-size: 5000 21 | policy: CallerRunsPolicy 22 | 23 | # ChatGPT SDK Config 24 | chatgpt: 25 | sdk: 26 | config: 27 | # 官网地址 https://api.openai.com/ 28 | api-host: https://api.xfg.im/b8b6/ 29 | # 官网申请 https://platform.openai.com/account/api-keys 30 | api-key: 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 31 | # 自主申请 http://api.xfg.im:8080/authorize?username=xfg&password=123 32 | auth-token: eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ4ZmciLCJleHAiOjE2OTAwMzI5MDQsImlhdCI6MTY5MDAyOTMwNCwianRpIjoiOTBkNzc5NjYtOWM5MS00MGVmLWJmNjktNzYzNDljNmUyMDkyIiwidXNlcm5hbWUiOiJ4ZmcifQ.x7P6Rc249SgbxpqWhgKahYP8A8AllNI_26DV08a1AZs 33 | 34 | # 日志 35 | logging: 36 | level: 37 | root: info 38 | config: classpath:logback-spring.xml -------------------------------------------------------------------------------- /chatgpt-data-domain/src/main/java/com/luckysj/chatgpt/data/domain/order/model/entity/UnpaidOrderEntity.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.domain.order.model.entity; 2 | 3 | import com.luckysj.chatgpt.data.domain.order.model.valobj.PayStatusVO; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Builder; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | 9 | import java.math.BigDecimal; 10 | 11 | /** 12 | * @author www.luckysj.top 刘仕杰 13 | * @description 未支付的有效订单实体 14 | * @create 2023/12/10 17:31:00 15 | */ 16 | @Data 17 | @Builder 18 | @AllArgsConstructor 19 | @NoArgsConstructor 20 | public class UnpaidOrderEntity { 21 | 22 | /** 23 | * 用户ID;微信分配的唯一ID编码 24 | */ 25 | private String openid; 26 | /** 27 | * 订单ID 28 | */ 29 | private String orderId; 30 | /** 31 | * 订单金额 32 | */ 33 | private BigDecimal totalAmount; 34 | /** 35 | * 商品名称 36 | */ 37 | private String productName; 38 | /** 39 | * 支付地址;创建支付后,获得的URL地址 40 | */ 41 | private String payUrl; 42 | /** 43 | * 支付状态;0-等待支付、1-支付完成、2-支付失败、3-放弃支付 44 | */ 45 | private PayStatusVO payStatus; 46 | 47 | } 48 | -------------------------------------------------------------------------------- /chatgpt-data-app/src/main/java/com/luckysj/chatgpt/data/config/AliPayConfigProperties.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.config; 2 | 3 | import lombok.Data; 4 | import org.springframework.boot.context.properties.ConfigurationProperties; 5 | import org.springframework.context.annotation.Configuration; 6 | 7 | /** 8 | * @author www.luckysj.top 刘仕杰 9 | * @description 支付配置信息 10 | * @create 2023/12/15 11:05:20 11 | */ 12 | @Data 13 | @Configuration 14 | @ConfigurationProperties(prefix = "pay.alipay", ignoreInvalidFields = true) 15 | public class AliPayConfigProperties { 16 | /** appid*/ 17 | private String appId; 18 | /** 应用私钥*/ 19 | private String app_private_key; 20 | /** 支付公钥*/ 21 | private String alipay_public_key; 22 | /** 支付宝网关地址*/ 23 | private String url; 24 | /** 二维码跳转地址*/ 25 | private String qrcode_url; 26 | /** 支付结果通知地址*/ 27 | private String notify_url; 28 | /** 编码*/ 29 | public static String CHARSET = "UTF-8"; 30 | /** 返回格式*/ 31 | public static String FORMAT = "json"; 32 | /** 日志记录目录*/ 33 | public static String log_path = "/log"; 34 | /** RSA2*/ 35 | public static String SIGNTYPE = "RSA2"; 36 | } 37 | -------------------------------------------------------------------------------- /chatgpt-data-trigger/src/main/java/com/luckysj/chatgpt/data/trigger/mq/OrderPaySuccessListener.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.trigger.mq; 2 | 3 | import com.google.common.eventbus.Subscribe; 4 | import com.luckysj.chatgpt.data.domain.order.service.IOrderService; 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.springframework.stereotype.Component; 7 | 8 | import javax.annotation.Resource; 9 | 10 | /** 11 | * @author www.luckysj.top 刘仕杰 12 | * @description 订单支付成功监听(Guava) 13 | * 1. 订单支付成功回调,最好是快速变更订单状态,避免超时重试次数上限后不能做业务。所以推送出MQ消息来做【发货】流程 14 | * 2. 因为ChatGPT项目目前规格较小,选择了轻量的技术栈,所以使用 Guava 的 EventBus 消息总线来模拟消息使用。如果你后续的场景较大,也可以替换为 RocketMQ 15 | * @create 2023/12/15 15:13:19 16 | */ 17 | @Slf4j 18 | // @Component 19 | public class OrderPaySuccessListener { 20 | 21 | @Resource 22 | private IOrderService orderService; 23 | 24 | @Subscribe 25 | public void handleEvent(String orderId) { 26 | try { 27 | log.info("支付完成,开始发货。订单:{}", orderId); 28 | orderService.deliverGoods(orderId); 29 | log.info("支付完成,发货完成。订单:{}", orderId); 30 | } catch (Exception e) { 31 | log.error("支付完成,发货失败。订单:{}", orderId, e); 32 | } 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /chatgpt-data-trigger/src/main/java/com/luckysj/chatgpt/data/trigger/mq/OrderPaySuccessRedisListener.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.trigger.mq; 2 | 3 | import com.luckysj.chatgpt.data.domain.order.service.IOrderService; 4 | import com.luckysj.chatgpt.data.types.annotation.RedisTopic; 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.redisson.api.listener.MessageListener; 7 | import org.springframework.stereotype.Service; 8 | 9 | import javax.annotation.Resource; 10 | 11 | /** 12 | * @author www.luckysj.top 刘仕杰 13 | * @description 支付完成后发货消息监听器(Redisson) 14 | * @create 2023/12/17 22:12:22 15 | */ 16 | @Slf4j 17 | @Service() 18 | @RedisTopic(topic = "delivery") 19 | public class OrderPaySuccessRedisListener implements MessageListener { 20 | @Resource 21 | private IOrderService orderService; 22 | 23 | @Override 24 | public void onMessage(CharSequence charSequence, String orderId) { 25 | try { 26 | log.info("支付完成,开始发货。订单:{}", orderId); 27 | orderService.deliverGoods(orderId); 28 | log.info("支付完成,发货完成。订单:{}", orderId); 29 | } catch (Exception e) { 30 | log.error("支付完成,发货失败。订单:{}", orderId, e); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /chatgpt-data-app/src/main/java/com/luckysj/chatgpt/data/config/ThreadPoolConfigProperties.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.config; 2 | 3 | import lombok.Data; 4 | import org.springframework.boot.context.properties.ConfigurationProperties; 5 | import org.springframework.context.annotation.Configuration; 6 | import org.springframework.context.annotation.Primary; 7 | 8 | /** 9 | * @author www.luckysj.top 刘仕杰 10 | * @description ChatGPTSDKConfig ChatGPTSDK配置信息 11 | * @create 2023/12/04 19:01:57 12 | */ 13 | @Data 14 | @Primary 15 | @Configuration 16 | @ConfigurationProperties(prefix = "thread.pool.executor.config", ignoreInvalidFields = true) 17 | public class ThreadPoolConfigProperties { 18 | /** 核心线程数 */ 19 | private Integer corePoolSize = 20; 20 | /** 最大线程数 */ 21 | private Integer maxPoolSize = 200; 22 | /** 最大等待时间 */ 23 | private Long keepAliveTime = 10L; 24 | /** 最大队列数 */ 25 | private Integer blockQueueSize = 5000; 26 | /* 27 | * AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。 28 | * DiscardPolicy:直接丢弃任务,但是不会抛出异常 29 | * DiscardOldestPolicy:将最早进入队列的任务删除,之后再尝试加入队列的任务被拒绝 30 | * CallerRunsPolicy:如果任务添加线程池失败,那么主线程自己执行该任务 31 | * */ 32 | private String policy = "AbortPolicy"; 33 | } 34 | -------------------------------------------------------------------------------- /chatgpt-data-infrastructure/src/main/java/com/luckysj/chatgpt/data/infrastructure/po/UserAccountPO.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.infrastructure.po; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | import java.util.Date; 9 | 10 | /** 11 | * @author www.luckysj.top 刘仕杰 12 | * @description 用户账户持久化对象 13 | * @create 2023/12/09 16:58:24 14 | */ 15 | @Data 16 | @Builder 17 | @NoArgsConstructor 18 | @AllArgsConstructor 19 | public class UserAccountPO { 20 | /** 21 | * 自增ID 22 | */ 23 | private Long id; 24 | /** 25 | * 用户ID;这里用的是微信ID作为唯一ID,你也可以给用户创建唯一ID,之后绑定微信ID 26 | */ 27 | private String openid; 28 | /** 29 | * 总量额度 30 | */ 31 | private Integer totalQuota; 32 | /** 33 | * 剩余额度 34 | */ 35 | private Integer surplusQuota; 36 | /** 37 | * 可用模型;gpt-3.5-turbo,gpt-3.5-turbo-16k,gpt-4,gpt-4-32k 38 | */ 39 | private String modelTypes; 40 | /** 41 | * 账户状态;0-可用、1-冻结 42 | */ 43 | private Integer status; 44 | /** 45 | * 创建时间 46 | */ 47 | private Date createTime; 48 | /** 49 | * 更新时间 50 | */ 51 | private Date updateTime; 52 | } 53 | -------------------------------------------------------------------------------- /chatgpt-data-app/src/main/java/com/luckysj/chatgpt/data/config/RedisCientConfigProperties.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.config; 2 | 3 | import lombok.Data; 4 | import org.springframework.boot.context.properties.ConfigurationProperties; 5 | 6 | /** 7 | * @author www.luckysj.top 刘仕杰 8 | * @description redis连接配置 9 | * @create 2023/12/17 20:48:13 10 | */ 11 | @Data 12 | @ConfigurationProperties(prefix = "redis.sdk.config", ignoreInvalidFields = true) 13 | public class RedisCientConfigProperties { 14 | /** host:ip */ 15 | private String host; 16 | /** 端口 */ 17 | private int port; 18 | /** 账密 */ 19 | private String password; 20 | /** 设置连接池的大小,默认为64 */ 21 | private int poolSize = 64; 22 | /** 设置连接池的最小空闲连接数,默认为10 */ 23 | private int minIdleSize = 10; 24 | /** 设置连接的最大空闲时间(单位:毫秒),超过该时间的空闲连接将被关闭,默认为10000 */ 25 | private int idleTimeout = 10000; 26 | /** 设置连接超时时间(单位:毫秒),默认为10000 */ 27 | private int connectTimeout = 10000; 28 | /** 设置连接重试次数,默认为3 */ 29 | private int retryAttempts = 3; 30 | /** 设置连接重试的间隔时间(单位:毫秒),默认为1000 */ 31 | private int retryInterval = 1000; 32 | /** 设置定期检查连接是否可用的时间间隔(单位:毫秒),默认为0,表示不进行定期检查 */ 33 | private int pingInterval = 0; 34 | /** 设置是否保持长连接,默认为true */ 35 | private boolean keepAlive = true; 36 | } 37 | -------------------------------------------------------------------------------- /chatgpt-data-infrastructure/src/main/java/com/luckysj/chatgpt/data/infrastructure/dao/IUserAccountDao.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.infrastructure.dao; 2 | 3 | import com.luckysj.chatgpt.data.infrastructure.po.UserAccountPO; 4 | import org.apache.ibatis.annotations.Mapper; 5 | 6 | /** 7 | * @author www.luckysj.top 刘仕杰 8 | * @description 用户账户dao 9 | * @create 2023/12/09 17:02:22 10 | */ 11 | @Mapper 12 | public interface IUserAccountDao { 13 | /** 14 | * @description 根据openid减少额度 15 | * @param openid 账户openid 16 | * @return 17 | * @date 2023/12/09 17:06:21 18 | */ 19 | int subAccountQuota(String openid); 20 | 21 | /** 22 | * @description 根据openid查询用户账户信息 23 | * @param openid 账户openid 24 | * @return UserAccountPO 账户实体 25 | * @date 2023/12/09 17:07:14 26 | */ 27 | UserAccountPO queryUserAccount(String openid); 28 | 29 | /** 30 | * @description 添加账户 31 | * @param userAccountPO 账户实体类 32 | * @return 影响的行数 33 | * @date 2023/12/10 16:36:59 34 | */ 35 | int insert(UserAccountPO userAccountPO); 36 | 37 | /** 38 | * @description 为账户添加额度 39 | * @param userAccountPOReq 添加信息 40 | * @return 影响的行数 41 | * @date 2023/12/10 16:37:32 42 | */ 43 | int addAccountQuota(UserAccountPO userAccountPOReq); 44 | } 45 | -------------------------------------------------------------------------------- /chatgpt-data-domain/src/main/java/com/luckysj/chatgpt/data/domain/order/model/entity/ProductEntity.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.domain.order.model.entity; 2 | 3 | import com.luckysj.chatgpt.data.types.enums.OpenAIProductEnableModel; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Builder; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | 9 | import java.math.BigDecimal; 10 | 11 | /** 12 | * @author www.luckysj.top 刘仕杰 13 | * @description 订单实体对象 14 | * @create 2023/12/10 17:30:28 15 | */ 16 | @Data 17 | @Builder 18 | @AllArgsConstructor 19 | @NoArgsConstructor 20 | public class ProductEntity { 21 | 22 | /** 23 | * 商品ID 24 | */ 25 | private Integer productId; 26 | /** 27 | * 商品名称 28 | */ 29 | private String productName; 30 | /** 31 | * 商品描述 32 | */ 33 | private String productDesc; 34 | /** 35 | * 额度次数 36 | */ 37 | private Integer quota; 38 | /** 39 | * 商品价格 40 | */ 41 | private BigDecimal price; 42 | /** 43 | * 商品状态;0无效、1有效 44 | */ 45 | private OpenAIProductEnableModel enable; 46 | 47 | /** 48 | * 是否有效;true = 有效,false = 无效 49 | */ 50 | public boolean isAvailable() { 51 | return OpenAIProductEnableModel.OPEN.equals(enable); 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /chatgpt-data-types/src/main/java/com/luckysj/chatgpt/data/types/enums/ChatGLMModel.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.types.enums; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | 6 | /** 7 | * @author www.luckysj.top 刘仕杰 8 | * @description ChatGlm模型对象 9 | * @create 2023/12/15 18:27:10 10 | */ 11 | @Getter 12 | @AllArgsConstructor 13 | public enum ChatGLMModel { 14 | 15 | CHATGLM_6B_SSE("chatGLM_6b_SSE"), 16 | CHATGLM_LITE("chatglm_lite"), 17 | CHATGLM_LITE_32K("chatglm_lite_32k"), 18 | CHATGLM_STD("chatglm_std"), 19 | CHATGLM_PRO("chatglm_pro"), 20 | 21 | ; 22 | private final String code; 23 | 24 | public static ChatGLMModel get(String code){ 25 | switch (code){ 26 | case "chatGLM_6b_SSE": 27 | return ChatGLMModel.CHATGLM_6B_SSE; 28 | case "chatglm_lite": 29 | return ChatGLMModel.CHATGLM_LITE; 30 | case "chatglm_lite_32k": 31 | return ChatGLMModel.CHATGLM_LITE_32K; 32 | case "chatglm_std": 33 | return ChatGLMModel.CHATGLM_STD; 34 | case "chatglm_pro": 35 | return ChatGLMModel.CHATGLM_PRO; 36 | default: 37 | return ChatGLMModel.CHATGLM_6B_SSE; 38 | } 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /chatgpt-data-domain/src/main/java/com/luckysj/chatgpt/data/domain/order/config/AliPayConfig.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.domain.order.config; 2 | 3 | import lombok.Data; 4 | import org.springframework.beans.factory.annotation.Value; 5 | import org.springframework.context.annotation.Configuration; 6 | 7 | /** 8 | * @author www.luckysj.top 刘仕杰 9 | * @description 支付宝支付配置 10 | * @create 2023/12/11 20:22:13 11 | */ 12 | @Data 13 | @Configuration 14 | public class AliPayConfig { 15 | // appid 16 | @Value("${pay.alipay.app_id}") 17 | private String APP_ID; 18 | // 私钥 19 | @Value("${pay.alipay.app_private_key}") 20 | private String APP_PRIVATE_KEY; 21 | // 公钥 22 | @Value("${pay.alipay.alipay_public_key}") 23 | private String ALIPAY_PUBLIC_KEY; 24 | // 请求网关地址 25 | @Value("${pay.alipay.url}") 26 | private String URL; 27 | // 二维码跳转地址 28 | @Value("${pay.alipay.qrcode_url}") 29 | private String QRCODE_URL; 30 | // 支付结果通知接口地址 31 | @Value("${pay.alipay.notify_url}") 32 | private String NOTIFY_URL; 33 | // 编码 34 | public static String CHARSET = "UTF-8"; 35 | // 返回格式 36 | public static String FORMAT = "json"; 37 | // 日志记录目录 38 | public static String log_path = "/log"; 39 | // RSA2 40 | public static String SIGNTYPE = "RSA2"; 41 | } 42 | -------------------------------------------------------------------------------- /chatgpt-data-domain/src/main/java/com/luckysj/chatgpt/data/domain/order/model/entity/PayOrderEntity.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.domain.order.model.entity; 2 | 3 | import com.luckysj.chatgpt.data.domain.order.model.valobj.PayStatusVO; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Builder; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | 9 | /** 10 | * @author www.luckysj.top 刘仕杰 11 | * @description 支付单实体对象 12 | * @create 2023/12/10 17:30:11 13 | */ 14 | @Data 15 | @Builder 16 | @AllArgsConstructor 17 | @NoArgsConstructor 18 | public class PayOrderEntity { 19 | 20 | /** 21 | * 用户ID 22 | */ 23 | private String openid; 24 | /** 25 | * 订单ID 26 | */ 27 | private String orderId; 28 | /** 29 | * 支付地址;创建支付后,获得的URL地址 30 | */ 31 | private String payUrl; 32 | /** 33 | * 支付状态;0-等待支付、1-支付完成、2-支付失败、3-放弃支付 34 | */ 35 | private PayStatusVO payStatus; 36 | 37 | @Override 38 | public String toString() { 39 | return "PayOrderEntity{" + 40 | "openid='" + openid + '\'' + 41 | ", orderId='" + orderId + '\'' + 42 | ", payUrl='" + payUrl + '\'' + 43 | ", payStatus=" + payStatus.getCode() + ": " + payStatus.getDesc() + 44 | '}'; 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /chatgpt-data-infrastructure/src/main/java/com/luckysj/chatgpt/data/infrastructure/po/OpenAIProductPO.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.infrastructure.po; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | import java.math.BigDecimal; 9 | import java.util.Date; 10 | 11 | /** 12 | * @author www.luckysj.top 刘仕杰 13 | * @description OpenAi 产品持久化对象 14 | * @create 2023/12/10 11:13:34 15 | */ 16 | @Data 17 | @Builder 18 | @AllArgsConstructor 19 | @NoArgsConstructor 20 | public class OpenAIProductPO { 21 | 22 | /** 23 | * 自增ID 24 | */ 25 | private Long id; 26 | /** 27 | * 商品ID 28 | */ 29 | private Integer productId; 30 | /** 31 | * 商品名称 32 | */ 33 | private String productName; 34 | /** 35 | * 商品描述 36 | */ 37 | private String productDesc; 38 | /** 39 | * 额度次数 40 | */ 41 | private Integer quota; 42 | /** 43 | * 商品价格 44 | */ 45 | private BigDecimal price; 46 | /** 47 | * 商品排序 48 | */ 49 | private Integer sort; 50 | /** 51 | * 是否有效;0无效、1有效 52 | */ 53 | private Integer isEnabled; 54 | /** 55 | * 创建时间 56 | */ 57 | private Date createTime; 58 | /** 59 | * 更新时间 60 | */ 61 | private Date updateTime; 62 | 63 | } 64 | -------------------------------------------------------------------------------- /chatgpt-data-domain/src/main/java/com/luckysj/chatgpt/data/domain/openai/model/entity/UserAccountQuotaEntity.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.domain.openai.model.entity; 2 | 3 | import com.luckysj.chatgpt.data.domain.openai.model.valobj.UserAccountStatusVO; 4 | import com.luckysj.chatgpt.data.types.common.Constants; 5 | import lombok.AllArgsConstructor; 6 | import lombok.Builder; 7 | import lombok.Data; 8 | import lombok.NoArgsConstructor; 9 | 10 | import java.util.Arrays; 11 | import java.util.List; 12 | 13 | /** 14 | * @author www.luckysj.top 刘仕杰 15 | * @description 用户账户额度实体对象 16 | * @create 2023/12/09 17:09:27 17 | */ 18 | @Data 19 | @NoArgsConstructor 20 | @AllArgsConstructor 21 | @Builder 22 | public class UserAccountQuotaEntity { 23 | 24 | /** 25 | * 用户ID 26 | */ 27 | private String openid; 28 | /** 29 | * 总量额度 30 | */ 31 | private Integer totalQuota; 32 | /** 33 | * 剩余额度 34 | */ 35 | private Integer surplusQuota; 36 | /** 37 | * 账户状态 38 | */ 39 | private UserAccountStatusVO userAccountStatusVO; 40 | /** 41 | * 模型类型;一个卡支持多个模型调用,这代表了允许使用的模型范围 42 | */ 43 | private List allowModelTypeList; 44 | 45 | public void transModelTypes(String modelTypes) { 46 | String[] vals = modelTypes.split(Constants.SPLIT); 47 | this.allowModelTypeList = Arrays.asList(vals); 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 27 | 28 | -------------------------------------------------------------------------------- /chatgpt-data-app/src/main/java/com/luckysj/chatgpt/data/config/RabbitDeliveryConfig.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.config; 2 | 3 | import com.luckysj.chatgpt.data.types.common.Constants; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.springframework.amqp.core.Binding; 6 | import org.springframework.amqp.core.BindingBuilder; 7 | import org.springframework.amqp.core.DirectExchange; 8 | import org.springframework.amqp.core.Queue; 9 | import org.springframework.context.annotation.Bean; 10 | import org.springframework.context.annotation.Configuration; 11 | 12 | /** 13 | * @author Luckysj @刘仕杰 14 | * @description 发货消息队列声明 15 | * @create 2024/03/24 19:28:25 16 | */ 17 | @Configuration 18 | @Slf4j 19 | public class RabbitDeliveryConfig { 20 | 21 | // 声明交换机 22 | @Bean 23 | public DirectExchange delayExchange() { 24 | // 对于普通交换机,不需要特殊的参数 25 | return new DirectExchange(Constants.MessageQueueKey.DeliveryExchange); 26 | } 27 | // 声明队列 28 | @Bean 29 | public Queue delayQueue() { 30 | // 普通队列的声明不需要特殊参数 31 | return new Queue(Constants.MessageQueueKey.DeliveryQueue, true); 32 | } 33 | // 声明路由,绑定交换机和队列 34 | @Bean 35 | public Binding delayBinding(Queue delayQueue, DirectExchange delayExchange) { 36 | // 使用普通交换机时,绑定不需要特殊参数 37 | return BindingBuilder.bind(delayQueue).to(delayExchange).with(Constants.MessageQueueKey.DeliveryKey); 38 | } 39 | 40 | } -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /chatgpt-data-app/src/test/java/com/luckysj/chatgpt/data/UtilsTest.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import com.alibaba.fastjson.JSONObject; 5 | import com.alipay.api.AlipayApiException; 6 | import com.alipay.api.AlipayClient; 7 | import com.alipay.api.DefaultAlipayClient; 8 | import com.alipay.api.request.AlipayTradeQueryRequest; 9 | import com.alipay.api.request.AlipayTradeWapPayRequest; 10 | import com.alipay.api.response.AlipayTradeQueryResponse; 11 | import com.luckysj.chatgpt.data.types.util.IdWorkerUtils; 12 | import org.junit.jupiter.api.Test; 13 | import org.junit.runner.RunWith; 14 | import org.springframework.beans.factory.annotation.Value; 15 | import org.springframework.boot.test.context.SpringBootTest; 16 | import org.springframework.test.context.junit4.SpringRunner; 17 | 18 | import java.util.Map; 19 | 20 | /** 21 | * @author Mr.M 22 | * @version 1.0 23 | * @description 支付宝查询接口 24 | * @date 2022/10/4 17:18 25 | */ 26 | @RunWith(SpringRunner.class) 27 | @SpringBootTest(classes = Application.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) 28 | public class UtilsTest { 29 | 30 | 31 | // 测试随机数字字符串工具类 32 | @Test 33 | public void IdWorkerUtilsTest() throws AlipayApiException { 34 | long payNo = IdWorkerUtils.getInstance().nextId(); 35 | System.out.println(payNo); 36 | System.out.println(payNo); 37 | System.out.println(payNo); 38 | System.out.println(payNo); 39 | } 40 | 41 | 42 | } 43 | -------------------------------------------------------------------------------- /chatgpt-data-app/src/main/java/com/luckysj/chatgpt/data/config/PrometheusConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.config; 2 | 3 | import io.micrometer.core.aop.CountedAspect; 4 | import io.micrometer.core.aop.TimedAspect; 5 | import io.micrometer.core.instrument.Clock; 6 | import io.micrometer.core.instrument.MeterRegistry; 7 | import io.micrometer.prometheus.PrometheusConfig; 8 | import io.micrometer.prometheus.PrometheusMeterRegistry; 9 | import io.prometheus.client.CollectorRegistry; 10 | import org.springframework.context.annotation.Bean; 11 | import org.springframework.context.annotation.Configuration; 12 | import org.springframework.context.annotation.EnableAspectJAutoProxy; 13 | 14 | /** 15 | * @author www.luckysj.top 刘仕杰 16 | * @description 启动监听服务 17 | * @create 2023/12/17 15:25:15 18 | */ 19 | @EnableAspectJAutoProxy 20 | @Configuration 21 | public class PrometheusConfiguration { 22 | 23 | @Bean 24 | public CollectorRegistry collectorRegistry() { 25 | return new CollectorRegistry(); 26 | } 27 | 28 | @Bean 29 | public PrometheusMeterRegistry prometheusMeterRegistry(PrometheusConfig config, CollectorRegistry collectorRegistry) { 30 | return new PrometheusMeterRegistry(config, collectorRegistry, Clock.SYSTEM); 31 | } 32 | 33 | @Bean 34 | public TimedAspect timedAspect(MeterRegistry registry) { 35 | return new TimedAspect(registry); 36 | } 37 | 38 | @Bean 39 | public CountedAspect countedAspect(MeterRegistry registry) { 40 | return new CountedAspect(registry); 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /chatgpt-data-app/src/main/resources/mybatis/mapper/openai_product_mapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 23 | 24 | 30 | 31 | -------------------------------------------------------------------------------- /chatgpt-data-app/src/main/java/com/luckysj/chatgpt/data/config/ChatGLMSDKConfig.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.config; 2 | 3 | import cn.bugstack.chatglm.session.OpenAiSession; 4 | import cn.bugstack.chatglm.session.OpenAiSessionFactory; 5 | import cn.bugstack.chatglm.session.defaults.DefaultOpenAiSessionFactory; 6 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 7 | import org.springframework.boot.context.properties.EnableConfigurationProperties; 8 | import org.springframework.context.annotation.Bean; 9 | import org.springframework.context.annotation.Configuration; 10 | 11 | /** 12 | * @author www.luckysj.top 刘仕杰 13 | * @description ChatGLMSDKConfig ChatGLMSDK会话bean声明 14 | * @create 2023/12/15 18:35:13 15 | */ 16 | @Configuration 17 | @EnableConfigurationProperties(ChatGLMSDKConfigProperties.class) 18 | public class ChatGLMSDKConfig { 19 | 20 | @Bean(name = "chatGlMOpenAiSession") 21 | @ConditionalOnProperty(value = "chatglm.sdk.config.enabled", havingValue = "true", matchIfMissing = false) 22 | public OpenAiSession openAiSession(ChatGLMSDKConfigProperties properties) { 23 | // 1. 配置文件 24 | cn.bugstack.chatglm.session.Configuration configuration = new cn.bugstack.chatglm.session.Configuration(); 25 | configuration.setApiHost(properties.getApiHost()); 26 | configuration.setApiSecretKey(properties.getApiSecretKey()); 27 | 28 | // 2. 会话工厂 29 | OpenAiSessionFactory factory = new DefaultOpenAiSessionFactory(configuration); 30 | 31 | // 3. 开启会话 32 | return factory.openSession(); 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /chatgpt-data-trigger/src/main/java/com/luckysj/chatgpt/data/trigger/job/TimeoutCloseOrderJob.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.trigger.job; 2 | 3 | import com.luckysj.chatgpt.data.domain.order.service.IOrderService; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.beans.factory.annotation.Value; 7 | import org.springframework.scheduling.annotation.Scheduled; 8 | import org.springframework.stereotype.Component; 9 | 10 | import javax.annotation.Resource; 11 | import java.util.List; 12 | 13 | /** 14 | * @author www.luckysj.top 刘仕杰 15 | * @description 超时关单任务 16 | * @create 2023/12/15 15:22:23 17 | */ 18 | @Slf4j 19 | @Component() 20 | public class TimeoutCloseOrderJob { 21 | 22 | @Resource 23 | private IOrderService orderService; 24 | 25 | @Scheduled(cron = "0 0/10 * * * ?") 26 | public void exec() { 27 | try { 28 | List orderIds = orderService.queryTimeoutCloseOrderList(); 29 | if (orderIds.isEmpty()) { 30 | log.info("定时任务,超时30分钟订单关闭,暂无超时未支付订单 orderIds is null"); 31 | return; 32 | } 33 | for (String orderId : orderIds) { 34 | // 设置订单状态为超时 35 | boolean status = orderService.changeOrderClose(orderId); 36 | // 以下可以向对接平台执行关单操作,这里暂不写 37 | 38 | log.info("定时任务,超时30分钟订单关闭 orderId: {} status:{}", orderId, status); 39 | } 40 | } catch (Exception e) { 41 | log.error("定时任务,超时15分钟订单关闭失败", e); 42 | } 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /chatgpt-data-app/src/main/java/com/luckysj/chatgpt/data/config/GoogleGuavaCodeCacheConfig.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.config; 2 | 3 | import com.google.common.cache.Cache; 4 | import com.google.common.cache.CacheBuilder; 5 | import com.google.common.eventbus.EventBus; 6 | import com.luckysj.chatgpt.data.trigger.mq.OrderPaySuccessListener; 7 | import org.springframework.beans.factory.annotation.Value; 8 | import org.springframework.context.annotation.Bean; 9 | 10 | import java.util.concurrent.TimeUnit; 11 | 12 | /** 13 | * @author www.luckysj.top 刘仕杰 14 | * @description 声明Google Guava库代码缓存bean 15 | * @create 2023/12/05 11:13:30 16 | */ 17 | // @Configuration 18 | public class GoogleGuavaCodeCacheConfig { 19 | @Value("${app.config.limit-count-time}") 20 | private Integer limitCountTime; 21 | 22 | // 存放验证码等不常变化的一些数据 23 | @Bean(name = "codeCache") 24 | public Cache codeCache() { 25 | return CacheBuilder.newBuilder() 26 | .expireAfterWrite(3, TimeUnit.MINUTES) 27 | .build(); 28 | } 29 | 30 | // 存放访问次数这种高频变化的数据 31 | @Bean(name = "visitCache") 32 | public Cache visitCache() { 33 | return CacheBuilder.newBuilder() 34 | .expireAfterWrite(limitCountTime, TimeUnit.HOURS) 35 | .build(); 36 | } 37 | 38 | // 事件总线,发布订阅消息(适用于一些简单场景,后续可以换成mq消息队列) 39 | @Bean 40 | public EventBus eventBusListener(OrderPaySuccessListener listener){ 41 | EventBus eventBus = new EventBus(); 42 | eventBus.register(listener); 43 | return eventBus; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /chatgpt-data-infrastructure/src/main/java/com/luckysj/chatgpt/data/infrastructure/po/OpenAIOrderPO.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.infrastructure.po; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | import java.math.BigDecimal; 9 | import java.util.Date; 10 | 11 | /** 12 | * @author www.luckysj.top 刘仕杰 13 | * @description OpenAi 订单持久化对象 14 | * @create 2023/12/10 11:13:12 15 | */ 16 | @Data 17 | @Builder 18 | @AllArgsConstructor 19 | @NoArgsConstructor 20 | public class OpenAIOrderPO { 21 | 22 | /** 自增ID */ 23 | private Long id; 24 | /** 用户ID;微信分配的唯一ID编码 */ 25 | private String openid; 26 | /** 商品ID */ 27 | private Integer productId; 28 | /** 商品名称 */ 29 | private String productName; 30 | /** 商品额度 */ 31 | private Integer productQuota; 32 | /** 订单编号 */ 33 | private String orderId; 34 | /** 下单时间 */ 35 | private Date orderTime; 36 | /** 订单状态;0-创建完成、1-等待发货、2-发货完成、3-系统关单 */ 37 | private Integer orderStatus; 38 | /** 订单金额 */ 39 | private BigDecimal totalAmount; 40 | /** 支付方式;0-微信支付 */ 41 | private Integer payType; 42 | /** 支付地址;创建支付后,获得的URL地址 */ 43 | private String payUrl; 44 | /** 支付金额;支付成功后,以回调信息更新金额 */ 45 | private BigDecimal payAmount; 46 | /** 交易单号;支付成功后,回调信息的交易单号 */ 47 | private String transactionId; 48 | /** 支付状态;0-等待支付、1-支付完成、2-支付失败、3-放弃支付 */ 49 | private Integer payStatus; 50 | /** 支付时间 */ 51 | private Date payTime; 52 | /** 创建时间 */ 53 | private Date createTime; 54 | /** 更新时间 */ 55 | private Date updateTime; 56 | 57 | } 58 | -------------------------------------------------------------------------------- /chatgpt-data-domain/src/main/java/com/luckysj/chatgpt/data/domain/order/repository/IOrderRepository.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.domain.order.repository; 2 | 3 | import com.luckysj.chatgpt.data.domain.order.model.aggregates.CreateOrderAggregate; 4 | import com.luckysj.chatgpt.data.domain.order.model.entity.PayOrderEntity; 5 | import com.luckysj.chatgpt.data.domain.order.model.entity.ProductEntity; 6 | import com.luckysj.chatgpt.data.domain.order.model.entity.ShopCartEntity; 7 | import com.luckysj.chatgpt.data.domain.order.model.entity.UnpaidOrderEntity; 8 | 9 | import java.math.BigDecimal; 10 | import java.util.Date; 11 | import java.util.List; 12 | 13 | /** 14 | * @author www.luckysj.top 刘仕杰 15 | * @description 订单仓储接口 16 | * @create 2023/12/10 17:36:00 17 | */ 18 | public interface IOrderRepository { 19 | 20 | UnpaidOrderEntity queryUnpaidOrder(ShopCartEntity shopCartEntity); 21 | 22 | ProductEntity queryProduct(Integer productId); 23 | 24 | void saveOrder(CreateOrderAggregate aggregate); 25 | 26 | void updateOrderPayInfo(PayOrderEntity payOrderEntity); 27 | 28 | boolean changeOrderPaySuccess(String orderId, String transactionId, BigDecimal totalAmount, Date payTime); 29 | 30 | CreateOrderAggregate queryOrder(String orderId); 31 | 32 | void deliverGoods(String orderId); 33 | 34 | List queryReplenishmentOrder(); 35 | 36 | List queryNoPayNotifyOrder(); 37 | 38 | List queryTimeoutCloseOrderList(); 39 | 40 | boolean changeOrderClose(String orderId); 41 | 42 | List queryProductList(); 43 | 44 | void publishDeliveryMessage(String orderId); 45 | } 46 | -------------------------------------------------------------------------------- /chatgpt-data-app/src/main/java/com/luckysj/chatgpt/data/config/SensitiveWordConfig.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.config; 2 | 3 | import com.github.houbb.sensitive.word.bs.SensitiveWordBs; 4 | import com.github.houbb.sensitive.word.utils.InnerWordCharUtils; 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.springframework.context.annotation.Bean; 7 | import org.springframework.context.annotation.Configuration; 8 | 9 | /** 10 | * @author www.luckysj.top 刘仕杰 11 | * @description 敏感词配置 12 | * @create 2023/12/07 11:34:08 13 | */ 14 | @Slf4j 15 | @Configuration 16 | public class SensitiveWordConfig { 17 | 18 | @Bean 19 | public SensitiveWordBs sensitiveWordBs(){ 20 | return SensitiveWordBs.newInstance() 21 | // 开启下述后可以记录日志,但是发现替换词变成了空格而不是*,暂不知如何解决 22 | // .wordReplace((stringBuilder, chars, wordResult, iWordContext) -> { 23 | // String sensitiveWord = InnerWordCharUtils.getString(chars, wordResult); 24 | // log.info("检测到敏感词: {}", sensitiveWord); 25 | // }) 26 | .ignoreCase(true) //忽略大小写 27 | .ignoreWidth(true) //忽略半角圆角 28 | .ignoreNumStyle(true) //忽略数字的写法 29 | .ignoreChineseStyle(true) //忽略中文的书写格式 30 | .ignoreEnglishStyle(true) //忽略英文的书写格式 31 | .ignoreRepeat(false) //忽略重复词 32 | .enableNumCheck(true) //是否启用数字检测。 33 | .enableEmailCheck(true) //是有启用邮箱检测 34 | .enableUrlCheck(true) //是否启用链接检测 35 | .enableWordCheck(true) //是否启用敏感单词检测 36 | .numCheckLen(1024) //数字检测,自定义指定长度 37 | .init(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /chatgpt-data-app/src/main/java/com/luckysj/chatgpt/data/config/ChatGPTSDKConfig.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.config; 2 | 3 | 4 | import cn.bugstack.chatgpt.session.OpenAiSession; 5 | import cn.bugstack.chatgpt.session.OpenAiSessionFactory; 6 | import cn.bugstack.chatgpt.session.defaults.DefaultOpenAiSessionFactory; 7 | import lombok.Data; 8 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 9 | import org.springframework.boot.context.properties.ConfigurationProperties; 10 | import org.springframework.boot.context.properties.EnableConfigurationProperties; 11 | import org.springframework.context.annotation.Bean; 12 | import org.springframework.context.annotation.Configuration; 13 | 14 | /** 15 | * @author www.luckysj.top 刘仕杰 16 | * @description ChatGPTSDKConfig ChatGPTSDK会话bean声明 17 | * @create 2023-12-04 18:53 18 | */ 19 | @Configuration 20 | @EnableConfigurationProperties(ChatGPTSDKConfigProperties.class) 21 | public class ChatGPTSDKConfig { 22 | @Bean(name = "chatGPTOpenAiSession") 23 | @ConditionalOnProperty(value = "chatgpt.sdk.config.enabled", havingValue = "true", matchIfMissing = false) 24 | public OpenAiSession openAiSession(ChatGPTSDKConfigProperties properties) { 25 | // 1. 配置文件 26 | cn.bugstack.chatgpt.session.Configuration configuration = new cn.bugstack.chatgpt.session.Configuration(); 27 | configuration.setApiHost(properties.getApiHost()); 28 | configuration.setAuthToken(properties.getAuthToken()); 29 | configuration.setApiKey(properties.getApiKey()); 30 | 31 | // 2. 会话工厂 32 | OpenAiSessionFactory factory = new DefaultOpenAiSessionFactory(configuration); 33 | 34 | // 3. 开启会话 35 | return factory.openSession(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /chatgpt-data-trigger/src/main/java/com/luckysj/chatgpt/data/trigger/mq/OrderPaySuccessMqListener.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.trigger.mq; 2 | 3 | import com.luckysj.chatgpt.data.domain.order.service.IOrderService; 4 | import com.luckysj.chatgpt.data.types.common.Constants; 5 | import com.rabbitmq.client.Channel; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.springframework.amqp.core.Message; 8 | import org.springframework.amqp.rabbit.annotation.Queue; 9 | import org.springframework.amqp.rabbit.annotation.RabbitListener; 10 | import org.springframework.stereotype.Component; 11 | 12 | import javax.annotation.Resource; 13 | import java.io.IOException; 14 | 15 | /** 16 | * @author www.luckysj.top 刘仕杰 17 | * @description 支付完成后发货消息监听器(rabbitmq) 18 | * @create 2023/12/17 22:12:22 19 | */ 20 | @Slf4j 21 | @Component 22 | public class OrderPaySuccessMqListener { 23 | @Resource 24 | private IOrderService orderService; 25 | 26 | // ackMode指定为手动提交模式 27 | @RabbitListener(queuesToDeclare = @Queue("chatgpt.quate.delivery.queue"), ackMode="MANUAL") 28 | public void stockUpdateHandler(String orderId , Message message, Channel channel) { 29 | // 如果手动ACK,消息会被监听消费,但是消息在队列中依旧存在,如果 未配置 acknowledge-mode 默认是会在消费完毕后自动ACK掉 30 | final long deliveryTag = message.getMessageProperties().getDeliveryTag(); 31 | try { 32 | log.info("发货消息开始消费,订单ID:{}", orderId); 33 | // 消费消息 34 | orderService.deliverGoods(orderId); 35 | 36 | // 通知 MQ 消息已被成功消费,可以ACK了 37 | channel.basicAck(deliveryTag, false); 38 | log.info("发货消息消费完成,订单ID:{}",orderId); 39 | } catch (IOException e) { 40 | try { 41 | // 处理失败,重新压入MQ 42 | channel.basicRecover(); 43 | } catch (IOException e1) { 44 | e1.printStackTrace(); 45 | } 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /chatgpt-data-domain/src/main/java/com/luckysj/chatgpt/data/domain/openai/model/aggregates/ChatProcessAggregate.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.domain.openai.model.aggregates; 2 | 3 | 4 | import com.luckysj.chatgpt.data.domain.openai.model.entity.MessageEntity; 5 | import com.luckysj.chatgpt.data.domain.openai.model.valobj.GenerativeModelVO; 6 | import com.luckysj.chatgpt.data.types.common.Constants; 7 | import com.luckysj.chatgpt.data.types.enums.ChatGPTModel; 8 | import com.luckysj.chatgpt.data.types.enums.OpenAiChannel; 9 | import lombok.AllArgsConstructor; 10 | import lombok.Builder; 11 | import lombok.Data; 12 | import lombok.NoArgsConstructor; 13 | 14 | import java.util.List; 15 | 16 | /** 17 | * @author www.luckysj.top 刘仕杰 18 | * @description 聊天信息聚合对象 19 | * @create 2023/12/04 19:48:33 20 | */ 21 | @Data 22 | @Builder 23 | @NoArgsConstructor 24 | @AllArgsConstructor 25 | public class ChatProcessAggregate { 26 | 27 | /** 用户ID */ 28 | private String openid; 29 | /** 默认模型 */ 30 | private String model = ChatGPTModel.GPT_3_5_TURBO.getCode(); 31 | /** 问题描述 */ 32 | private List messages; 33 | 34 | /** 判断当前对话用户是否在白名单内*/ 35 | public boolean isWhiteList(String whiteListStr) { 36 | String[] whiteList = whiteListStr.split(Constants.SPLIT); 37 | for (String whiteOpenid : whiteList) { 38 | if (whiteOpenid.equals(openid)) return true; 39 | } 40 | return false; 41 | } 42 | 43 | /** 判断当前会话选择的模型属于哪个渠道*/ 44 | public OpenAiChannel getChannel(){ 45 | return OpenAiChannel.getChannel(this.model); 46 | } 47 | 48 | /** 判断当前会话选择的模型属于渠道中的哪个服务*/ 49 | public GenerativeModelVO getGenerativeModelVO() { 50 | // dall模型调用图片服务,其他调用文字服务 51 | switch (this.model) { 52 | case "dall-e-2": 53 | case "dall-e-3": 54 | return GenerativeModelVO.IMAGES; 55 | default: 56 | return GenerativeModelVO.TEXT; 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /chatgpt-data-domain/src/main/java/com/luckysj/chatgpt/data/domain/openai/service/rule/impl/ModelTypeFilter.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.domain.openai.service.rule.impl; 2 | 3 | import com.luckysj.chatgpt.data.domain.openai.annotation.LogicStrategy; 4 | import com.luckysj.chatgpt.data.domain.openai.model.aggregates.ChatProcessAggregate; 5 | import com.luckysj.chatgpt.data.domain.openai.model.entity.RuleLogicEntity; 6 | import com.luckysj.chatgpt.data.domain.openai.model.entity.UserAccountQuotaEntity; 7 | import com.luckysj.chatgpt.data.domain.openai.model.valobj.LogicCheckTypeVO; 8 | import com.luckysj.chatgpt.data.domain.openai.service.rule.ILogicFilter; 9 | import com.luckysj.chatgpt.data.domain.openai.service.rule.factory.DefaultLogicFactory; 10 | import lombok.extern.slf4j.Slf4j; 11 | import org.springframework.stereotype.Component; 12 | 13 | import java.util.List; 14 | 15 | /** 16 | * @author www.luckysj.top 刘仕杰 17 | * @description 可用模型校验 18 | * @create 2023/12/09 18:00:27 19 | */ 20 | @Slf4j 21 | @Component 22 | @LogicStrategy(logicMode = DefaultLogicFactory.LogicModel.MODEL_TYPE) 23 | public class ModelTypeFilter implements ILogicFilter { 24 | @Override 25 | public RuleLogicEntity fileter(ChatProcessAggregate chatProcessAggregate, UserAccountQuotaEntity data) throws Exception { 26 | List allowModelTypeList = data.getAllowModelTypeList(); 27 | String model = chatProcessAggregate.getModel(); 28 | 29 | if(allowModelTypeList.contains(model)){ 30 | return RuleLogicEntity.builder() 31 | .type(LogicCheckTypeVO.SUCCESS) 32 | .data(chatProcessAggregate) 33 | .build(); 34 | } 35 | 36 | return RuleLogicEntity.builder() 37 | .type(LogicCheckTypeVO.REFUSE) 38 | .info("当前账户不支持使用 " + model + " 模型!可以联系客服升级账户。") 39 | .data(chatProcessAggregate) 40 | .build(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /chatgpt-data-app/src/main/resources/mybatis/mapper/user_account_mapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | INSERT INTO user_account (openid, total_quota, surplus_quota, model_types, status, create_time, update_time) 17 | VALUES (#{openid}, #{totalQuota}, #{surplusQuota}, #{modelTypes}, #{status}, now(), now()) 18 | 19 | 20 | 25 | 26 | 27 | UPDATE user_account SET surplus_quota = surplus_quota - 1 28 | WHERE openid = #{openid} AND surplus_quota > 0 AND status = 0 29 | 30 | 31 | 32 | UPDATE user_account SET 33 | total_quota = total_quota + #{totalQuota}, 34 | surplus_quota = surplus_quota + #{surplusQuota}, 35 | update_time = now() 36 | WHERE openid = #{openid} 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /chatgpt-data-infrastructure/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | chatgpt-data 7 | com.luckysj 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | chatgpt-data-infrastructure 13 | 14 | 15 | 8 16 | 8 17 | UTF-8 18 | 19 | 20 | 21 | 22 | 23 | com.luckysj 24 | chatgpt-data-domain 25 | 1.0-SNAPSHOT 26 | 27 | 28 | 29 | com.luckysj 30 | chatgpt-data-types 31 | 1.0-SNAPSHOT 32 | 33 | 34 | 35 | org.mybatis.spring.boot 36 | mybatis-spring-boot-starter 37 | 38 | 39 | org.projectlombok 40 | lombok 41 | 42 | 43 | org.redisson 44 | redisson-spring-boot-starter 45 | ${redisson-spring-boot-starter.version} 46 | 47 | 48 | 49 | 50 | org.springframework.boot 51 | spring-boot-starter-amqp 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /chatgpt-data-types/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | chatgpt-data 7 | com.luckysj 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | chatgpt-data-types 13 | 14 | 15 | 16 | org.springframework.boot 17 | spring-boot-starter-web 18 | 19 | 20 | org.projectlombok 21 | lombok 22 | 23 | 24 | 25 | 26 | com.thoughtworks.xstream 27 | xstream 28 | 29 | 30 | dom4j 31 | dom4j 32 | 33 | 34 | 35 | org.apache.commons 36 | commons-lang3 37 | 38 | 39 | 40 | 41 | com.google.zxing 42 | core 43 | 3.3.3 44 | 45 | 46 | 47 | com.google.zxing 48 | javase 49 | 3.3.3 50 | 51 | 52 | 53 | 54 | chatgpt-data-types 55 | 56 | 57 | -------------------------------------------------------------------------------- /chatgpt-data-trigger/src/main/java/com/luckysj/chatgpt/data/trigger/job/OrderReplenishmentJob.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.trigger.job; 2 | 3 | import com.google.common.eventbus.EventBus; 4 | import com.luckysj.chatgpt.data.domain.order.service.IOrderService; 5 | import com.luckysj.chatgpt.data.types.common.Constants; 6 | import io.micrometer.core.annotation.Timed; 7 | import lombok.extern.slf4j.Slf4j; 8 | import org.redisson.api.RTopic; 9 | import org.springframework.amqp.rabbit.core.RabbitTemplate; 10 | import org.springframework.scheduling.annotation.Scheduled; 11 | import org.springframework.stereotype.Component; 12 | 13 | import javax.annotation.Resource; 14 | import java.util.List; 15 | 16 | /** 17 | * @author www.luckysj.top 刘仕杰 18 | * @description 订单补货任务 19 | * @create 2023/12/15 15:22:10 20 | */ 21 | @Slf4j 22 | @Component() 23 | public class OrderReplenishmentJob { 24 | 25 | @Resource 26 | private IOrderService orderService; 27 | // @Resource 28 | // private EventBus eventBus; 29 | 30 | // @Resource(name = "delivery") 31 | // private RTopic redisTopic; 32 | 33 | @Resource 34 | private RabbitTemplate rabbitTemplate; 35 | 36 | /** 37 | * 执行订单补货,超时3分钟,已支付,待发货未发货的订单 38 | */ 39 | @Timed(value="order_replenish_job",description="定时任务,订单补货检查") 40 | @Scheduled(cron = "0 0/10 * * * ?") 41 | public void exec() { 42 | try { 43 | List orderIds = orderService.queryReplenishmentOrder(); 44 | if (orderIds.isEmpty()) { 45 | log.info("定时任务,补货补偿,不存在待发货的订单"); 46 | return; 47 | } 48 | for (String orderId : orderIds) { 49 | log.info("定时任务,补货补偿,订单补货开始。orderId: {}", orderId); 50 | // eventBus.post(orderId); 51 | // redisTopic.publish(orderId); 52 | // rabbitTemplate.convertAndSend(Constants.MessageQueueKey.DeliveryExchange, Constants.MessageQueueKey.DeliveryKey, orderId); 53 | orderService.publishDeliveryMessage(orderId); //发布发货消息 54 | } 55 | 56 | } catch (Exception e) { 57 | log.error("定时任务,订单补货失败。", e); 58 | } 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /chatgpt-data-domain/src/main/java/com/luckysj/chatgpt/data/domain/auth/service/AuthService.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.domain.auth.service; 2 | 3 | import com.luckysj.chatgpt.data.domain.auth.model.entity.AuthStateEntity; 4 | import com.luckysj.chatgpt.data.domain.auth.model.valobj.AuthTypeVo; 5 | import com.luckysj.chatgpt.data.domain.auth.repository.IAuthRepository; 6 | import io.jsonwebtoken.Claims; 7 | import lombok.extern.slf4j.Slf4j; 8 | import org.apache.commons.lang3.StringUtils; 9 | import org.springframework.stereotype.Service; 10 | 11 | import javax.annotation.Resource; 12 | 13 | /** 14 | * @author www.luckysj.top 刘仕杰 15 | * @description 鉴权服务 16 | * @create 2023/12/05 15:25:06 17 | */ 18 | @Slf4j 19 | @Service 20 | public class AuthService extends AbstractAuthService{ 21 | 22 | @Resource 23 | private IAuthRepository authRepository; 24 | 25 | private static final String Key = "weixin_code:"; 26 | 27 | @Override 28 | public AuthStateEntity checkCode(String code){ 29 | 30 | // 从缓存中读取验证码 31 | String openId = authRepository.getCodeUserOpenId(code); 32 | if(StringUtils.isBlank(openId)){ 33 | log.info("鉴权失败,用户输入的验证码不存在 {}", code); 34 | return AuthStateEntity.builder() 35 | .code(AuthTypeVo.CODE_NOT_EXIST.getCode()) 36 | .info(AuthTypeVo.CODE_NOT_EXIST.getInfo()) 37 | .build(); 38 | } 39 | 40 | // 移除缓存Key值 41 | authRepository.removeCodeByOpenId(code, openId); 42 | 43 | // 返回校验成功结果 44 | return AuthStateEntity.builder() 45 | .code(AuthTypeVo.CODE_SUCCESS.getCode()) 46 | .info(AuthTypeVo.CODE_SUCCESS.getInfo()) 47 | .openId(openId) 48 | .build(); 49 | } 50 | 51 | @Override 52 | public boolean checkToken(String token) { 53 | 54 | return isVerify(token); 55 | } 56 | 57 | @Override 58 | public String parseOpenid(String token) { 59 | Claims claims = decode(token); 60 | return claims.get("openId").toString(); 61 | } 62 | 63 | @Override 64 | public String getAuthCode(String openid) { 65 | return authRepository.genCodeTest(openid); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /chatgpt-data-domain/src/main/java/com/luckysj/chatgpt/data/domain/order/service/IOrderService.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.domain.order.service; 2 | 3 | 4 | import com.luckysj.chatgpt.data.domain.order.model.aggregates.CreateOrderAggregate; 5 | import com.luckysj.chatgpt.data.domain.order.model.entity.PayOrderEntity; 6 | import com.luckysj.chatgpt.data.domain.order.model.entity.ProductEntity; 7 | import com.luckysj.chatgpt.data.domain.order.model.entity.ShopCartEntity; 8 | 9 | import java.math.BigDecimal; 10 | import java.util.Date; 11 | import java.util.List; 12 | import java.util.Map; 13 | 14 | /** 15 | * @author Fuzhengwei bugstack.cn @小傅哥 16 | * @description 订单服务 17 | * 1. 用户下单 createOrder 18 | * @create 2023-10-05 10:49 19 | */ 20 | public interface IOrderService { 21 | 22 | /** 23 | * 用户下单,创建订单,通过购物车信息,返回下单后的支付单 24 | * 25 | * @param shopCartEntity 简单购物车 26 | * @return 支付单实体对象 27 | */ 28 | PayOrderEntity createOrder(ShopCartEntity shopCartEntity); 29 | 30 | /** 31 | * 变更;订单支付成功 32 | */ 33 | boolean changeOrderPaySuccess(String orderId, String transactionId, BigDecimal totalAmount, Date payTime); 34 | 35 | /** 36 | * 查询订单信息 37 | * 38 | * @param orderId 订单ID 39 | * @return 查询结果 40 | */ 41 | CreateOrderAggregate queryOrder(String orderId); 42 | 43 | /** 44 | * 订单商品发货 45 | * 46 | * @param orderId 订单ID 47 | */ 48 | void deliverGoods(String orderId); 49 | 50 | /** 51 | * 查询待补货订单 52 | */ 53 | List queryReplenishmentOrder(); 54 | 55 | /** 56 | * 查询有效期内,未接收到支付回调的订单 57 | */ 58 | List queryNoPayNotifyOrder(); 59 | 60 | /** 61 | * 查询超时15分钟,未支付订单 62 | */ 63 | List queryTimeoutCloseOrderList(); 64 | 65 | /** 66 | * 变更;订单支付关闭 67 | */ 68 | boolean changeOrderClose(String orderId); 69 | 70 | /** 71 | * 查询商品列表 72 | */ 73 | List queryProductList(); 74 | 75 | /** 76 | * 接受支付通知结果 77 | * 78 | * @param params 本次通知结果的相关信息 79 | */ 80 | String receiveNotify(Map params); 81 | 82 | /** 83 | * 发布 84 | * 85 | * @param orderId 订单ID 86 | */ 87 | void publishDeliveryMessage(String orderId); 88 | } -------------------------------------------------------------------------------- /chatgpt-data-app/src/main/java/com/luckysj/chatgpt/data/config/ThreadPoolConfig.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.config; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 5 | import org.springframework.boot.context.properties.EnableConfigurationProperties; 6 | import org.springframework.context.annotation.Bean; 7 | import org.springframework.context.annotation.Configuration; 8 | import org.springframework.scheduling.annotation.EnableAsync; 9 | 10 | import java.util.concurrent.*; 11 | 12 | /** 13 | * @author www.luckysj.top 刘仕杰 14 | * @description 线程池bean声明 15 | * @create 2023/12/04 19:05:36 16 | */ 17 | @Slf4j 18 | @EnableAsync 19 | @Configuration 20 | @EnableConfigurationProperties(ThreadPoolConfigProperties.class) 21 | public class ThreadPoolConfig { 22 | 23 | @Bean 24 | @ConditionalOnMissingBean(ThreadPoolExecutor.class) 25 | public ThreadPoolExecutor threadPoolExecutor(ThreadPoolConfigProperties properties) throws ClassNotFoundException, InstantiationException, IllegalAccessException { 26 | // 实例化策略 27 | RejectedExecutionHandler handler; 28 | switch (properties.getPolicy()){ 29 | case "AbortPolicy": 30 | handler = new ThreadPoolExecutor.AbortPolicy(); 31 | break; 32 | case "DiscardPolicy": 33 | handler = new ThreadPoolExecutor.DiscardPolicy(); 34 | break; 35 | case "DiscardOldestPolicy": 36 | handler = new ThreadPoolExecutor.DiscardOldestPolicy(); 37 | break; 38 | case "CallerRunsPolicy": 39 | handler = new ThreadPoolExecutor.CallerRunsPolicy(); 40 | break; 41 | default: 42 | handler = new ThreadPoolExecutor.AbortPolicy(); 43 | break; 44 | } 45 | // 创建线程池 46 | return new ThreadPoolExecutor(properties.getCorePoolSize(), 47 | properties.getMaxPoolSize(), 48 | properties.getKeepAliveTime(), 49 | TimeUnit.SECONDS, 50 | new LinkedBlockingQueue<>(properties.getBlockQueueSize()), 51 | Executors.defaultThreadFactory(), 52 | handler); 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /chatgpt-data-types/src/main/java/com/luckysj/chatgpt/data/types/sdk/weixin/SignUtil.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.types.sdk.weixin; 2 | 3 | import java.security.MessageDigest; 4 | import java.security.NoSuchAlgorithmException; 5 | import java.util.Arrays; 6 | 7 | // 微信验签工具 8 | public class SignUtil { 9 | private static String token = "b8b6"; 10 | 11 | /** 12 | * 校验签名 13 | * @param signature 签名 14 | * @param timestamp 时间戳 15 | * @param nonce 随机数 16 | * @return 布尔值 17 | */ 18 | public static boolean checkSignature(String signature,String timestamp,String nonce){ 19 | String checktext = null; 20 | if (null != signature) { 21 | //对ToKen,timestamp,nonce 按字典排序 22 | String[] paramArr = new String[]{token,timestamp,nonce}; 23 | Arrays.sort(paramArr); 24 | //将排序后的结果拼成一个字符串 25 | String content = paramArr[0].concat(paramArr[1]).concat(paramArr[2]); 26 | 27 | try { 28 | MessageDigest md = MessageDigest.getInstance("SHA-1"); 29 | //对接后的字符串进行sha1加密 30 | byte[] digest = md.digest(content.toString().getBytes()); 31 | checktext = byteToStr(digest); 32 | } catch (NoSuchAlgorithmException e){ 33 | e.printStackTrace(); 34 | } 35 | } 36 | //将加密后的字符串与signature进行对比 37 | return checktext !=null ? checktext.equals(signature.toUpperCase()) : false; 38 | } 39 | 40 | /** 41 | * 将字节数组转化我16进制字符串 42 | * @param byteArrays 字符数组 43 | * @return 字符串 44 | */ 45 | private static String byteToStr(byte[] byteArrays){ 46 | String str = ""; 47 | for (int i = 0; i < byteArrays.length; i++) { 48 | str += byteToHexStr(byteArrays[i]); 49 | } 50 | return str; 51 | } 52 | 53 | /** 54 | * 将字节转化为十六进制字符串 55 | * @param myByte 字节 56 | * @return 字符串 57 | */ 58 | private static String byteToHexStr(byte myByte) { 59 | char[] Digit = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; 60 | char[] tampArr = new char[2]; 61 | tampArr[0] = Digit[(myByte >>> 4) & 0X0F]; 62 | tampArr[1] = Digit[myByte & 0X0F]; 63 | String str = new String(tampArr); 64 | return str; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /chatgpt-data-domain/src/main/java/com/luckysj/chatgpt/data/domain/openai/service/rule/impl/AccountStatusFilter.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.domain.openai.service.rule.impl; 2 | 3 | import com.luckysj.chatgpt.data.domain.openai.annotation.LogicStrategy; 4 | import com.luckysj.chatgpt.data.domain.openai.model.aggregates.ChatProcessAggregate; 5 | import com.luckysj.chatgpt.data.domain.openai.model.entity.RuleLogicEntity; 6 | import com.luckysj.chatgpt.data.domain.openai.model.entity.UserAccountQuotaEntity; 7 | import com.luckysj.chatgpt.data.domain.openai.model.valobj.LogicCheckTypeVO; 8 | import com.luckysj.chatgpt.data.domain.openai.model.valobj.UserAccountStatusVO; 9 | import com.luckysj.chatgpt.data.domain.openai.service.rule.ILogicFilter; 10 | import com.luckysj.chatgpt.data.domain.openai.service.rule.factory.DefaultLogicFactory; 11 | import lombok.extern.slf4j.Slf4j; 12 | import org.springframework.stereotype.Component; 13 | 14 | /** 15 | * @author www.luckysj.top 刘仕杰 16 | * @description 账户状态校验 17 | * @create 2023/12/09 18:00:10 18 | */ 19 | @Slf4j 20 | @Component 21 | @LogicStrategy(logicMode = DefaultLogicFactory.LogicModel.ACCOUNT_STATUS) 22 | public class AccountStatusFilter implements ILogicFilter { 23 | @Override 24 | public RuleLogicEntity fileter(ChatProcessAggregate chatProcessAggregate, UserAccountQuotaEntity userAccount) throws Exception { 25 | // 首先判断账户是否存在 26 | if(userAccount == null){ 27 | return RuleLogicEntity.builder() 28 | .type(LogicCheckTypeVO.REFUSE) 29 | .data(chatProcessAggregate) 30 | .info("当前账户信息不存在,暂时不可使用,请尝试重新登录,或者联系网站作者QQ1321928757") 31 | .build(); 32 | } 33 | 34 | // 判断账户状态是否为可用状态 35 | if(UserAccountStatusVO.AVAILABLE.equals(userAccount.getUserAccountStatusVO())){ 36 | return RuleLogicEntity.builder() 37 | .type(LogicCheckTypeVO.SUCCESS) 38 | .data(chatProcessAggregate) 39 | .build(); 40 | } 41 | 42 | // 校验通过 43 | return RuleLogicEntity.builder() 44 | .info("您的账户已冻结,暂时不可使用。如果有疑问,可以联系客户解冻账户。") 45 | .type(LogicCheckTypeVO.REFUSE) 46 | .data(chatProcessAggregate) 47 | .build(); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /chatgpt-data-domain/src/main/java/com/luckysj/chatgpt/data/domain/openai/service/rule/impl/UserQuotaFilter.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.domain.openai.service.rule.impl; 2 | 3 | import com.luckysj.chatgpt.data.domain.openai.annotation.LogicStrategy; 4 | import com.luckysj.chatgpt.data.domain.openai.model.aggregates.ChatProcessAggregate; 5 | import com.luckysj.chatgpt.data.domain.openai.model.entity.RuleLogicEntity; 6 | import com.luckysj.chatgpt.data.domain.openai.model.entity.UserAccountQuotaEntity; 7 | import com.luckysj.chatgpt.data.domain.openai.model.valobj.LogicCheckTypeVO; 8 | import com.luckysj.chatgpt.data.domain.openai.repository.IOpenAiRepository; 9 | import com.luckysj.chatgpt.data.domain.openai.service.rule.ILogicFilter; 10 | import com.luckysj.chatgpt.data.domain.openai.service.rule.factory.DefaultLogicFactory; 11 | import lombok.extern.slf4j.Slf4j; 12 | import org.springframework.stereotype.Component; 13 | 14 | import javax.annotation.Resource; 15 | 16 | /** 17 | * @author www.luckysj.top 刘仕杰 18 | * @description 账户额度校验 19 | * @create 2023/12/09 17:59:33 20 | */ 21 | @Slf4j 22 | @Component 23 | @LogicStrategy(logicMode = DefaultLogicFactory.LogicModel.USER_QUOTA) 24 | public class UserQuotaFilter implements ILogicFilter { 25 | 26 | @Resource 27 | private IOpenAiRepository openAiRepository; 28 | 29 | @Override 30 | public RuleLogicEntity fileter(ChatProcessAggregate chatProcessAggregate, UserAccountQuotaEntity data) throws Exception { 31 | if (data.getSurplusQuota() > 0) { 32 | // 扣减账户额度;因为是个人账户数据,无资源竞争,所以直接使用数据库也可以。但为了效率,也可以优化为 Redis 扣减。 33 | int updateCount = openAiRepository.subAccountQuota(data.getOpenid()); 34 | if (0 != updateCount) { 35 | return RuleLogicEntity.builder() 36 | .type(LogicCheckTypeVO.SUCCESS).data(chatProcessAggregate).build(); 37 | } 38 | 39 | return RuleLogicEntity.builder() 40 | .info("个人账户,总额度【" + data.getTotalQuota() + "】次,已耗尽!") 41 | .type(LogicCheckTypeVO.REFUSE).data(chatProcessAggregate).build(); 42 | } 43 | 44 | return RuleLogicEntity.builder() 45 | .info("个人账户,总额度【" + data.getTotalQuota() + "】次,已耗尽!") 46 | .type(LogicCheckTypeVO.REFUSE).data(chatProcessAggregate).build(); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /chatgpt-data-types/src/main/java/com/luckysj/chatgpt/data/types/sdk/weixin/SignatureUtil.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.types.sdk.weixin; 2 | 3 | import java.security.MessageDigest; 4 | import java.security.NoSuchAlgorithmException; 5 | 6 | // 微信验签工具 7 | public class SignatureUtil { 8 | /** 9 | * 验证签名 10 | */ 11 | public static boolean check(String token, String signature, String timestamp, String nonce) { 12 | String[] arr = new String[]{token, timestamp, nonce}; 13 | // 将token、timestamp、nonce三个参数进行字典序排序 14 | sort(arr); 15 | StringBuilder content = new StringBuilder(); 16 | for (String s : arr) { 17 | content.append(s); 18 | } 19 | MessageDigest md; 20 | String tmpStr = null; 21 | try { 22 | md = MessageDigest.getInstance("SHA-1"); 23 | // 将三个参数字符串拼接成一个字符串进行sha1加密 24 | byte[] digest = md.digest(content.toString().getBytes()); 25 | tmpStr = byteToStr(digest); 26 | } catch (NoSuchAlgorithmException e) { 27 | e.printStackTrace(); 28 | } 29 | // 将sha1加密后的字符串可与signature对比,标识该请求来源于微信 30 | return tmpStr != null && tmpStr.equals(signature.toUpperCase()); 31 | } 32 | 33 | /** 34 | * 将字节数组转换为十六进制字符串 35 | */ 36 | private static String byteToStr(byte[] byteArray) { 37 | StringBuilder strDigest = new StringBuilder(); 38 | for (byte b : byteArray) { 39 | strDigest.append(byteToHexStr(b)); 40 | } 41 | return strDigest.toString(); 42 | } 43 | 44 | /** 45 | * 将字节转换为十六进制字符串 46 | */ 47 | private static String byteToHexStr(byte mByte) { 48 | char[] Digit = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; 49 | char[] tempArr = new char[2]; 50 | tempArr[0] = Digit[(mByte >>> 4) & 0X0F]; 51 | tempArr[1] = Digit[mByte & 0X0F]; 52 | return new String(tempArr); 53 | } 54 | 55 | /** 56 | * 进行字典排序 57 | */ 58 | private static void sort(String[] str) { 59 | for (int i = 0; i < str.length - 1; i++) { 60 | for (int j = i + 1; j < str.length; j++) { 61 | if (str[j].compareTo(str[i]) < 0) { 62 | String temp = str[i]; 63 | str[i] = str[j]; 64 | str[j] = temp; 65 | } 66 | } 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /chatgpt-data-domain/src/main/java/com/luckysj/chatgpt/data/domain/openai/service/rule/factory/DefaultLogicFactory.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.domain.openai.service.rule.factory; 2 | 3 | import com.luckysj.chatgpt.data.domain.openai.annotation.LogicStrategy; 4 | import com.luckysj.chatgpt.data.domain.openai.service.rule.ILogicFilter; 5 | import org.springframework.core.annotation.AnnotationUtils; 6 | import org.springframework.stereotype.Service; 7 | 8 | import java.util.List; 9 | import java.util.Map; 10 | import java.util.concurrent.ConcurrentHashMap; 11 | 12 | /** 13 | * @author www.luckysj.top 刘仕杰 14 | * @description 校验规则工厂 15 | * @create 2023/12/07 19:48:27 16 | */ 17 | @Service 18 | public class DefaultLogicFactory { 19 | // 过滤规则 20 | private Map logicFilterMap = new ConcurrentHashMap<>(); 21 | 22 | public DefaultLogicFactory(List logicFilters) { 23 | logicFilters.forEach(logic -> { 24 | // 根据类和注解名找到校验规则上的注解 25 | LogicStrategy strategy = AnnotationUtils.findAnnotation(logic.getClass(), LogicStrategy.class); 26 | if (null != strategy) { 27 | // 以注解值为key,校验规则为logic保存到logicFilterMap中 28 | logicFilterMap.put(strategy.logicMode().getCode(), logic); 29 | } 30 | }); 31 | } 32 | 33 | // 获取校验规则集 34 | public Map openLogicFilter() { 35 | return logicFilterMap; 36 | } 37 | 38 | /** 39 | * 规则逻辑枚举 40 | */ 41 | public enum LogicModel { 42 | 43 | NULL("NULL", "放行不用过滤"), 44 | ACCESS_LIMIT("ACCESS_LIMIT", "访问次数过滤"), 45 | SENSITIVE_WORD("SENSITIVE_WORD", "敏感词过滤"), 46 | USER_QUOTA("USER_QUOTA", "用户额度过滤"), 47 | MODEL_TYPE("MODEL_TYPE", "模型可用范围过滤"), 48 | ACCOUNT_STATUS("ACCOUNT_STATUS", "账户状态过滤"), 49 | ; 50 | 51 | private String code; 52 | private String info; 53 | 54 | LogicModel(String code, String info) { 55 | this.code = code; 56 | this.info = info; 57 | } 58 | 59 | public String getCode() { 60 | return code; 61 | } 62 | 63 | public void setCode(String code) { 64 | this.code = code; 65 | } 66 | 67 | public String getInfo() { 68 | return info; 69 | } 70 | 71 | public void setInfo(String info) { 72 | this.info = info; 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /chatgpt-data-infrastructure/src/main/java/com/luckysj/chatgpt/data/infrastructure/repository/WeiXinRepository.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.infrastructure.repository; 2 | 3 | import com.luckysj.chatgpt.data.domain.weixin.repository.IWeiXinRepository; 4 | import com.luckysj.chatgpt.data.infrastructure.redis.RedisService; 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.apache.commons.lang3.RandomStringUtils; 7 | import org.apache.commons.lang3.StringUtils; 8 | import org.redisson.api.RLock; 9 | import org.springframework.stereotype.Repository; 10 | 11 | import javax.annotation.Resource; 12 | import java.util.concurrent.TimeUnit; 13 | 14 | @Slf4j 15 | @Repository 16 | public class WeiXinRepository implements IWeiXinRepository { 17 | 18 | // 验证码前缀 19 | private static final String Key = "weixin_code:"; 20 | 21 | @Resource 22 | private RedisService redisService; 23 | 24 | @Override 25 | public String genCode(String openid) { 26 | // 首先判断是否已经存在验证码 27 | String codeOld = redisService.getValue(Key + openid); 28 | if(StringUtils.isNoneBlank(codeOld)){ 29 | return codeOld; 30 | } 31 | 32 | // 获取锁,防止多次验证码生成 33 | RLock lock = redisService.getLock(Key); 34 | try { 35 | // 上锁并设置超时时间为15秒 36 | lock.lock(15, TimeUnit.SECONDS); 37 | 38 | // 生成验证码,这里可能会存在验证码重复的问题,因为我们登录只需要验证码,如果验证码重复就要重新生成 39 | String code = RandomStringUtils.randomNumeric(6); 40 | // 防重校验&重新生成 41 | for (int i = 0; i < 10 && StringUtils.isNotBlank(redisService.getValue(Key + code)); i++) { 42 | if (i < 3) { 43 | code = RandomStringUtils.randomNumeric(6); 44 | log.warn("验证码重复,生成6位字符串验证码 {} {}", openid, code); 45 | } else if (i < 5) { 46 | code = RandomStringUtils.randomNumeric(7); 47 | log.warn("验证码重复,生成7位字符串验证码 {} {}", openid, code); 48 | } else if (i < 9) { 49 | code = RandomStringUtils.randomNumeric(8); 50 | log.warn("验证码重复,生成8位字符串验证码 {} {}", openid, code); 51 | } else { 52 | return ""; 53 | } 54 | } 55 | 56 | // 保存验证码到缓存 57 | redisService.setValue(Key + openid, code); 58 | redisService.setValue(Key + code, openid); 59 | return code; 60 | } finally { 61 | lock.unlock(); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /chatgpt-data-infrastructure/src/main/java/com/luckysj/chatgpt/data/infrastructure/repository/OpenAiRepository.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.infrastructure.repository; 2 | 3 | import com.luckysj.chatgpt.data.domain.openai.model.entity.UserAccountQuotaEntity; 4 | import com.luckysj.chatgpt.data.domain.openai.model.valobj.UserAccountStatusVO; 5 | import com.luckysj.chatgpt.data.domain.openai.repository.IOpenAiRepository; 6 | import com.luckysj.chatgpt.data.infrastructure.dao.IUserAccountDao; 7 | import com.luckysj.chatgpt.data.infrastructure.po.UserAccountPO; 8 | import com.luckysj.chatgpt.data.infrastructure.redis.IRedisService; 9 | import org.springframework.stereotype.Repository; 10 | 11 | import javax.annotation.Resource; 12 | 13 | /** 14 | * @author www.luckysj.top 刘仕杰 15 | * @description openai仓储服务 16 | * @create 2023/12/09 17:05:34 17 | */ 18 | @Repository 19 | public class OpenAiRepository implements IOpenAiRepository { 20 | @Resource 21 | private IUserAccountDao userAccountDao; 22 | 23 | @Resource 24 | private IRedisService redisService; 25 | 26 | @Override 27 | public int subAccountQuota(String openai) { 28 | return userAccountDao.subAccountQuota(openai); 29 | } 30 | 31 | public final String visitCountLimitPrex = "visitCountLimit:"; 32 | 33 | @Override 34 | public UserAccountQuotaEntity queryUserAccount(String openid) { 35 | UserAccountPO userAccountPO = userAccountDao.queryUserAccount(openid); 36 | if(userAccountPO == null) return null; 37 | 38 | UserAccountQuotaEntity userAccountQuotaEntity = new UserAccountQuotaEntity(); 39 | userAccountQuotaEntity.setOpenid(userAccountPO.getOpenid()); 40 | userAccountQuotaEntity.setTotalQuota(userAccountPO.getTotalQuota()); 41 | userAccountQuotaEntity.setSurplusQuota(userAccountPO.getSurplusQuota()); 42 | userAccountQuotaEntity.transModelTypes(userAccountPO.getModelTypes()); 43 | userAccountQuotaEntity.setUserAccountStatusVO(UserAccountStatusVO.get(userAccountPO.getStatus())); 44 | return userAccountQuotaEntity; 45 | } 46 | 47 | @Override 48 | public void putRedisVisitCount(String key, Integer value, Long time) { 49 | redisService.setValue(visitCountLimitPrex + key, value, time); 50 | } 51 | 52 | @Override 53 | public int getRedisVisitCount(String key) { 54 | Integer value = redisService.getValue(visitCountLimitPrex + key); 55 | 56 | return value == null ? 0 : value; 57 | 58 | } 59 | 60 | 61 | 62 | } 63 | -------------------------------------------------------------------------------- /chatgpt-data-domain/src/main/java/com/luckysj/chatgpt/data/domain/weixin/model/entity/MessageTextEntity.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.domain.weixin.model.entity; 2 | 3 | import com.thoughtworks.xstream.annotations.XStreamAlias; 4 | import lombok.ToString; 5 | 6 | /** 7 | * @author www.luckysj.top 刘仕杰 8 | * @description 消息实体类 9 | * @create 2023/12/05 16:04:23 10 | */ 11 | @ToString 12 | public class MessageTextEntity { 13 | 14 | @XStreamAlias("MsgId") 15 | private String msgId; 16 | @XStreamAlias("ToUserName") 17 | private String toUserName; 18 | @XStreamAlias("FromUserName") 19 | private String fromUserName; 20 | @XStreamAlias("CreateTime") 21 | private String createTime; 22 | @XStreamAlias("MsgType") 23 | private String msgType; 24 | @XStreamAlias("Content") 25 | private String content; 26 | @XStreamAlias("Event") 27 | private String event; 28 | @XStreamAlias("EventKey") 29 | private String eventKey; 30 | 31 | public MessageTextEntity() { 32 | } 33 | 34 | public String getMsgId() { 35 | return msgId; 36 | } 37 | 38 | public void setMsgId(String msgId) { 39 | this.msgId = msgId; 40 | } 41 | 42 | public String getToUserName() { 43 | return toUserName; 44 | } 45 | 46 | public void setToUserName(String toUserName) { 47 | this.toUserName = toUserName; 48 | } 49 | 50 | public String getFromUserName() { 51 | return fromUserName; 52 | } 53 | 54 | public void setFromUserName(String fromUserName) { 55 | this.fromUserName = fromUserName; 56 | } 57 | 58 | public String getCreateTime() { 59 | return createTime; 60 | } 61 | 62 | public void setCreateTime(String createTime) { 63 | this.createTime = createTime; 64 | } 65 | 66 | public String getMsgType() { 67 | return msgType; 68 | } 69 | 70 | public void setMsgType(String msgType) { 71 | this.msgType = msgType; 72 | } 73 | 74 | public String getContent() { 75 | return content; 76 | } 77 | 78 | public void setContent(String content) { 79 | this.content = content; 80 | } 81 | 82 | public String getEvent() { 83 | return event; 84 | } 85 | 86 | public void setEvent(String event) { 87 | this.event = event; 88 | } 89 | 90 | public String getEventKey() { 91 | return eventKey; 92 | } 93 | 94 | public void setEventKey(String eventKey) { 95 | this.eventKey = eventKey; 96 | } 97 | } -------------------------------------------------------------------------------- /chatgpt-data-types/src/main/java/com/luckysj/chatgpt/data/types/util/QRCodeUtil.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.types.util; 2 | 3 | import com.google.zxing.BarcodeFormat; 4 | import com.google.zxing.EncodeHintType; 5 | import com.google.zxing.client.j2se.MatrixToImageWriter; 6 | import com.google.zxing.common.BitMatrix; 7 | import com.google.zxing.qrcode.QRCodeWriter; 8 | import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel; 9 | import org.apache.commons.lang3.StringUtils; 10 | 11 | import javax.imageio.ImageIO; 12 | import javax.servlet.ServletOutputStream; 13 | import java.awt.image.BufferedImage; 14 | import java.io.ByteArrayOutputStream; 15 | import java.io.IOException; 16 | import java.util.HashMap; 17 | 18 | /** 19 | * @description 二维码生成工具 20 | * @author Mr.M 21 | * @date 2022/10/3 0:03 22 | * @version 1.0 23 | */ 24 | public class QRCodeUtil { 25 | /** 26 | * 生成二维码 27 | * 28 | * @param content 二维码对应的URL 29 | * @param width 二维码图片宽度 30 | * @param height 二维码图片高度 31 | * @return 32 | */ 33 | public String createQRCode(String content, int width, int height) throws IOException { 34 | String resultImage = ""; 35 | //除了尺寸,传入内容不能为空 36 | if (!StringUtils.isEmpty(content)) { 37 | ServletOutputStream stream = null; 38 | ByteArrayOutputStream os = new ByteArrayOutputStream(); 39 | //二维码参数 40 | @SuppressWarnings("rawtypes") 41 | HashMap hints = new HashMap<>(); 42 | //指定字符编码为“utf-8” 43 | hints.put(EncodeHintType.CHARACTER_SET, "utf-8"); 44 | //L M Q H四个纠错等级从低到高,指定二维码的纠错等级为M 45 | //纠错级别越高,可以修正的错误就越多,需要的纠错码的数量也变多,相应的二维吗可储存的数据就会减少 46 | hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.M); 47 | //设置图片的边距 48 | hints.put(EncodeHintType.MARGIN, 1); 49 | 50 | try { 51 | //zxing生成二维码核心类 52 | QRCodeWriter writer = new QRCodeWriter(); 53 | //把输入文本按照指定规则转成二维吗 54 | BitMatrix bitMatrix = writer.encode(content, BarcodeFormat.QR_CODE, width, height, hints); 55 | //生成二维码图片流 56 | BufferedImage bufferedImage = MatrixToImageWriter.toBufferedImage(bitMatrix); 57 | //输出流 58 | ImageIO.write(bufferedImage, "png", os); 59 | /** 60 | * 原生转码前面没有 data:image/png;base64 这些字段,返回给前端是无法被解析,所以加上前缀 61 | */ 62 | resultImage = new String("data:image/png;base64," + EncryptUtil.encodeBase64(os.toByteArray())); 63 | return resultImage; 64 | } catch (Exception e) { 65 | e.printStackTrace(); 66 | throw new RuntimeException("生成二维码出错"); 67 | } finally { 68 | if (stream != null) { 69 | stream.flush(); 70 | stream.close(); 71 | } 72 | } 73 | } 74 | return null; 75 | } 76 | 77 | } -------------------------------------------------------------------------------- /chatgpt-data-domain/src/main/java/com/luckysj/chatgpt/data/domain/openai/service/channel/impl/ChatGPTService.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.domain.openai.service.channel.impl; 2 | 3 | import cn.bugstack.chatgpt.common.Constants; 4 | import cn.bugstack.chatgpt.domain.chat.ChatChoice; 5 | import cn.bugstack.chatgpt.domain.chat.ChatCompletionRequest; 6 | import cn.bugstack.chatgpt.domain.chat.ChatCompletionResponse; 7 | import cn.bugstack.chatgpt.domain.chat.Message; 8 | import cn.bugstack.chatgpt.session.OpenAiSession; 9 | import com.alibaba.fastjson.JSON; 10 | import com.fasterxml.jackson.core.JsonProcessingException; 11 | import com.luckysj.chatgpt.data.domain.openai.model.aggregates.ChatProcessAggregate; 12 | import com.luckysj.chatgpt.data.domain.openai.model.valobj.GenerativeModelVO; 13 | import com.luckysj.chatgpt.data.domain.openai.service.channel.OpenAiGroupService; 14 | import com.luckysj.chatgpt.data.domain.openai.service.channel.service.IGenerativeModelService; 15 | import com.luckysj.chatgpt.data.domain.openai.service.channel.service.impl.ImageGenerativeModelServiceImpl; 16 | import com.luckysj.chatgpt.data.domain.openai.service.channel.service.impl.TextGenerativeModelServiceImpl; 17 | import okhttp3.sse.EventSource; 18 | import okhttp3.sse.EventSourceListener; 19 | import org.apache.commons.lang3.StringUtils; 20 | import org.jetbrains.annotations.NotNull; 21 | import org.jetbrains.annotations.Nullable; 22 | import org.springframework.stereotype.Service; 23 | import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyEmitter; 24 | 25 | import javax.annotation.Resource; 26 | import java.util.HashMap; 27 | import java.util.List; 28 | import java.util.Map; 29 | import java.util.stream.Collectors; 30 | 31 | /** 32 | * @author www.luckysj.top 刘仕杰 33 | * @description chatgpt服务 34 | * @create 2023/12/15 18:26:20 35 | */ 36 | @Service 37 | public class ChatGPTService implements OpenAiGroupService { 38 | 39 | private Map modelServiceGroup = new HashMap<>(); 40 | 41 | // 初始化自动注入对话和图片服务 42 | public ChatGPTService(ImageGenerativeModelServiceImpl imageGenerativeModelService, TextGenerativeModelServiceImpl textGenerativeModelService) { 43 | modelServiceGroup.put(GenerativeModelVO.IMAGES, imageGenerativeModelService); 44 | modelServiceGroup.put(GenerativeModelVO.TEXT, textGenerativeModelService); 45 | } 46 | @Override 47 | public void doMessageResponse(ChatProcessAggregate chatProcess, ResponseBodyEmitter emitter) throws Exception { 48 | GenerativeModelVO generativeModelVO = chatProcess.getGenerativeModelVO(); 49 | modelServiceGroup.get(generativeModelVO).doMessageResponse(chatProcess, emitter); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /chatgpt-data-domain/src/main/java/com/luckysj/chatgpt/data/domain/openai/service/rule/impl/AccessLimitFilter.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.domain.openai.service.rule.impl; 2 | 3 | import com.luckysj.chatgpt.data.domain.openai.annotation.LogicStrategy; 4 | import com.luckysj.chatgpt.data.domain.openai.model.aggregates.ChatProcessAggregate; 5 | import com.luckysj.chatgpt.data.domain.openai.model.entity.RuleLogicEntity; 6 | import com.luckysj.chatgpt.data.domain.openai.model.entity.UserAccountQuotaEntity; 7 | import com.luckysj.chatgpt.data.domain.openai.model.valobj.LogicCheckTypeVO; 8 | import com.luckysj.chatgpt.data.domain.openai.repository.IOpenAiRepository; 9 | import com.luckysj.chatgpt.data.domain.openai.service.rule.ILogicFilter; 10 | import com.luckysj.chatgpt.data.domain.openai.service.rule.factory.DefaultLogicFactory; 11 | import lombok.extern.slf4j.Slf4j; 12 | import org.springframework.beans.factory.annotation.Value; 13 | import org.springframework.stereotype.Component; 14 | 15 | import javax.annotation.Resource; 16 | 17 | /** 18 | * @author www.luckysj.top 刘仕杰 19 | * @description 访问总次数校验规则 20 | * @create 2023/12/08 17:02:36 21 | */ 22 | @Slf4j 23 | @Component 24 | @LogicStrategy(logicMode = DefaultLogicFactory.LogicModel.ACCESS_LIMIT) 25 | public class AccessLimitFilter implements ILogicFilter { 26 | 27 | // 访问限制次数 28 | @Value("${app.config.limit-count}") 29 | private Integer limitCount; 30 | 31 | // 访问时间周期 32 | @Value("${app.config.limit-count-time}") 33 | private Long limitCountTime; 34 | 35 | // 访问白名单 36 | @Value("${app.config.white-list}") 37 | private String whiteListStr; 38 | 39 | @Resource 40 | private IOpenAiRepository openAiRepository; 41 | 42 | @Override 43 | public RuleLogicEntity fileter(ChatProcessAggregate chatProcess, UserAccountQuotaEntity userData) throws Exception { 44 | // 白名单用户不做限制 45 | if(chatProcess.isWhiteList(whiteListStr)){ 46 | return RuleLogicEntity.builder() 47 | .type(LogicCheckTypeVO.SUCCESS) 48 | .data(chatProcess) 49 | .build(); 50 | } 51 | 52 | String openid = chatProcess.getOpenid(); 53 | // 判断在时间周期内的访问次数 54 | int visitCount = openAiRepository.getRedisVisitCount(openid); 55 | if (visitCount < limitCount) { 56 | openAiRepository.putRedisVisitCount(openid, visitCount + 1, limitCountTime * 60 * 1000); 57 | return RuleLogicEntity.builder() 58 | .type(LogicCheckTypeVO.SUCCESS).data(chatProcess).build(); 59 | } 60 | 61 | return RuleLogicEntity.builder() 62 | .info("账户在" + limitCountTime + "分钟内访问次数超过" + limitCount + "次,访问过于频繁,请稍后重试!") 63 | .type(LogicCheckTypeVO.REFUSE).data(chatProcess).build(); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /chatgpt-data-types/src/main/java/com/luckysj/chatgpt/data/types/util/EncryptUtil.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.types.util; 2 | 3 | 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | 7 | import java.io.UnsupportedEncodingException; 8 | import java.net.URLDecoder; 9 | import java.net.URLEncoder; 10 | import java.util.Base64; 11 | 12 | public class EncryptUtil { 13 | private static final Logger logger = LoggerFactory.getLogger(com.luckysj.chatgpt.data.types.util.EncryptUtil.class); 14 | 15 | public static String encodeBase64(byte[] bytes){ 16 | String encoded = Base64.getEncoder().encodeToString(bytes); 17 | return encoded; 18 | } 19 | 20 | public static byte[] decodeBase64(String str){ 21 | byte[] bytes = null; 22 | bytes = Base64.getDecoder().decode(str); 23 | return bytes; 24 | } 25 | 26 | public static String encodeUTF8StringBase64(String str){ 27 | String encoded = null; 28 | try { 29 | encoded = Base64.getEncoder().encodeToString(str.getBytes("utf-8")); 30 | } catch (UnsupportedEncodingException e) { 31 | logger.warn("不支持的编码格式",e); 32 | } 33 | return encoded; 34 | 35 | } 36 | 37 | public static String decodeUTF8StringBase64(String str){ 38 | String decoded = null; 39 | byte[] bytes = Base64.getDecoder().decode(str); 40 | try { 41 | decoded = new String(bytes,"utf-8"); 42 | }catch(UnsupportedEncodingException e){ 43 | logger.warn("不支持的编码格式",e); 44 | } 45 | return decoded; 46 | } 47 | 48 | public static String encodeURL(String url) { 49 | String encoded = null; 50 | try { 51 | encoded = URLEncoder.encode(url, "utf-8"); 52 | } catch (UnsupportedEncodingException e) { 53 | logger.warn("URLEncode失败", e); 54 | } 55 | return encoded; 56 | } 57 | 58 | 59 | public static String decodeURL(String url) { 60 | String decoded = null; 61 | try { 62 | decoded = URLDecoder.decode(url, "utf-8"); 63 | } catch (UnsupportedEncodingException e) { 64 | logger.warn("URLDecode失败", e); 65 | } 66 | return decoded; 67 | } 68 | 69 | public static void main(String [] args){ 70 | String str = "abcd{'a':'b'}"; 71 | String encoded = com.luckysj.chatgpt.data.types.util.EncryptUtil.encodeUTF8StringBase64(str); 72 | String decoded = com.luckysj.chatgpt.data.types.util.EncryptUtil.decodeUTF8StringBase64(encoded); 73 | System.out.println(str); 74 | System.out.println(encoded); 75 | System.out.println(decoded); 76 | 77 | String url = "== wo"; 78 | String urlEncoded = com.luckysj.chatgpt.data.types.util.EncryptUtil.encodeURL(url); 79 | String urlDecoded = com.luckysj.chatgpt.data.types.util.EncryptUtil.decodeURL(urlEncoded); 80 | 81 | System.out.println(url); 82 | System.out.println(urlEncoded); 83 | System.out.println(urlDecoded); 84 | } 85 | 86 | 87 | } 88 | -------------------------------------------------------------------------------- /chatgpt-data-app/data/log/log-info-2023-12-16.0.log: -------------------------------------------------------------------------------- 1 | 23-12-16.21:15:04.752 [main ] INFO TongYiQianWenTest - Starting TongYiQianWenTest using Java 1.8.0_361 on DESKTOP-BU4MLU6 with PID 10060 (started by kitie in D:\IDEA_project\chatgpt-data\chatgpt-data-app) 2 | 23-12-16.21:15:04.752 [main ] INFO TongYiQianWenTest - The following 1 profile is active: "dev" 3 | 23-12-16.21:15:06.162 [main ] INFO TomcatWebServer - Tomcat initialized with port(s): 0 (http) 4 | 23-12-16.21:15:06.171 [main ] INFO Http11NioProtocol - Initializing ProtocolHandler ["http-nio-auto-1"] 5 | 23-12-16.21:15:06.171 [main ] INFO StandardService - Starting service [Tomcat] 6 | 23-12-16.21:15:06.171 [main ] INFO StandardEngine - Starting Servlet engine: [Apache Tomcat/9.0.75] 7 | 23-12-16.21:15:06.318 [main ] INFO [/] - Initializing Spring embedded WebApplicationContext 8 | 23-12-16.21:15:06.318 [main ] INFO ServletWebServerApplicationContext - Root WebApplicationContext: initialization completed in 1543 ms 9 | 23-12-16.21:15:07.882 [main ] INFO Http11NioProtocol - Starting ProtocolHandler ["http-nio-auto-1"] 10 | 23-12-16.21:15:07.907 [main ] INFO TomcatWebServer - Tomcat started on port(s): 50921 (http) with context path '' 11 | 23-12-16.21:15:07.921 [main ] INFO TongYiQianWenTest - Started TongYiQianWenTest in 3.481 seconds (JVM running for 4.241) 12 | 23-12-16.21:17:52.704 [main ] INFO TongYiQianWenTest - Starting TongYiQianWenTest using Java 1.8.0_361 on DESKTOP-BU4MLU6 with PID 7804 (started by kitie in D:\IDEA_project\chatgpt-data\chatgpt-data-app) 13 | 23-12-16.21:17:52.704 [main ] INFO TongYiQianWenTest - The following 1 profile is active: "dev" 14 | 23-12-16.21:17:53.882 [main ] INFO TomcatWebServer - Tomcat initialized with port(s): 0 (http) 15 | 23-12-16.21:17:53.895 [main ] INFO Http11NioProtocol - Initializing ProtocolHandler ["http-nio-auto-1"] 16 | 23-12-16.21:17:53.895 [main ] INFO StandardService - Starting service [Tomcat] 17 | 23-12-16.21:17:53.895 [main ] INFO StandardEngine - Starting Servlet engine: [Apache Tomcat/9.0.75] 18 | 23-12-16.21:17:54.012 [main ] INFO [/] - Initializing Spring embedded WebApplicationContext 19 | 23-12-16.21:17:54.012 [main ] INFO ServletWebServerApplicationContext - Root WebApplicationContext: initialization completed in 1290 ms 20 | 23-12-16.21:17:55.491 [main ] INFO Http11NioProtocol - Starting ProtocolHandler ["http-nio-auto-1"] 21 | 23-12-16.21:17:55.512 [main ] INFO TomcatWebServer - Tomcat started on port(s): 51797 (http) with context path '' 22 | 23-12-16.21:17:55.522 [main ] INFO TongYiQianWenTest - Started TongYiQianWenTest in 3.109 seconds (JVM running for 3.808) 23 | 23-12-16.21:18:00.032 [scheduling-1 ] INFO HikariDataSource - HikariPool-1 - Starting... 24 | -------------------------------------------------------------------------------- /chatgpt-data-app/src/test/java/com/luckysj/chatgpt/data/TongYiQianWenTest.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data; 2 | 3 | import com.alibaba.dashscope.aigc.generation.Generation; 4 | import com.alibaba.dashscope.aigc.generation.GenerationResult; 5 | import com.alibaba.dashscope.aigc.generation.models.QwenParam; 6 | import com.alibaba.dashscope.common.Message; 7 | import com.alibaba.dashscope.common.MessageManager; 8 | import com.alibaba.dashscope.common.Role; 9 | import com.alibaba.dashscope.exception.InputRequiredException; 10 | import com.alibaba.dashscope.exception.NoApiKeyException; 11 | import com.alibaba.dashscope.utils.Constants; 12 | import com.alibaba.dashscope.utils.JsonUtils; 13 | import com.alipay.api.AlipayApiException; 14 | import com.alipay.api.AlipayClient; 15 | import com.alipay.api.DefaultAlipayClient; 16 | import com.alipay.api.request.AlipayTradeWapPayRequest; 17 | import org.junit.jupiter.api.Test; 18 | import org.junit.runner.RunWith; 19 | import org.springframework.boot.test.context.SpringBootTest; 20 | import org.springframework.test.context.junit4.SpringRunner; 21 | 22 | /** 23 | * @author www.luckysj.top 刘仕杰 24 | * @description 通义千问测试接口 25 | * @create 2023/12/16 21:12:51 26 | */ 27 | @RunWith(SpringRunner.class) 28 | @SpringBootTest(classes = Application.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) 29 | public class TongYiQianWenTest { 30 | /** 31 | * @description 对话测试(非流式) 32 | * @date 2023/12/16 21:13:20 33 | */ 34 | @Test 35 | public void testChatRquest(){ 36 | try { 37 | Constants.apiKey="sk-d2275c7a8b35449db8fb8b1d053c4ba5";//这里填写自己申请的APIKEY 38 | Generation gen = new Generation(); 39 | MessageManager msgManager = new MessageManager(10); 40 | Message systemMsg = 41 | Message.builder().role(Role.SYSTEM.getValue()).content("You are a helpful assistant.").build(); 42 | Message userMsg = Message.builder().role(Role.USER.getValue()).content("你好,周末去哪里玩?").build(); 43 | msgManager.add(systemMsg); 44 | msgManager.add(userMsg); 45 | QwenParam param = 46 | QwenParam.builder().model(Generation.Models.QWEN_PLUS).messages(msgManager.get()) 47 | .resultFormat(QwenParam.ResultFormat.MESSAGE) 48 | .topP(0.8) 49 | .enableSearch(true) 50 | .build(); 51 | GenerationResult result = gen.call(param); 52 | System.out.println(result); 53 | msgManager.add(result); 54 | System.out.println(JsonUtils.toJson(result)); 55 | param.setPrompt("你好,请问你可以帮我完成哪些工作"); 56 | param.setMessages(msgManager.get()); 57 | result = gen.call(param); 58 | System.out.println(result); 59 | System.out.println(JsonUtils.toJson(result)); 60 | } catch (NoApiKeyException e) { 61 | e.printStackTrace(); 62 | } catch (InputRequiredException e) { 63 | e.printStackTrace(); 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /chatgpt-data-trigger/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | chatgpt-data 7 | com.luckysj 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | chatgpt-data-trigger 13 | 14 | 15 | 16 | org.springframework.boot 17 | spring-boot-starter-web 18 | 19 | 20 | com.alibaba 21 | fastjson 22 | 23 | 24 | org.springframework 25 | spring-tx 26 | 27 | 28 | org.apache.commons 29 | commons-lang3 30 | 31 | 32 | 33 | cn.bugstack.chatgpt 34 | chatgpt-sdk-java 35 | 36 | 37 | com.luckysj 38 | chatgpt-data-types 39 | 1.0-SNAPSHOT 40 | 41 | 42 | com.luckysj 43 | chatgpt-data-domain 44 | 1.0-SNAPSHOT 45 | compile 46 | 47 | 48 | 49 | org.springframework.boot 50 | spring-boot-starter-actuator 51 | 52 | 53 | 54 | io.micrometer 55 | micrometer-registry-prometheus 56 | 57 | 58 | org.redisson 59 | redisson-spring-boot-starter 60 | ${redisson-spring-boot-starter.version} 61 | 62 | 63 | org.aspectj 64 | aspectjweaver 65 | 66 | 67 | 68 | org.springframework.boot 69 | spring-boot-starter-amqp 70 | 71 | 72 | 73 | 74 | 75 | chatgpt-data-trigger 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /chatgpt-data-domain/src/main/java/com/luckysj/chatgpt/data/domain/openai/service/ChatService.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.domain.openai.service; 2 | 3 | import cn.bugstack.chatgpt.common.Constants; 4 | import cn.bugstack.chatgpt.domain.chat.ChatChoice; 5 | import cn.bugstack.chatgpt.domain.chat.ChatCompletionRequest; 6 | import cn.bugstack.chatgpt.domain.chat.ChatCompletionResponse; 7 | import cn.bugstack.chatgpt.domain.chat.Message; 8 | import com.alibaba.fastjson.JSON; 9 | import com.fasterxml.jackson.core.JsonProcessingException; 10 | import com.luckysj.chatgpt.data.domain.openai.model.aggregates.ChatProcessAggregate; 11 | import com.luckysj.chatgpt.data.domain.openai.model.entity.RuleLogicEntity; 12 | import com.luckysj.chatgpt.data.domain.openai.model.entity.UserAccountQuotaEntity; 13 | import com.luckysj.chatgpt.data.domain.openai.model.valobj.LogicCheckTypeVO; 14 | import com.luckysj.chatgpt.data.domain.openai.service.channel.impl.ChatGLMService; 15 | import com.luckysj.chatgpt.data.domain.openai.service.channel.impl.ChatGPTService; 16 | import com.luckysj.chatgpt.data.domain.openai.service.rule.ILogicFilter; 17 | import com.luckysj.chatgpt.data.domain.openai.service.rule.factory.DefaultLogicFactory; 18 | import okhttp3.sse.EventSource; 19 | import okhttp3.sse.EventSourceListener; 20 | import org.apache.commons.lang3.StringUtils; 21 | import org.jetbrains.annotations.NotNull; 22 | import org.jetbrains.annotations.Nullable; 23 | import org.springframework.stereotype.Service; 24 | import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyEmitter; 25 | 26 | import javax.annotation.Resource; 27 | import java.util.List; 28 | import java.util.Map; 29 | import java.util.stream.Collectors; 30 | 31 | @Service 32 | public class ChatService extends AbstractChatService{ 33 | 34 | // 校验规则工厂 35 | @Resource 36 | private DefaultLogicFactory logicFactory; 37 | 38 | public ChatService(ChatGPTService chatGPTService, ChatGLMService chatGLMService) { 39 | super(chatGPTService, chatGLMService); 40 | } 41 | 42 | @Override 43 | protected RuleLogicEntity doCheckLogic(ChatProcessAggregate chatProcess, UserAccountQuotaEntity userAccountQuotaEntity, String... logics) throws Exception { 44 | // 获取到校验规则map 45 | Map logicFilterMap = logicFactory.openLogicFilter(); 46 | // 调用对应校验规则进行校验 47 | RuleLogicEntity entity = null; 48 | for (String code : logics) { 49 | // 传入规则为空,不需要校验 50 | if(DefaultLogicFactory.LogicModel.NULL.getCode().equals(code)) continue; 51 | // 校验 52 | entity = logicFilterMap.get(code).fileter(chatProcess, userAccountQuotaEntity); 53 | if (!LogicCheckTypeVO.SUCCESS.equals(entity.getType())){ 54 | return entity; 55 | } 56 | } 57 | 58 | // entity 为 null的情况一般为传入的logics校验规则参数为空,即没有使用任何校验规则,这样也算校验通过 59 | return entity != null ? entity : RuleLogicEntity.builder() 60 | .data(chatProcess).type(LogicCheckTypeVO.SUCCESS).build(); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /chatgpt-data-domain/src/main/java/com/luckysj/chatgpt/data/domain/openai/service/rule/impl/SensitiveWordFilter.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.domain.openai.service.rule.impl; 2 | 3 | import com.github.houbb.sensitive.word.bs.SensitiveWordBs; 4 | import com.luckysj.chatgpt.data.domain.openai.annotation.LogicStrategy; 5 | import com.luckysj.chatgpt.data.domain.openai.model.aggregates.ChatProcessAggregate; 6 | import com.luckysj.chatgpt.data.domain.openai.model.entity.MessageEntity; 7 | import com.luckysj.chatgpt.data.domain.openai.model.entity.RuleLogicEntity; 8 | import com.luckysj.chatgpt.data.domain.openai.model.entity.UserAccountQuotaEntity; 9 | import com.luckysj.chatgpt.data.domain.openai.model.valobj.LogicCheckTypeVO; 10 | import com.luckysj.chatgpt.data.domain.openai.service.rule.ILogicFilter; 11 | import com.luckysj.chatgpt.data.domain.openai.service.rule.factory.DefaultLogicFactory; 12 | import lombok.extern.slf4j.Slf4j; 13 | import org.springframework.beans.factory.annotation.Value; 14 | import org.springframework.stereotype.Component; 15 | import org.springframework.stereotype.Service; 16 | 17 | import javax.annotation.Resource; 18 | import java.util.List; 19 | import java.util.stream.Collectors; 20 | 21 | /** 22 | * @author www.luckysj.top 刘仕杰 23 | * @description 敏感词校验规则 24 | * @create 2023/12/08 17:02:36 25 | */ 26 | @Slf4j 27 | @Component 28 | @LogicStrategy(logicMode = DefaultLogicFactory.LogicModel.SENSITIVE_WORD) 29 | public class SensitiveWordFilter implements ILogicFilter { 30 | // 敏感词的引导类 31 | @Resource 32 | private SensitiveWordBs words; 33 | 34 | // 访问白名单 35 | @Value("${app.config.white-list}") 36 | private String whiteListStr; 37 | 38 | @Override 39 | public RuleLogicEntity fileter(ChatProcessAggregate chatProcess, UserAccountQuotaEntity userData) throws Exception { 40 | // 白名单用户不做限制 41 | if(chatProcess.isWhiteList(whiteListStr)){ 42 | return RuleLogicEntity.builder() 43 | .type(LogicCheckTypeVO.SUCCESS).data(chatProcess).build(); 44 | } 45 | 46 | ChatProcessAggregate newChatProcessAggregate = new ChatProcessAggregate(); 47 | newChatProcessAggregate.setOpenid(chatProcess.getOpenid()); 48 | newChatProcessAggregate.setModel(chatProcess.getModel()); 49 | 50 | List newMessages = chatProcess.getMessages().stream() 51 | .map(message -> { 52 | String content = message.getContent(); 53 | // 替换敏感词 54 | String replace = words.replace(content); 55 | return MessageEntity.builder() 56 | .role(message.getRole()) 57 | .name(message.getName()) 58 | .content(replace) 59 | .build(); 60 | }) 61 | .collect(Collectors.toList()); 62 | 63 | newChatProcessAggregate.setMessages(newMessages); 64 | 65 | return RuleLogicEntity.builder() 66 | .type(LogicCheckTypeVO.SUCCESS) 67 | .data(newChatProcessAggregate) 68 | .build(); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /chatgpt-data-domain/src/main/java/com/luckysj/chatgpt/data/domain/weixin/service/message/WeiXinBehaviorService.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.domain.weixin.service.message; 2 | 3 | import com.google.common.cache.Cache; 4 | import com.luckysj.chatgpt.data.domain.weixin.model.entity.MessageTextEntity; 5 | import com.luckysj.chatgpt.data.domain.weixin.model.entity.UserBehaviorMessageEntity; 6 | import com.luckysj.chatgpt.data.domain.weixin.model.valobj.ContentCodeVO; 7 | import com.luckysj.chatgpt.data.domain.weixin.model.valobj.MsgTypeVO; 8 | import com.luckysj.chatgpt.data.domain.weixin.repository.IWeiXinRepository; 9 | import com.luckysj.chatgpt.data.domain.weixin.service.IWeiXinBehaviorService; 10 | import com.luckysj.chatgpt.data.types.exception.ChatGPTException; 11 | import com.luckysj.chatgpt.data.types.sdk.weixin.XmlUtil; 12 | import org.apache.commons.lang3.RandomStringUtils; 13 | import org.apache.commons.lang3.StringUtils; 14 | import org.springframework.beans.factory.annotation.Value; 15 | import org.springframework.stereotype.Service; 16 | 17 | import javax.annotation.Resource; 18 | 19 | /** 20 | * @author www.luckysj.top 刘仕杰 21 | * @description 受理用户行为接口实现类 22 | * @create 2023/12/05 15:46:34 23 | */ 24 | @Service 25 | public class WeiXinBehaviorService implements IWeiXinBehaviorService { 26 | 27 | @Value("${wx.config.originalid}") 28 | private String originalId; 29 | 30 | // 现在已替换成redis作为缓存 31 | // @Resource 32 | // private Cache codeCache; 33 | 34 | @Resource 35 | private IWeiXinRepository weiXinRepository; 36 | 37 | /** 38 | * 1. 用户的请求行文,分为事件event、消息text,这里我们只处理消息内容 39 | * 2. 用户行为、消息类型,是多样性的,这部分如果用户有更多的扩展需求,可以使用设计模式【模板模式 + 策略模式 + 工厂模式】,分拆逻辑。 40 | */ 41 | @Override 42 | public String acceptUserBehavior(UserBehaviorMessageEntity userBehaviorMessageEntity) { 43 | // Event 事件类型,忽略处理 44 | if (MsgTypeVO.EVENT.getCode().equals(userBehaviorMessageEntity.getMsgType())) { 45 | return ""; 46 | } 47 | 48 | // Text 文本类型 49 | if (MsgTypeVO.TEXT.getCode().equals(userBehaviorMessageEntity.getMsgType())) { 50 | 51 | String messageContent = userBehaviorMessageEntity.getContent(); 52 | String replay = ""; 53 | if(ContentCodeVO.GENCODE.getCode().equals(messageContent)){ 54 | // 获取验证码 55 | String code = weiXinRepository.genCode(userBehaviorMessageEntity.getOpenId()); 56 | replay = String.format("您的验证码为:%s 有效期%d分钟!", code, 3); 57 | } else if (ContentCodeVO.GETOPENID.getCode().equals(messageContent)) { 58 | // 获取到openid 59 | String openid = userBehaviorMessageEntity.getOpenId(); 60 | replay = String.format("您的OpenId为:%s 请谨慎保管,以免账户泄露!", openid); 61 | } 62 | 63 | // 反馈信息[文本] 64 | MessageTextEntity res = new MessageTextEntity(); 65 | res.setToUserName(userBehaviorMessageEntity.getOpenId()); 66 | res.setFromUserName(originalId); 67 | res.setCreateTime(String.valueOf(System.currentTimeMillis() / 1000L)); 68 | res.setMsgType("text"); 69 | res.setContent(replay); 70 | return XmlUtil.beanToXml(res); 71 | } 72 | 73 | throw new ChatGPTException(userBehaviorMessageEntity.getMsgType() + " 未被处理的行为类型 Err!"); 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /chatgpt-data-types/src/main/java/com/luckysj/chatgpt/data/types/util/IPUtils.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.types.util; 2 | 3 | 4 | import org.apache.commons.lang3.StringUtils; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | 8 | import javax.servlet.http.HttpServletRequest; 9 | import java.net.InetAddress; 10 | import java.net.UnknownHostException; 11 | 12 | /** 13 | * @author www.luckysj.top 刘仕杰 14 | * @description Ip地址工具类 15 | * @create 2024/01/01 15:53:42 16 | */ 17 | public class IPUtils { 18 | private static Logger logger = LoggerFactory.getLogger(IPUtils.class); 19 | private static final String IP_UTILS_FLAG = ","; 20 | private static final String UNKNOWN = "unknown"; 21 | private static final String LOCALHOST_IP = "0:0:0:0:0:0:0:1"; 22 | private static final String LOCALHOST_IP1 = "127.0.0.1"; 23 | 24 | /** 25 | * 获取IP地址 26 | *

27 | * 使用Nginx等反向代理软件, 则不能通过request.getRemoteAddr()获取IP地址 28 | * 如果使用了多级反向代理的话,X-Forwarded-For的值并不止一个,而是一串IP地址,X-Forwarded-For中第一个非unknown的有效IP字符串,则为真实IP地址 29 | */ 30 | public static String getIpAddr(HttpServletRequest request) { 31 | String ip = null; 32 | try { 33 | //以下两个获取在k8s中,将真实的客户端IP,放到了x-Original-Forwarded-For。而将WAF的回源地址放到了 x-Forwarded-For了。 34 | ip = request.getHeader("X-Original-Forwarded-For"); 35 | if (StringUtils.isEmpty(ip) || UNKNOWN.equalsIgnoreCase(ip)) { 36 | ip = request.getHeader("X-Forwarded-For"); 37 | } 38 | //获取nginx等代理的ip 39 | if (StringUtils.isEmpty(ip) || UNKNOWN.equalsIgnoreCase(ip)) { 40 | ip = request.getHeader("x-forwarded-for"); 41 | } 42 | if (StringUtils.isEmpty(ip) || UNKNOWN.equalsIgnoreCase(ip)) { 43 | ip = request.getHeader("Proxy-Client-IP"); 44 | } 45 | if (StringUtils.isEmpty(ip) || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) { 46 | ip = request.getHeader("WL-Proxy-Client-IP"); 47 | } 48 | if (StringUtils.isEmpty(ip) || UNKNOWN.equalsIgnoreCase(ip)) { 49 | ip = request.getHeader("HTTP_CLIENT_IP"); 50 | } 51 | if (StringUtils.isEmpty(ip) || UNKNOWN.equalsIgnoreCase(ip)) { 52 | ip = request.getHeader("HTTP_X_FORWARDED_FOR"); 53 | } 54 | //兼容k8s集群获取ip 55 | if (StringUtils.isEmpty(ip) || UNKNOWN.equalsIgnoreCase(ip)) { 56 | ip = request.getRemoteAddr(); 57 | if (LOCALHOST_IP1.equalsIgnoreCase(ip) || LOCALHOST_IP.equalsIgnoreCase(ip)) { 58 | //根据网卡取本机配置的IP 59 | InetAddress iNet = null; 60 | try { 61 | iNet = InetAddress.getLocalHost(); 62 | } catch (UnknownHostException e) { 63 | logger.error("getClientIp error: {}", e); 64 | } 65 | ip = iNet.getHostAddress(); 66 | } 67 | } 68 | } catch (Exception e) { 69 | logger.error("IPUtils ERROR ", e); 70 | } 71 | //使用代理,则获取第一个IP地址 72 | if (!StringUtils.isEmpty(ip) && ip.indexOf(IP_UTILS_FLAG) > 0) { 73 | ip = ip.substring(0, ip.indexOf(IP_UTILS_FLAG)); 74 | } 75 | 76 | return ip; 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /chatgpt-data-domain/src/main/java/com/luckysj/chatgpt/data/domain/openai/service/channel/impl/ChatGLMService.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.domain.openai.service.channel.impl; 2 | 3 | import cn.bugstack.chatglm.model.*; 4 | import cn.bugstack.chatglm.session.OpenAiSession; 5 | import com.alibaba.fastjson.JSON; 6 | import com.fasterxml.jackson.core.JsonProcessingException; 7 | import com.luckysj.chatgpt.data.domain.openai.model.aggregates.ChatProcessAggregate; 8 | import com.luckysj.chatgpt.data.domain.openai.service.channel.OpenAiGroupService; 9 | import com.luckysj.chatgpt.data.types.enums.ChatGLMModel; 10 | import com.luckysj.chatgpt.data.types.exception.ChatGPTException; 11 | import lombok.extern.slf4j.Slf4j; 12 | import okhttp3.sse.EventSource; 13 | import okhttp3.sse.EventSourceListener; 14 | import org.jetbrains.annotations.Nullable; 15 | import org.springframework.stereotype.Service; 16 | import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyEmitter; 17 | 18 | import javax.annotation.Resource; 19 | import java.util.List; 20 | import java.util.stream.Collectors; 21 | 22 | /** 23 | * @author www.luckysj.top 刘仕杰 24 | * @description Chatglm服务 25 | * @create 2023/12/15 18:26:07 26 | */ 27 | @Slf4j 28 | @Service 29 | public class ChatGLMService implements OpenAiGroupService { 30 | 31 | @Resource 32 | protected OpenAiSession chatGlMOpenAiSession; 33 | 34 | @Override 35 | public void doMessageResponse(ChatProcessAggregate chatProcess, ResponseBodyEmitter emitter) throws JsonProcessingException { 36 | // 1. 请求消息 37 | List prompts = chatProcess.getMessages().stream() 38 | .map(entity -> ChatCompletionRequest.Prompt.builder() 39 | .role(Role.user.getCode()) 40 | .content(entity.getContent()) 41 | .build()) 42 | .collect(Collectors.toList()); 43 | 44 | // 2. 封装参数 45 | ChatCompletionRequest request = new ChatCompletionRequest(); 46 | request.setModel(Model.valueOf(ChatGLMModel.get(chatProcess.getModel()).name())); // chatGLM_6b_SSE、chatglm_lite、chatglm_lite_32k、chatglm_std、chatglm_pro 47 | request.setPrompt(prompts); 48 | 49 | // 3.请求应答 50 | chatGlMOpenAiSession.completions(request, new EventSourceListener() { 51 | @Override 52 | public void onEvent(EventSource eventSource, @Nullable String id, @Nullable String type, String data) { 53 | ChatCompletionResponse response = JSON.parseObject(data, ChatCompletionResponse.class); 54 | 55 | // 发送信息 56 | if (EventType.add.getCode().equals(type)){ 57 | try { 58 | emitter.send(response.getData()); 59 | } catch (Exception e) { 60 | throw new ChatGPTException(e.getMessage()); 61 | } 62 | } 63 | 64 | // type 消息类型,add 增量,finish 结束,error 错误,interrupted 中断 65 | if (EventType.finish.getCode().equals(type)) { 66 | ChatCompletionResponse.Meta meta = JSON.parseObject(response.getMeta(), ChatCompletionResponse.Meta.class); 67 | log.info("[输出结束] Tokens {}", JSON.toJSONString(meta)); 68 | } 69 | } 70 | 71 | @Override 72 | public void onClosed(EventSource eventSource) { 73 | emitter.complete(); 74 | } 75 | 76 | }); 77 | 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /chatgpt-data-domain/src/main/java/com/luckysj/chatgpt/data/domain/openai/service/channel/service/impl/ImageGenerativeModelServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.domain.openai.service.channel.service.impl; 2 | 3 | import cn.bugstack.chatgpt.common.Constants; 4 | import cn.bugstack.chatgpt.domain.images.ImageEnum; 5 | import cn.bugstack.chatgpt.domain.images.ImageRequest; 6 | import cn.bugstack.chatgpt.domain.images.ImageResponse; 7 | import cn.bugstack.chatgpt.domain.images.Item; 8 | import cn.bugstack.chatgpt.session.OpenAiSession; 9 | import com.luckysj.chatgpt.data.domain.openai.model.aggregates.ChatProcessAggregate; 10 | import com.luckysj.chatgpt.data.domain.openai.model.entity.MessageEntity; 11 | import com.luckysj.chatgpt.data.domain.openai.service.channel.service.IGenerativeModelService; 12 | import lombok.extern.slf4j.Slf4j; 13 | import org.springframework.beans.factory.annotation.Autowired; 14 | import org.springframework.stereotype.Service; 15 | import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyEmitter; 16 | 17 | import java.io.IOException; 18 | import java.util.List; 19 | import java.util.concurrent.CompletableFuture; 20 | 21 | /** 22 | * @author www.luckysj.top 刘仕杰 23 | * @description 图片生成服务 24 | * @create 2023/12/18 17:50:07 25 | */ 26 | @Slf4j 27 | @Service 28 | public class ImageGenerativeModelServiceImpl implements IGenerativeModelService { 29 | 30 | @Autowired(required = false) 31 | protected OpenAiSession openAiSession; 32 | 33 | @Override 34 | public void doMessageResponse(ChatProcessAggregate chatProcess, ResponseBodyEmitter emitter) throws IOException { 35 | if (null == openAiSession) { 36 | emitter.send("模型调用未开启,可以选择其他模型对话!"); 37 | emitter.complete(); 38 | return; 39 | } 40 | 41 | emitter.send("图画开始生成,平均时间为10s-20s,请耐心等待~\n"); 42 | emitter.send("-------------------------------------\n"); 43 | 44 | // 异步执行绘画请求,不异步执行的话会导致上面的提示信息无法提前响应给前端 45 | CompletableFuture.runAsync(() -> { 46 | try { 47 | // 封装请求信息,这里我们提取最近的的用户消息作为上下文 48 | StringBuilder prompt = new StringBuilder(); 49 | List messages = chatProcess.getMessages(); 50 | for (MessageEntity message : messages) { 51 | String role = message.getRole(); 52 | if (Constants.Role.USER.getCode().equals(role)) { 53 | prompt.append(message.getContent()); 54 | prompt.append("\r\n"); 55 | } 56 | } 57 | 58 | // 绘图请求信息 59 | ImageRequest request = ImageRequest.builder() 60 | .prompt(prompt.toString()) 61 | .model(chatProcess.getModel()) 62 | .size(ImageEnum.Size.size_1024.getCode()) 63 | .build(); 64 | 65 | // 异步请求绘图 66 | ImageResponse imageResponse = openAiSession.genImages(request); 67 | List items = imageResponse.getData(); 68 | 69 | // chatgpt可以一次生成多张图片,默认是一张 70 | for (Item item : items) { 71 | String url = item.getUrl(); 72 | log.info("url:{}", url); 73 | // md5格式输出给前端 74 | emitter.send("![](" + url + ")"); 75 | } 76 | 77 | emitter.complete(); 78 | } catch (IOException e) { 79 | log.error("Error generating images: {}", e.getMessage(), e); 80 | emitter.completeWithError(e); 81 | } 82 | }); 83 | 84 | } 85 | 86 | } 87 | -------------------------------------------------------------------------------- /chatgpt-data-trigger/src/main/java/com/luckysj/chatgpt/data/trigger/http/AuthController.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.trigger.http; 2 | 3 | import com.luckysj.chatgpt.data.domain.auth.model.entity.AuthStateEntity; 4 | import com.luckysj.chatgpt.data.domain.auth.model.valobj.AuthTypeVo; 5 | import com.luckysj.chatgpt.data.domain.auth.service.IAuthService; 6 | import com.luckysj.chatgpt.data.types.annotation.AccessInterceptor; 7 | import com.luckysj.chatgpt.data.types.common.Constants; 8 | import com.luckysj.chatgpt.data.types.model.Response; 9 | import io.micrometer.core.annotation.Timed; 10 | import lombok.extern.slf4j.Slf4j; 11 | import org.springframework.web.bind.annotation.*; 12 | 13 | import javax.annotation.Resource; 14 | import javax.servlet.http.HttpServletRequest; 15 | 16 | /** 17 | * @author www.luckysj.top 刘仕杰 18 | * @description 登录授权接口 19 | * @create 2023/12/05 16:18:06 20 | */ 21 | @Slf4j 22 | @RestController 23 | @CrossOrigin("${app.config.cross-origin}") 24 | @RequestMapping("/api/${app.config.api-version}/auth/") 25 | public class AuthController { 26 | @Resource 27 | private IAuthService authService; 28 | 29 | /** 30 | * @description 验证码登录授权接口 31 | * @param code 验证码 32 | * @return Response data为token的Response 33 | * @date 2023/12/08 16:29:01 34 | */ 35 | @Timed(value="login_http",description="用户验证码登录接口") 36 | @PostMapping(value = "/login") 37 | @AccessInterceptor(key = "request_ip", fallbackMethod = "loginErr", permitsPerSecond = 10L, blacklistCount = 10) 38 | public Response doLogin(@RequestParam String code, HttpServletRequest request){ 39 | try { 40 | AuthStateEntity authStateEntity = authService.doLogin(code); 41 | // 校验不通过 42 | if(!authStateEntity.getCode().equals(AuthTypeVo.CODE_SUCCESS.getCode())){ 43 | return Response.builder() 44 | .code(Constants.ResponseCode.TOKEN_ERROR.getCode()) 45 | .info(Constants.ResponseCode.TOKEN_ERROR.getInfo()) 46 | .build(); 47 | } 48 | 49 | //校验通过,放行,携带token信息 50 | return Response.builder() 51 | .code(Constants.ResponseCode.SUCCESS.getCode()) 52 | .info(Constants.ResponseCode.SUCCESS.getInfo()) 53 | .data(authStateEntity.getToken()) 54 | .build(); 55 | } catch (Exception e) { 56 | log.error("鉴权登录校验失败,验证码: {},错误消息:{}", code, e.getMessage()); 57 | return Response.builder() 58 | .code(Constants.ResponseCode.UN_ERROR.getCode()) 59 | .info(Constants.ResponseCode.UN_ERROR.getInfo()) 60 | .build(); 61 | } 62 | } 63 | 64 | // 获取身份信息测试接口(本地测试用,不需要通过微信公众号登录,防止影响原项目的运行) 65 | @RequestMapping(value = "getCode", method = RequestMethod.GET) 66 | public Response getACode(@RequestParam String openid) { 67 | String code = authService.getAuthCode(openid); 68 | return Response.builder() 69 | .code(Constants.ResponseCode.SUCCESS.getCode()) 70 | .info(Constants.ResponseCode.SUCCESS.getInfo()) 71 | .data(code) 72 | .build(); 73 | } 74 | 75 | public Response loginErr(String code, HttpServletRequest request) { 76 | System.out.println(code); 77 | return Response.builder() 78 | .code(Constants.ResponseCode.FREQUENCY_LIMITED.getCode()) 79 | .info(Constants.ResponseCode.FREQUENCY_LIMITED.getInfo()) 80 | .data(code) 81 | .build(); 82 | } 83 | 84 | 85 | } 86 | -------------------------------------------------------------------------------- /chatgpt-data-types/src/main/java/com/luckysj/chatgpt/data/types/util/IdWorkerUtils.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.types.util; 2 | 3 | import java.util.Random; 4 | 5 | /** 6 | * snow flow . 7 | * 8 | */ 9 | public final class IdWorkerUtils { 10 | 11 | private static final Random RANDOM = new Random(); 12 | 13 | private static final long WORKER_ID_BITS = 5L; 14 | 15 | private static final long DATACENTERIDBITS = 5L; 16 | 17 | private static final long MAX_WORKER_ID = ~(-1L << WORKER_ID_BITS); 18 | 19 | private static final long MAX_DATACENTER_ID = ~(-1L << DATACENTERIDBITS); 20 | 21 | private static final long SEQUENCE_BITS = 12L; 22 | 23 | private static final long WORKER_ID_SHIFT = SEQUENCE_BITS; 24 | 25 | private static final long DATACENTER_ID_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS; 26 | 27 | private static final long TIMESTAMP_LEFT_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS + DATACENTERIDBITS; 28 | 29 | private static final long SEQUENCE_MASK = ~(-1L << SEQUENCE_BITS); 30 | 31 | private static final IdWorkerUtils ID_WORKER_UTILS = new IdWorkerUtils(); 32 | 33 | private long workerId; 34 | 35 | private long datacenterId; 36 | 37 | private long idepoch; 38 | 39 | private long sequence = '0'; 40 | 41 | private long lastTimestamp = -1L; 42 | 43 | private IdWorkerUtils() { 44 | this(RANDOM.nextInt((int) MAX_WORKER_ID), RANDOM.nextInt((int) MAX_DATACENTER_ID), 1288834974657L); 45 | } 46 | 47 | private IdWorkerUtils(final long workerId, final long datacenterId, final long idepoch) { 48 | if (workerId > MAX_WORKER_ID || workerId < 0) { 49 | throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", MAX_WORKER_ID)); 50 | } 51 | if (datacenterId > MAX_DATACENTER_ID || datacenterId < 0) { 52 | throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", MAX_DATACENTER_ID)); 53 | } 54 | this.workerId = workerId; 55 | this.datacenterId = datacenterId; 56 | this.idepoch = idepoch; 57 | } 58 | 59 | /** 60 | * Gets instance. 61 | * 62 | * @return the instance 63 | */ 64 | public static IdWorkerUtils getInstance() { 65 | return ID_WORKER_UTILS; 66 | } 67 | 68 | public synchronized long nextId() { 69 | long timestamp = timeGen(); 70 | if (timestamp < lastTimestamp) { 71 | throw new RuntimeException(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp)); 72 | } 73 | if (lastTimestamp == timestamp) { 74 | sequence = (sequence + 1) & SEQUENCE_MASK; 75 | if (sequence == 0) { 76 | timestamp = tilNextMillis(lastTimestamp); 77 | } 78 | } else { 79 | sequence = 0L; 80 | } 81 | 82 | lastTimestamp = timestamp; 83 | 84 | return ((timestamp - idepoch) << TIMESTAMP_LEFT_SHIFT) 85 | | (datacenterId << DATACENTER_ID_SHIFT) 86 | | (workerId << WORKER_ID_SHIFT) | sequence; 87 | } 88 | 89 | private long tilNextMillis(final long lastTimestamp) { 90 | long timestamp = timeGen(); 91 | while (timestamp <= lastTimestamp) { 92 | timestamp = timeGen(); 93 | } 94 | return timestamp; 95 | } 96 | 97 | private long timeGen() { 98 | return System.currentTimeMillis(); 99 | } 100 | 101 | /** 102 | * Build part number string. 103 | * 104 | * @return the string 105 | */ 106 | public String buildPartNumber() { 107 | return String.valueOf(ID_WORKER_UTILS.nextId()); 108 | } 109 | 110 | /** 111 | * Create uuid string. 112 | * 113 | * @return the string 114 | */ 115 | public String createUUID() { 116 | return String.valueOf(ID_WORKER_UTILS.nextId()); 117 | } 118 | 119 | public static void main(String[] args) { 120 | System.out.println(IdWorkerUtils.getInstance().nextId()); 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /chatgpt-data-app/src/test/java/com/luckysj/chatgpt/data/AliPayTest.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import com.alibaba.fastjson.JSONObject; 5 | import com.alipay.api.AlipayApiException; 6 | import com.alipay.api.AlipayClient; 7 | import com.alipay.api.DefaultAlipayClient; 8 | import com.alipay.api.request.AlipayTradeQueryRequest; 9 | import com.alipay.api.request.AlipayTradeWapPayRequest; 10 | import com.alipay.api.response.AlipayTradeQueryResponse; 11 | import org.junit.jupiter.api.Test; 12 | import org.junit.runner.RunWith; 13 | import org.springframework.beans.factory.annotation.Value; 14 | import org.springframework.boot.test.context.SpringBootTest; 15 | import org.springframework.test.context.junit4.SpringRunner; 16 | 17 | 18 | import java.util.Map; 19 | 20 | /** 21 | * @author Mr.M 22 | * @version 1.0 23 | * @description 支付宝查询接口 24 | * @date 2022/10/4 17:18 25 | */ 26 | @RunWith(SpringRunner.class) 27 | @SpringBootTest(classes = Application.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) 28 | public class AliPayTest { 29 | 30 | @Value("${pay.alipay.app_id}") 31 | String APP_ID; 32 | 33 | @Value("${pay.alipay.app_private_key}") 34 | String APP_PRIVATE_KEY; 35 | 36 | @Value("${pay.alipay.alipay_public_key}") 37 | String ALIPAY_PUBLIC_KEY; 38 | 39 | // 请求网关地址 40 | @Value("${pay.alipay.url}") 41 | public String URL; 42 | 43 | // 编码 44 | public static String CHARSET = "UTF-8"; 45 | // 返回格式 46 | public static String FORMAT = "json"; 47 | // 日志记录目录 48 | public static String log_path = "/log"; 49 | // RSA2 50 | public static String SIGNTYPE = "RSA2"; 51 | 52 | @Test 53 | public void queryPayResult() throws AlipayApiException { 54 | AlipayClient alipayClient = new DefaultAlipayClient(URL, APP_ID, APP_PRIVATE_KEY, "json", CHARSET, ALIPAY_PUBLIC_KEY, SIGNTYPE); //获得初始化的AlipayClient 55 | AlipayTradeQueryRequest request = new AlipayTradeQueryRequest(); 56 | JSONObject bizContent = new JSONObject(); 57 | bizContent.put("out_trade_no", "202210100010101002"); 58 | //bizContent.put("trade_no", "2014112611001004680073956707"); 59 | request.setBizContent(bizContent.toString()); 60 | AlipayTradeQueryResponse response = alipayClient.execute(request); 61 | if (response.isSuccess()) { 62 | System.out.println("调用成功"); 63 | String resultJson = response.getBody(); 64 | //转map 65 | Map resultMap = JSON.parseObject(resultJson, Map.class); 66 | Map alipay_trade_query_response = (Map) resultMap.get("alipay_trade_query_response"); 67 | //支付结果 68 | String trade_status = (String) alipay_trade_query_response.get("trade_status"); 69 | System.out.println(trade_status); 70 | } else { 71 | System.out.println("调用失败"); 72 | } 73 | } 74 | 75 | @Test 76 | public void testRequestPay(){ 77 | AlipayClient alipayClient = new DefaultAlipayClient(URL, APP_ID, APP_PRIVATE_KEY, FORMAT, CHARSET, ALIPAY_PUBLIC_KEY,SIGNTYPE); 78 | //获得初始化的AlipayClient 79 | AlipayTradeWapPayRequest alipayRequest = new AlipayTradeWapPayRequest();//创建API对应的request 80 | // alipayRequest.setReturnUrl("http://domain.com/CallBack/return_url.jsp"); 81 | // alipayRequest.setNotifyUrl("http://domain.com/CallBack/notify_url.jsp");//在公共参数中设置回跳和通知地址 82 | alipayRequest.setBizContent("{" + 83 | " \"out_trade_no\":\"202210100010101002\"," + 84 | " \"total_amount\":0.1," + 85 | " \"subject\":\"Iphone6 16G\"," + 86 | " \"product_code\":\"QUICK_WAP_WAY\"" + 87 | " }");//填充业务参数 88 | try { 89 | String form = alipayClient.pageExecute(alipayRequest).getBody(); //调用SDK生成表单 90 | System.out.println("发起交易测试"); 91 | } catch (AlipayApiException e) { 92 | e.printStackTrace(); 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /chatgpt-data-domain/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | chatgpt-data 7 | com.luckysj 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | chatgpt-data-domain 13 | 14 | 15 | 16 | org.springframework 17 | spring-webmvc 18 | 19 | 20 | org.apache.tomcat.embed 21 | tomcat-embed-core 22 | 23 | 24 | org.projectlombok 25 | lombok 26 | 27 | 28 | com.alibaba 29 | fastjson 30 | 31 | 32 | org.apache.commons 33 | commons-lang3 34 | 35 | 36 | com.google.guava 37 | guava 38 | 39 | 40 | io.jsonwebtoken 41 | jjwt 42 | 43 | 44 | com.auth0 45 | java-jwt 46 | 47 | 48 | commons-codec 49 | commons-codec 50 | 51 | 52 | 53 | cn.bugstack.chatgpt 54 | chatgpt-sdk-java 55 | 56 | 57 | cn.bugstack 58 | chatglm-sdk-java 59 | 60 | 61 | com.luckysj 62 | chatgpt-data-types 63 | 1.0-SNAPSHOT 64 | 65 | 66 | com.github.houbb 67 | sensitive-word 68 | 69 | 70 | 71 | com.alipay.sdk 72 | alipay-sdk-java 73 | 3.7.73.ALL 74 | 75 | 76 | 77 | commons-logging 78 | commons-logging 79 | 1.2 80 | 81 | 82 | 83 | org.springframework.boot 84 | spring-boot-starter-test 85 | test 86 | 87 | 88 | 89 | 90 | 91 | chatgpt-data-domain 92 | 93 | 94 | org.apache.maven.plugins 95 | maven-compiler-plugin 96 | 97 | ${java.version} 98 | ${java.version} 99 | ${java.version} 100 | 101 | 102 | 103 | 104 | 105 | 106 | -------------------------------------------------------------------------------- /chatgpt-data-domain/src/main/java/com/luckysj/chatgpt/data/domain/order/service/AbstractOrderService.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.domain.order.service; 2 | 3 | 4 | import com.luckysj.chatgpt.data.domain.order.config.AliPayConfig; 5 | import com.luckysj.chatgpt.data.domain.order.model.entity.*; 6 | import com.luckysj.chatgpt.data.domain.order.model.valobj.PayStatusVO; 7 | import com.luckysj.chatgpt.data.domain.order.repository.IOrderRepository; 8 | import com.luckysj.chatgpt.data.types.common.Constants; 9 | import com.luckysj.chatgpt.data.types.exception.ChatGPTException; 10 | import lombok.extern.slf4j.Slf4j; 11 | 12 | import javax.annotation.Resource; 13 | import java.math.BigDecimal; 14 | 15 | /** 16 | * @author www.luckysj.top 刘仕杰 17 | * @description 抽象订单服务 18 | * @create 2023/12/11 21:28:17 19 | */ 20 | @Slf4j 21 | public abstract class AbstractOrderService implements IOrderService { 22 | 23 | @Resource 24 | protected IOrderRepository orderRepository; 25 | 26 | @Resource 27 | protected AliPayConfig aliPayConfig; 28 | 29 | @Override 30 | public PayOrderEntity createOrder(ShopCartEntity shopCartEntity) { 31 | try { 32 | // 0. 基础信息 33 | String openid = shopCartEntity.getOpenid(); 34 | Integer productId = shopCartEntity.getProductId(); 35 | 36 | // 1. 查询有效的未支付订单,如果存在直接返回支付宝支付 CodeUrl 37 | UnpaidOrderEntity unpaidOrderEntity = orderRepository.queryUnpaidOrder(shopCartEntity); 38 | if (null != unpaidOrderEntity && PayStatusVO.WAIT.equals(unpaidOrderEntity.getPayStatus()) && null != unpaidOrderEntity.getPayUrl()) { 39 | log.info("创建订单-存在,已生成支付宝支付,返回 openid: {} orderId: {} payUrl: {}", openid, unpaidOrderEntity.getOrderId(), unpaidOrderEntity.getPayUrl()); 40 | return PayOrderEntity.builder() 41 | .openid(openid) 42 | .orderId(unpaidOrderEntity.getOrderId()) 43 | .payUrl(unpaidOrderEntity.getPayUrl()) 44 | .payStatus(unpaidOrderEntity.getPayStatus()) 45 | .build(); 46 | } else if (null != unpaidOrderEntity && null == unpaidOrderEntity.getPayUrl()) { 47 | log.info("创建订单-存在,未生成支付宝支付,返回 openid: {} orderId: {}", openid, unpaidOrderEntity.getOrderId()); 48 | PayOrderEntity payOrderEntity = this.doPrepayOrder(openid, unpaidOrderEntity.getOrderId(), unpaidOrderEntity.getProductName(), unpaidOrderEntity.getTotalAmount()); 49 | log.info("创建订单-完成,生成支付单。openid: {} orderId: {} payUrl: {}", openid, payOrderEntity.getOrderId(), payOrderEntity.getPayUrl()); 50 | return payOrderEntity; 51 | } 52 | 53 | // 2. 商品查询 54 | ProductEntity productEntity = orderRepository.queryProduct(productId); 55 | // 商品有效性判断 56 | if (!productEntity.isAvailable()) { 57 | throw new ChatGPTException(Constants.ResponseCode.ORDER_PRODUCT_ERR.getCode(), Constants.ResponseCode.ORDER_PRODUCT_ERR.getInfo()); 58 | } 59 | 60 | // 3. 保存订单 61 | OrderEntity orderEntity = this.doSaveOrder(openid, productEntity); 62 | 63 | // 4. 创建支付 64 | PayOrderEntity payOrderEntity = this.doPrepayOrder(openid, orderEntity.getOrderId(), productEntity.getProductName(), orderEntity.getTotalAmount()); 65 | log.info("创建订单-完成,生成支付单。openid: {} orderId: {} payUrl: {}", openid, orderEntity.getOrderId(), payOrderEntity.getPayUrl()); 66 | 67 | return payOrderEntity; 68 | } catch (Exception e) { 69 | log.error("创建订单,已生成微信支付,返回 openid: {} productId: {}", shopCartEntity.getOpenid(), shopCartEntity.getProductId()); 70 | throw new ChatGPTException(Constants.ResponseCode.UN_ERROR.getCode(), Constants.ResponseCode.UN_ERROR.getInfo()); 71 | } 72 | } 73 | 74 | protected abstract OrderEntity doSaveOrder(String openid, ProductEntity productEntity); 75 | 76 | protected abstract PayOrderEntity doPrepayOrder(String openid, String orderId, String productName, BigDecimal amountTotal); 77 | 78 | } 79 | -------------------------------------------------------------------------------- /chatgpt-data-domain/src/main/java/com/luckysj/chatgpt/data/domain/openai/service/channel/service/impl/TextGenerativeModelServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.domain.openai.service.channel.service.impl; 2 | 3 | import cn.bugstack.chatgpt.common.Constants; 4 | import cn.bugstack.chatgpt.domain.chat.ChatChoice; 5 | import cn.bugstack.chatgpt.domain.chat.ChatCompletionRequest; 6 | import cn.bugstack.chatgpt.domain.chat.ChatCompletionResponse; 7 | import cn.bugstack.chatgpt.domain.chat.Message; 8 | import cn.bugstack.chatgpt.session.OpenAiSession; 9 | import com.alibaba.fastjson.JSON; 10 | import com.luckysj.chatgpt.data.domain.openai.model.aggregates.ChatProcessAggregate; 11 | import com.luckysj.chatgpt.data.domain.openai.service.channel.service.IGenerativeModelService; 12 | import lombok.extern.slf4j.Slf4j; 13 | import okhttp3.sse.EventSource; 14 | import okhttp3.sse.EventSourceListener; 15 | import org.apache.commons.lang3.StringUtils; 16 | import org.jetbrains.annotations.NotNull; 17 | import org.jetbrains.annotations.Nullable; 18 | import org.springframework.beans.factory.annotation.Autowired; 19 | import org.springframework.stereotype.Service; 20 | import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyEmitter; 21 | 22 | import java.util.List; 23 | import java.util.stream.Collectors; 24 | 25 | /** 26 | * @author www.luckysj.top 刘仕杰 27 | * @description 对话生成服务 28 | * @create 2023/12/18 17:55:43 29 | */ 30 | @Slf4j 31 | @Service 32 | public class TextGenerativeModelServiceImpl implements IGenerativeModelService { 33 | @Autowired(required = false) 34 | protected OpenAiSession openAiSession; 35 | 36 | @Override 37 | public void doMessageResponse(ChatProcessAggregate chatProcess, ResponseBodyEmitter emitter) throws Exception { 38 | if (null == openAiSession) { 39 | emitter.send("模型调用未开启,可以选择其他模型对话!"); 40 | return; 41 | } 42 | 43 | // 1. 处理请求消息,使用stream流将chatProcessAggregate中的消息数组转换为Message数组 44 | List messages = chatProcess.getMessages().stream() 45 | .map(entity -> Message.builder() 46 | .role(Constants.Role.valueOf(entity.getRole().toUpperCase())) 47 | .content(entity.getContent()) 48 | .name(entity.getName()) 49 | .build()) 50 | .collect(Collectors.toList()); 51 | 52 | // 2. 封装本次询问的相关参数,如消息内容,使用模型等 53 | ChatCompletionRequest chatCompletion = ChatCompletionRequest 54 | .builder() 55 | .stream(true) 56 | .messages(messages) 57 | .model(ChatCompletionRequest.Model.GPT_3_5_TURBO.getCode()) 58 | .build(); 59 | 60 | // 3.2 请求应答 61 | openAiSession.chatCompletions(chatCompletion, new EventSourceListener() { 62 | @Override 63 | public void onEvent(@NotNull EventSource eventSource, @Nullable String id, @Nullable String type, @NotNull String data) { 64 | // 解析接收到的JSON数据为ChatCompletionResponse对象。 65 | ChatCompletionResponse chatCompletionResponse = JSON.parseObject(data, ChatCompletionResponse.class); 66 | // choices中有本次回复内容等信息 67 | List choices = chatCompletionResponse.getChoices(); 68 | for (ChatChoice chatChoice : choices) { 69 | Message delta = chatChoice.getDelta(); 70 | if (Constants.Role.ASSISTANT.getCode().equals(delta.getRole())) continue; 71 | 72 | // 应答完成 73 | String finishReason = chatChoice.getFinishReason(); 74 | if (StringUtils.isNoneBlank(finishReason) && "stop".equals(finishReason)) { 75 | emitter.complete(); 76 | break; 77 | } 78 | 79 | // 发送信息 80 | try { 81 | emitter.send(delta.getContent()); 82 | } catch (Exception e) { 83 | throw new RuntimeException(e); 84 | } 85 | } 86 | 87 | } 88 | }); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /chatgpt-data-domain/src/main/java/com/luckysj/chatgpt/data/domain/openai/service/AbstractChatService.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.domain.openai.service; 2 | 3 | import cn.bugstack.chatgpt.session.OpenAiSession; 4 | import com.fasterxml.jackson.core.JsonProcessingException; 5 | import com.luckysj.chatgpt.data.domain.openai.model.aggregates.ChatProcessAggregate; 6 | import com.luckysj.chatgpt.data.domain.openai.model.entity.RuleLogicEntity; 7 | import com.luckysj.chatgpt.data.domain.openai.model.entity.UserAccountQuotaEntity; 8 | import com.luckysj.chatgpt.data.domain.openai.model.valobj.LogicCheckTypeVO; 9 | import com.luckysj.chatgpt.data.domain.openai.repository.IOpenAiRepository; 10 | import com.luckysj.chatgpt.data.domain.openai.service.channel.OpenAiGroupService; 11 | import com.luckysj.chatgpt.data.domain.openai.service.channel.impl.ChatGLMService; 12 | import com.luckysj.chatgpt.data.domain.openai.service.channel.impl.ChatGPTService; 13 | import com.luckysj.chatgpt.data.domain.openai.service.rule.factory.DefaultLogicFactory; 14 | import com.luckysj.chatgpt.data.types.common.Constants; 15 | import com.luckysj.chatgpt.data.types.enums.OpenAiChannel; 16 | import com.luckysj.chatgpt.data.types.exception.ChatGPTException; 17 | import lombok.extern.slf4j.Slf4j; 18 | import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyEmitter; 19 | 20 | import javax.annotation.Resource; 21 | import java.util.HashMap; 22 | import java.util.Map; 23 | 24 | /** 25 | * @author www.luckysj.top 刘仕杰 26 | * @description 对话服务模板模式抽象类 27 | * @create 2023/12/04 19:50:27 28 | */ 29 | @Slf4j 30 | public abstract class AbstractChatService implements IChatService { 31 | 32 | @Resource 33 | private IOpenAiRepository openAiRepository; 34 | 35 | // 对话服务组 36 | private final Map openAiGroup = new HashMap<>(); 37 | 38 | public AbstractChatService(ChatGPTService chatGPTService, ChatGLMService chatGLMService) { 39 | openAiGroup.put(OpenAiChannel.ChatGPT, chatGPTService); 40 | openAiGroup.put(OpenAiChannel.ChatGLM, chatGLMService); 41 | } 42 | 43 | 44 | @Override 45 | public ResponseBodyEmitter completions(ResponseBodyEmitter emitter, ChatProcessAggregate chatProcess) { 46 | try { 47 | // 1.绑定应答回调事件 48 | emitter.onCompletion(() -> { 49 | log.info("流式问答请求完成,使用模型:{}", chatProcess.getModel()); 50 | }); 51 | emitter.onError(throwable -> log.error("流式问答请求错误,使用模型:{}", chatProcess.getModel(), throwable)); 52 | 53 | // 2.查询账户 54 | UserAccountQuotaEntity userAccountQuotaEntity = openAiRepository.queryUserAccount(chatProcess.getOpenid()); 55 | 56 | // 3.规则过滤,需要把ACCOUNT_STATUS放在最前面,防止账户不存在导致的Null异常 57 | RuleLogicEntity ruleLogicEntity = this.doCheckLogic(chatProcess, userAccountQuotaEntity, 58 | DefaultLogicFactory.LogicModel.ACCOUNT_STATUS.getCode(), 59 | DefaultLogicFactory.LogicModel.ACCESS_LIMIT.getCode(), 60 | DefaultLogicFactory.LogicModel.SENSITIVE_WORD.getCode(), 61 | DefaultLogicFactory.LogicModel.MODEL_TYPE.getCode(), 62 | DefaultLogicFactory.LogicModel.USER_QUOTA.getCode() 63 | ); 64 | // 判断校验是否通过 65 | if (!LogicCheckTypeVO.SUCCESS.equals(ruleLogicEntity.getType())) { 66 | emitter.send(ruleLogicEntity.getInfo()); 67 | emitter.complete(); 68 | return emitter; 69 | } 70 | 71 | // 4.请求应答处理【使用策略模式判断使用GPT的会话还是其他模型的会话】 72 | openAiGroup.get(chatProcess.getChannel()).doMessageResponse(ruleLogicEntity.getData(), emitter); 73 | 74 | } catch (Exception e) { 75 | throw new ChatGPTException(Constants.ResponseCode.UN_ERROR.getCode(), e.getMessage()); 76 | } 77 | 78 | // 3. 返回结果 79 | return emitter; 80 | 81 | } 82 | 83 | // 规则校验 84 | protected abstract RuleLogicEntity doCheckLogic( 85 | ChatProcessAggregate chatProcessAggregate, UserAccountQuotaEntity userAccountQuotaEntity, 86 | String... logics) throws Exception; 87 | 88 | } 89 | -------------------------------------------------------------------------------- /chatgpt-data-infrastructure/src/main/java/com/luckysj/chatgpt/data/infrastructure/repository/AuthRepository.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.infrastructure.repository; 2 | 3 | import com.luckysj.chatgpt.data.domain.auth.repository.IAuthRepository; 4 | import com.luckysj.chatgpt.data.domain.openai.model.valobj.UserAccountStatusVO; 5 | import com.luckysj.chatgpt.data.infrastructure.dao.IUserAccountDao; 6 | import com.luckysj.chatgpt.data.infrastructure.po.UserAccountPO; 7 | import com.luckysj.chatgpt.data.infrastructure.redis.IRedisService; 8 | import lombok.extern.slf4j.Slf4j; 9 | import org.apache.commons.lang3.RandomStringUtils; 10 | import org.apache.commons.lang3.StringUtils; 11 | import org.redisson.api.RLock; 12 | import org.springframework.beans.factory.annotation.Value; 13 | import org.springframework.stereotype.Repository; 14 | import org.springframework.stereotype.Service; 15 | 16 | import javax.annotation.Resource; 17 | import java.util.concurrent.TimeUnit; 18 | 19 | @Slf4j 20 | @Repository 21 | public class AuthRepository implements IAuthRepository { 22 | @Resource 23 | private IUserAccountDao userAccountDao; 24 | @Resource 25 | private IRedisService redisService; 26 | @Value("${app.config.new-user-quota}") 27 | private Integer initialQuota; //新用户默认额度 28 | @Value("${app.config.new-user-model}") 29 | private String initialModel; //新用户默认模型 30 | 31 | // 验证码前缀 32 | private static final String Key = "weixin_code:"; 33 | 34 | @Override 35 | public boolean insertUserIfNotExist(String openid) { 36 | // 首先判断是否已存在 37 | UserAccountPO userAccount = userAccountDao.queryUserAccount(openid); 38 | if(userAccount != null) return true; 39 | 40 | // 插入账号信息 41 | UserAccountPO userAccountPO = new UserAccountPO(); 42 | userAccountPO.setOpenid(openid); 43 | userAccountPO.setTotalQuota(initialQuota); 44 | userAccountPO.setSurplusQuota(initialQuota); 45 | userAccountPO.setModelTypes(initialModel); 46 | userAccountPO.setStatus(UserAccountStatusVO.AVAILABLE.getCode()); 47 | return userAccountDao.insert(userAccountPO) > 0 ? true : false; 48 | } 49 | 50 | @Override 51 | public String getCodeUserOpenId(String code) { 52 | return redisService.getValue(Key + code); 53 | } 54 | 55 | @Override 56 | public void removeCodeByOpenId(String code, String openId) { 57 | redisService.remove(Key + openId); 58 | redisService.remove(Key + code); 59 | } 60 | 61 | @Override 62 | public String genCodeTest(String openid) { 63 | // 首先判断是否已经存在验证码 64 | String codeOld = redisService.getValue(Key + openid); 65 | if(StringUtils.isNoneBlank(codeOld)){ 66 | return codeOld; 67 | } 68 | 69 | // 获取锁,防止多次验证码生成 70 | RLock lock = redisService.getLock(Key); 71 | try { 72 | // 上锁并设置超时时间为15秒 73 | lock.lock(15, TimeUnit.SECONDS); 74 | 75 | // 生成验证码,这里可能会存在验证码重复的问题,因为我们登录只需要验证码,如果验证码重复,重新生成 76 | String code = RandomStringUtils.randomNumeric(6); 77 | 78 | // 防重校验&重新生成 79 | for (int i = 0; i < 10 && StringUtils.isNotBlank(redisService.getValue(Key + "_" + code)); i++) { 80 | if (i < 3) { 81 | code = RandomStringUtils.randomNumeric(6); 82 | log.warn("验证码重复,生成6位字符串验证码 {} {}", openid, code); 83 | } else if (i < 5) { 84 | code = RandomStringUtils.randomNumeric(7); 85 | log.warn("验证码重复,生成7位字符串验证码 {} {}", openid, code); 86 | } else if (i < 9) { 87 | code = RandomStringUtils.randomNumeric(8); 88 | log.warn("验证码重复,生成8位字符串验证码 {} {}", openid, code); 89 | } else { 90 | return ""; 91 | } 92 | } 93 | 94 | // 保存验证码到缓存 95 | redisService.setValue(Key + openid, code, 5 * 60 * 1000); 96 | redisService.setValue(Key + code, openid, 5 * 60 * 1000 ); 97 | return code; 98 | } finally { 99 | lock.unlock(); 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /chatgpt-data-trigger/src/main/java/com/luckysj/chatgpt/data/trigger/http/ChatGPTAIServiceController.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.trigger.http; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import com.luckysj.chatgpt.data.domain.auth.service.IAuthService; 5 | import com.luckysj.chatgpt.data.domain.openai.model.aggregates.ChatProcessAggregate; 6 | import com.luckysj.chatgpt.data.domain.openai.model.entity.MessageEntity; 7 | import com.luckysj.chatgpt.data.domain.openai.service.IChatService; 8 | import com.luckysj.chatgpt.data.trigger.http.dto.ChatGPTRequestDTO; 9 | import com.luckysj.chatgpt.data.types.common.Constants; 10 | import com.luckysj.chatgpt.data.types.exception.ChatGPTException; 11 | import io.micrometer.core.annotation.Timed; 12 | import lombok.extern.slf4j.Slf4j; 13 | import org.springframework.web.bind.annotation.*; 14 | import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyEmitter; 15 | 16 | import javax.annotation.Resource; 17 | import javax.servlet.http.HttpServletResponse; 18 | import java.io.IOException; 19 | import java.util.stream.Collectors; 20 | 21 | /** 22 | * @author www.luckysj.top 刘仕杰 23 | * @description chatgpt对话服务接口 24 | * @create 2023/12/04 19:28:04 25 | */ 26 | @Slf4j 27 | @RestController() 28 | @CrossOrigin("${app.config.cross-origin}") 29 | @RequestMapping("/api/${app.config.api-version}/") 30 | public class ChatGPTAIServiceController { 31 | 32 | @Resource 33 | private IChatService chatService; 34 | 35 | @Resource 36 | private IAuthService authService; 37 | 38 | // 接口测试 curl -X POST http://localhost:7070/api/v1/chatgpt/chat/completions -H "Content-Type: application/json" -H "Authorization: eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMTQ1MTQiLCJvcGVuSWQiOiIxMTQ1MTQiLCJleHAiOjE3MDkyOTcxOTAsImlhdCI6MTcwODY5MjM5MCwianRpIjoiOWYyNjFiMjgtNjNiOC00YzAwLWJmZDMtNmI1NDRiYzc2YTE4In0.x9eXcOJr6QQYn81397ch7T2Ejk_75pybXSS-5RixOfc" -d "{\"messages\":[{\"content\":\"写一个java冒泡排序\",\"role\":\"user\"}],\"model\":\"gpt-3.5-turbo\"}" 39 | @Timed(value="chat_completions_http",description="请求对话接口") 40 | @PostMapping(value = "chatgpt/chat/completions") 41 | public ResponseBodyEmitter completionsStream(@RequestBody ChatGPTRequestDTO request, @RequestHeader("Authorization") String token, HttpServletResponse response) { 42 | log.info("流式问答请求开始,使用模型:{} 请求信息:{}", request.getModel(), JSON.toJSONString(request.getMessages())); 43 | try { 44 | // 1. 基础配置;流式输出、编码、禁用缓存 45 | response.setContentType("text/event-stream"); 46 | response.setCharacterEncoding("UTF-8"); 47 | response.setHeader("Cache-Control", "no-cache"); 48 | 49 | // 2.构造异步响应对象,token验证 50 | ResponseBodyEmitter emitter = new ResponseBodyEmitter(3 * 60 * 1000L); 51 | boolean authResult = authService.checkToken(token); 52 | // token验证不通过,返回错误码,关闭异步响应 53 | if (!authResult) { 54 | try { 55 | emitter.send(Constants.ResponseCode.TOKEN_ERROR.getCode()); 56 | } catch (IOException e) { 57 | throw new RuntimeException(e); 58 | } 59 | emitter.complete(); 60 | return emitter; 61 | } 62 | 63 | // 3.获取openid 64 | String openid = authService.parseOpenid(token); 65 | 66 | // 2. 构建 贯穿整个业务流程的聚合对象 67 | ChatProcessAggregate chatProcessAggregate = ChatProcessAggregate.builder() 68 | .openid(openid) 69 | .model(request.getModel()) 70 | .messages(request.getMessages().stream() 71 | .map(entity -> MessageEntity.builder() 72 | .role(entity.getRole()) 73 | .content(entity.getContent()) 74 | .name(entity.getName()) 75 | .build()) 76 | .collect(Collectors.toList())) 77 | .build(); 78 | 79 | // 3. 请求结果&返回 80 | return chatService.completions(emitter, chatProcessAggregate); 81 | } catch (Exception e) { 82 | log.error("流式应答,请求模型:{} 发生异常 {}", request.getModel(), e.getMessage()); 83 | throw new ChatGPTException(e.getMessage()); 84 | } 85 | } 86 | 87 | } 88 | -------------------------------------------------------------------------------- /chatgpt-data-infrastructure/src/main/java/com/luckysj/chatgpt/data/infrastructure/redis/RedisService.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.infrastructure.redis; 2 | 3 | import org.redisson.api.*; 4 | import org.springframework.stereotype.Service; 5 | 6 | import javax.annotation.Resource; 7 | import java.time.Duration; 8 | 9 | @Service("redissonService") 10 | public class RedisService implements IRedisService{ 11 | @Resource 12 | private RedissonClient redissonClient; 13 | 14 | public void setValue(String key, T value) { 15 | redissonClient.getBucket(key).set(value); 16 | } 17 | 18 | @Override 19 | public void setValue(String key, T value, long expired) { 20 | RBucket bucket = redissonClient.getBucket(key); 21 | bucket.set(value, Duration.ofMillis(expired)); 22 | } 23 | 24 | public T getValue(String key) { 25 | return redissonClient.getBucket(key).get(); 26 | } 27 | 28 | @Override 29 | public RQueue getQueue(String key) { 30 | return redissonClient.getQueue(key); 31 | } 32 | 33 | @Override 34 | public RBlockingQueue getBlockingQueue(String key) { 35 | return redissonClient.getBlockingQueue(key); 36 | } 37 | 38 | @Override 39 | public RDelayedQueue getDelayedQueue(RBlockingQueue rBlockingQueue) { 40 | return redissonClient.getDelayedQueue(rBlockingQueue); 41 | } 42 | 43 | @Override 44 | public long incr(String key) { 45 | return redissonClient.getAtomicLong(key).incrementAndGet(); 46 | } 47 | 48 | @Override 49 | public long incrBy(String key, long delta) { 50 | return redissonClient.getAtomicLong(key).addAndGet(delta); 51 | } 52 | 53 | @Override 54 | public long decr(String key) { 55 | return redissonClient.getAtomicLong(key).decrementAndGet(); 56 | } 57 | 58 | @Override 59 | public long decrBy(String key, long delta) { 60 | return redissonClient.getAtomicLong(key).addAndGet(-delta); 61 | } 62 | 63 | @Override 64 | public void remove(String key) { 65 | redissonClient.getBucket(key).delete(); 66 | } 67 | 68 | @Override 69 | public boolean isExists(String key) { 70 | return redissonClient.getBucket(key).isExists(); 71 | } 72 | 73 | public void addToSet(String key, String value) { 74 | RSet set = redissonClient.getSet(key); 75 | set.add(value); 76 | } 77 | 78 | public boolean isSetMember(String key, String value) { 79 | RSet set = redissonClient.getSet(key); 80 | return set.contains(value); 81 | } 82 | 83 | public void addToList(String key, String value) { 84 | RList list = redissonClient.getList(key); 85 | list.add(value); 86 | } 87 | 88 | public String getFromList(String key, int index) { 89 | RList list = redissonClient.getList(key); 90 | return list.get(index); 91 | } 92 | 93 | public void addToMap(String key, String field, String value) { 94 | RMap map = redissonClient.getMap(key); 95 | map.put(field, value); 96 | } 97 | 98 | public String getFromMap(String key, String field) { 99 | RMap map = redissonClient.getMap(key); 100 | return map.get(field); 101 | } 102 | 103 | public void addToSortedSet(String key, String value) { 104 | RSortedSet sortedSet = redissonClient.getSortedSet(key); 105 | sortedSet.add(value); 106 | } 107 | 108 | @Override 109 | public RLock getLock(String key) { 110 | return redissonClient.getLock(key); 111 | } 112 | 113 | @Override 114 | public RLock getFairLock(String key) { 115 | return redissonClient.getFairLock(key); 116 | } 117 | 118 | @Override 119 | public RReadWriteLock getReadWriteLock(String key) { 120 | return redissonClient.getReadWriteLock(key); 121 | } 122 | 123 | @Override 124 | public RSemaphore getSemaphore(String key) { 125 | return redissonClient.getSemaphore(key); 126 | } 127 | 128 | @Override 129 | public RPermitExpirableSemaphore getPermitExpirableSemaphore(String key) { 130 | return redissonClient.getPermitExpirableSemaphore(key); 131 | } 132 | 133 | @Override 134 | public RCountDownLatch getCountDownLatch(String key) { 135 | return redissonClient.getCountDownLatch(key); 136 | } 137 | 138 | @Override 139 | public RBloomFilter getBloomFilter(String key) { 140 | return redissonClient.getBloomFilter(key); 141 | } 142 | 143 | } 144 | -------------------------------------------------------------------------------- /chatgpt-data-trigger/src/main/java/com/luckysj/chatgpt/data/trigger/job/NoPayNotifyOrderJob.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.trigger.job; 2 | 3 | import cn.hutool.json.JSONObject; 4 | import com.alibaba.fastjson.JSON; 5 | import com.alipay.api.AlipayClient; 6 | import com.alipay.api.request.AlipayTradeQueryRequest; 7 | import com.alipay.api.response.AlipayTradeQueryResponse; 8 | import com.google.common.eventbus.EventBus; 9 | 10 | import com.luckysj.chatgpt.data.domain.order.model.valobj.AliPayTradeTypeVo; 11 | import com.luckysj.chatgpt.data.domain.order.service.IOrderService; 12 | import com.luckysj.chatgpt.data.types.common.Constants; 13 | import io.micrometer.core.annotation.Timed; 14 | import lombok.extern.slf4j.Slf4j; 15 | import org.redisson.api.RTopic; 16 | import org.springframework.amqp.rabbit.core.RabbitTemplate; 17 | import org.springframework.beans.factory.annotation.Autowired; 18 | import org.springframework.beans.factory.annotation.Value; 19 | import org.springframework.scheduling.annotation.Scheduled; 20 | import org.springframework.stereotype.Component; 21 | 22 | import javax.annotation.Resource; 23 | import java.math.BigDecimal; 24 | import java.math.RoundingMode; 25 | import java.text.SimpleDateFormat; 26 | import java.util.Date; 27 | import java.util.List; 28 | import java.util.Map; 29 | 30 | /** 31 | * @author www.luckysj.top 刘仕杰 32 | * @description 检测未接收到或未正确处理的支付回调通知 33 | * @create 2023/12/15 15:21:54 34 | */ 35 | @Slf4j 36 | @Component() 37 | public class NoPayNotifyOrderJob { 38 | 39 | @Resource 40 | private IOrderService orderService; 41 | 42 | // @Resource 43 | // private EventBus eventBus; 44 | 45 | // @Resource(name = "delivery") 46 | // private RTopic redisTopic; 47 | 48 | @Resource 49 | private RabbitTemplate rabbitTemplate; 50 | 51 | @Resource 52 | private AlipayClient alipayClient; 53 | 54 | private final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX"); 55 | 56 | @Timed(value="no_pay_notify_order_job",description="定时任务,订单支付状态更新") 57 | @Scheduled(cron = "0 0/3 * * * ?") //三分钟执行一次 58 | public void exec() { 59 | try { 60 | List orderIds = orderService.queryNoPayNotifyOrder(); 61 | if (orderIds.isEmpty()) { 62 | log.info("定时任务,订单支付状态更新,暂无未更新订单 orderId is null"); 63 | return; 64 | } 65 | for (String orderId : orderIds) { 66 | // 查询结果 67 | AlipayTradeQueryRequest request = new AlipayTradeQueryRequest(); 68 | JSONObject bizContent = new JSONObject(); 69 | bizContent.put("out_trade_no", orderId); 70 | request.setBizContent(bizContent.toString()); 71 | AlipayTradeQueryResponse response = alipayClient.execute(request); 72 | if (response.isSuccess()) { 73 | String resultJson = response.getBody(); 74 | //转map 75 | Map resultMap = JSON.parseObject(resultJson, Map.class); 76 | Map alipay_trade_query_response = (Map) resultMap.get("alipay_trade_query_response"); 77 | //支付结果 78 | String trade_status = (String) alipay_trade_query_response.get("trade_status"); 79 | log.info("定时任务,查询支付结果查询成功,支付状态为{},orderId is {}", trade_status, orderId); 80 | 81 | if(AliPayTradeTypeVo.TRADE_SUCCESS.getCode().equals(trade_status)){ 82 | String transactionId =response.getTradeNo(); 83 | String totalAmountStr = response.getTotalAmount(); 84 | BigDecimal totalAmount = new BigDecimal(totalAmountStr); 85 | Date successTime = response.getSendPayDate(); 86 | boolean isSuccess = orderService.changeOrderPaySuccess(orderId, transactionId, totalAmount, successTime); 87 | if (isSuccess) { 88 | // 发布消息 89 | // eventBus.post(orderId); 90 | // redisTopic.publish(orderId); 91 | // rabbitTemplate.convertAndSend(Constants.MessageQueueKey.DeliveryExchange, Constants.MessageQueueKey.DeliveryKey, orderId); 92 | orderService.publishDeliveryMessage(orderId); //发布发货消息 93 | } 94 | } 95 | } else { 96 | log.info("定时任务,查询支付结果失败,失败原因:{},orderId is {}",response.getSubMsg(), orderId); 97 | continue; 98 | } 99 | 100 | } 101 | } catch (Exception e) { 102 | log.error("定时任务,订单支付状态更新失败", e); 103 | } 104 | } 105 | 106 | } 107 | -------------------------------------------------------------------------------- /chatgpt-data-app/src/main/java/com/luckysj/chatgpt/data/config/RedisClientConfig.java: -------------------------------------------------------------------------------- 1 | package com.luckysj.chatgpt.data.config; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import com.alibaba.fastjson.serializer.SerializerFeature; 5 | import com.luckysj.chatgpt.data.types.annotation.RedisTopic; 6 | import io.netty.buffer.ByteBuf; 7 | import io.netty.buffer.ByteBufAllocator; 8 | import io.netty.buffer.ByteBufInputStream; 9 | import io.netty.buffer.ByteBufOutputStream; 10 | import org.redisson.Redisson; 11 | import org.redisson.api.RTopic; 12 | import org.redisson.api.RedissonClient; 13 | import org.redisson.api.listener.MessageListener; 14 | import org.redisson.client.codec.BaseCodec; 15 | import org.redisson.client.protocol.Decoder; 16 | import org.redisson.client.protocol.Encoder; 17 | import org.redisson.config.Config; 18 | import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; 19 | import org.springframework.boot.context.properties.EnableConfigurationProperties; 20 | import org.springframework.context.ConfigurableApplicationContext; 21 | import org.springframework.context.annotation.Bean; 22 | import org.springframework.context.annotation.Configuration; 23 | 24 | import java.io.IOException; 25 | 26 | /** 27 | * @author www.luckysj.top 刘仕杰 28 | * @description redis客户端 29 | * @create 2023/12/17 20:49:41 30 | */ 31 | @Configuration 32 | @EnableConfigurationProperties(RedisCientConfigProperties.class) 33 | public class RedisClientConfig { 34 | 35 | @Bean("redissonClient") 36 | public RedissonClient redissonClient(ConfigurableApplicationContext applicationContext, RedisCientConfigProperties properties) { 37 | Config config = new Config(); 38 | // 根据需要可以设定编解码器;https://github.com/redisson/redisson/wiki/4.-%E6%95%B0%E6%8D%AE%E5%BA%8F%E5%88%97%E5%8C%96 39 | // config.setCodec(new RedisCodec()); 40 | 41 | config.useSingleServer() 42 | .setAddress("redis://" + properties.getHost() + ":" + properties.getPort()) 43 | .setPassword(properties.getPassword()) 44 | .setConnectionPoolSize(properties.getPoolSize()) 45 | .setConnectionMinimumIdleSize(properties.getMinIdleSize()) 46 | .setIdleConnectionTimeout(properties.getIdleTimeout()) 47 | .setConnectTimeout(properties.getConnectTimeout()) 48 | .setRetryAttempts(properties.getRetryAttempts()) 49 | .setRetryInterval(properties.getRetryInterval()) 50 | .setPingConnectionInterval(properties.getPingInterval()) 51 | .setKeepAlive(properties.isKeepAlive()) 52 | ; 53 | 54 | RedissonClient redissonClient = Redisson.create(config); 55 | 56 | // 注册消息发布订阅主题Topic 57 | // 找到所有实现了Redisson中MessageListener接口的bean名字 58 | String[] beanNamesForType = applicationContext.getBeanNamesForType(MessageListener.class); 59 | for (String beanName : beanNamesForType) { 60 | // 通过bean名字获取到监听bean 61 | MessageListener bean = applicationContext.getBean(beanName, MessageListener.class); 62 | 63 | Class beanClass = bean.getClass(); 64 | 65 | // 如果bean的注解里包含我们的自定义注解RedisTopic.class,则以RedisTopic注解的值作为name将该bean注册到bean工厂,方便在别处注入 66 | if (beanClass.isAnnotationPresent(RedisTopic.class)) { 67 | RedisTopic redisTopic = beanClass.getAnnotation(RedisTopic.class); 68 | 69 | RTopic topic = redissonClient.getTopic(redisTopic.topic()); 70 | topic.addListener(String.class, bean); 71 | 72 | ConfigurableListableBeanFactory beanFactory = applicationContext.getBeanFactory(); 73 | beanFactory.registerSingleton(redisTopic.topic(), topic); 74 | } 75 | } 76 | 77 | return redissonClient; 78 | } 79 | 80 | static class RedisCodec extends BaseCodec { 81 | 82 | private final Encoder encoder = in -> { 83 | ByteBuf out = ByteBufAllocator.DEFAULT.buffer(); 84 | try { 85 | ByteBufOutputStream os = new ByteBufOutputStream(out); 86 | JSON.writeJSONString(os, in, SerializerFeature.WriteClassName); 87 | return os.buffer(); 88 | } catch (IOException e) { 89 | out.release(); 90 | throw e; 91 | } catch (Exception e) { 92 | out.release(); 93 | throw new IOException(e); 94 | } 95 | }; 96 | 97 | private final Decoder decoder = (buf, state) -> JSON.parseObject(new ByteBufInputStream(buf), Object.class); 98 | 99 | @Override 100 | public Decoder getValueDecoder() { 101 | return decoder; 102 | } 103 | 104 | @Override 105 | public Encoder getValueEncoder() { 106 | return encoder; 107 | } 108 | 109 | } 110 | 111 | } --------------------------------------------------------------------------------