Răsfoiți Sursa

webchat 服务端微服务拆分版本v.0 : 对话功能重构

wangqi49 1 lună în urmă
părinte
comite
e84092222a
100 a modificat fișierele cu 3595 adăugiri și 0 ștergeri
  1. BIN
      .DS_Store
  2. 133 0
      pom.xml
  3. BIN
      resources/.DS_Store
  4. 0 0
      resources/database-sql/webchat-core.sql
  5. 0 0
      resources/database-sql/webchat-event.sql
  6. 44 0
      resources/database-sql/webchat-payment.sql
  7. 17 0
      resources/database-sql/webchat-ugc.sql
  8. 82 0
      resources/database-sql/webchat-user.sql
  9. 0 0
      resources/nacos-yaml/webchat-act-service-dev.yaml
  10. 11 0
      resources/nacos-yaml/webchat-admin-service-dev.yaml
  11. 11 0
      resources/nacos-yaml/webchat-client-chat-service-dev.yaml
  12. 12 0
      resources/nacos-yaml/webchat-connect-service-dev.yaml
  13. 23 0
      resources/nacos-yaml/webchat-pay-service-dev.yaml
  14. 30 0
      resources/nacos-yaml/webchat-pgc-service-dev.yaml
  15. 0 0
      resources/nacos-yaml/webchat-search-service-dev.yaml
  16. 0 0
      resources/nacos-yaml/webchat-sso-service-dev.yaml
  17. 21 0
      resources/nacos-yaml/webchat-user-service-dev.yaml
  18. 0 0
      resources/webchat.md
  19. 51 0
      webchat-act/pom.xml
  20. 19 0
      webchat-act/src/main/java/com/webchat/act/WebchatActApplication.java
  21. 1 0
      webchat-act/src/main/resources/application.yml
  22. 16 0
      webchat-act/src/main/resources/bootstrap.yml
  23. BIN
      webchat-admin/.DS_Store
  24. 19 0
      webchat-admin/.mvn/wrapper/maven-wrapper.properties
  25. 51 0
      webchat-admin/pom.xml
  26. BIN
      webchat-admin/src/.DS_Store
  27. BIN
      webchat-admin/src/main/.DS_Store
  28. 18 0
      webchat-admin/src/main/java/com/webchat/admin/WebchatAdminApplication.java
  29. 64 0
      webchat-admin/src/main/java/com/webchat/admin/advice/GlobalExceptionAdvice.java
  30. 33 0
      webchat-admin/src/main/java/com/webchat/admin/config/AdminWebMvcConfig.java
  31. 56 0
      webchat-admin/src/main/java/com/webchat/admin/config/interceptor/ValidatePermissionInterceptor.java
  32. 93 0
      webchat-admin/src/main/java/com/webchat/admin/controller/AccountManagementController.java
  33. 44 0
      webchat-admin/src/main/java/com/webchat/admin/controller/FileController.java
  34. 33 0
      webchat-admin/src/main/java/com/webchat/admin/controller/WalletManagementController.java
  35. 114 0
      webchat-admin/src/main/java/com/webchat/admin/filter/OauthFilter.java
  36. 79 0
      webchat-admin/src/main/java/com/webchat/admin/service/AccountManagementService.java
  37. 36 0
      webchat-admin/src/main/java/com/webchat/admin/service/AccountRoleService.java
  38. 28 0
      webchat-admin/src/main/java/com/webchat/admin/service/AccountService.java
  39. 52 0
      webchat-admin/src/main/java/com/webchat/admin/service/UserSessionService.java
  40. 21 0
      webchat-admin/src/main/java/com/webchat/admin/service/WalletManagementService.java
  41. 1 0
      webchat-admin/src/main/resources/application.yml
  42. 21 0
      webchat-admin/src/main/resources/bootstrap.yml
  43. BIN
      webchat-aigc/.DS_Store
  44. 52 0
      webchat-aigc/pom.xml
  45. BIN
      webchat-aigc/src/.DS_Store
  46. BIN
      webchat-aigc/src/main/.DS_Store
  47. 19 0
      webchat-aigc/src/main/java/com/webchat/aigc/WebchatAIGCApplication.java
  48. 64 0
      webchat-aigc/src/main/java/com/webchat/aigc/advice/GlobalExceptionAdvice.java
  49. 27 0
      webchat-aigc/src/main/java/com/webchat/aigc/config/RedisConfig.java
  50. 15 0
      webchat-aigc/src/main/java/com/webchat/aigc/config/properties/DeepseekLLMPropertiesConfig.java
  51. 15 0
      webchat-aigc/src/main/java/com/webchat/aigc/config/properties/KimiLLMPropertiesConfig.java
  52. 15 0
      webchat-aigc/src/main/java/com/webchat/aigc/config/properties/LibLibPropertiesConfig.java
  53. 29 0
      webchat-aigc/src/main/java/com/webchat/aigc/consumer/redis/RobotChatRedisQueueListener.java
  54. 30 0
      webchat-aigc/src/main/java/com/webchat/aigc/consumer/rocketmq/RobotChatRocketQueueConsumer.java
  55. 28 0
      webchat-aigc/src/main/java/com/webchat/aigc/controller/javasse/EventStreamConnectController.java
  56. 28 0
      webchat-aigc/src/main/java/com/webchat/aigc/llm/AbstractGenerateImageService.java
  57. 35 0
      webchat-aigc/src/main/java/com/webchat/aigc/llm/AbstractLLMChatService.java
  58. 104 0
      webchat-aigc/src/main/java/com/webchat/aigc/llm/AiAgentFlowService.java
  59. 56 0
      webchat-aigc/src/main/java/com/webchat/aigc/llm/AiFunctionCallService.java
  60. 94 0
      webchat-aigc/src/main/java/com/webchat/aigc/llm/DeepSeekAIService.java
  61. 80 0
      webchat-aigc/src/main/java/com/webchat/aigc/llm/GPTChatService.java
  62. 97 0
      webchat-aigc/src/main/java/com/webchat/aigc/llm/KimiAIService.java
  63. 58 0
      webchat-aigc/src/main/java/com/webchat/aigc/llm/LLMServiceFactory.java
  64. 171 0
      webchat-aigc/src/main/java/com/webchat/aigc/llm/LibLibGenerateImageService.java
  65. 12 0
      webchat-aigc/src/main/java/com/webchat/aigc/llm/RAGBotService.java
  66. 28 0
      webchat-aigc/src/main/java/com/webchat/aigc/service/UserService.java
  67. 1 0
      webchat-aigc/src/main/resources/application.yml
  68. 21 0
      webchat-aigc/src/main/resources/bootstrap.yml
  69. 18 0
      webchat-aigc/src/main/resources/templates/ftl/RAG.ftl
  70. 12 0
      webchat-aigc/src/main/resources/templates/ftl/ROBOT_CHAT.ftl
  71. 23 0
      webchat-aigc/src/main/resources/templates/ftl/ROBOT_FC.ftl
  72. BIN
      webchat-client-chat/.DS_Store
  73. 19 0
      webchat-client-chat/.mvn/wrapper/maven-wrapper.properties
  74. 50 0
      webchat-client-chat/pom.xml
  75. BIN
      webchat-client-chat/src/.DS_Store
  76. BIN
      webchat-client-chat/src/main/.DS_Store
  77. 17 0
      webchat-client-chat/src/main/java/com/webchat/client/WebchatClientApplication.java
  78. 64 0
      webchat-client-chat/src/main/java/com/webchat/client/advice/GlobalExceptionAdvice.java
  79. 57 0
      webchat-client-chat/src/main/java/com/webchat/client/chat/controller/AccountController.java
  80. 120 0
      webchat-client-chat/src/main/java/com/webchat/client/chat/controller/AccountRelationController.java
  81. 54 0
      webchat-client-chat/src/main/java/com/webchat/client/chat/controller/ChatMessageController.java
  82. 44 0
      webchat-client-chat/src/main/java/com/webchat/client/chat/controller/FileController.java
  83. 113 0
      webchat-client-chat/src/main/java/com/webchat/client/chat/filter/OauthFilter.java
  84. 128 0
      webchat-client-chat/src/main/java/com/webchat/client/chat/service/AccountRelationService.java
  85. 46 0
      webchat-client-chat/src/main/java/com/webchat/client/chat/service/AccountService.java
  86. 48 0
      webchat-client-chat/src/main/java/com/webchat/client/chat/service/ChatMessageService.java
  87. 69 0
      webchat-client-chat/src/main/java/com/webchat/client/chat/service/UserSessionService.java
  88. 1 0
      webchat-client-chat/src/main/resources/application.yml
  89. 21 0
      webchat-client-chat/src/main/resources/bootstrap.yml
  90. 93 0
      webchat-common/pom.xml
  91. 50 0
      webchat-common/src/main/java/com/webchat/common/bean/APIPageResponseBean.java
  92. 20 0
      webchat-common/src/main/java/com/webchat/common/bean/APIResponseBean.java
  93. 49 0
      webchat-common/src/main/java/com/webchat/common/bean/APIResponseBeanUtil.java
  94. 38 0
      webchat-common/src/main/java/com/webchat/common/config/annotation/SafeClick.java
  95. 20 0
      webchat-common/src/main/java/com/webchat/common/config/annotation/ValidateLogin.java
  96. 25 0
      webchat-common/src/main/java/com/webchat/common/config/annotation/ValidatePermission.java
  97. 23 0
      webchat-common/src/main/java/com/webchat/common/config/configuration/MinioConfig.java
  98. 28 0
      webchat-common/src/main/java/com/webchat/common/config/configuration/RocketMQProducerConfig.java
  99. 19 0
      webchat-common/src/main/java/com/webchat/common/config/properties/MinioProperties.java
  100. 13 0
      webchat-common/src/main/java/com/webchat/common/config/properties/OauthProperties.java

BIN
.DS_Store


+ 133 - 0
pom.xml

@@ -0,0 +1,133 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.springframework.boot</groupId>
+        <artifactId>spring-boot-starter-parent</artifactId>
+        <version>3.2.12</version>
+    </parent>
+
+    <groupId>com.webchat</groupId>
+    <artifactId>webchat</artifactId>
+    <version>0.0.1-SNAPSHOT</version>
+    <packaging>pom</packaging>
+    <name>webchat</name>
+    <description>web chat spring cloud</description>
+
+    <modules>
+        <module>webchat-gateway</module>
+        <module>webchat-sso</module>
+        <module>webchat-admin</module>
+        <module>webchat-client-chat</module>
+        <module>webchat-domain</module>
+        <module>webchat-common</module>
+        <module>webchat-remote</module>
+        <module>webchat-pgc</module>
+        <module>webchat-ugc</module>
+        <module>webchat-aigc</module>
+        <module>webchat-connect</module>
+        <module>webchat-user</module>
+        <module>webchat-pay</module>
+        <module>webchat-search</module>
+        <module>webchat-act</module>
+    </modules>
+
+    <properties>
+        <project.build.jdk>17</project.build.jdk>
+        <java.version>17</java.version>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+        <spring-boot.version>3.2.2</spring-boot.version>
+        <spring-cloud.version>2023.0.5</spring-cloud.version>
+        <lombok.version>1.18.30</lombok.version>
+        <guava-version>22.0</guava-version>
+    </properties>
+
+    <dependencies>
+        <!-- Spring Boot Web依赖 -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+        </dependency>
+        <!-- Spring Boot与Nacos整合的核心依赖 -->
+        <dependency>
+            <groupId>com.alibaba.cloud</groupId>
+            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
+            <version>2023.0.1.0</version> <!-- 与Spring Boot 3.2.x兼容 -->
+        </dependency>
+        <!-- 配置管理依赖,如果你需要配置管理功能 -->
+        <dependency>
+            <groupId>com.alibaba.cloud</groupId>
+            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
+            <version>2023.0.1.0</version>
+        </dependency>
+        <!--提供了引导配置的支持,允许应用程序在启动时从外部配置中心(如 Nacos、Consul、Config Server 等)加载配置。-->
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-bootstrap</artifactId>
+            <version>4.1.4</version>
+        </dependency>
+        <!--sentinel容错组件(隔离、超时、限流、熔断、降级)-->
+        <dependency>
+            <groupId>com.alibaba.cloud</groupId>
+            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
+            <version>2023.0.3.2</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-text</artifactId>
+            <version>1.6</version>
+        </dependency>
+
+        <!--lombok-->
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+            <version>${lombok.version}</version>
+            <scope>provided</scope>
+        </dependency>
+
+        <!-- guava -->
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+            <version>${guava-version}</version>
+        </dependency>
+    </dependencies>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>org.springframework.cloud</groupId>
+                <artifactId>spring-cloud-dependencies</artifactId>
+                <version>${spring-cloud.version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>dev</id>
+            <properties>
+                <profile>dev</profile>
+            </properties>
+            <activation>
+                <!-- 默认环境 -->
+                <activeByDefault>true</activeByDefault>
+            </activation>
+        </profile>
+    </profiles>
+</project>

BIN
resources/.DS_Store


+ 0 - 0
resources/database-sql/webchat-core.sql


+ 0 - 0
resources/database-sql/webchat-event.sql


+ 44 - 0
resources/database-sql/webchat-payment.sql

@@ -0,0 +1,44 @@
+-- 红包信息表
+CREATE TABLE webchat_payment.`web_chat_red_packet` (
+   `ID` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '自增ID',
+   `send_user_id` char(100) NOT NULL COMMENT '红包发送人',
+   `receiver_user_id` char(100) NOT NULL COMMENT '接受人',
+   `type` int(4) NOT NULL COMMENT '消息类型',
+   `count` int(4) NOT NULL COMMENT '红包个数',
+   `status` int(4) NOT NULL COMMENT '状态',
+   `total_money` DECIMAL(10, 2) default '0.00' COMMENT '金额',
+   `CREATE_BY` char(100) DEFAULT NULL COMMENT '创建人',
+   `CREATE_DATE` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+   `expire_date` datetime NOT NULL COMMENT '过期时间',
+   `UPDATE_BY` char(100) DEFAULT NULL COMMENT '更新人',
+   `UPDATE_DATE` datetime DEFAULT NULL COMMENT '更新时间',
+   `VERSION` int DEFAULT '0' COMMENT '版本',
+   PRIMARY KEY (`ID`),
+   KEY `INDEX_SEND_USER_ID` (`send_user_id`),
+   KEY `INDEX_STATUS_EXPIRE_DATE` (`status`, `expire_date`)
+) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='红包信息表';
+
+-- 红包拆分记录明细表
+CREATE TABLE webchat_payment.`web_chat_red_packet_record` (
+      `ID` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '自增ID',
+      `red_packet_id` bigint NOT NULL COMMENT '红包id',
+      `user_id` char(100) NOT NULL COMMENT '领取人',
+      `money` DECIMAL(10, 2) default '0.00' COMMENT '领取金额',
+      `CREATE_DATE` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+      PRIMARY KEY (`ID`),
+      KEY `INDEX_RED_PACKET_ID` (`red_packet_id`),
+      KEY `INDEX_USER_ID` (`user_id`)
+) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='红包拆分记录明细表';
+
+-- 用户钱包
+CREATE TABLE webchat_payment.`web_chat_user_wallet` (
+    `ID` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '自增ID',
+    `trans_event` int(4) NOT NULL COMMENT '事件类型',
+    `trans_type` int(4) NOT NULL COMMENT '收入/支出',
+    `user_id` char(100) NOT NULL COMMENT '用户id',
+    `target_user_id` char(100) NOT NULL COMMENT '目标用户',
+    `money` DECIMAL(10, 2) default '0.00' COMMENT '流转金额',
+    `trans_date` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '流转时间',
+    PRIMARY KEY (`ID`),
+    KEY `INDEX_USER_ID` (`user_id`)
+) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='用户钱包';

+ 17 - 0
resources/database-sql/webchat-ugc.sql

@@ -0,0 +1,17 @@
+-- 消息持久化数据表
+CREATE TABLE webchat_ugc.`web_chat_message` (
+     `ID` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '自增ID',
+     `sender` char(100) NOT NULL COMMENT '发送人',
+     `receiver` char(100) NOT NULL COMMENT '接收人',
+     `proxy_sender` char(100) DEFAULT NULL COMMENT '消息代理发送人(应用在群聊场景)',
+     `message` text DEFAULT NULL COMMENT '消息内容',
+     `image` varchar(300) DEFAULT NULL COMMENT '图片',
+     `type` tinyint(1) DEFAULT 0 COMMENT '消息类型',
+     `IS_READ` tinyint(1) DEFAULT 0 COMMENT '是否已读',
+     `SEND_DATE` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '消息时间',
+     `UPDATE_DATE` datetime DEFAULT NULL COMMENT '更新时间',
+     `VERSION` int DEFAULT '0' COMMENT '版本',
+     PRIMARY KEY (`ID`),
+     KEY `INDEX_SENDER_PROXY_SENDER` (`sender`, `proxy_sender`),
+     KEY `INDEX_RECEIVER` (`receiver`)
+) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='消息持久化数据表';

+ 82 - 0
resources/database-sql/webchat-user.sql

@@ -0,0 +1,82 @@
+-- 用户信息主表
+CREATE TABLE webchat_user.`web_chat_user` (
+     `ID` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '自增ID',
+     `USER_ID` char(60) NOT NULL COMMENT '用户ID',
+     `USER_NAME` char(30) NOT NULL COMMENT '用户名',
+     `PHOTO` varchar(400) NOT NULL COMMENT '用户头像',
+     `MOBILE` char(20) NOT NULL COMMENT '手机号',
+     `PASSWORD` char(100) NOT NULL COMMENT '密码',
+     `SIGNATURE` varchar(500) DEFAULT '暂无签名' COMMENT '签名',
+     `STATUS` INT NOT NULL DEFAULT 1 COMMENT '用户状态状态',
+     `ROLE_CODE` INT NOT NULL DEFAULT 1 COMMENT '角色',
+     `CREATE_BY` char(100) DEFAULT NULL COMMENT '创建人',
+     `CREATE_DATE` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+     `UPDATE_BY` char(100) DEFAULT NULL COMMENT '更新人',
+     `UPDATE_DATE` datetime DEFAULT NULL COMMENT '更新时间',
+     `VERSION` int DEFAULT '0' COMMENT '版本',
+     PRIMARY KEY (`ID`),
+     KEY `INDEX_USER_ID` (`USER_ID`),
+     KEY `INDEX_MOBILE_PASSWORD` (`MOBILE`, `PASSWORD`),
+     KEY `INDEX_STATUS` (`STATUS`)
+) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='用户信息主表';
+
+-- 初始化默认管理员账号
+INSERT INTO `webchat_user`.`web_chat_user` (`ID`, `USER_ID`, `USER_NAME`, `PHOTO`, `MOBILE`, `PASSWORD`, `STATUS`, `ROLE_CODE`,
+                                       `CREATE_BY`, `CREATE_DATE`, `UPDATE_BY`, `UPDATE_DATE`, `VERSION`) VALUES
+    (1, 'U_770cce9f632543588b4e8aa6ec43e6a2', '管理员', 'https://coderutil.oss-cn-beijing.aliyuncs.com/bbs-image/file_dd489633f1bb4513a9db81be6e9d692f.png',
+     'admin', '06525f4969c6cf1886ee0db86bef82df', 1, 2, 'U_770cce9f632543588b4e8aa6ec43e6a2',
+     '2022-03-12 05:55:26', NULL, '2022-03-22 10:28:38', 1);
+-- 初始化文件传输助手
+INSERT INTO `webchat_user`.`web_chat_user` (`USER_ID`, `USER_NAME`, `PHOTO`, `MOBILE`, `PASSWORD`, `STATUS`, `ROLE_CODE`,
+                                            `CREATE_BY`, `CREATE_DATE`, `UPDATE_BY`, `UPDATE_DATE`, `VERSION`) VALUES
+    ('F_ef352f698ad7b60c1d8a3aaa4a948030', '文件传输助手', 'https://coderutil.oss-cn-beijing.aliyuncs.com/bbs-image/file_42a26236a73f4df8b742397ac9c46c4f.png',
+     'filesender', 'F_ef352f698ad7b60c1d8a3aaa4a948030', 1, 0, 'F_ef352f698ad7b60c1d8a3aaa4a948030',
+     '2022-03-12 05:55:26', NULL, '2022-03-22 10:28:38', 1);
+
+-- 好友关系表
+CREATE TABLE webchat_user.`web_chat_friend` (
+   `ID` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '自增ID',
+   `USER_ID` char(60) NOT NULL COMMENT '用户ID',
+   `FRIEND_ID` char(60) NOT NULL COMMENT '好友ID',
+   `STATUS` int(11) DEFAULT '0' COMMENT '好友状态',
+   `APPLY_DATE` datetime DEFAULT NULL COMMENT '申请时间',
+   `HANDLE_DATE` datetime DEFAULT NULL COMMENT '处理时间',
+   `VERSION` int DEFAULT '0' COMMENT '版本',
+   PRIMARY KEY (`ID`),
+   KEY `INDEX_USER_ID` (`USER_ID`),
+   KEY `INDEX_FRIEND_ID` (`FRIEND_ID`),
+   KEY `INDEX_STATUS` (`STATUS`)
+) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='好友关系表';
+
+
+-- 群组用户表
+CREATE TABLE webchat_user.`web_chat_group_user` (
+                                       `ID` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '自增ID',
+                                       `GROUP_ID` char(60) NOT NULL COMMENT '群组ID',
+                                       `USER_ID` char(60) NOT NULL COMMENT '用户ID',
+                                       `STATUS` INT NOT NULL DEFAULT 1 COMMENT '用户状态状态',
+                                       `CREATE_BY` char(100) DEFAULT NULL COMMENT '创建人',
+                                       `CREATE_DATE` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+                                       `UPDATE_BY` char(100) DEFAULT NULL COMMENT '更新人',
+                                       `UPDATE_DATE` datetime DEFAULT NULL COMMENT '更新时间',
+                                       `VERSION` int DEFAULT '0' COMMENT '版本',
+                                       PRIMARY KEY (`ID`),
+                                       KEY `INDEX_GROUP_ID` (`GROUP_ID`),
+                                       KEY `INDEX_USER_ID` (`USER_ID`),
+                                       KEY `INDEX_STATUS` (`STATUS`)
+) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='群组用户表';
+
+
+-- 账号关系表
+CREATE TABLE webchat_user.`web_chat_account_relation` (
+  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '关系主键',
+  `source_account` char(64) NOT NULL COMMENT '原账号',
+  `target_account` char(64) NOT NULL COMMENT '目标账号',
+  `type` int(4) NOT NULL COMMENT '关系类型',
+  `status` int(4) NOT NULL COMMENT '关系状态',
+  `create_date` date NOT NULL COMMENT '创建时间',
+  `update_date` date NOT NULL COMMENT '更新时间',
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `UK_IDX_SOURCE_TARGET_ACCOUNT`(source_account, target_account),
+  KEY `IDX_STATUS_TYPE`(status, type)
+) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='账号关系表';

+ 0 - 0
resources/nacos-yaml/webchat-act-service-dev.yaml


+ 11 - 0
resources/nacos-yaml/webchat-admin-service-dev.yaml

@@ -0,0 +1,11 @@
+spring:
+  servlet:
+    multipart:
+      max-file-size: 10MB
+      max-request-size: 10MB
+
+minio:
+  endpoint: http://127.0.0.1:9000
+  accessKey: BArEoI6PE8uGh13jHRuN
+  secretKey: eHRL9vpXUH6vGOGkL5Vth2ZQRpzh7AvdKzNlASko
+  bucketName: webchat

+ 11 - 0
resources/nacos-yaml/webchat-client-chat-service-dev.yaml

@@ -0,0 +1,11 @@
+spring:
+  servlet:
+    multipart:
+      max-file-size: 10MB
+      max-request-size: 10MB
+
+minio:
+  endpoint: http://127.0.0.1:9000
+  accessKey: BArEoI6PE8uGh13jHRuN
+  secretKey: eHRL9vpXUH6vGOGkL5Vth2ZQRpzh7AvdKzNlASko
+  bucketName: webchat

+ 12 - 0
resources/nacos-yaml/webchat-connect-service-dev.yaml

@@ -0,0 +1,12 @@
+spring:
+  data:
+    redis:
+      host: 127.0.0.1
+      port: 6379
+      database: 0
+      jedis:
+      timeout: 5000
+      pool:
+        max-active: 100
+        max-wait: -1
+        min-idle: 10

+ 23 - 0
resources/nacos-yaml/webchat-pay-service-dev.yaml

@@ -0,0 +1,23 @@
+spring:
+  # set mysql config
+  datasource:
+    url: jdbc:mysql://127.0.0.1:3306/webchat?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2b8&useSSL=false
+    username: root
+    password: 12345678
+    driver-class-name: com.mysql.jdbc.Driver
+    hikari:
+      maximum-pool-size: 50
+  jpa:
+    show-sql: true
+  # set redis config
+  data:
+    redis:
+      host: 127.0.0.1
+      port: 6379
+      database: 0
+      jedis:
+        timeout: 5000
+        pool:
+          max-active: 100
+          max-wait: -1
+          min-idle: 10

+ 30 - 0
resources/nacos-yaml/webchat-pgc-service-dev.yaml

@@ -0,0 +1,30 @@
+#---------------------------------数据库配置----------------------------------#
+spring:
+  datasource:
+    url: jdbc:mysql://127.0.0.1:3306/webchat?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2b8&&useSSL=false
+    username: root
+    password: 12345678
+    driver-class-name: com.mysql.jdbc.Driver
+    type: com.zaxxer.hikari.HikariDataSource
+    hikari:
+    minimum-idle: 10
+    maximum-pool-size: 50
+    auto-commit: false
+    idle-timeout: 30000
+    pool-name: DatebookHikariCP
+    max-lifetime: 1800000
+    connection-timeout: 30000
+    connection-test-query: SELECT 1
+    connection-init-sql: set names utf8mb4
+  jpa:
+    show-sql: true
+  #---------------------------------redis----------------------------------#
+  data:
+    redis:
+    port: 6379
+    database: 0
+    jedis:
+      pool:
+      max-active: 100
+      max-wait: -1
+      min-idle: 10

+ 0 - 0
resources/nacos-yaml/webchat-search-service-dev.yaml


+ 0 - 0
resources/nacos-yaml/webchat-sso-service-dev.yaml


+ 21 - 0
resources/nacos-yaml/webchat-user-service-dev.yaml

@@ -0,0 +1,21 @@
+spring:
+  datasource:
+    url: jdbc:mysql://127.0.0.1:3306/webchat_user?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2b8
+    username: root
+    password: 12345678
+    driver-class-name: com.mysql.jdbc.Driver
+    hikari:
+      maximum-pool-size: 50
+  jpa:
+    show-sql: true
+  data:
+    redis:
+      host: 127.0.0.1
+      port: 6379
+      database: 0
+      jedis:
+        timeout: 5000
+        pool:
+          max-active: 100
+          max-wait: -1
+          min-idle: 10

+ 0 - 0
resources/webchat.md


+ 51 - 0
webchat-act/pom.xml

@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>com.webchat</groupId>
+        <artifactId>webchat</artifactId>
+        <version>0.0.1-SNAPSHOT</version>
+    </parent>
+
+    <groupId>com.webchat</groupId>
+    <artifactId>webchat-act</artifactId>
+    <version>0.0.1-SNAPSHOT</version>
+    <packaging>jar</packaging>
+
+    <name>webchat-act</name>
+    <description>webchat-act</description>
+
+    <properties>
+        <java.version>17</java.version>
+    </properties>
+    <dependencies>
+
+        <!--引入mysql驱动-->
+<!--        <dependency>-->
+<!--            <groupId>mysql</groupId>-->
+<!--            <artifactId>mysql-connector-java</artifactId>-->
+<!--            <version>5.1.46</version>-->
+<!--        </dependency>-->
+        <!--使用JPA作为ORM框架 -->
+<!--        <dependency>-->
+<!--            <groupId>org.springframework.boot</groupId>-->
+<!--            <artifactId>spring-boot-starter-data-jpa</artifactId>-->
+<!--        </dependency>-->
+
+        <dependency>
+            <groupId>com.webchat</groupId>
+            <artifactId>webchat-remote</artifactId>
+            <version>1.0-SNAPSHOT</version>
+        </dependency>
+    </dependencies>
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+</project>

+ 19 - 0
webchat-act/src/main/java/com/webchat/act/WebchatActApplication.java

@@ -0,0 +1,19 @@
+package com.webchat.act;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
+import org.springframework.cloud.openfeign.EnableFeignClients;
+import org.springframework.context.annotation.ComponentScan;
+
+@SpringBootApplication
+@EnableDiscoveryClient
+@ComponentScan("com.webchat")
+@EnableFeignClients("com.webchat.rmi")
+public class WebchatActApplication {
+
+    public static void main(String[] args) {
+        SpringApplication.run(WebchatActApplication.class, args);
+    }
+
+}

+ 1 - 0
webchat-act/src/main/resources/application.yml

@@ -0,0 +1 @@
+server:
  port: 8018

+ 16 - 0
webchat-act/src/main/resources/bootstrap.yml

@@ -0,0 +1,16 @@
+spring:
+  application:
+    name: webchat-act-service
+  profiles:
+    active: dev
+  cloud:
+    nacos:
+      config:
+        server-addr: 127.0.0.1:8848
+        namespace: 2cdfb8e3-f127-4020-93df-2d4c3dc805b5
+        file-extension: yaml
+        group: DEFAULT_GROUP
+        refresh-enabled: true
+      discovery:
+        server-addr: 127.0.0.1:8848
+        namespace: 2cdfb8e3-f127-4020-93df-2d4c3dc805b5

BIN
webchat-admin/.DS_Store


+ 19 - 0
webchat-admin/.mvn/wrapper/maven-wrapper.properties

@@ -0,0 +1,19 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+wrapperVersion=3.3.2
+distributionType=only-script
+distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip

+ 51 - 0
webchat-admin/pom.xml

@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>com.webchat</groupId>
+        <artifactId>webchat</artifactId>
+        <version>0.0.1-SNAPSHOT</version>
+    </parent>
+
+    <groupId>com.webchat</groupId>
+    <artifactId>webchat-admin</artifactId>
+    <version>0.0.1-SNAPSHOT</version>
+    <packaging>jar</packaging>
+
+    <name>webchat-admin</name>
+    <description>webchat-admin</description>
+
+    <properties>
+        <java.version>17</java.version>
+    </properties>
+    <dependencies>
+
+        <dependency>
+            <groupId>com.webchat</groupId>
+            <artifactId>webchat-remote</artifactId>
+            <version>1.0-SNAPSHOT</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>

BIN
webchat-admin/src/.DS_Store


BIN
webchat-admin/src/main/.DS_Store


+ 18 - 0
webchat-admin/src/main/java/com/webchat/admin/WebchatAdminApplication.java

@@ -0,0 +1,18 @@
+package com.webchat.admin;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.web.servlet.ServletComponentScan;
+import org.springframework.cloud.openfeign.EnableFeignClients;
+import org.springframework.context.annotation.ComponentScan;
+
+@SpringBootApplication
+@ComponentScan("com.webchat")
+@EnableFeignClients("com.webchat.rmi")
+public class WebchatAdminApplication {
+
+    public static void main(String[] args) {
+        SpringApplication.run(WebchatAdminApplication.class, args);
+    }
+
+}

+ 64 - 0
webchat-admin/src/main/java/com/webchat/admin/advice/GlobalExceptionAdvice.java

@@ -0,0 +1,64 @@
+package com.webchat.admin.advice;
+
+import com.webchat.common.bean.APIResponseBean;
+import com.webchat.common.bean.APIResponseBeanUtil;
+import com.webchat.common.enums.APIErrorCommonEnum;
+import com.webchat.common.exception.AuthException;
+import com.webchat.common.exception.BusinessException;
+import com.webchat.common.exception.NotFoundException;
+import jakarta.servlet.http.HttpServletRequest;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.http.converter.HttpMessageConversionException;
+import org.springframework.web.HttpRequestMethodNotSupportedException;
+import org.springframework.web.bind.MethodArgumentNotValidException;
+import org.springframework.web.bind.annotation.ControllerAdvice;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+
+/***
+ * 统一异常处理器
+ * @Author 程序员七七 ==> http://www.coderutil.com网站作者
+ */
+@Slf4j
+@ControllerAdvice
+public class GlobalExceptionAdvice {
+
+    @ExceptionHandler(Exception.class)
+    @ResponseBody
+    public ResponseEntity<APIResponseBean> exceptionHandler(HttpServletRequest request, Exception e) {
+        log.error("异常详情信息:", e);
+        if (e instanceof AuthException) {
+            APIResponseBean apiResponseBean = APIResponseBeanUtil.error(((AuthException) e).getCode(), e.getMessage());
+            return new ResponseEntity(apiResponseBean, HttpStatus.OK);
+        }
+        if (e instanceof IllegalArgumentException) {
+            APIResponseBean apiResponseBean = APIResponseBeanUtil.error(500, e.getMessage());
+            return new ResponseEntity(apiResponseBean, HttpStatus.OK);
+        } else if (e instanceof BusinessException) {
+            APIResponseBean apiResponseBean = APIResponseBeanUtil.error(((BusinessException) e).getCode(), e.getMessage());
+            return new ResponseEntity(apiResponseBean, HttpStatus.OK);
+        } else if (e instanceof NotFoundException) {
+            APIResponseBean apiResponseBean = APIResponseBeanUtil.error(((NotFoundException) e).getCode(), e.getMessage());
+            return new ResponseEntity(apiResponseBean, HttpStatus.OK);
+        } else if (e instanceof HttpRequestMethodNotSupportedException) {
+            APIResponseBean apiResponseBean = APIResponseBeanUtil.error(500, e.getMessage());
+            return new ResponseEntity(apiResponseBean, HttpStatus.OK);
+        } else if (e instanceof MethodArgumentNotValidException) {
+            /***
+             * 处理@Validated参数校验失败异常
+             */
+            APIResponseBean apiResponseBean = APIResponseBeanUtil.error(APIErrorCommonEnum.VALID_EXCEPTION.getCode(),
+                    "参数校验异常");
+            return new ResponseEntity(apiResponseBean, HttpStatus.OK);
+        } else if (e instanceof HttpMessageConversionException) {
+            APIResponseBean apiResponseBean = APIResponseBeanUtil.error(APIErrorCommonEnum.VALID_EXCEPTION.getCode(),
+                    "参数转换异常");
+            return new ResponseEntity(apiResponseBean, HttpStatus.OK);
+        }
+        APIResponseBean apiResponseBean = APIResponseBeanUtil.error(500, "服务端繁忙");
+        return new ResponseEntity(apiResponseBean, HttpStatus.INTERNAL_SERVER_ERROR);
+    }
+}

+ 33 - 0
webchat-admin/src/main/java/com/webchat/admin/config/AdminWebMvcConfig.java

@@ -0,0 +1,33 @@
+package com.webchat.admin.config;
+
+import com.webchat.admin.config.interceptor.ValidatePermissionInterceptor;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+@Configuration
+public class AdminWebMvcConfig implements WebMvcConfigurer {
+
+    /**
+     * 注册自定义拦截器
+     * @param registry
+     */
+    @Override
+    public void addInterceptors(InterceptorRegistry registry) {
+        /**
+         * 注册权限控制拦截器
+         */
+        registry.addInterceptor(validatePermissionInterceptor()).addPathPatterns("/**");
+    }
+
+    /***
+     * 登录核验拦截器
+     * @return
+     */
+    @Bean
+    public ValidatePermissionInterceptor validatePermissionInterceptor() {
+        return new ValidatePermissionInterceptor();
+    }
+
+}

+ 56 - 0
webchat-admin/src/main/java/com/webchat/admin/config/interceptor/ValidatePermissionInterceptor.java

@@ -0,0 +1,56 @@
+package com.webchat.admin.config.interceptor;
+
+
+import com.webchat.admin.service.AccountRoleService;
+import com.webchat.common.config.annotation.ValidatePermission;
+import com.webchat.common.enums.RoleCodeEnum;
+import com.webchat.common.exception.AuthException;
+import com.webchat.common.helper.SessionHelper;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.method.HandlerMethod;
+import org.springframework.web.servlet.HandlerInterceptor;
+
+import java.lang.reflect.Method;
+
+public class ValidatePermissionInterceptor implements HandlerInterceptor {
+
+    @Autowired
+    private AccountRoleService accountRoleService;
+
+    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
+
+        HandlerMethod handlerMethod = (HandlerMethod) handler;
+        // 获取拦截到的方法
+        Method method = handlerMethod.getMethod();
+        // 获取方法到的RequestPermission注解
+        ValidatePermission requestPermission = method.getAnnotation(ValidatePermission.class);
+        if (requestPermission == null) {
+            // 未获取到权控注解,说明不需要拦截,直接放过
+            return true;
+        }
+
+        /***
+         * 权限校验
+         */
+        if (requestPermission.role().length > 0) {
+            RoleCodeEnum[] roleEnums = requestPermission.role();
+            // 获取当前登录的用户ID
+            String userId = SessionHelper.getCurrentUserId();
+            if (StringUtils.isBlank(userId)) {
+                // 未登录!权限校验失败
+                throw new AuthException("未登录!");
+            }
+            // 一个一个校验,或者的关系:即满足任一权限则通过
+            for (RoleCodeEnum roleEnum : roleEnums) {
+                // 用户任一权限校验通过,则认为拥有操作权限
+                if (accountRoleService.hasRole(userId, roleEnum.getCode())) {
+                    return true;
+                }
+            }
+        }
+        throw new AuthException("无权限!");
+    }
+}

+ 93 - 0
webchat-admin/src/main/java/com/webchat/admin/controller/AccountManagementController.java

@@ -0,0 +1,93 @@
+package com.webchat.admin.controller;
+
+
+import com.webchat.admin.service.AccountManagementService;
+import com.webchat.common.bean.APIPageResponseBean;
+import com.webchat.common.bean.APIResponseBean;
+import com.webchat.common.bean.APIResponseBeanUtil;
+import com.webchat.common.helper.SessionHelper;
+import com.webchat.domain.vo.request.CreatePublicAccountRequestVO;
+import com.webchat.domain.vo.request.CreateRobotRequestVO;
+import com.webchat.domain.vo.response.UserBaseResponseInfoVO;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+
+@RestController
+@RequestMapping("/admin-service/account")
+public class AccountManagementController {
+
+    @Autowired
+    private AccountManagementService accountManagementService;
+
+    /**
+     * 账号分页查询
+     *
+     * @param roleCode
+     * @param userId
+     * @param userName
+     * @param mobile
+     * @param pageNo
+     * @param pageSize
+     * @return
+     */
+    @GetMapping("/page")
+    public APIPageResponseBean<UserBaseResponseInfoVO> page(
+            @RequestParam(value = "roleCode") Integer roleCode,
+            @RequestParam(value = "userId", required = false) String userId,
+            @RequestParam(value = "userName", required = false) String userName,
+            @RequestParam(value = "mobile", required = false) String mobile,
+            @RequestParam(value = "pageNo", required = false, defaultValue = "1") Integer pageNo,
+            @RequestParam(value = "pageSize", required = false, defaultValue = "10") Integer pageSize) {
+
+        return accountManagementService.page(userId, roleCode, userName, mobile, pageNo, pageSize);
+    }
+
+    /**
+     * 账号搜索
+     *
+     * @param query
+     * @param size
+     * @return
+     */
+    @GetMapping("/suggest")
+    public APIResponseBean<List<UserBaseResponseInfoVO>> suggest(
+            @RequestParam(value = "q", required = false) String query,
+            @RequestParam(value = "size", required = false, defaultValue = "10") Integer size) {
+
+        return APIResponseBeanUtil.success(null);
+    }
+
+    /**
+     * 创建机器人
+     *
+     * @return
+     */
+    @PostMapping("/createRobot")
+    public APIResponseBean createRobot(@RequestBody CreateRobotRequestVO requestPram) {
+        String userId = SessionHelper.getCurrentUserId();
+        requestPram.setCreateUserId(userId);
+        accountManagementService.createRobot(requestPram);
+        return APIResponseBeanUtil.success("机器人创建成功");
+    }
+
+    /**
+     * 创建公众号
+     * 默认只有管理员可以创建公众号
+     *
+     * @return
+     */
+    @PostMapping("/createPublicAccount")
+    public APIResponseBean createPublicAccount(@RequestBody CreatePublicAccountRequestVO requestPram) {
+        String userId = SessionHelper.getCurrentUserId();
+        requestPram.setCreateUserId(userId);
+        accountManagementService.createPublicAccount(requestPram);
+        return APIResponseBeanUtil.success("公众号创建成功");
+    }
+}

+ 44 - 0
webchat-admin/src/main/java/com/webchat/admin/controller/FileController.java

@@ -0,0 +1,44 @@
+package com.webchat.admin.controller;
+
+import com.webchat.common.bean.APIResponseBean;
+import com.webchat.common.bean.APIResponseBeanUtil;
+import com.webchat.common.service.MinioService;
+import com.webchat.common.util.DateUtils;
+import com.webchat.domain.vo.response.UploadResultVO;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.util.Assert;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.util.Date;
+
+@RestController
+@RequestMapping("/admin-service/file")
+public class FileController {
+
+    @Autowired
+    private MinioService minioService;
+    @Autowired
+    private HttpServletRequest request;
+
+    @PostMapping("/upload")
+    public APIResponseBean<UploadResultVO> upload(MultipartFile file) {
+        String path = request.getHeader("upload-path");
+        Assert.isTrue(StringUtils.isNotBlank(path), "上传路径不能为空");
+        String timePackage = DateUtils.getDate2String(DateUtils.YYYYMMDD, new Date());
+        UploadResultVO uploadResultDTO = minioService.upload(path + "/" + timePackage, file, true);
+        return APIResponseBeanUtil.success(uploadResultDTO);
+    }
+
+    @GetMapping("/download")
+    public void download(@RequestParam String filename, HttpServletResponse response) {
+        minioService.download(filename, response);
+    }
+}

+ 33 - 0
webchat-admin/src/main/java/com/webchat/admin/controller/WalletManagementController.java

@@ -0,0 +1,33 @@
+package com.webchat.admin.controller;
+
+
+import com.webchat.admin.service.WalletManagementService;
+import com.webchat.common.bean.APIPageResponseBean;
+import com.webchat.common.config.annotation.ValidatePermission;
+import com.webchat.domain.vo.response.UserWalletDetailResponseVO;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@RequestMapping("/admin-service/wallet")
+public class WalletManagementController {
+
+    @Autowired
+    private WalletManagementService walletManagementService;
+
+    @ValidatePermission
+    @GetMapping("/page")
+    public APIPageResponseBean<UserWalletDetailResponseVO> page(
+            @RequestParam(value = "transType", required = false) Integer transType,
+            @RequestParam(value = "eventType", required = false) Integer eventType,
+            @RequestParam(value = "userId", required = false) String userId,
+            @RequestParam(value = "pageNo", required = false, defaultValue = "1") Integer pageNo,
+            @RequestParam(value = "pageSize", required = false, defaultValue = "10") Integer pageSize) {
+
+        return walletManagementService.page(transType, eventType, userId, pageNo, pageSize);
+    }
+
+}

+ 114 - 0
webchat-admin/src/main/java/com/webchat/admin/filter/OauthFilter.java

@@ -0,0 +1,114 @@
+package com.webchat.admin.filter;
+
+import com.webchat.admin.service.UserSessionService;
+import com.webchat.common.config.properties.OauthProperties;
+import com.webchat.common.constants.CookieConstants;
+import com.webchat.common.enums.BaseErrCodeEnum;
+import com.webchat.common.helper.SessionHelper;
+import com.webchat.common.util.JsonUtil;
+import com.webchat.rmi.sso.OauthServiceClient;
+import jakarta.servlet.Filter;
+import jakarta.servlet.FilterChain;
+import jakarta.servlet.FilterConfig;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.ServletRequest;
+import jakarta.servlet.ServletResponse;
+import jakarta.servlet.annotation.WebFilter;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.annotation.Order;
+import org.springframework.stereotype.Component;
+
+import java.io.IOException;
+import java.net.URLEncoder;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @Author: 程序员七七
+ * @Date: 16.11.21 11:26 下午
+ */
+@Slf4j
+@Order(-6)
+@WebFilter(filterName = "oauthFilter", urlPatterns = {"/*"})
+@Component
+public class OauthFilter implements Filter {
+
+    @Autowired
+    private OauthProperties oauthProperties;
+
+    @Autowired
+    private OauthServiceClient oauthServiceClient;
+
+    @Autowired
+    private UserSessionService userSessionService;
+
+    @Override
+    public void init(FilterConfig filterConfig) throws ServletException {
+
+    }
+
+    @Override
+    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
+        HttpServletRequest request = (HttpServletRequest) servletRequest;
+        HttpServletResponse response = (HttpServletResponse) servletResponse;
+        // 取集中session服务判断是否已经登录,如果已经登录则直接进入后续方法。
+        String oauthCode = request.getHeader("oauth-code");
+        String userId = userSessionService.getUserIdByRequest(request);
+        if (StringUtils.isNotBlank(userId)) {
+            // 已经登录
+            SessionHelper.setUserId(userId);
+            try {
+                filterChain.doFilter(request, response);
+            } finally {
+                SessionHelper.clear();
+            }
+            return;
+        }
+        if (StringUtils.isNotBlank(oauthCode)) {
+            userId = oauthServiceClient.getUserIdByOauthCode(oauthCode).getData();
+            if (StringUtils.isNotBlank(userId)) {
+                userSessionService.createUserLoginSession(response, userId);
+                SessionHelper.setUserId(userId);
+                try {
+                    filterChain.doFilter(request, response);
+                } finally {
+                    SessionHelper.clear();
+                }
+                return;
+            }
+        }
+        /**
+         * 302 重定向到统一登录页面
+         */
+        this.unLogin(request, response);
+    }
+
+    private void unLogin(HttpServletRequest request, HttpServletResponse response) {
+        Map<String, Object> responseMap = new HashMap<>();
+        response.setCharacterEncoding("UTF-8"); // 设置字符编码为 UTF-8
+        response.setContentType("application/json;charset=UTF-8"); // 设置内容类型和字符编码
+        responseMap.put("code", BaseErrCodeEnum.UN_LOGIN.getCode());
+        responseMap.put("message", BaseErrCodeEnum.UN_LOGIN.getMessage());
+        responseMap.put("redirect_url", getRedirectUrl(request));
+        try {
+            response.getWriter().println(JsonUtil.toJsonString(responseMap));
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private String getRedirectUrl(HttpServletRequest request) {
+        String serverUrl = oauthProperties.getServerUrl();
+        String originUrl = request.getHeader("origin-url");
+        return String.format(serverUrl, URLEncoder.encode(originUrl));
+    }
+
+    @Override
+    public void destroy() {
+
+    }
+}

+ 79 - 0
webchat-admin/src/main/java/com/webchat/admin/service/AccountManagementService.java

@@ -0,0 +1,79 @@
+package com.webchat.admin.service;
+
+import com.webchat.common.bean.APIPageResponseBean;
+import com.webchat.common.bean.APIResponseBean;
+import com.webchat.common.bean.APIResponseBeanUtil;
+import com.webchat.common.exception.BusinessException;
+import com.webchat.domain.vo.request.CreatePublicAccountRequestVO;
+import com.webchat.domain.vo.request.CreateRobotRequestVO;
+import com.webchat.domain.vo.response.UserBaseResponseInfoVO;
+import com.webchat.rmi.user.UserServiceClient;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.web.bind.annotation.RequestParam;
+
+import java.util.List;
+
+@Service
+public class AccountManagementService {
+
+
+    @Autowired
+    private UserServiceClient userServiceClient;
+
+    /**
+     * 分页查询账号信息
+     *
+     * @param userId
+     * @param roleCode
+     * @param userName
+     * @param mobile
+     * @param pageNo
+     * @param pageSize
+     * @return
+     */
+    public APIPageResponseBean<UserBaseResponseInfoVO> page(String userId, Integer roleCode, String userName, String mobile, Integer pageNo, Integer pageSize) {
+
+        return userServiceClient.page(userId, roleCode, userName, mobile, pageNo, pageSize);
+    }
+
+    /**
+     * 账号搜索
+     *
+     * @param query
+     * @param size
+     * @return
+     */
+    public List<UserBaseResponseInfoVO> suggest(String query, Integer size) {
+
+        return null;
+    }
+
+
+    /**
+     * 创建机器人
+     *
+     * 复用用户信息表,账号角色为 ROBOT
+     */
+    public void createRobot(CreateRobotRequestVO requestPram) {
+        APIResponseBean apiResponseBean = userServiceClient.createRobot(requestPram);
+        if (APIResponseBeanUtil.isOk(apiResponseBean)) {
+            return;
+        }
+        throw new BusinessException(apiResponseBean.getMsg());
+    }
+
+    /**
+     * 创建公众号
+     *
+     * 复用用户信息表,账号角色为 PUBLIC_ACCOUNT
+     */
+    public void createPublicAccount(CreatePublicAccountRequestVO requestVO) {
+        APIResponseBean apiResponseBean = userServiceClient.createPublicAccount(requestVO);
+        if (APIResponseBeanUtil.isOk(apiResponseBean)) {
+            return;
+        }
+        throw new BusinessException(apiResponseBean.getMsg());
+    }
+
+}

+ 36 - 0
webchat-admin/src/main/java/com/webchat/admin/service/AccountRoleService.java

@@ -0,0 +1,36 @@
+package com.webchat.admin.service;
+
+
+import com.webchat.domain.vo.response.UserBaseResponseInfoVO;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.util.Assert;
+
+@Slf4j
+@Service
+public class AccountRoleService {
+
+
+    @Autowired
+    private AccountService accountService;
+
+    /**
+     * 校验账号是否拥有权限
+     *
+     * @param account   用户ID
+     * @param roleCode 权限CODE
+     * @return
+     */
+    public boolean hasRole(String account, Integer roleCode) {
+        if (StringUtils.isBlank(account) || roleCode == null) {
+            log.error("{} not has role: {}!", account, roleCode);
+            return false;
+        }
+        UserBaseResponseInfoVO accountInfo = accountService.accountInfo(account);
+        Assert.isTrue(accountInfo != null, "无权限");
+        return roleCode.equals(accountInfo.getRoleCode());
+    }
+
+}

+ 28 - 0
webchat-admin/src/main/java/com/webchat/admin/service/AccountService.java

@@ -0,0 +1,28 @@
+package com.webchat.admin.service;
+
+
+import com.webchat.common.bean.APIResponseBean;
+import com.webchat.common.bean.APIResponseBeanUtil;
+import com.webchat.domain.vo.response.UserBaseResponseInfoVO;
+import com.webchat.rmi.user.UserServiceClient;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Service
+public class AccountService {
+
+
+    @Autowired
+    private UserServiceClient userServiceClient;
+
+
+    public UserBaseResponseInfoVO accountInfo(String account) {
+
+        APIResponseBean<UserBaseResponseInfoVO> responseBean = userServiceClient.userInfo(account);
+        if (APIResponseBeanUtil.isOk(responseBean)) {
+            return responseBean.getData();
+        }
+        return null;
+    }
+
+}

+ 52 - 0
webchat-admin/src/main/java/com/webchat/admin/service/UserSessionService.java

@@ -0,0 +1,52 @@
+package com.webchat.admin.service;
+
+import com.webchat.common.constants.CookieConstants;
+import com.webchat.common.enums.RedisKeyEnum;
+import com.webchat.common.service.RedisService;
+import com.webchat.common.util.DateUtils;
+import com.webchat.common.util.MD5Utils;
+import jakarta.servlet.http.Cookie;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Service
+public class UserSessionService {
+
+    @Autowired
+    private RedisService redisService;
+
+    public String getUserIdByRequest(HttpServletRequest request) {
+        Cookie[] cookies = request.getCookies();
+        if (cookies != null) {
+            for (Cookie cookie : cookies) {
+                if (CookieConstants.WEB_CHAT_ADMIN_USER_COOKIE_KEY.equals(cookie.getName())) {
+                    String sessionId = cookie.getValue();
+                    return this.getUserIdBySessionId(sessionId);
+                }
+            }
+        }
+        return null;
+    }
+
+    public void createUserLoginSession(HttpServletResponse response, String userId) {
+        String sessionId = MD5Utils.md5(userId + DateUtils.getCurrentFormatDate());
+        this.setUserIdBySessionId(sessionId, userId);
+        // 种浏览器Cookie
+        Cookie cookie = new Cookie(CookieConstants.WEB_CHAT_ADMIN_USER_COOKIE_KEY, sessionId);
+        cookie.setPath("/");
+        cookie.setMaxAge(CookieConstants.COOKIE_OUT_TIME);
+        response.addCookie(cookie);
+    }
+
+    public String getUserIdBySessionId(String sessionId) {
+        String sessionCacheKey = RedisKeyEnum.USER_SESSION_PREFIX.getKey(sessionId);
+        return redisService.get(sessionCacheKey);
+    }
+
+    public void setUserIdBySessionId(String sessionId, String userId) {
+        String sessionCacheKey = RedisKeyEnum.USER_SESSION_PREFIX.getKey(sessionId);
+        redisService.set(sessionCacheKey, userId, RedisKeyEnum.USER_SESSION_PREFIX.getExpireTime());
+    }
+}

+ 21 - 0
webchat-admin/src/main/java/com/webchat/admin/service/WalletManagementService.java

@@ -0,0 +1,21 @@
+package com.webchat.admin.service;
+
+import com.webchat.common.bean.APIPageResponseBean;
+import com.webchat.domain.vo.response.UserWalletDetailResponseVO;
+import com.webchat.rmi.pay.PaymentServiceClient;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Service
+public class WalletManagementService {
+
+
+    @Autowired
+    private PaymentServiceClient paymentServiceClient;
+
+    public APIPageResponseBean<UserWalletDetailResponseVO> page(Integer transType, Integer eventType, String userId,
+                                                                Integer pageNo, Integer pageSize) {
+
+        return paymentServiceClient.page(transType, eventType, userId, pageNo, pageSize);
+    }
+}

+ 1 - 0
webchat-admin/src/main/resources/application.yml

@@ -0,0 +1 @@
+server:
  port: 8081

+ 21 - 0
webchat-admin/src/main/resources/bootstrap.yml

@@ -0,0 +1,21 @@
+spring:
+  application:
+    name: webchat-admin-service
+  profiles:
+    active: dev
+  cloud:
+    nacos:
+      config:
+        server-addr: 127.0.0.1:8848
+        namespace: 2cdfb8e3-f127-4020-93df-2d4c3dc805b5
+        file-extension: yaml
+        group: DEFAULT_GROUP
+        refresh-enabled: true
+      discovery:
+        server-addr: 127.0.0.1:8848
+        namespace: 2cdfb8e3-f127-4020-93df-2d4c3dc805b5
+    sentinel:
+      transport:
+        port: 8719
+        dashboard: 127.0.0.1:8080
+      eager: true

BIN
webchat-aigc/.DS_Store


+ 52 - 0
webchat-aigc/pom.xml

@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>com.webchat</groupId>
+        <artifactId>webchat</artifactId>
+        <version>0.0.1-SNAPSHOT</version>
+    </parent>
+
+    <groupId>com.webchat</groupId>
+    <artifactId>webchat-aigc</artifactId>
+    <version>0.0.1-SNAPSHOT</version>
+    <packaging>jar</packaging>
+
+    <name>webchat-aigc</name>
+    <description>webchat-aigc</description>
+
+
+    <properties>
+        <java.version>17</java.version>
+    </properties>
+    <dependencies>
+
+        <dependency>
+            <groupId>com.webchat</groupId>
+            <artifactId>webchat-remote</artifactId>
+            <version>1.0-SNAPSHOT</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>

BIN
webchat-aigc/src/.DS_Store


BIN
webchat-aigc/src/main/.DS_Store


+ 19 - 0
webchat-aigc/src/main/java/com/webchat/aigc/WebchatAIGCApplication.java

@@ -0,0 +1,19 @@
+package com.webchat.aigc;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
+import org.springframework.cloud.openfeign.EnableFeignClients;
+import org.springframework.context.annotation.ComponentScan;
+
+@SpringBootApplication
+@EnableDiscoveryClient
+@ComponentScan("com.webchat")
+@EnableFeignClients("com.webchat.rmi")
+public class WebchatAIGCApplication {
+
+    public static void main(String[] args) {
+        SpringApplication.run(WebchatAIGCApplication.class, args);
+    }
+
+}

+ 64 - 0
webchat-aigc/src/main/java/com/webchat/aigc/advice/GlobalExceptionAdvice.java

@@ -0,0 +1,64 @@
+package com.webchat.aigc.advice;
+
+import com.webchat.common.bean.APIResponseBean;
+import com.webchat.common.bean.APIResponseBeanUtil;
+import com.webchat.common.enums.APIErrorCommonEnum;
+import com.webchat.common.exception.AuthException;
+import com.webchat.common.exception.BusinessException;
+import com.webchat.common.exception.NotFoundException;
+import jakarta.servlet.http.HttpServletRequest;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.http.converter.HttpMessageConversionException;
+import org.springframework.web.HttpRequestMethodNotSupportedException;
+import org.springframework.web.bind.MethodArgumentNotValidException;
+import org.springframework.web.bind.annotation.ControllerAdvice;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+
+/***
+ * 统一异常处理器
+ * @Author 程序员七七 ==> http://www.coderutil.com网站作者
+ */
+@Slf4j
+@ControllerAdvice
+public class GlobalExceptionAdvice {
+
+    @ExceptionHandler(Exception.class)
+    @ResponseBody
+    public ResponseEntity<APIResponseBean> exceptionHandler(HttpServletRequest request, Exception e) {
+        log.error("异常详情信息:", e);
+        if (e instanceof AuthException) {
+            APIResponseBean apiResponseBean = APIResponseBeanUtil.error(((AuthException) e).getCode(), e.getMessage());
+            return new ResponseEntity(apiResponseBean, HttpStatus.OK);
+        }
+        if (e instanceof IllegalArgumentException) {
+            APIResponseBean apiResponseBean = APIResponseBeanUtil.error(500, e.getMessage());
+            return new ResponseEntity(apiResponseBean, HttpStatus.OK);
+        } else if (e instanceof BusinessException) {
+            APIResponseBean apiResponseBean = APIResponseBeanUtil.error(((BusinessException) e).getCode(), e.getMessage());
+            return new ResponseEntity(apiResponseBean, HttpStatus.OK);
+        } else if (e instanceof NotFoundException) {
+            APIResponseBean apiResponseBean = APIResponseBeanUtil.error(((NotFoundException) e).getCode(), e.getMessage());
+            return new ResponseEntity(apiResponseBean, HttpStatus.OK);
+        } else if (e instanceof HttpRequestMethodNotSupportedException) {
+            APIResponseBean apiResponseBean = APIResponseBeanUtil.error(500, e.getMessage());
+            return new ResponseEntity(apiResponseBean, HttpStatus.OK);
+        } else if (e instanceof MethodArgumentNotValidException) {
+            /***
+             * 处理@Validated参数校验失败异常
+             */
+            APIResponseBean apiResponseBean = APIResponseBeanUtil.error(APIErrorCommonEnum.VALID_EXCEPTION.getCode(),
+                    "参数校验异常");
+            return new ResponseEntity(apiResponseBean, HttpStatus.OK);
+        } else if (e instanceof HttpMessageConversionException) {
+            APIResponseBean apiResponseBean = APIResponseBeanUtil.error(APIErrorCommonEnum.VALID_EXCEPTION.getCode(),
+                    "参数转换异常");
+            return new ResponseEntity(apiResponseBean, HttpStatus.OK);
+        }
+        APIResponseBean apiResponseBean = APIResponseBeanUtil.error(500, "服务端繁忙");
+        return new ResponseEntity(apiResponseBean, HttpStatus.INTERNAL_SERVER_ERROR);
+    }
+}

+ 27 - 0
webchat-aigc/src/main/java/com/webchat/aigc/config/RedisConfig.java

@@ -0,0 +1,27 @@
+package com.webchat.aigc.config;
+
+import com.webchat.aigc.consumer.redis.RobotChatRedisQueueListener;
+import com.webchat.common.enums.messagequeue.MessageBroadChannelEnum;
+import jakarta.annotation.Resource;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.redis.connection.RedisConnectionFactory;
+import org.springframework.data.redis.listener.ChannelTopic;
+import org.springframework.data.redis.listener.RedisMessageListenerContainer;
+
+@Configuration
+public class RedisConfig {
+
+    @Resource
+    private RobotChatRedisQueueListener robotChatRedisQueueListener;
+
+    @Bean
+    public RedisMessageListenerContainer container(RedisConnectionFactory redisConnectionFactory) {
+        RedisMessageListenerContainer container = new RedisMessageListenerContainer();
+        // 设置链接工厂
+        container.setConnectionFactory(redisConnectionFactory);
+        // 配置消息监听器
+        container.addMessageListener(robotChatRedisQueueListener, new ChannelTopic(MessageBroadChannelEnum.QUEUE_CHAT_ROBOT.getChannel()));
+        return container;
+    }
+}

+ 15 - 0
webchat-aigc/src/main/java/com/webchat/aigc/config/properties/DeepseekLLMPropertiesConfig.java

@@ -0,0 +1,15 @@
+package com.webchat.aigc.config.properties;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+@Data
+@Component
+@ConfigurationProperties(prefix = "llm.config.deepseek")
+public class DeepseekLLMPropertiesConfig {
+
+    private String apiKey;
+    
+    private String model;
+}

+ 15 - 0
webchat-aigc/src/main/java/com/webchat/aigc/config/properties/KimiLLMPropertiesConfig.java

@@ -0,0 +1,15 @@
+package com.webchat.aigc.config.properties;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+@Data
+@Component
+@ConfigurationProperties(prefix = "llm.config.kimi")
+public class KimiLLMPropertiesConfig {
+
+    private String apiKey;
+
+    private String model;
+}

+ 15 - 0
webchat-aigc/src/main/java/com/webchat/aigc/config/properties/LibLibPropertiesConfig.java

@@ -0,0 +1,15 @@
+package com.webchat.aigc.config.properties;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+@Data
+@Component
+@ConfigurationProperties(prefix = "llm.config.liblib")
+public class LibLibPropertiesConfig {
+
+    private String accessKey;
+
+    private String SecretKey;
+}

+ 29 - 0
webchat-aigc/src/main/java/com/webchat/aigc/consumer/redis/RobotChatRedisQueueListener.java

@@ -0,0 +1,29 @@
+package com.webchat.aigc.consumer.redis;
+
+import com.webchat.aigc.llm.AiAgentFlowService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.connection.Message;
+import org.springframework.data.redis.connection.MessageListener;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.stereotype.Component;
+
+@Slf4j
+@Component
+public class RobotChatRedisQueueListener implements MessageListener {
+
+    @Autowired
+    private RedisTemplate redisTemplate;
+
+    @Autowired
+    private AiAgentFlowService aiAgentFlowService;
+
+    @Override
+    public void onMessage(Message message, byte[] pattern) {
+        String channel = (String) redisTemplate.getStringSerializer().deserialize(message.getChannel());
+        String messageStr = (String) redisTemplate.getValueSerializer().deserialize(message.getBody());
+        log.info("ChatMessageRedisQueueListener.onMessage =====> channel:{} messageStr:{}", channel, messageStr);
+
+        aiAgentFlowService.doAgent(messageStr);
+    }
+}

+ 30 - 0
webchat-aigc/src/main/java/com/webchat/aigc/consumer/rocketmq/RobotChatRocketQueueConsumer.java

@@ -0,0 +1,30 @@
+package com.webchat.aigc.consumer.rocketmq;
+
+import com.webchat.aigc.llm.AiAgentFlowService;
+import org.apache.rocketmq.spring.annotation.MessageModel;
+import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
+import org.apache.rocketmq.spring.core.RocketMQListener;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+/**
+ * 当前对话消息队列,因为需要WebSocket或SSE服务端主动推送,为了解决分布式websocketsession及ssemetter共享问题,这里必须广播模式
+ */
+@Component
+@RocketMQMessageListener(consumerGroup = "web_chat", topic = "queue_chat_robot", messageModel = MessageModel.BROADCASTING)
+public class RobotChatRocketQueueConsumer implements RocketMQListener<String> {
+
+    @Autowired
+    private AiAgentFlowService aiAgentFlowService;
+
+    /**
+     * 处理来自IM 对话相关消息
+     *
+     * @param message
+     */
+    @Override
+    public void onMessage(String message) {
+
+        aiAgentFlowService.doAgent(message);
+    }
+}

+ 28 - 0
webchat-aigc/src/main/java/com/webchat/aigc/controller/javasse/EventStreamConnectController.java

@@ -0,0 +1,28 @@
+package com.webchat.aigc.controller.javasse;
+
+import com.webchat.common.config.annotation.ValidateLogin;
+import com.webchat.common.helper.SseEmitterHelper;
+import jakarta.servlet.http.HttpServletRequest;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
+
+@RestController
+@RequestMapping("/aigc-service/event/")
+public class EventStreamConnectController {
+
+    /**
+     * 获取SSE链接对象
+     *
+     * @param request
+     * @return
+     */
+    @ValidateLogin
+    @GetMapping(path = "/stream", produces = "text/event-stream")
+    public SseEmitter stream(HttpServletRequest request) {
+        String biz = request.getParameter("bizCode");
+        String userId = request.getParameter("userId");
+        return SseEmitterHelper.get(biz, userId);
+    }
+}

+ 28 - 0
webchat-aigc/src/main/java/com/webchat/aigc/llm/AbstractGenerateImageService.java

@@ -0,0 +1,28 @@
+package com.webchat.aigc.llm;
+
+
+import com.webchat.domain.vo.llm.image.ImageGenerateResponse;
+import com.webchat.domain.vo.llm.image.RequestGenerateBase;
+
+public abstract class AbstractGenerateImageService<T extends RequestGenerateBase> {
+
+
+    protected abstract ImageGenerateResponse generate(String userId, T param) throws Exception;
+
+    /**
+     * 文生图
+     *
+     * @param param
+     * @return
+     */
+    protected abstract String doSubmit(T param) throws Exception;
+
+
+    /**
+     * 查询生图结果
+     *
+     * @param taskId
+     * @return
+     */
+    protected abstract ImageGenerateResponse getResult(String taskId) throws Exception;
+}

+ 35 - 0
webchat-aigc/src/main/java/com/webchat/aigc/llm/AbstractLLMChatService.java

@@ -0,0 +1,35 @@
+package com.webchat.aigc.llm;
+
+import com.webchat.domain.vo.llm.ChatCompletionMessage;
+import com.webchat.domain.vo.llm.ChatCompletionResponse;
+import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
+
+import java.util.List;
+
+/**
+ * @author 程序员王七七 https://www.coderutil.com 网站作者
+ * @date 2024/10/29 22:47
+ *
+ * 抽象大模型对话服务
+ */
+public abstract class AbstractLLMChatService {
+
+    /**
+     * 同步对话
+     *
+     * @param messageList
+     * @return
+     * @throws Exception
+     */
+    protected abstract ChatCompletionResponse chat(List<ChatCompletionMessage> messageList) throws Exception;
+
+    /**
+     * 流式对话
+     *
+     * @param emitter
+     * @param messageList
+     * @return
+     * @throws Exception
+     */
+    protected abstract void chat(SseEmitter emitter, List<ChatCompletionMessage> messageList) throws Exception;
+}

+ 104 - 0
webchat-aigc/src/main/java/com/webchat/aigc/llm/AiAgentFlowService.java

@@ -0,0 +1,104 @@
+package com.webchat.aigc.llm;
+
+
+import com.webchat.common.constants.ConnectConstants;
+import com.webchat.common.enums.AiFunctionEnum;
+import com.webchat.common.helper.SseEmitterHelper;
+import com.webchat.common.util.JsonUtil;
+import com.webchat.domain.vo.llm.FunctionCallResponse;
+import com.webchat.domain.vo.llm.image.ImageGenerateResponse;
+import com.webchat.domain.vo.llm.image.RequestGenerateBaseParam;
+import com.webchat.domain.vo.llm.image.RequestGenerateParam;
+import com.webchat.domain.vo.request.mess.ChatMessageRequestVO;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections.CollectionUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
+
+import java.util.List;
+
+@Slf4j
+@Service
+public class AiAgentFlowService {
+
+    @Autowired
+    private GPTChatService gptChatService;
+    @Autowired
+    private AiFunctionCallService aiFunctionCallService;
+    @Autowired
+    private LibLibGenerateImageService libLibGenerateImageService;
+
+    /**
+     * 旗舰版文生图模版id
+     */
+    private static final String TEMPLATE_UUID = "5d7e67009b344550bc1aa6ccbfa1d7f4";
+
+    private String getSSEBizCode() {
+        return ConnectConstants.ConnectBiz.getBizCode(ConnectConstants.ClientEnum.PC,
+                                                      ConnectConstants.AppEnum.WEB,
+                                                      ConnectConstants.BizEnum.CHAT);
+    }
+
+    /**
+     * 机器人问答Agent服务
+     * 1、意图识别
+     * 2、文生文(kimi/deepseek) / 文生图(LibLib模型)
+     *
+     * @param message
+     */
+    public void doAgent(String message) {
+        ChatMessageRequestVO chatMessage = JsonUtil.fromJson(message, ChatMessageRequestVO.class);
+        String senderId = chatMessage.getSenderId();
+        /**
+         * 1. 意图识别
+         */
+        SseEmitterHelper.send(this.getSSEBizCode(), senderId, "意图识别中...");
+        FunctionCallResponse functionCallResponse;
+        try {
+            functionCallResponse =
+                    aiFunctionCallService.getFunction(chatMessage.getMessage());
+            log.info("意图识别结果 =====> input: {}, response:{}",
+                    chatMessage.getMessage(), JsonUtil.toJsonString(functionCallResponse));
+            String functionInfo = "意图识别:"+ AiFunctionEnum.getFunctionName(functionCallResponse.getFunction());
+            SseEmitterHelper.send(this.getSSEBizCode(), senderId, functionInfo);
+        } catch (Exception e) {
+            log.error("意图识别异常 =====> input: {}", chatMessage.getMessage(), e);
+            SseEmitterHelper.send(this.getSSEBizCode(), senderId, "意图识别失败,稍后重试~");
+            return;
+        }
+        /**
+         * 2. 根据意图执行对应工作流
+         */
+        SseEmitter sseEmitter = SseEmitterHelper.get(this.getSSEBizCode(), senderId);
+        if (AiFunctionEnum.CHAT.name().equals(functionCallResponse.getFunction())) {
+            // 机器人对话
+            gptChatService.chat(sseEmitter, chatMessage);
+        } else if (AiFunctionEnum.IMAGE.name().equals(functionCallResponse.getFunction())) {
+            // 机器人对话
+            RequestGenerateBaseParam baseParam = RequestGenerateBaseParam.of(chatMessage.getMessage());
+            RequestGenerateParam param = RequestGenerateParam.of(TEMPLATE_UUID, baseParam);
+            try {
+                ImageGenerateResponse response = libLibGenerateImageService.generate(senderId, param);
+                List<String> images = response.getImages();
+                if (CollectionUtils.isEmpty(images)) {
+                    SseEmitterHelper.send(this.getSSEBizCode(), senderId, "图片生成失败~");
+                    return;
+                }
+                String imageMd = " ![%s](%s \"%s\")";
+                StringBuilder stringBuilder = new StringBuilder();
+                for (int i = 0; i < images.size(); i++) {
+                    String imageName = "图片" + (i + 1);
+                    stringBuilder.append(String.format(imageMd, imageName, images.get(i), imageName));
+                    if (i == images.size() - 1) {
+                        stringBuilder.append("\n");
+                    }
+                }
+                SseEmitterHelper.send(this.getSSEBizCode(), senderId, stringBuilder.toString());
+            } catch (Exception e) {
+                log.error("文生图服务异常 =====> param: {}", JsonUtil.toJsonString(param), e);
+                SseEmitterHelper.send(this.getSSEBizCode(), senderId, "意图识别失败,稍后重试~");
+            }
+        }
+    }
+}

+ 56 - 0
webchat-aigc/src/main/java/com/webchat/aigc/llm/AiFunctionCallService.java

@@ -0,0 +1,56 @@
+package com.webchat.aigc.llm;
+
+
+import com.webchat.common.enums.PromptTemplateEnum;
+import com.webchat.common.service.FreeMarkEngineService;
+import com.webchat.common.util.JsonExtractorFromMarkdown;
+import com.webchat.common.util.JsonUtil;
+import com.webchat.domain.vo.llm.ChatCompletionChoice;
+import com.webchat.domain.vo.llm.ChatCompletionMessage;
+import com.webchat.domain.vo.llm.ChatCompletionResponse;
+import com.webchat.domain.vo.llm.ChatMessageRole;
+import com.webchat.domain.vo.llm.FunctionCallResponse;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@Slf4j
+@Service
+public class AiFunctionCallService {
+
+
+    @Value("${llm.config.model}")
+    private String model;
+
+    @Autowired
+    private FreeMarkEngineService freeMarkEngineService;
+
+    /**
+     * 用户输入意图识别
+     *
+     * @param input
+     * @return
+     */
+    public FunctionCallResponse getFunction(String input) throws Exception {
+        // prompt 构建
+        Map<String, Object> vars = new HashMap<>();
+        vars.put("input", input);
+        String prompt = freeMarkEngineService.getContentByTemplate(PromptTemplateEnum.ROBOT_FC.getPath(), vars);
+        // 调用大模型,意图识别
+        final List<ChatCompletionMessage> messageList = Arrays.asList(
+                new ChatCompletionMessage(ChatMessageRole.USER.value(), prompt));
+        AbstractLLMChatService abstractLLMChatService = LLMServiceFactory.getLLMService(model);
+        ChatCompletionResponse chatCompletionResponse = abstractLLMChatService.chat(messageList);
+        ChatCompletionChoice chatCompletionChoice = chatCompletionResponse.getChoices().get(0);
+        String functionMdJson = chatCompletionChoice.getMessage().getContent();
+        String functionJson = JsonExtractorFromMarkdown.getJson(functionMdJson);
+        return JsonUtil.fromJson(functionJson, FunctionCallResponse.class);
+    }
+
+}

+ 94 - 0
webchat-aigc/src/main/java/com/webchat/aigc/llm/DeepSeekAIService.java

@@ -0,0 +1,94 @@
+package com.webchat.aigc.llm;
+
+import com.webchat.aigc.config.properties.DeepseekLLMPropertiesConfig;
+import com.webchat.common.util.llm.DeepSeekAIClient;
+import com.webchat.domain.vo.llm.ChatCompletionMessage;
+import com.webchat.domain.vo.llm.ChatCompletionRequest;
+import com.webchat.domain.vo.llm.ChatCompletionResponse;
+import com.webchat.domain.vo.llm.ChatCompletionStreamChoice;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
+
+import java.util.List;
+
+/**
+ * @Author 程序员七七
+ * @webSite https://www.coderutil.com
+ * @Date 2024/10/29 23:15
+ * @description
+ */
+@Service
+public class DeepSeekAIService extends AbstractLLMChatService {
+
+    @Autowired
+    private DeepseekLLMPropertiesConfig deepseekLLMPropertiesConfig;
+
+    /**
+     * 同步对话
+     *
+     * @param messageList
+     * @return
+     * @throws Exception
+     */
+    public ChatCompletionResponse chat(List<ChatCompletionMessage> messageList) throws Exception {
+        DeepSeekAIClient client = new DeepSeekAIClient(deepseekLLMPropertiesConfig.getApiKey());
+        final List<ChatCompletionMessage> messages = messageList;
+        return client.ChatCompletion(new ChatCompletionRequest(
+                deepseekLLMPropertiesConfig.getModel(),
+                messages,
+                2000,
+                0.3f,
+                1
+        ));
+    }
+
+    /**
+     * 流式对话
+     *
+     * @param emitter
+     * @param messageList
+     * @throws Exception
+     */
+    public void chat(SseEmitter emitter, List<ChatCompletionMessage> messageList) throws Exception {
+        DeepSeekAIClient client = new DeepSeekAIClient(deepseekLLMPropertiesConfig.getApiKey());
+        final List<ChatCompletionMessage> messages = messageList;
+        try {
+            client.ChatCompletionStream(new ChatCompletionRequest(
+                    deepseekLLMPropertiesConfig.getModel(),
+                    messages,
+                    2000,
+                    0.3f,
+                    1
+            )).subscribe(
+                    streamResponse -> {
+                        if (streamResponse.getChoices().isEmpty()) {
+                            return;
+                        }
+                        for (ChatCompletionStreamChoice choice : streamResponse.getChoices()) {
+                            String finishReason = choice.getFinishReason();
+                            if (finishReason != null) {
+                                emitter.send("finished");
+                                continue;
+                            }
+                            String responseContent = choice.getDelta().getContent();
+                            if (responseContent.equals("```") || responseContent.equals("json") ) {
+                                continue;
+                            }
+                            responseContent = responseContent.replaceAll("\n", "<br>");
+                            System.out.println(responseContent);
+                            emitter.send(responseContent);
+                        }
+                    },
+                    error -> {
+                        error.printStackTrace();
+                    },
+                    () -> {
+//                        emitter.complete(); // 完成事件流发送
+                    }
+            );
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+}

+ 80 - 0
webchat-aigc/src/main/java/com/webchat/aigc/llm/GPTChatService.java

@@ -0,0 +1,80 @@
+package com.webchat.aigc.llm;
+
+import com.webchat.aigc.service.UserService;
+import com.webchat.common.enums.PromptTemplateEnum;
+import com.webchat.common.helper.SseEmitterHelper;
+import com.webchat.common.service.FreeMarkEngineService;
+import com.webchat.domain.vo.llm.ChatCompletionMessage;
+import com.webchat.domain.vo.llm.ChatMessageRole;
+import com.webchat.domain.vo.request.mess.ChatMessageRequestVO;
+import com.webchat.domain.vo.response.UserBaseResponseInfoVO;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author 程序员王七七 https://www.coderutil.com 网站作者
+ * @date 2024/10/29 13:42
+ */
+@Slf4j
+@Service
+public class GPTChatService {
+
+    @Autowired
+    private UserService userService;
+
+    @Autowired
+    private FreeMarkEngineService freeMarkEngineService;
+
+    @Value("${llm.config.model}")
+    private String model;
+
+    /**
+     * 没有设定角色时,使用默认角色
+     */
+    private static final String DEFAULT_GPT_ROLE = "你是一个智能助手,帮我解决一切问题。";
+    private static final String DEFAULT_ROLE = "暂无签名";
+
+    public void chat( SseEmitter sseEmitter, ChatMessageRequestVO chatMessage) {
+        // 使用工厂模式,取对应的LLM服务,来完成对话
+        AbstractLLMChatService abstractLLMChatService = LLMServiceFactory.getLLMService(model);
+        // 获取用户发送消息内容
+        String message = chatMessage.getMessage();
+        // 取消息发送人、接收人信息
+        String sendUserId = chatMessage.getSenderId();
+        String receiverId = chatMessage.getReceiverId();
+        UserBaseResponseInfoVO receiver = userService.accountInfo(receiverId);
+        String gptRoleSetting = receiver.getSignature();
+        if (StringUtils.isBlank(gptRoleSetting) || DEFAULT_ROLE.equals(gptRoleSetting)) {
+            gptRoleSetting = DEFAULT_GPT_ROLE;
+        }
+        Map<String, Object> vars = new HashMap<>();
+        vars.put("roleSetting", gptRoleSetting);
+        vars.put("content", message);
+        String prompt;
+        try {
+            prompt = freeMarkEngineService.getContentByTemplate(PromptTemplateEnum.ROBOT_CHAT.getPath(), vars);
+        } catch (Exception e) {
+            log.error("GPTChatService.chat create prompt error. sendUserId:{}, receiverId:{}, msg:{}",
+                    sendUserId, receiverId, message, e);
+            return;
+        }
+        final List<ChatCompletionMessage> messageList = Arrays.asList(
+                new ChatCompletionMessage(ChatMessageRole.SYSTEM.value(), gptRoleSetting),
+                new ChatCompletionMessage(ChatMessageRole.USER.value(), prompt));
+        try {
+            abstractLLMChatService.chat(sseEmitter, messageList);
+        } catch (Exception e) {
+            log.error("GPTChatService.chat error. sendUserId:{}, receiverId:{}, msg:{}",
+                    sendUserId, receiverId, message, e);
+        }
+    }
+}

+ 97 - 0
webchat-aigc/src/main/java/com/webchat/aigc/llm/KimiAIService.java

@@ -0,0 +1,97 @@
+package com.webchat.aigc.llm;
+
+import com.webchat.aigc.config.properties.KimiLLMPropertiesConfig;
+import com.webchat.common.util.llm.MoonShotAIClient;
+import com.webchat.domain.vo.llm.ChatCompletionMessage;
+import com.webchat.domain.vo.llm.ChatCompletionRequest;
+import com.webchat.domain.vo.llm.ChatCompletionResponse;
+import com.webchat.domain.vo.llm.ChatCompletionStreamChoice;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
+
+import java.util.List;
+
+/**
+ * @Author 程序员七七
+ * @webSite https://www.coderutil.com
+ * @Date 2024/10/29 00:22
+ * @description
+ */
+@Service
+public class KimiAIService extends AbstractLLMChatService {
+
+    @Autowired
+    private KimiLLMPropertiesConfig kimiLLMPropertiesConfig;
+
+    /**
+     * 同步对话
+     *
+     * @param messageList
+     * @return
+     * @throws Exception
+     */
+    public ChatCompletionResponse chat(List<ChatCompletionMessage> messageList) throws Exception {
+        MoonShotAIClient client = new MoonShotAIClient(kimiLLMPropertiesConfig.getApiKey());
+        final List<ChatCompletionMessage> messages = messageList;
+        return client.ChatCompletion(new ChatCompletionRequest(
+                kimiLLMPropertiesConfig.getModel(),
+                messages,
+                4096,
+                0.3f,
+                1
+        ));
+    }
+
+    /**
+     * 流式对话
+     *
+     * @param emitter
+     * @param messageList
+     * @throws Exception
+     */
+    public void chat(SseEmitter emitter, List<ChatCompletionMessage> messageList) throws Exception {
+
+
+        MoonShotAIClient client = new MoonShotAIClient(kimiLLMPropertiesConfig.getApiKey());
+
+
+        final List<ChatCompletionMessage> messages = messageList;
+        try {
+            client.ChatCompletionStream(new ChatCompletionRequest(
+                    kimiLLMPropertiesConfig.getModel(),
+                    messages,
+                    2000,
+                    0.3f,
+                    1
+            )).subscribe(
+                    streamResponse -> {
+                        if (streamResponse.getChoices().isEmpty()) {
+                            return;
+                        }
+                        for (ChatCompletionStreamChoice choice : streamResponse.getChoices()) {
+                            String finishReason = choice.getFinishReason();
+                            if (finishReason != null) {
+                                emitter.send("finished");
+                                continue;
+                            }
+                            String responseContent = choice.getDelta().getContent();
+                            if (responseContent != null) {
+                                responseContent = responseContent.replaceAll("\n", "<br>");
+                                System.out.println(responseContent);
+                                emitter.send(responseContent);
+                            }
+                        }
+                    },
+                    error -> {
+                        error.printStackTrace();
+                    },
+                    () -> {
+//                        emitter.complete(); // 完成事件流发送
+                    }
+            );
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+}

+ 58 - 0
webchat-aigc/src/main/java/com/webchat/aigc/llm/LLMServiceFactory.java

@@ -0,0 +1,58 @@
+package com.webchat.aigc.llm;
+
+import com.webchat.common.enums.LlmModelEnum;
+import com.webchat.common.exception.BusinessException;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.stereotype.Component;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author 程序员王七七 https://www.coderutil.com 网站作者
+ * @date 2024/10/29 22:47
+ *
+ * 抽象大模型对话工厂服务
+ */
+@Component
+public class LLMServiceFactory implements InitializingBean, ApplicationContextAware {
+
+    private ApplicationContext applicationContext;
+
+    private static final Map<String, AbstractLLMChatService> serviceMap = new HashMap<>();
+
+    @Override
+    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
+        this.applicationContext = applicationContext;
+    }
+
+    @Override
+    public void afterPropertiesSet() throws Exception {
+        this.initServiceMap();
+    }
+
+    /**
+     * 初始化模型服务
+     */
+    private void initServiceMap() {
+        /**
+         * kimi
+         */
+        serviceMap.put(LlmModelEnum.KIMI.getModel(), applicationContext.getBean(KimiAIService.class));
+        /**
+         * deepseek
+         */
+        serviceMap.put(LlmModelEnum.DEEPSEEK.getModel(), applicationContext.getBean(DeepSeekAIService.class));
+    }
+
+    public static AbstractLLMChatService getLLMService(String model) {
+        AbstractLLMChatService llmChatService = serviceMap.get(model);
+        if (llmChatService == null) {
+            throw new BusinessException("不支持的模型");
+        }
+        return llmChatService;
+    }
+}

+ 171 - 0
webchat-aigc/src/main/java/com/webchat/aigc/llm/LibLibGenerateImageService.java

@@ -0,0 +1,171 @@
+package com.webchat.aigc.llm;
+
+import com.webchat.aigc.config.properties.LibLibPropertiesConfig;
+import com.webchat.common.constants.ConnectConstants;
+import com.webchat.common.exception.BusinessException;
+import com.webchat.common.helper.SseEmitterHelper;
+import com.webchat.common.util.HttpClientUtil;
+import com.webchat.common.util.IDGenerateUtil;
+import com.webchat.common.util.JsonUtil;
+import com.webchat.domain.vo.llm.image.ImageGenerateResponse;
+import com.webchat.domain.vo.llm.image.LibLibGenImageResponse;
+import com.webchat.domain.vo.llm.image.LibLibGenImageResponseData;
+import com.webchat.domain.vo.llm.image.LibLibGenerateResponse;
+import com.webchat.domain.vo.llm.image.RequestGenerateParam;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.lang3.ObjectUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.stereotype.Service;
+
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import org.apache.commons.codec.binary.Base64;
+
+@Service
+public class LibLibGenerateImageService extends AbstractGenerateImageService<RequestGenerateParam> {
+
+    @Autowired
+    private LibLibPropertiesConfig libLibPropertiesConfig;
+
+
+    private Map<String, SignatureParam> signatureMap = new HashMap<>();
+
+    /**
+     * 线上API域名
+     */
+    private static final String HOST = "https://openapi.liblibai.cloud";
+    /**
+     * 旗舰版F.1生图 API
+     */
+    private static final String GENERATE_API = "/api/generate/webui/text2img/ultra";
+    /**
+     * 查询结果API
+     */
+    private static final String RESULT_API = "/api/generate/webui/status";
+
+    private static final String API_PARAM = "?AccessKey=%s&Signature=%s&Timestamp=%s&SignatureNonce=%s";
+
+    @Override
+    protected ImageGenerateResponse generate(String userId, RequestGenerateParam param) throws Exception {
+        // 提交生图任务
+        String taskId = doSubmit(param);
+        if (StringUtils.isBlank(taskId)) {
+            throw new BusinessException("图片生成失败,请稍后重试~");
+        }
+        SseEmitterHelper.send(this.getSSEBizCode(), userId, "任务排队中,稍等片刻~");
+        ImageGenerateResponse response = null;
+        // 查询任务结果
+        int count = 1;
+        while (response == null) {
+            if (count > 15) {
+                throw new BusinessException("图片生成失败,请稍后重试~");
+            }
+            response = getResult(taskId);
+            Thread.sleep(2000);
+            ++ count;
+        }
+        SseEmitterHelper.send(this.getSSEBizCode(), userId, "图片已经生成,请查收");
+        return response;
+    }
+
+    @Override
+    protected String doSubmit(RequestGenerateParam param) throws Exception {
+        String api = HOST + GENERATE_API + API_PARAM;
+        String signatureNonce = IDGenerateUtil.uuid();
+        long timestamp = System.currentTimeMillis();
+        String signature = this.makeSign(GENERATE_API, libLibPropertiesConfig.getSecretKey(), timestamp, signatureNonce);
+        api = String.format(api, libLibPropertiesConfig.getAccessKey(), signature, timestamp, signatureNonce);
+        String responseJson = HttpClientUtil.postObjectForUrl(api, param, String.class);
+        LibLibGenerateResponse libLibGenerateResponse = JsonUtil.fromJson(responseJson, LibLibGenerateResponse.class);
+        if (libLibGenerateResponse == null) {
+            return null;
+        }
+        if (ObjectUtils.equals(libLibGenerateResponse.getCode(), 0)) {
+            String taskId = libLibGenerateResponse.getData().getGenerateUuid();
+            signatureMap.put(taskId, new SignatureParam(signatureNonce, timestamp, signature));
+            return taskId;
+        }
+        return null;
+    }
+
+    @Override
+    protected ImageGenerateResponse getResult(String taskId) throws Exception {
+
+        String api = HOST + RESULT_API + API_PARAM;
+        SignatureParam signatureParam = signatureMap.get(taskId);
+        String signatureNonce = signatureParam.getSignatureNonce();
+        long timestamp = signatureParam.getTimestamp();
+        String signature = this.makeSign(RESULT_API, libLibPropertiesConfig.getSecretKey(), timestamp, signatureNonce);
+        api = String.format(api, libLibPropertiesConfig.getAccessKey(), signature, timestamp, signatureNonce);
+        Map<String, Object> body = new HashMap<>();
+        body.put("generateUuid", taskId);
+        Map<String, String> header = new HashMap<>();
+        header.put("Content-Type", "application/json");
+        String responseJson = HttpClientUtil.postObjectForUrl(api, body, header, String.class);
+
+        LibLibGenImageResponse libLibGenImageResponse = JsonUtil.fromJson(responseJson, LibLibGenImageResponse.class);
+        if (libLibGenImageResponse != null && ObjectUtils.equals(libLibGenImageResponse.getCode(), 0)) {
+            LibLibGenImageResponseData data = libLibGenImageResponse.getData();
+            if (data == null) {
+                return null;
+            }
+            List<LibLibGenImageResponseData.LibLibGenImageResponseDataImage> images = data.getImages();
+            if (CollectionUtils.isEmpty(images)) {
+                return null;
+            }
+            ImageGenerateResponse response = new ImageGenerateResponse();
+            List<String> imageUrls = images.stream().map(LibLibGenImageResponseData.LibLibGenImageResponseDataImage::getImageUrl).collect(Collectors.toList());
+            response.setImages(imageUrls);
+            response.setCode(HttpStatus.OK.value());
+            return response;
+        }
+        return null;
+    }
+
+    public String makeSign(String api, String sk, long timestamp, String signatureNonce) {
+        // 拼接请求数据
+        String content = api + "&" + timestamp + "&" + signatureNonce;
+        try {
+            // 生成签名
+            SecretKeySpec secret = new SecretKeySpec(sk.getBytes(), "HmacSHA1");
+            Mac mac = Mac.getInstance("HmacSHA1");
+            mac.init(secret);
+            return Base64.encodeBase64URLSafeString(mac.doFinal(content.getBytes()));
+        } catch (NoSuchAlgorithmException e) {
+            throw new RuntimeException("no such algorithm");
+        } catch (InvalidKeyException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private String getSSEBizCode() {
+        return ConnectConstants.ConnectBiz.getBizCode(ConnectConstants.ClientEnum.PC,
+                ConnectConstants.AppEnum.WEB,
+                ConnectConstants.BizEnum.CHAT);
+    }
+
+
+    @Data
+    @NoArgsConstructor
+    @AllArgsConstructor
+    public static class SignatureParam {
+
+        String signatureNonce;
+
+        long timestamp;
+
+        String signature;
+    }
+}

+ 12 - 0
webchat-aigc/src/main/java/com/webchat/aigc/llm/RAGBotService.java

@@ -0,0 +1,12 @@
+package com.webchat.aigc.llm;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+@Slf4j
+@Service
+public class RAGBotService {
+
+
+
+}

+ 28 - 0
webchat-aigc/src/main/java/com/webchat/aigc/service/UserService.java

@@ -0,0 +1,28 @@
+package com.webchat.aigc.service;
+
+import com.webchat.common.bean.APIResponseBean;
+import com.webchat.common.bean.APIResponseBeanUtil;
+import com.webchat.common.util.JsonUtil;
+import com.webchat.domain.vo.response.UserBaseResponseInfoVO;
+import com.webchat.rmi.user.UserServiceClient;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Slf4j
+@Service
+public class UserService {
+
+    @Autowired
+    private UserServiceClient userServiceClient;
+
+    public UserBaseResponseInfoVO accountInfo(String account) {
+        APIResponseBean<UserBaseResponseInfoVO> responseBean = userServiceClient.userInfo(account);
+        if (APIResponseBeanUtil.isOk(responseBean)) {
+            return responseBean.getData();
+        }
+        log.error("获取用户信息远程调用异常 =====> account:{}, response:{}", account, JsonUtil.toJsonString(responseBean));
+        return null;
+    }
+
+}

+ 1 - 0
webchat-aigc/src/main/resources/application.yml

@@ -0,0 +1 @@
+server:
  port: 8019

+ 21 - 0
webchat-aigc/src/main/resources/bootstrap.yml

@@ -0,0 +1,21 @@
+spring:
+  application:
+    name: webchat-aigc-service
+  profiles:
+    active: dev
+  cloud:
+    nacos:
+      config:
+        server-addr: 127.0.0.1:8848
+        namespace: 2cdfb8e3-f127-4020-93df-2d4c3dc805b5
+        file-extension: yaml
+        group: DEFAULT_GROUP
+        refresh-enabled: true
+      discovery:
+        server-addr: 127.0.0.1:8848
+        namespace: 2cdfb8e3-f127-4020-93df-2d4c3dc805b5
+    sentinel:
+      transport:
+        port: 8719
+        dashboard: 127.0.0.1:8080
+      eager: true

+ 18 - 0
webchat-aigc/src/main/resources/templates/ftl/RAG.ftl

@@ -0,0 +1,18 @@
+# 任务说明
+你叫`小C`, 是《WebChat项目》的智能问答助手。
+
+# 任务限制
+- 优先使用`参考资料`内容做回答依据,当没有参考资料,可以自己稍微发挥回答,但要注意保证答案的准确定
+- 要求回答简洁明了,直击关键问题
+
+# 用户问题
+```
+${input}
+```
+
+# 参考资料
+```
+${document}
+```
+
+回答:

+ 12 - 0
webchat-aigc/src/main/resources/templates/ftl/ROBOT_CHAT.ftl

@@ -0,0 +1,12 @@
+# 角色设定
+你是由https://www.coderutil.com编程技术社区作者【程序员王七七】开发一个webchat智能对话助手机器人。
+
+# 角色设定补充信息
+```
+${roleSetting}
+```
+
+# 我的问题/我给的内容
+```
+${content}
+```

+ 23 - 0
webchat-aigc/src/main/resources/templates/ftl/ROBOT_FC.ftl

@@ -0,0 +1,23 @@
+# 任务描述
+用户输入内容意图识别
+
+# 意图清单
+- CHAT: 对话/问答/交流
+- IMAGE: 文生图
+
+# 任务限制
+- 意图必须来自清单中所支持其中一个最相关的意图,比如对话返回给我 CHAT 即可,不需要返回给我对话等解释性内容
+- 如果用户意图描述不清晰、不专业,可以帮用户进行优化
+
+# 返回结构
+```json
+{
+    "function": "用户意图",
+    "prompt": "优化后的prompt指令"
+}
+```
+
+# 用户输入
+${input}
+
+输出:

BIN
webchat-client-chat/.DS_Store


+ 19 - 0
webchat-client-chat/.mvn/wrapper/maven-wrapper.properties

@@ -0,0 +1,19 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+wrapperVersion=3.3.2
+distributionType=only-script
+distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip

+ 50 - 0
webchat-client-chat/pom.xml

@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>com.webchat</groupId>
+        <artifactId>webchat</artifactId>
+        <version>0.0.1-SNAPSHOT</version>
+    </parent>
+
+    <groupId>com.webchat</groupId>
+    <artifactId>webchat-client-chat</artifactId>
+    <version>0.0.1-SNAPSHOT</version>
+    <packaging>jar</packaging>
+
+    <name>webchat-client-chat</name>
+    <description>webchat-client-chat</description>
+
+    <properties>
+        <java.version>17</java.version>
+    </properties>
+    <dependencies>
+
+        <dependency>
+            <groupId>com.webchat</groupId>
+            <artifactId>webchat-remote</artifactId>
+            <version>1.0-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>

BIN
webchat-client-chat/src/.DS_Store


BIN
webchat-client-chat/src/main/.DS_Store


+ 17 - 0
webchat-client-chat/src/main/java/com/webchat/client/WebchatClientApplication.java

@@ -0,0 +1,17 @@
+package com.webchat.client;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.cloud.openfeign.EnableFeignClients;
+import org.springframework.context.annotation.ComponentScan;
+
+@SpringBootApplication
+@ComponentScan("com.webchat")
+@EnableFeignClients("com.webchat.rmi")
+public class WebchatClientApplication {
+
+    public static void main(String[] args) {
+        SpringApplication.run(WebchatClientApplication.class, args);
+    }
+
+}

+ 64 - 0
webchat-client-chat/src/main/java/com/webchat/client/advice/GlobalExceptionAdvice.java

@@ -0,0 +1,64 @@
+package com.webchat.client.advice;
+
+import com.webchat.common.bean.APIResponseBean;
+import com.webchat.common.bean.APIResponseBeanUtil;
+import com.webchat.common.enums.APIErrorCommonEnum;
+import com.webchat.common.exception.AuthException;
+import com.webchat.common.exception.BusinessException;
+import com.webchat.common.exception.NotFoundException;
+import jakarta.servlet.http.HttpServletRequest;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.http.converter.HttpMessageConversionException;
+import org.springframework.web.HttpRequestMethodNotSupportedException;
+import org.springframework.web.bind.MethodArgumentNotValidException;
+import org.springframework.web.bind.annotation.ControllerAdvice;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+
+/***
+ * 统一异常处理器
+ * @Author 程序员七七 ==> http://www.coderutil.com网站作者
+ */
+@Slf4j
+@ControllerAdvice
+public class GlobalExceptionAdvice {
+
+    @ExceptionHandler(Exception.class)
+    @ResponseBody
+    public ResponseEntity<APIResponseBean> exceptionHandler(HttpServletRequest request, Exception e) {
+        log.error("异常详情信息:", e);
+        if (e instanceof AuthException) {
+            APIResponseBean apiResponseBean = APIResponseBeanUtil.error(((AuthException) e).getCode(), e.getMessage());
+            return new ResponseEntity(apiResponseBean, HttpStatus.OK);
+        }
+        if (e instanceof IllegalArgumentException) {
+            APIResponseBean apiResponseBean = APIResponseBeanUtil.error(500, e.getMessage());
+            return new ResponseEntity(apiResponseBean, HttpStatus.OK);
+        } else if (e instanceof BusinessException) {
+            APIResponseBean apiResponseBean = APIResponseBeanUtil.error(((BusinessException) e).getCode(), e.getMessage());
+            return new ResponseEntity(apiResponseBean, HttpStatus.OK);
+        } else if (e instanceof NotFoundException) {
+            APIResponseBean apiResponseBean = APIResponseBeanUtil.error(((NotFoundException) e).getCode(), e.getMessage());
+            return new ResponseEntity(apiResponseBean, HttpStatus.OK);
+        } else if (e instanceof HttpRequestMethodNotSupportedException) {
+            APIResponseBean apiResponseBean = APIResponseBeanUtil.error(500, e.getMessage());
+            return new ResponseEntity(apiResponseBean, HttpStatus.OK);
+        } else if (e instanceof MethodArgumentNotValidException) {
+            /***
+             * 处理@Validated参数校验失败异常
+             */
+            APIResponseBean apiResponseBean = APIResponseBeanUtil.error(APIErrorCommonEnum.VALID_EXCEPTION.getCode(),
+                    "参数校验异常");
+            return new ResponseEntity(apiResponseBean, HttpStatus.OK);
+        } else if (e instanceof HttpMessageConversionException) {
+            APIResponseBean apiResponseBean = APIResponseBeanUtil.error(APIErrorCommonEnum.VALID_EXCEPTION.getCode(),
+                    "参数转换异常");
+            return new ResponseEntity(apiResponseBean, HttpStatus.OK);
+        }
+        APIResponseBean apiResponseBean = APIResponseBeanUtil.error(500, "服务端繁忙");
+        return new ResponseEntity(apiResponseBean, HttpStatus.INTERNAL_SERVER_ERROR);
+    }
+}

+ 57 - 0
webchat-client-chat/src/main/java/com/webchat/client/chat/controller/AccountController.java

@@ -0,0 +1,57 @@
+package com.webchat.client.chat.controller;
+
+
+import com.webchat.client.chat.service.AccountService;
+import com.webchat.client.chat.service.UserSessionService;
+import com.webchat.common.bean.APIResponseBean;
+import com.webchat.common.bean.APIResponseBeanUtil;
+import com.webchat.common.helper.SessionHelper;
+import com.webchat.domain.vo.response.UserBaseResponseInfoVO;
+import jakarta.servlet.http.HttpServletRequest;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@RequestMapping("/client-service/chat/account")
+public class AccountController {
+
+    @Autowired
+    private AccountService userService;
+
+    @Autowired
+    private UserSessionService userSessionService;
+
+    @Autowired
+    private HttpServletRequest request;
+
+    @GetMapping("/current/info")
+    public APIResponseBean<UserBaseResponseInfoVO> getCurrentUserInfo() {
+        String userId = SessionHelper.getCurrentUserId();
+        return APIResponseBeanUtil.success(userService.userInfo(userId));
+    }
+
+    /**
+     * 退出登录
+     *
+     * @return
+     */
+    @GetMapping("/logout")
+    public APIResponseBean<Boolean> logout() {
+        userSessionService.logout(request);
+        return APIResponseBeanUtil.success(true);
+    }
+
+    /**
+     *
+     * @return
+     */
+    @GetMapping("/query")
+    public APIResponseBean<UserBaseResponseInfoVO> queryAccount(@RequestParam String account) {
+
+        return APIResponseBeanUtil.success(userService.queryAccount(account));
+    }
+
+}

+ 120 - 0
webchat-client-chat/src/main/java/com/webchat/client/chat/controller/AccountRelationController.java

@@ -0,0 +1,120 @@
+package com.webchat.client.chat.controller;
+
+
+import com.webchat.client.chat.service.AccountRelationService;
+import com.webchat.common.bean.APIResponseBean;
+import com.webchat.common.bean.APIResponseBeanUtil;
+import com.webchat.common.helper.SessionHelper;
+import com.webchat.domain.vo.request.CreateGroupRequestVO;
+import com.webchat.domain.vo.response.AccountRelationResponseVO;
+import com.webchat.domain.vo.response.WaitConfirmUserResponseVO;
+import org.apache.commons.collections.CollectionUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.util.Assert;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+import java.util.Set;
+
+@RestController
+@RequestMapping("/client-service/chat/account-relation")
+public class AccountRelationController {
+
+
+    @Autowired
+    private AccountRelationService accountRelationService;
+
+    /**
+     * 取消订阅/关注
+     *
+     * @param account
+     * @return
+     */
+    @PostMapping("/subscribe/{account}")
+    public APIResponseBean<Boolean> subscribe(@PathVariable String account) {
+        String userId = SessionHelper.getCurrentUserId();
+        return APIResponseBeanUtil.success(accountRelationService.subscribe(userId, account));
+    }
+
+    /**
+     * 取消订阅/关注
+     *
+     * @param account
+     * @return
+     */
+    @PostMapping("/unsubscribe/{account}")
+    public APIResponseBean<Boolean> unsubscribe(@PathVariable String account) {
+        String userId = SessionHelper.getCurrentUserId();
+        return APIResponseBeanUtil.success(accountRelationService.unsubscribe(userId, account));
+    }
+
+    /**
+     * 同意好友关系
+     *
+     * @param relationId
+     * @return
+     */
+    @PostMapping("/confirmSubscribe/{relationId}")
+    public APIResponseBean<Boolean> confirmSubscribe(@PathVariable Long relationId) {
+        String userId = SessionHelper.getCurrentUserId();
+        return APIResponseBeanUtil.success(accountRelationService.confirmSubscribe(userId, relationId));
+    }
+
+    /**
+     * 拒绝好友
+     *
+     * @param relationId
+     * @return
+     */
+    @PostMapping("/rejectSubscribe/{relationId}")
+    public APIResponseBean<Boolean> rejectSubscribe(@PathVariable Long relationId) {
+        String userId = SessionHelper.getCurrentUserId();
+        return APIResponseBeanUtil.success(accountRelationService.rejectSubscribe(userId, relationId));
+    }
+
+    /**
+     * 加载关系账号:关注用户/机器人/群组/公众号
+     * @return
+     */
+    @GetMapping("/list")
+    public APIResponseBean<AccountRelationResponseVO> loadAccountRelations() {
+        String userId = SessionHelper.getCurrentUserId();
+        return APIResponseBeanUtil.success(accountRelationService.loadAccountRelations(userId));
+    }
+
+    /**
+     * 查询用户待审核的关系列表
+     *
+     * @return
+     */
+    @GetMapping("/wait-confirm/list")
+    public APIResponseBean<List<WaitConfirmUserResponseVO>> listWaitConformUsers() {
+        // 获取当前登录用户
+        String userId = SessionHelper.getCurrentUserId();
+        return APIResponseBeanUtil.success(accountRelationService.listWaitConformUsers(userId));
+    }
+
+
+    /**
+     * 创建群聊
+     *
+     * @param createGroupRequest
+     * @return
+     */
+    @PostMapping("/createGroup")
+    public APIResponseBean<String> createChatGroup(@RequestBody CreateGroupRequestVO createGroupRequest) {
+        Assert.notNull(createGroupRequest, "参数为空");
+        Set<String> userIds = createGroupRequest.getUserIds();
+        Assert.isTrue(CollectionUtils.isNotEmpty(createGroupRequest.getUserIds()) &&
+                userIds.size() >= 3 && userIds.size() <= 500, "群成员3~500人");
+        // 设置群聊创建人、默认群主
+        String userId = SessionHelper.getCurrentUserId();
+        createGroupRequest.setCreateUserId(userId);
+        return APIResponseBeanUtil.success(accountRelationService.createChatGroup(createGroupRequest));
+    }
+}

+ 54 - 0
webchat-client-chat/src/main/java/com/webchat/client/chat/controller/ChatMessageController.java

@@ -0,0 +1,54 @@
+package com.webchat.client.chat.controller;
+
+
+import com.webchat.client.chat.service.ChatMessageService;
+import com.webchat.common.bean.APIResponseBean;
+import com.webchat.common.bean.APIResponseBeanUtil;
+import com.webchat.common.helper.SessionHelper;
+import com.webchat.domain.vo.response.chatting.ChattingListResponseVO;
+import com.webchat.domain.vo.response.mess.ChatMessageResponseVO;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+
+@RestController
+@RequestMapping("/client-service/chat/message")
+public class ChatMessageController {
+
+    @Autowired
+    private ChatMessageService chatMessageService;
+
+    /**
+     * 查询用户对话列表
+     *
+     * @param lastChatTime 采用瀑布流式翻页加载
+     * @param size 对话列表展示最新对话数
+     * @return
+     */
+    @GetMapping("/chatting/list")
+    public APIResponseBean<List<ChattingListResponseVO>> listChatting(@RequestParam(name = "lastChatTime", required = false) Long lastChatTime,
+                                                                      @RequestParam(name = "size", required = false, defaultValue = "100") Integer size) {
+        String userId = SessionHelper.getCurrentUserId();
+        return APIResponseBeanUtil.success(chatMessageService.listChatting(userId, lastChatTime, size));
+    }
+
+    /***
+     * 查询跟用户聊天记录
+     * @return
+     */
+    @GetMapping("/list/{chatUserId}")
+    public APIResponseBean<List<ChatMessageResponseVO>> list(@PathVariable String chatUserId,
+                                                             @RequestParam(value = "size", required = false, defaultValue = "50") Integer size,
+                                                             @RequestParam(value = "lastId", required = false) Long lastId,
+                                                             @RequestParam(value = "fixedMessageId", required = false) Long fixedMessageId) {
+        String currUserId = SessionHelper.getCurrentUserId();
+        return APIResponseBeanUtil.success(
+                chatMessageService.list(currUserId, chatUserId, lastId, fixedMessageId, size));
+    }
+
+}

+ 44 - 0
webchat-client-chat/src/main/java/com/webchat/client/chat/controller/FileController.java

@@ -0,0 +1,44 @@
+package com.webchat.client.chat.controller;
+
+import com.webchat.common.bean.APIResponseBean;
+import com.webchat.common.bean.APIResponseBeanUtil;
+import com.webchat.common.service.MinioService;
+import com.webchat.common.util.DateUtils;
+import com.webchat.domain.vo.response.UploadResultVO;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.util.Assert;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.util.Date;
+
+@RestController
+@RequestMapping("/client-service/chat/file")
+public class FileController {
+
+    @Autowired
+    private MinioService minioService;
+    @Autowired
+    private HttpServletRequest request;
+
+    @PostMapping("/upload")
+    public APIResponseBean<UploadResultVO> upload(MultipartFile file) {
+        String path = request.getHeader("upload-path");
+        Assert.isTrue(StringUtils.isNotBlank(path), "上传路径不能为空");
+        String timePackage = DateUtils.getDate2String(DateUtils.YYYYMMDD, new Date());
+        UploadResultVO uploadResultDTO = minioService.upload(path + "/" + timePackage, file, true);
+        return APIResponseBeanUtil.success(uploadResultDTO);
+    }
+
+    @GetMapping("/download")
+    public void download(@RequestParam String filename, HttpServletResponse response) {
+        minioService.download(filename, response);
+    }
+}

+ 113 - 0
webchat-client-chat/src/main/java/com/webchat/client/chat/filter/OauthFilter.java

@@ -0,0 +1,113 @@
+package com.webchat.client.chat.filter;
+
+import com.webchat.client.chat.service.UserSessionService;
+import com.webchat.common.config.properties.OauthProperties;
+import com.webchat.common.enums.BaseErrCodeEnum;
+import com.webchat.common.helper.SessionHelper;
+import com.webchat.common.util.JsonUtil;
+import com.webchat.rmi.sso.OauthServiceClient;
+import jakarta.servlet.Filter;
+import jakarta.servlet.FilterChain;
+import jakarta.servlet.FilterConfig;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.ServletRequest;
+import jakarta.servlet.ServletResponse;
+import jakarta.servlet.annotation.WebFilter;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.annotation.Order;
+import org.springframework.stereotype.Component;
+
+import java.io.IOException;
+import java.net.URLEncoder;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @Author: 程序员七七
+ * @Date: 16.11.21 11:26 下午
+ */
+@Slf4j
+@Order(-6)
+@WebFilter(filterName = "oauthFilter", urlPatterns = {"/*"})
+@Component
+public class OauthFilter implements Filter {
+
+    @Autowired
+    private OauthProperties oauthProperties;
+
+    @Autowired
+    private OauthServiceClient oauthServiceClient;
+
+    @Autowired
+    private UserSessionService userSessionService;
+
+    @Override
+    public void init(FilterConfig filterConfig) throws ServletException {
+
+    }
+
+    @Override
+    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
+        HttpServletRequest request = (HttpServletRequest) servletRequest;
+        HttpServletResponse response = (HttpServletResponse) servletResponse;
+        // 取集中session服务判断是否已经登录,如果已经登录则直接进入后续方法。
+        String oauthCode = request.getHeader("oauth-code");
+        String userId = userSessionService.getUserIdByRequest(request);
+        if (StringUtils.isNotBlank(userId)) {
+            // 已经登录
+            SessionHelper.setUserId(userId);
+            try {
+                filterChain.doFilter(request, response);
+            } finally {
+                SessionHelper.clear();
+            }
+            return;
+        }
+        if (StringUtils.isNotBlank(oauthCode)) {
+            userId = oauthServiceClient.getUserIdByOauthCode(oauthCode).getData();
+            if (StringUtils.isNotBlank(userId)) {
+                userSessionService.createUserLoginSession(response, userId);
+                SessionHelper.setUserId(userId);
+                try {
+                    filterChain.doFilter(request, response);
+                } finally {
+                    SessionHelper.clear();
+                }
+                return;
+            }
+        }
+        /**
+         * 302 重定向到统一登录页面
+         */
+        this.unLogin(request, response);
+    }
+
+    private void unLogin(HttpServletRequest request, HttpServletResponse response) {
+        Map<String, Object> responseMap = new HashMap<>();
+        response.setCharacterEncoding("UTF-8"); // 设置字符编码为 UTF-8
+        response.setContentType("application/json;charset=UTF-8"); // 设置内容类型和字符编码
+        responseMap.put("code", BaseErrCodeEnum.UN_LOGIN.getCode());
+        responseMap.put("message", BaseErrCodeEnum.UN_LOGIN.getMessage());
+        responseMap.put("redirect_url", getRedirectUrl(request));
+        try {
+            response.getWriter().println(JsonUtil.toJsonString(responseMap));
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private String getRedirectUrl(HttpServletRequest request) {
+        String serverUrl = oauthProperties.getServerUrl();
+        String originUrl = request.getHeader("origin-url");
+        return String.format(serverUrl, URLEncoder.encode(originUrl));
+    }
+
+    @Override
+    public void destroy() {
+
+    }
+}

+ 128 - 0
webchat-client-chat/src/main/java/com/webchat/client/chat/service/AccountRelationService.java

@@ -0,0 +1,128 @@
+package com.webchat.client.chat.service;
+
+
+import com.webchat.common.bean.APIResponseBean;
+import com.webchat.common.bean.APIResponseBeanUtil;
+import com.webchat.common.exception.BusinessException;
+import com.webchat.domain.vo.request.CreateGroupRequestVO;
+import com.webchat.domain.vo.response.AccountRelationResponseVO;
+import com.webchat.domain.vo.response.WaitConfirmUserResponseVO;
+import com.webchat.rmi.user.AccountRelationClient;
+import org.apache.commons.lang3.ObjectUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.web.bind.annotation.RequestBody;
+
+import java.util.List;
+
+@Service
+public class AccountRelationService {
+
+
+    @Autowired
+    private AccountRelationClient accountRelationClient;
+
+
+    /**
+     * 订阅/关注
+     *
+     * @param userId
+     * @param account
+     * @return
+     */
+    public Boolean subscribe(String userId, String account) {
+        APIResponseBean<Boolean> responseBean = accountRelationClient.subscribe(userId, account);
+        if (APIResponseBeanUtil.isOk(responseBean)) {
+            return ObjectUtils.equals(responseBean.getData(), true);
+        }
+        throw new BusinessException(responseBean.getMsg());
+    }
+
+    /**
+     * 取消订阅/关注
+     *
+     * @param account
+     * @return
+     */
+    public Boolean unsubscribe(String userId, String account) {
+        APIResponseBean<Boolean> responseBean = accountRelationClient.unsubscribe(userId, account);
+        if (APIResponseBeanUtil.isOk(responseBean)) {
+            return ObjectUtils.equals(responseBean.getData(), true);
+        }
+        throw new BusinessException(responseBean.getMsg());
+    }
+
+    /**
+     * 确认好友
+     *
+     * @param userId
+     * @param relationId
+     * @return
+     */
+    public Boolean confirmSubscribe(String userId, Long relationId) {
+        APIResponseBean<Boolean> responseBean = accountRelationClient.confirmSubscribe(userId, relationId);
+        if (APIResponseBeanUtil.isOk(responseBean)) {
+            return ObjectUtils.equals(responseBean.getData(), true);
+        }
+        throw new BusinessException(responseBean.getMsg());
+    }
+
+    /**
+     * 拒绝好友
+     *
+     * @param relationId
+     * @return
+     */
+    public Boolean rejectSubscribe(String userId, Long relationId) {
+        APIResponseBean<Boolean> responseBean = accountRelationClient.rejectSubscribe(userId, relationId);
+        if (APIResponseBeanUtil.isOk(responseBean)) {
+            return ObjectUtils.equals(responseBean.getData(), true);
+        }
+        throw new BusinessException(responseBean.getMsg());
+    }
+
+
+    /**
+     * 加载用户好友/群组/机器人/公众号列表
+     *
+     * @param userId
+     * @return
+     */
+    public AccountRelationResponseVO loadAccountRelations(String userId) {
+        APIResponseBean<AccountRelationResponseVO> responseBean = accountRelationClient.listRelations(userId);
+        if (APIResponseBeanUtil.isOk(responseBean)) {
+            return responseBean.getData();
+        }
+        throw new BusinessException(responseBean.getMsg());
+    }
+
+    /**
+     * 查询用户待审核用户列表
+     *
+     * @param account
+     * @return
+     */
+    public List<WaitConfirmUserResponseVO> listWaitConformUsers(String account) {
+
+        APIResponseBean<List<WaitConfirmUserResponseVO>> responseBean = accountRelationClient.listWaitConformUsers(account);
+        if (APIResponseBeanUtil.isOk(responseBean)) {
+            return responseBean.getData();
+        }
+        // RPC远程调用异常 TODO,需要预警,上报异常到Prometheus
+        throw new BusinessException(responseBean.getMsg());
+    }
+
+    /**
+     * 创建群聊
+     *
+     * @param createGroupRequest
+     * @return
+     */
+    public String createChatGroup(CreateGroupRequestVO createGroupRequest) {
+        APIResponseBean<String> responseBean = accountRelationClient.createChatGroup(createGroupRequest);
+        if (APIResponseBeanUtil.isOk(responseBean)) {
+            return responseBean.getData();
+        }
+        throw new BusinessException(responseBean.getMsg());
+    }
+}

+ 46 - 0
webchat-client-chat/src/main/java/com/webchat/client/chat/service/AccountService.java

@@ -0,0 +1,46 @@
+package com.webchat.client.chat.service;
+
+import com.webchat.common.bean.APIResponseBean;
+import com.webchat.common.bean.APIResponseBeanUtil;
+import com.webchat.domain.vo.response.AccountRelationResponseVO;
+import com.webchat.domain.vo.response.UserBaseResponseInfoVO;
+import com.webchat.rmi.user.UserServiceClient;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Service
+public class AccountService {
+
+    @Autowired
+    private UserServiceClient userServiceClient;
+
+    /**
+     * 获取用户信息
+     *
+     * @param userId
+     * @return
+     */
+    public UserBaseResponseInfoVO userInfo(String userId) {
+        APIResponseBean<UserBaseResponseInfoVO> responseBean = userServiceClient.userInfo(userId);
+        if (APIResponseBeanUtil.isOk(responseBean)) {
+            return responseBean.getData();
+        }
+        // TODO
+        return null;
+    }
+
+    /**
+     * 用户账号查询详情
+     *
+     * @param account
+     * @return
+     */
+    public UserBaseResponseInfoVO queryAccount( String account) {
+        APIResponseBean<UserBaseResponseInfoVO> responseBean = userServiceClient.queryAccount(account);
+        if (APIResponseBeanUtil.isOk(responseBean)) {
+            return responseBean.getData();
+        }
+        // TODO
+        return null;
+    }
+}

+ 48 - 0
webchat-client-chat/src/main/java/com/webchat/client/chat/service/ChatMessageService.java

@@ -0,0 +1,48 @@
+package com.webchat.client.chat.service;
+
+
+import com.webchat.common.bean.APIResponseBean;
+import com.webchat.common.bean.APIResponseBeanUtil;
+import com.webchat.common.exception.BusinessException;
+import com.webchat.common.util.JsonUtil;
+import com.webchat.domain.vo.response.chatting.ChattingListResponseVO;
+import com.webchat.domain.vo.response.mess.ChatMessageResponseVO;
+import com.webchat.rmi.ugc.ChatMessageClient;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestParam;
+
+import java.util.Collections;
+import java.util.List;
+
+
+@Slf4j
+@Service
+public class ChatMessageService {
+
+
+    @Autowired
+    private ChatMessageClient chatMessageClient;
+
+    public List<ChatMessageResponseVO> list(String currentUserId, String chatUserId, Long lastId, Long fixedMessageId, Integer size) {
+        APIResponseBean<List<ChatMessageResponseVO>> messageResponse = chatMessageClient.list(currentUserId, chatUserId, size, lastId, fixedMessageId);
+        if (APIResponseBeanUtil.isOk(messageResponse)) {
+            return messageResponse.getData();
+        }
+        return Collections.emptyList();
+    }
+
+    public List<ChattingListResponseVO> listChatting(String userId, Long lastChatTime, Integer size) {
+
+        // RPC 远程调用,请求 UGC 核心服务获取对话列表数据
+        APIResponseBean<List<ChattingListResponseVO>> responseBean = chatMessageClient.listChatting(userId, lastChatTime, size);
+        if (APIResponseBeanUtil.isOk(responseBean)) {
+            return responseBean.getData();
+        }
+        log.error("chatMessageClient.listChatting RPC invoke error =====> " +
+                "userId:{}, lastChatTime:{}, size:{}, response:{}", userId, lastChatTime, size, JsonUtil.toJsonString(responseBean));
+        throw new BusinessException(responseBean.getMsg());
+    }
+}

+ 69 - 0
webchat-client-chat/src/main/java/com/webchat/client/chat/service/UserSessionService.java

@@ -0,0 +1,69 @@
+package com.webchat.client.chat.service;
+
+import com.webchat.common.constants.CookieConstants;
+import com.webchat.common.enums.RedisKeyEnum;
+import com.webchat.common.service.RedisService;
+import com.webchat.common.util.DateUtils;
+import com.webchat.common.util.MD5Utils;
+import jakarta.servlet.http.Cookie;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Service
+public class UserSessionService {
+
+    @Autowired
+    private RedisService redisService;
+
+    public String getUserIdByRequest(HttpServletRequest request) {
+        Cookie[] cookies = request.getCookies();
+        if (cookies != null) {
+            for (Cookie cookie : cookies) {
+                if (CookieConstants.WEB_CHAT_CLIENT_USER_COOKIE_KEY.equals(cookie.getName())) {
+                    String sessionId = cookie.getValue();
+                    return this.getUserIdBySessionId(sessionId);
+                }
+            }
+        }
+        return null;
+    }
+
+    public void logout(HttpServletRequest request) {
+        Cookie[] cookies = request.getCookies();
+        if (cookies != null) {
+            for (Cookie cookie : cookies) {
+                if (CookieConstants.WEB_CHAT_CLIENT_USER_COOKIE_KEY.equals(cookie.getName())) {
+                    String sessionId = cookie.getValue();
+                    this.deleteUserLoginSession(sessionId);
+                }
+            }
+        }
+    }
+
+    public void createUserLoginSession(HttpServletResponse response, String userId) {
+        String sessionId = MD5Utils.md5(userId + DateUtils.getCurrentFormatDate());
+        this.setUserIdBySessionId(sessionId, userId);
+        // 种浏览器Cookie
+        Cookie cookie = new Cookie(CookieConstants.WEB_CHAT_CLIENT_USER_COOKIE_KEY, sessionId);
+        cookie.setPath("/");
+        cookie.setMaxAge(CookieConstants.COOKIE_OUT_TIME);
+        response.addCookie(cookie);
+    }
+
+    private void deleteUserLoginSession(String sessionId) {
+        String sessionCacheKey = RedisKeyEnum.USER_SESSION_PREFIX.getKey(sessionId);
+        redisService.remove(sessionCacheKey);
+    }
+
+    public String getUserIdBySessionId(String sessionId) {
+        String sessionCacheKey = RedisKeyEnum.USER_SESSION_PREFIX.getKey(sessionId);
+        return redisService.get(sessionCacheKey);
+    }
+
+    public void setUserIdBySessionId(String sessionId, String userId) {
+        String sessionCacheKey = RedisKeyEnum.USER_SESSION_PREFIX.getKey(sessionId);
+        redisService.set(sessionCacheKey, userId, RedisKeyEnum.USER_SESSION_PREFIX.getExpireTime());
+    }
+}

+ 1 - 0
webchat-client-chat/src/main/resources/application.yml

@@ -0,0 +1 @@
+server:
  port: 8082

+ 21 - 0
webchat-client-chat/src/main/resources/bootstrap.yml

@@ -0,0 +1,21 @@
+spring:
+  application:
+    name: webchat-client-chat-service
+  profiles:
+    active: dev
+  cloud:
+    nacos:
+      config:
+        server-addr: 127.0.0.1:8848
+        namespace: 2cdfb8e3-f127-4020-93df-2d4c3dc805b5
+        file-extension: yaml
+        group: DEFAULT_GROUP
+        refresh-enabled: true
+      discovery:
+        server-addr: 127.0.0.1:8848
+        namespace: 2cdfb8e3-f127-4020-93df-2d4c3dc805b5
+    sentinel:
+      transport:
+        port: 8719
+        dashboard: 127.0.0.1:8080
+      eager: true

+ 93 - 0
webchat-common/pom.xml

@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>com.webchat</groupId>
+        <artifactId>webchat</artifactId>
+        <version>0.0.1-SNAPSHOT</version>
+    </parent>
+
+    <groupId>com.webchat</groupId>
+    <artifactId>webchat-common</artifactId>
+    <version>1.0-SNAPSHOT</version>
+    <packaging>jar</packaging>
+
+    <properties>
+        <maven.compiler.source>17</maven.compiler.source>
+        <maven.compiler.target>17</maven.compiler.target>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>io.minio</groupId>
+            <artifactId>minio</artifactId>
+            <version>8.4.2</version>
+        </dependency>
+        <dependency>
+            <groupId>com.webchat</groupId>
+            <artifactId>webchat-domain</artifactId>
+            <version>1.0-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>redis.clients</groupId>
+            <artifactId>jedis</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-data-redis</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.rocketmq</groupId>
+            <artifactId>rocketmq-spring-boot-starter</artifactId>
+            <version>2.3.1</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-freemarker</artifactId>
+        </dependency>
+
+        <!-- 添加依赖 -->
+        <dependency>
+            <groupId>com.squareup.okhttp3</groupId>
+            <artifactId>okhttp</artifactId>
+            <version>4.12.0</version>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-simple</artifactId>
+            <version>2.0.12</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>io.reactivex.rxjava2</groupId>
+            <artifactId>rxjava</artifactId>
+            <version>2.2.21</version>
+        </dependency>
+        <!-- 二维码 -->
+        <dependency>
+            <groupId>com.google.zxing</groupId>
+            <artifactId>core</artifactId>
+            <version>3.1.0</version>
+        </dependency>
+        <dependency>
+            <groupId>com.google.zxing</groupId>
+            <artifactId>javase</artifactId>
+            <version>3.1.0</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.httpcomponents.client5</groupId>
+            <artifactId>httpclient5</artifactId>
+            <version>5.2.1</version>
+        </dependency>
+        <!-- 用于html转图片 -->
+        <dependency>
+            <groupId>org.xhtmlrenderer</groupId>
+            <artifactId>core-renderer</artifactId>
+            <version>R8</version>
+        </dependency>
+    </dependencies>
+</project>

+ 50 - 0
webchat-common/src/main/java/com/webchat/common/bean/APIPageResponseBean.java

@@ -0,0 +1,50 @@
+package com.webchat.common.bean;
+
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+public class APIPageResponseBean<T> {
+
+    private Boolean success;
+
+    private Integer code;
+
+    private String msg;
+
+    private Long pageNo;
+
+    private Long pageSize;
+
+    private Long total;
+
+    private Long pageTotal;
+
+    private List<T> data;
+
+    public APIPageResponseBean() {
+
+    }
+
+    public APIPageResponseBean error() {
+        APIPageResponseBean responseBean = new APIPageResponseBean();
+        responseBean.success = false;
+        responseBean.code = 500;
+        responseBean.msg = "ERROR";
+        return responseBean;
+    }
+
+    public static APIPageResponseBean success(long pageNo, long pageSize, long total, List data) {
+        APIPageResponseBean responseBean = new APIPageResponseBean();
+        responseBean.success = true;
+        responseBean.code = 200;
+        responseBean.msg = "OK";
+        responseBean.pageNo = pageNo;
+        responseBean.pageSize = pageSize;
+        responseBean.data = data;
+        responseBean.total = total;
+        responseBean.pageTotal = total % pageSize == 0 ? total / pageSize : total / pageSize + 1;
+        return responseBean;
+    }
+}

+ 20 - 0
webchat-common/src/main/java/com/webchat/common/bean/APIResponseBean.java

@@ -0,0 +1,20 @@
+package com.webchat.common.bean;
+
+import lombok.Data;
+
+@Data
+public class APIResponseBean<T> {
+
+    private Boolean success;
+
+    private Integer code;
+
+    private String msg;
+
+    /***
+     * 耗时
+     */
+    private Long cost;
+
+    private T data;
+}

+ 49 - 0
webchat-common/src/main/java/com/webchat/common/bean/APIResponseBeanUtil.java

@@ -0,0 +1,49 @@
+package com.webchat.common.bean;
+
+
+import org.apache.commons.lang3.ObjectUtils;
+
+public class APIResponseBeanUtil {
+
+    /***
+     * 构建成功结果
+     * @param data
+     * @return
+     */
+    public static APIResponseBean success(Object data) {
+        APIResponseBean apiResponseBean = new APIResponseBean();
+        apiResponseBean.setSuccess(true);
+        apiResponseBean.setCode(200);
+        apiResponseBean.setMsg("OK");
+        apiResponseBean.setData(data);
+        return apiResponseBean;
+    }
+
+    public static APIResponseBean success() {
+        return success(null);
+    }
+
+    /***
+     * 构建失败结果
+     * @param code
+     * @param msg
+     * @return
+     */
+    public static APIResponseBean error(Integer code, String msg) {
+        APIResponseBean apiResponseBean = new APIResponseBean();
+        apiResponseBean.setSuccess(false);
+        apiResponseBean.setCode(code);
+        apiResponseBean.setMsg(msg);
+        return apiResponseBean;
+    }
+
+    /**
+     * 校验返回结果是否成功
+     *
+     * @param apiResponseBean
+     * @return
+     */
+    public static boolean isOk(APIResponseBean apiResponseBean) {
+        return apiResponseBean != null && ObjectUtils.equals(apiResponseBean.getSuccess(), true);
+    }
+}

+ 38 - 0
webchat-common/src/main/java/com/webchat/common/config/annotation/SafeClick.java

@@ -0,0 +1,38 @@
+package com.webchat.common.config.annotation;
+
+import com.webchat.common.enums.ClickEvent;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * @Author: 程序员七七
+ * @Date: 27.11.21 12:26 上午
+ * 安全点击时间拦截自定义注解
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD})
+@Documented
+public @interface SafeClick {
+
+    /***
+     * 事件KEY
+     * @return
+     */
+    ClickEvent event();
+
+    /***
+     * 默认500ml内只能允许一次点击操作,单位:毫秒
+     * @return
+     */
+    long time() default 500L;
+
+    /***
+     * 异常文案提示
+     * @return
+     */
+    String message() default "点击太频繁,休息会吧";
+}

+ 20 - 0
webchat-common/src/main/java/com/webchat/common/config/annotation/ValidateLogin.java

@@ -0,0 +1,20 @@
+package com.webchat.common.config.annotation;
+
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * @Author: 程序员七七
+ * @Date: 26.11.21 11:56 下午
+ * 自定义注解实现:校验是否登录
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD})
+@Documented
+public @interface ValidateLogin {
+
+}

+ 25 - 0
webchat-common/src/main/java/com/webchat/common/config/annotation/ValidatePermission.java

@@ -0,0 +1,25 @@
+package com.webchat.common.config.annotation;
+
+import com.webchat.common.enums.RoleCodeEnum;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * 自定义注解实现:校验是否管理员
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD})
+@Documented
+public @interface ValidatePermission {
+
+    /***
+     * 需要进行校验的权限类型,默认校验管理员
+     * @see RoleCodeEnum
+     * @return
+     */
+    RoleCodeEnum[] role() default {RoleCodeEnum.ADMIN};
+}

+ 23 - 0
webchat-common/src/main/java/com/webchat/common/config/configuration/MinioConfig.java

@@ -0,0 +1,23 @@
+package com.webchat.common.config.configuration;
+
+
+import com.webchat.common.config.properties.MinioProperties;
+import io.minio.MinioClient;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class MinioConfig {
+
+    @Autowired
+    private MinioProperties properties;
+
+    @Bean
+    public MinioClient minioClient() {
+        return MinioClient.builder()
+                .endpoint(properties.getEndpoint())
+                .credentials(properties.getAccessKey(), properties.getSecretKey())
+                .build();
+    }
+}

+ 28 - 0
webchat-common/src/main/java/com/webchat/common/config/configuration/RocketMQProducerConfig.java

@@ -0,0 +1,28 @@
+package com.webchat.common.config.configuration;
+
+import org.apache.rocketmq.client.producer.DefaultMQProducer;
+import org.apache.rocketmq.spring.core.RocketMQTemplate;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class RocketMQProducerConfig {
+
+    @Value("${rocketmq.producer.group:webchat}")
+    private String producerGroup;
+
+    @Value("${rocketmq.name-server:127.0.0.1:9876}")
+    private String nameServer;
+
+    @Bean
+    public RocketMQTemplate rocketMQTemplate() {
+        RocketMQTemplate rocketMQTemplate = new RocketMQTemplate();
+        DefaultMQProducer producer = new DefaultMQProducer(producerGroup);
+        producer.setNamesrvAddr(nameServer);
+        rocketMQTemplate.setProducer(producer);
+        return rocketMQTemplate;
+    }
+}
+
+

+ 19 - 0
webchat-common/src/main/java/com/webchat/common/config/properties/MinioProperties.java

@@ -0,0 +1,19 @@
+package com.webchat.common.config.properties;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+
+@Data
+@Configuration
+@ConfigurationProperties(prefix = "minio")
+public class MinioProperties {
+
+    private String endpoint = "http://localhost:9000";
+
+    private String accessKey = "BArEoI6PE8uGh13jHRuN";
+
+    private String secretKey = "eHRL9vpXUH6vGOGkL5Vth2ZQRpzh7AvdKzNlASko";;
+
+    private String bucketName = "webchat";
+}

+ 13 - 0
webchat-common/src/main/java/com/webchat/common/config/properties/OauthProperties.java

@@ -0,0 +1,13 @@
+package com.webchat.common.config.properties;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+
+@Data
+@Configuration
+@ConfigurationProperties(prefix = "oauth")
+public class OauthProperties {
+
+    private String serverUrl = "";
+}

Unele fișiere nu au fost afișate deoarece prea multe fișiere au fost modificate în acest diff