diff --git a/qa-service/README.md b/qa-service/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..6b73c856ac94d243f266bf1b419c7af3009b4ef4
--- /dev/null
+++ b/qa-service/README.md
@@ -0,0 +1,185 @@
+# QA智能问答系统
+
+基于Spring Boot + RabbitMQ + KIMI API的异步问答系统
+
+## 功能特性
+
+- 🤖 集成KIMI AI,提供智能问答服务
+- 🔄 基于RabbitMQ的异步处理架构
+- 💾 MySQL数据库持久化存储
+- 📚 Swagger/Knife4j API文档
+- 🎨 响应式Web界面
+- ⚡ 高性能异步处理
+
+## 系统架构
+
+```
+用户提交问题 → 保存到数据库 → 发送到MQ队列 → 后台异步处理 → 调用KIMI API → 更新数据库
+```
+
+## 技术栈
+
+- **后端框架**: Spring Boot 3.0.2
+- **数据库**: MySQL + Spring Data JPA
+- **消息队列**: RabbitMQ
+- **AI服务**: KIMI API
+- **API文档**: Knife4j (Swagger)
+- **前端**: 原生HTML + JavaScript
+
+## 快速开始
+
+### 1. 环境准备
+
+确保已安装以下环境:
+- JDK 21+
+- MySQL 8.0+
+- RabbitMQ 3.8+
+- Maven 3.6+
+
+### 2. 数据库配置
+
+创建数据库:
+```sql
+CREATE DATABASE qa_system CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+```
+
+### 3. 配置文件
+
+修改 `application.properties` 中的配置:
+
+```properties
+# 数据库配置
+spring.datasource.url=jdbc:mysql://localhost:3306/qa_system?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8
+spring.datasource.username=your_username
+spring.datasource.password=your_password
+
+# RabbitMQ配置
+spring.rabbitmq.host=localhost
+spring.rabbitmq.port=5672
+spring.rabbitmq.username=guest
+spring.rabbitmq.password=guest
+
+# KIMI配置
+moonshot.key=your_kimi_api_key
+```
+
+### 4. 启动应用
+
+```bash
+cd qa-service
+mvn spring-boot:run
+```
+
+### 5. 访问应用
+
+- **Web界面**: http://localhost:8080
+- **API文档**: http://localhost:8080/doc.html
+- **健康检查**: http://localhost:8080/actuator/health
+
+## API接口
+
+### 提交问题
+```http
+POST /qa/ask
+Content-Type: application/json
+
+{
+ "question": "你好,请介绍一下北京"
+}
+```
+
+### 快速问答
+```http
+GET /qa/simple?question=你好
+```
+
+### 获取记录
+```http
+GET /qa/records
+GET /qa/records/{id}
+GET /qa/records/status/{status}
+```
+
+## 项目结构
+
+```
+qa-service/
+├── src/main/java/com/example/qa/service/
+│ ├── QaServiceApplication.java # 启动类
+│ ├── config/ # 配置类
+│ │ ├── MoonShotConfig.java # KIMI配置
+│ │ ├── RabbitConfig.java # RabbitMQ配置
+│ │ └── RestConfig.java # HTTP客户端配置
+│ ├── controller/ # 控制器
+│ │ └── QaController.java # 问答控制器
+│ ├── dto/ # 数据传输对象
+│ │ ├── ChatCompletionRequestDTO.java
+│ │ ├── ChatCompletionResponseDTO.java
+│ │ ├── Choice.java
+│ │ ├── Message.java
+│ │ ├── QaRequestDTO.java
+│ │ └── QaResponseDTO.java
+│ ├── entity/ # 实体类
+│ │ └── QaRecord.java # 问答记录实体
+│ ├── listener/ # 消息监听器
+│ │ └── QaResultListener.java # 问答结果监听器
+│ ├── repository/ # 数据访问层
+│ │ └── QaRecordRepository.java # 问答记录仓库
+│ └── service/ # 服务层
+│ └── QaService.java # 问答服务
+├── src/main/resources/
+│ ├── application.properties # 配置文件
+│ └── static/
+│ └── index.html # 前端页面
+└── pom.xml # Maven配置
+```
+
+## 使用说明
+
+1. **提交问题**: 通过Web界面或API提交问题
+2. **异步处理**: 系统将问题发送到MQ队列进行异步处理
+3. **AI回答**: 后台调用KIMI API获取智能回答
+4. **结果保存**: 将回答结果保存到数据库
+5. **状态查询**: 可以查询问题的处理状态和结果
+
+## 状态说明
+
+- `PROCESSING`: 处理中
+- `COMPLETED`: 已完成
+- `FAILED`: 处理失败
+
+## 注意事项
+
+1. 确保KIMI API密钥有效且有足够配额
+2. RabbitMQ服务需要正常运行
+3. 数据库连接配置正确
+4. 建议在生产环境中使用连接池和更安全的配置
+
+## 扩展功能
+
+- [ ] 用户认证和授权
+- [ ] 问题分类和标签
+- [ ] 批量问答处理
+- [ ] 答案评分和反馈
+- [ ] 对话上下文管理
+- [ ] 多模型支持
+
+## 故障排除
+
+### 常见问题
+
+1. **数据库连接失败**: 检查数据库服务状态和连接配置
+2. **RabbitMQ连接失败**: 确认RabbitMQ服务运行正常
+3. **KIMI API调用失败**: 检查API密钥和网络连接
+4. **端口占用**: 修改server.port配置
+
+### 日志查看
+
+查看应用日志定位问题:
+```bash
+tail -f logs/spring.log
+```
+
+## 许可证
+
+MIT License
diff --git a/qa-service/pom.xml b/qa-service/pom.xml
index 476ca31ee84446204ef71aedf1d07bdd6d774ad2..38a2d1ac079eb5efc8559b4e1f915bee75d926ae 100644
--- a/qa-service/pom.xml
+++ b/qa-service/pom.xml
@@ -16,7 +16,43 @@
org.springframework.boot
- spring-boot-starter
+ spring-boot-starter-web
+
+
+
+ org.springframework.boot
+ spring-boot-starter-amqp
+
+
+
+ org.springframework.boot
+ spring-boot-starter-data-jpa
+
+
+
+ mysql
+ mysql-connector-java
+ 8.0.33
+ runtime
+
+
+
+ org.projectlombok
+ lombok
+ true
+
+
+
+
+ com.github.xiaoymin
+ knife4j-openapi3-jakarta-spring-boot-starter
+ 4.5.0
+
+
+
+ com.alibaba.fastjson2
+ fastjson2
+ 2.0.58
@@ -24,6 +60,12 @@
spring-boot-starter-test
test
+
+
+ org.springframework.amqp
+ spring-rabbit-test
+ test
+
diff --git a/qa-service/src/main/java/com/example/qa/service/QaServiceApplication.java b/qa-service/src/main/java/com/example/qa/service/QaServiceApplication.java
new file mode 100644
index 0000000000000000000000000000000000000000..6698f6ebb25c7a873c8ae42624c2c89deaf313ba
--- /dev/null
+++ b/qa-service/src/main/java/com/example/qa/service/QaServiceApplication.java
@@ -0,0 +1,13 @@
+package com.example.qa.service;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class QaServiceApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(QaServiceApplication.class, args);
+ }
+
+}
diff --git a/qa-service/src/main/java/com/example/qa/service/config/MoonShotConfig.java b/qa-service/src/main/java/com/example/qa/service/config/MoonShotConfig.java
new file mode 100644
index 0000000000000000000000000000000000000000..1e552f458919c4d3f72fe0e4f1c2a3dc4026b61a
--- /dev/null
+++ b/qa-service/src/main/java/com/example/qa/service/config/MoonShotConfig.java
@@ -0,0 +1,14 @@
+package com.example.qa.service.config;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+
+@Data
+@Configuration
+@ConfigurationProperties(prefix = "moonshot")
+public class MoonShotConfig {
+ private String url;
+ private String key;
+ private String model;
+}
diff --git a/qa-service/src/main/java/com/example/qa/service/config/RabbitConfig.java b/qa-service/src/main/java/com/example/qa/service/config/RabbitConfig.java
new file mode 100644
index 0000000000000000000000000000000000000000..18169ee197918e63e92612d547ae2d6515e8b06a
--- /dev/null
+++ b/qa-service/src/main/java/com/example/qa/service/config/RabbitConfig.java
@@ -0,0 +1,48 @@
+package com.example.qa.service.config;
+
+import com.example.qa.service.entity.QaRecord;
+import org.springframework.amqp.core.Queue;
+import org.springframework.amqp.rabbit.connection.ConnectionFactory;
+import org.springframework.amqp.rabbit.core.RabbitTemplate;
+import org.springframework.amqp.support.converter.DefaultJackson2JavaTypeMapper;
+import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
+import org.springframework.amqp.support.converter.MessageConverter;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import java.util.HashMap;
+import java.util.Map;
+
+@Configuration
+public class RabbitConfig {
+
+ // QA结果队列
+ public static final String QA_RESULT_QUEUE = "qa.result.queue";
+
+ @Bean
+ public Queue qaResultQueue() {
+ return new Queue(QA_RESULT_QUEUE);
+ }
+
+ @Bean
+ public MessageConverter messageConverter() {
+ Jackson2JsonMessageConverter converter = new Jackson2JsonMessageConverter();
+ // 配置类型映射器以支持 QaRecord 类的反序列化
+ DefaultJackson2JavaTypeMapper typeMapper = new DefaultJackson2JavaTypeMapper();
+ Map> idClassMapping = new HashMap<>();
+ // 添加 QaRecord 类的映射
+ idClassMapping.put("com.example.qa.service.entity.QaRecord", QaRecord.class);
+ typeMapper.setIdClassMapping(idClassMapping);
+ typeMapper.setTrustedPackages("com.example.qa.service.entity");
+ converter.setJavaTypeMapper(typeMapper);
+
+ return converter;
+ }
+
+ @Bean
+ public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
+ RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
+ rabbitTemplate.setMessageConverter(messageConverter());
+ return rabbitTemplate;
+ }
+}
diff --git a/qa-service/src/main/java/com/example/qa/service/config/RestConfig.java b/qa-service/src/main/java/com/example/qa/service/config/RestConfig.java
new file mode 100644
index 0000000000000000000000000000000000000000..ad04c510477591a9f14d6c0cc1a5efcaefef4705
--- /dev/null
+++ b/qa-service/src/main/java/com/example/qa/service/config/RestConfig.java
@@ -0,0 +1,14 @@
+package com.example.qa.service.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.client.RestTemplate;
+
+@Configuration
+public class RestConfig {
+
+ @Bean
+ public RestTemplate restTemplate() {
+ return new RestTemplate();
+ }
+}
diff --git a/qa-service/src/main/java/com/example/qa/service/controller/QaController.java b/qa-service/src/main/java/com/example/qa/service/controller/QaController.java
new file mode 100644
index 0000000000000000000000000000000000000000..7c9d2dc81378b440c5c48f4f55d3262116ece919
--- /dev/null
+++ b/qa-service/src/main/java/com/example/qa/service/controller/QaController.java
@@ -0,0 +1,82 @@
+package com.example.qa.service.controller;
+
+import com.example.qa.service.dto.QaRequestDTO;
+import com.example.qa.service.dto.QaResponseDTO;
+import com.example.qa.service.entity.QaRecord;
+import com.example.qa.service.service.QaService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.annotation.Resource;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+@Slf4j
+@Tag(name = "问答服务", description = "基于KIMI的问答服务接口")
+@RestController
+@RequestMapping("/qa")
+public class QaController {
+
+ @Resource
+ private QaService qaService;
+
+ @Operation(summary = "提交问题", description = "异步提交问题给KIMI处理")
+ @PostMapping("/ask")
+ public ResponseEntity askQuestion(@RequestBody QaRequestDTO request) {
+ log.info("收到问题: {}", request.getQuestion());
+
+ if (request.getQuestion() == null || request.getQuestion().trim().isEmpty()) {
+ return ResponseEntity.badRequest()
+ .body(QaResponseDTO.error("问题不能为空"));
+ }
+
+ QaResponseDTO response = qaService.askQuestion(request);
+ return ResponseEntity.ok(response);
+ }
+
+ @Operation(summary = "获取所有问答记录", description = "获取所有问答记录,按创建时间倒序")
+ @GetMapping("/records")
+ public ResponseEntity> getAllRecords() {
+ List records = qaService.getAllRecords();
+ return ResponseEntity.ok(records);
+ }
+
+ @Operation(summary = "根据ID获取问答记录", description = "根据记录ID获取具体的问答记录")
+ @GetMapping("/records/{id}")
+ public ResponseEntity getRecordById(
+ @Parameter(description = "记录ID") @PathVariable Long id) {
+ QaRecord record = qaService.getRecordById(id);
+ if (record == null) {
+ return ResponseEntity.notFound().build();
+ }
+ return ResponseEntity.ok(record);
+ }
+
+ @Operation(summary = "根据状态获取问答记录", description = "根据处理状态获取问答记录")
+ @GetMapping("/records/status/{status}")
+ public ResponseEntity> getRecordsByStatus(
+ @Parameter(description = "记录状态:PROCESSING, COMPLETED, FAILED")
+ @PathVariable String status) {
+ try {
+ QaRecord.Status recordStatus = QaRecord.Status.valueOf(status.toUpperCase());
+ List records = qaService.getRecordsByStatus(recordStatus);
+ return ResponseEntity.ok(records);
+ } catch (IllegalArgumentException e) {
+ return ResponseEntity.badRequest().build();
+ }
+ }
+
+ @Operation(summary = "简单问答接口", description = "快速提交问题的简化接口")
+ @GetMapping("/simple")
+ public ResponseEntity simpleAsk(
+ @Parameter(description = "问题内容") @RequestParam String question) {
+ QaRequestDTO request = new QaRequestDTO();
+ request.setQuestion(question);
+
+ QaResponseDTO response = qaService.askQuestion(request);
+ return ResponseEntity.ok(response);
+ }
+}
diff --git a/qa-service/src/main/java/com/example/qa/service/dto/ChatCompletionRequestDTO.java b/qa-service/src/main/java/com/example/qa/service/dto/ChatCompletionRequestDTO.java
new file mode 100644
index 0000000000000000000000000000000000000000..905c7ad489344e6d26611ddfc43934ab80e29cd1
--- /dev/null
+++ b/qa-service/src/main/java/com/example/qa/service/dto/ChatCompletionRequestDTO.java
@@ -0,0 +1,11 @@
+package com.example.qa.service.dto;
+
+import lombok.Data;
+import java.util.List;
+
+@Data
+public class ChatCompletionRequestDTO {
+ private String model;
+ private List messages;
+ private double temperature;
+}
diff --git a/qa-service/src/main/java/com/example/qa/service/dto/ChatCompletionResponseDTO.java b/qa-service/src/main/java/com/example/qa/service/dto/ChatCompletionResponseDTO.java
new file mode 100644
index 0000000000000000000000000000000000000000..33c6381762a705e1eb96e30bdb3f43490cd1dcfa
--- /dev/null
+++ b/qa-service/src/main/java/com/example/qa/service/dto/ChatCompletionResponseDTO.java
@@ -0,0 +1,13 @@
+package com.example.qa.service.dto;
+
+import lombok.Data;
+import java.util.List;
+
+@Data
+public class ChatCompletionResponseDTO {
+ private String id;
+ private String object;
+ private long created;
+ private String model;
+ private List choices;
+}
diff --git a/qa-service/src/main/java/com/example/qa/service/dto/Choice.java b/qa-service/src/main/java/com/example/qa/service/dto/Choice.java
new file mode 100644
index 0000000000000000000000000000000000000000..e061205b40c546153e770456f59060e63198b29e
--- /dev/null
+++ b/qa-service/src/main/java/com/example/qa/service/dto/Choice.java
@@ -0,0 +1,10 @@
+package com.example.qa.service.dto;
+
+import lombok.Data;
+
+@Data
+public class Choice {
+ private int index;
+ private Message message;
+ private String finish_reason;
+}
diff --git a/qa-service/src/main/java/com/example/qa/service/dto/Message.java b/qa-service/src/main/java/com/example/qa/service/dto/Message.java
new file mode 100644
index 0000000000000000000000000000000000000000..9727445850dc2c03780d623d1af2436391b684a4
--- /dev/null
+++ b/qa-service/src/main/java/com/example/qa/service/dto/Message.java
@@ -0,0 +1,9 @@
+package com.example.qa.service.dto;
+
+import lombok.Data;
+
+@Data
+public class Message {
+ private String role;
+ private String content;
+}
diff --git a/qa-service/src/main/java/com/example/qa/service/dto/QaRequestDTO.java b/qa-service/src/main/java/com/example/qa/service/dto/QaRequestDTO.java
new file mode 100644
index 0000000000000000000000000000000000000000..c29a8bdb6dd2b032203d7e4d29accdcfaf5c8544
--- /dev/null
+++ b/qa-service/src/main/java/com/example/qa/service/dto/QaRequestDTO.java
@@ -0,0 +1,8 @@
+package com.example.qa.service.dto;
+
+import lombok.Data;
+
+@Data
+public class QaRequestDTO {
+ private String question;
+}
diff --git a/qa-service/src/main/java/com/example/qa/service/dto/QaResponseDTO.java b/qa-service/src/main/java/com/example/qa/service/dto/QaResponseDTO.java
new file mode 100644
index 0000000000000000000000000000000000000000..840321a55197fb807fcd23b286aca7164aef59a8
--- /dev/null
+++ b/qa-service/src/main/java/com/example/qa/service/dto/QaResponseDTO.java
@@ -0,0 +1,29 @@
+package com.example.qa.service.dto;
+
+import lombok.Data;
+import com.example.qa.service.entity.QaRecord.Status;
+
+@Data
+public class QaResponseDTO {
+ private Long id;
+ private String question;
+ private String answer;
+ private Status status;
+ private String message;
+
+ public static QaResponseDTO success(Long id, String question) {
+ QaResponseDTO response = new QaResponseDTO();
+ response.setId(id);
+ response.setQuestion(question);
+ response.setStatus(Status.PROCESSING);
+ response.setMessage("问题已提交,正在处理中...");
+ return response;
+ }
+
+ public static QaResponseDTO error(String message) {
+ QaResponseDTO response = new QaResponseDTO();
+ response.setStatus(Status.FAILED);
+ response.setMessage(message);
+ return response;
+ }
+}
diff --git a/qa-service/src/main/java/com/example/qa/service/entity/QaRecord.java b/qa-service/src/main/java/com/example/qa/service/entity/QaRecord.java
new file mode 100644
index 0000000000000000000000000000000000000000..e2ce04be862e665088b64523511bb86128d68c31
--- /dev/null
+++ b/qa-service/src/main/java/com/example/qa/service/entity/QaRecord.java
@@ -0,0 +1,47 @@
+package com.example.qa.service.entity;
+
+import jakarta.persistence.*;
+import lombok.Data;
+import java.time.LocalDateTime;
+
+@Data
+@Entity
+@Table(name = "qa_records")
+public class QaRecord {
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private Long id;
+
+ @Column(nullable = false, columnDefinition = "TEXT")
+ private String question;
+
+ @Column(columnDefinition = "TEXT")
+ private String answer;
+
+ @Column(name = "created_time", nullable = false)
+ private LocalDateTime createdTime;
+
+ @Column(name = "response_time")
+ private LocalDateTime responseTime;
+
+ @Enumerated(EnumType.STRING)
+ @Column(nullable = false)
+ private Status status;
+
+ public enum Status {
+ PROCESSING, // 处理中
+ COMPLETED, // 已完成
+ FAILED // 失败
+ }
+
+ @PrePersist
+ public void prePersist() {
+ if (createdTime == null) {
+ createdTime = LocalDateTime.now();
+ }
+ if (status == null) {
+ status = Status.PROCESSING;
+ }
+ }
+}
diff --git a/qa-service/src/main/java/com/example/qa/service/listener/QaResultListener.java b/qa-service/src/main/java/com/example/qa/service/listener/QaResultListener.java
new file mode 100644
index 0000000000000000000000000000000000000000..7c9137655ca7aa6e49d6bb1bfe7dcba26e9dfb9b
--- /dev/null
+++ b/qa-service/src/main/java/com/example/qa/service/listener/QaResultListener.java
@@ -0,0 +1,29 @@
+package com.example.qa.service.listener;
+
+import com.example.qa.service.config.RabbitConfig;
+import com.example.qa.service.entity.QaRecord;
+import com.example.qa.service.service.QaService;
+import jakarta.annotation.Resource;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.amqp.rabbit.annotation.RabbitHandler;
+import org.springframework.amqp.rabbit.annotation.RabbitListener;
+import org.springframework.stereotype.Component;
+
+@Slf4j
+@Component
+@RabbitListener(queues = RabbitConfig.QA_RESULT_QUEUE)
+public class QaResultListener {
+
+ @Resource
+ private QaService qaService;
+
+ @RabbitHandler
+ public void handleQaRequest(QaRecord qaRecord) {
+ log.info("收到问答请求,记录ID: {}, 问题: {}", qaRecord.getId(), qaRecord.getQuestion());
+
+ // 处理问答请求
+ qaService.processKimiRequest(qaRecord);
+
+ log.info("问答请求处理完成,记录ID: {}", qaRecord.getId());
+ }
+}
diff --git a/qa-service/src/main/java/com/example/qa/service/repository/QaRecordRepository.java b/qa-service/src/main/java/com/example/qa/service/repository/QaRecordRepository.java
new file mode 100644
index 0000000000000000000000000000000000000000..147bcfa658114e42c642cb82b4de50bdf7caf71d
--- /dev/null
+++ b/qa-service/src/main/java/com/example/qa/service/repository/QaRecordRepository.java
@@ -0,0 +1,15 @@
+package com.example.qa.service.repository;
+
+import com.example.qa.service.entity.QaRecord;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+
+@Repository
+public interface QaRecordRepository extends JpaRepository {
+
+ List findByStatusOrderByCreatedTimeDesc(QaRecord.Status status);
+
+ List findAllByOrderByCreatedTimeDesc();
+}
diff --git a/qa-service/src/main/java/com/example/qa/service/service/QaService.java b/qa-service/src/main/java/com/example/qa/service/service/QaService.java
new file mode 100644
index 0000000000000000000000000000000000000000..944beb8da37201f5914e2f59596411d7fa75166c
--- /dev/null
+++ b/qa-service/src/main/java/com/example/qa/service/service/QaService.java
@@ -0,0 +1,156 @@
+package com.example.qa.service.service;
+
+import com.example.qa.service.config.MoonShotConfig;
+import com.example.qa.service.config.RabbitConfig;
+import com.example.qa.service.dto.*;
+import com.example.qa.service.entity.QaRecord;
+import com.example.qa.service.repository.QaRecordRepository;
+import jakarta.annotation.Resource;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.amqp.rabbit.core.RabbitTemplate;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.MediaType;
+import org.springframework.stereotype.Service;
+import org.springframework.web.client.RestTemplate;
+
+import java.time.LocalDateTime;
+import java.util.Arrays;
+import java.util.List;
+
+@Slf4j
+@Service
+public class QaService {
+
+ @Resource
+ private QaRecordRepository qaRecordRepository;
+
+ @Resource
+ private RabbitTemplate rabbitTemplate;
+
+ @Resource
+ private RestTemplate restTemplate;
+
+ @Resource
+ private MoonShotConfig moonShotConfig;
+
+ /**
+ * 异步处理问答请求
+ */
+ public QaResponseDTO askQuestion(QaRequestDTO request) {
+ try {
+ // 1. 保存问题记录到数据库
+ QaRecord qaRecord = new QaRecord();
+ qaRecord.setQuestion(request.getQuestion());
+ qaRecord.setStatus(QaRecord.Status.PROCESSING);
+ qaRecord = qaRecordRepository.save(qaRecord);
+
+ // 2. 发送到MQ进行异步处理
+ rabbitTemplate.convertAndSend(RabbitConfig.QA_RESULT_QUEUE, qaRecord);
+
+ log.info("问题已提交处理,记录ID: {}", qaRecord.getId());
+
+ return QaResponseDTO.success(qaRecord.getId(), request.getQuestion());
+
+ } catch (Exception e) {
+ log.error("提交问题失败", e);
+ return QaResponseDTO.error("提交问题失败: " + e.getMessage());
+ }
+ }
+
+ /**
+ * 处理KIMI API调用(由MQ消费者调用)
+ */
+ public void processKimiRequest(QaRecord qaRecord) {
+ try {
+ log.info("开始处理问题: {}", qaRecord.getQuestion());
+
+ // 调用KIMI API
+ String answer = callKimiApi(qaRecord.getQuestion());
+
+ // 更新数据库记录
+ qaRecord.setAnswer(answer);
+ qaRecord.setStatus(QaRecord.Status.COMPLETED);
+ qaRecord.setResponseTime(LocalDateTime.now());
+ qaRecordRepository.save(qaRecord);
+
+ log.info("问题处理完成,记录ID: {}", qaRecord.getId());
+
+ } catch (Exception e) {
+ log.error("处理问题失败,记录ID: " + qaRecord.getId(), e);
+
+ // 更新为失败状态
+ qaRecord.setAnswer("处理失败: " + e.getMessage());
+ qaRecord.setStatus(QaRecord.Status.FAILED);
+ qaRecord.setResponseTime(LocalDateTime.now());
+ qaRecordRepository.save(qaRecord);
+ }
+ }
+
+ /**
+ * 调用KIMI API
+ */
+ private String callKimiApi(String question) {
+ // 创建请求头
+ HttpHeaders headers = new HttpHeaders();
+ headers.setContentType(MediaType.APPLICATION_JSON);
+ headers.set("Authorization", "Bearer " + moonShotConfig.getKey());
+
+ // 创建请求体
+ ChatCompletionRequestDTO request = new ChatCompletionRequestDTO();
+ request.setModel(moonShotConfig.getModel());
+ request.setTemperature(0.6);
+
+ // 创建消息列表
+ Message systemMessage = new Message();
+ systemMessage.setRole("system");
+ systemMessage.setContent("你是 Kimi,由 Moonshot AI 提供的人工智能助手,你更擅长中文和英文的对话。你会为用户提供安全,有帮助,准确的回答。同时,你会拒绝一切涉及恐怖主义,种族歧视,黄色暴力等问题的回答。Moonshot AI 为专有名词,不可翻译成其他语言。");
+
+ Message userMessage = new Message();
+ userMessage.setRole("user");
+ userMessage.setContent(question);
+
+ request.setMessages(Arrays.asList(systemMessage, userMessage));
+
+ // 发送请求
+ HttpEntity response = restTemplate.exchange(
+ moonShotConfig.getUrl(),
+ HttpMethod.POST,
+ new HttpEntity<>(request, headers),
+ ChatCompletionResponseDTO.class
+ );
+
+ // 提取答案
+ ChatCompletionResponseDTO responseBody = response.getBody();
+ if (responseBody != null && responseBody.getChoices() != null && !responseBody.getChoices().isEmpty()) {
+ Choice choice = responseBody.getChoices().get(0);
+ if (choice.getMessage() != null) {
+ return choice.getMessage().getContent();
+ }
+ }
+
+ throw new RuntimeException("未能从KIMI API获取有效响应");
+ }
+
+ /**
+ * 获取问答记录
+ */
+ public List getAllRecords() {
+ return qaRecordRepository.findAllByOrderByCreatedTimeDesc();
+ }
+
+ /**
+ * 根据ID获取问答记录
+ */
+ public QaRecord getRecordById(Long id) {
+ return qaRecordRepository.findById(id).orElse(null);
+ }
+
+ /**
+ * 根据状态获取问答记录
+ */
+ public List getRecordsByStatus(QaRecord.Status status) {
+ return qaRecordRepository.findByStatusOrderByCreatedTimeDesc(status);
+ }
+}
diff --git a/qa-service/src/main/resources/application.properties b/qa-service/src/main/resources/application.properties
new file mode 100644
index 0000000000000000000000000000000000000000..8983f49060fa49064869e25876c23b79f279cf6e
--- /dev/null
+++ b/qa-service/src/main/resources/application.properties
@@ -0,0 +1,30 @@
+# 服务端口
+server.port=8080
+
+# 数据库配置
+spring.datasource.url=jdbc:mysql://localhost:3306/qa_system?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8
+spring.datasource.username=root
+spring.datasource.password=123456
+spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
+
+# JPA配置
+spring.jpa.hibernate.ddl-auto=update
+spring.jpa.show-sql=true
+spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQLDialect
+
+# RabbitMQ配置
+spring.rabbitmq.host=localhost
+spring.rabbitmq.port=5672
+spring.rabbitmq.username=guest
+spring.rabbitmq.password=guest
+
+# KIMI配置
+moonshot.url=https://api.moonshot.cn/v1/chat/completions
+moonshot.key=sk-iVPnVTWEfvyQbnB2ePZte3MWzfQE5HmBsgZZ6zfJpBojnKdM
+moonshot.model=kimi-k2-0905-preview
+
+# Knife4j配置
+knife4j.enable=true
+knife4j.openapi.title=QA Service API
+knife4j.openapi.description=QA服务接口文档
+knife4j.openapi.version=1.0.0
diff --git a/qa-service/src/main/resources/static/index.html b/qa-service/src/main/resources/static/index.html
new file mode 100644
index 0000000000000000000000000000000000000000..ea9f9982518c75d00f20fb13e39ba659836c1553
--- /dev/null
+++ b/qa-service/src/main/resources/static/index.html
@@ -0,0 +1,331 @@
+
+
+
+
+
+ QA智能问答系统
+
+
+
+
+
+
+
+
+
API接口说明:
+ • 提交问题:
POST /qa/ask
+ • 快速问答:
GET /qa/simple?question=你的问题
+ • 查看记录:
GET /qa/records
+ • API文档:
Knife4j文档
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/qa-service/src/test/java/com/example/qa/service/QaServiceApplicationTests.java b/qa-service/src/test/java/com/example/qa/service/QaServiceApplicationTests.java
new file mode 100644
index 0000000000000000000000000000000000000000..cd38f0acb5dd8680f7089e7d6f84d45fc6c80ec2
--- /dev/null
+++ b/qa-service/src/test/java/com/example/qa/service/QaServiceApplicationTests.java
@@ -0,0 +1,13 @@
+package com.example.qa.service;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+
+@SpringBootTest
+class QaServiceApplicationTests {
+
+ @Test
+ void contextLoads() {
+ }
+
+}
diff --git a/qa-service/start.bat b/qa-service/start.bat
new file mode 100644
index 0000000000000000000000000000000000000000..19c0f3c2c900220ba28a0c497bf0c73c1e7df9b6
--- /dev/null
+++ b/qa-service/start.bat
@@ -0,0 +1,43 @@
+@echo off
+echo ========================================
+echo QA智能问答系统 - 启动脚本
+echo ========================================
+echo.
+
+echo 检查Java环境...
+java -version
+if %ERRORLEVEL% neq 0 (
+ echo 错误: 未找到Java环境,请安装JDK 21或更高版本
+ pause
+ exit /b 1
+)
+
+echo.
+echo 检查Maven环境...
+mvn -version
+if %ERRORLEVEL% neq 0 (
+ echo 错误: 未找到Maven环境,请安装Maven 3.6+
+ pause
+ exit /b 1
+)
+
+echo.
+echo 开始编译项目...
+mvn clean compile
+if %ERRORLEVEL% neq 0 (
+ echo 错误: 项目编译失败
+ pause
+ exit /b 1
+)
+
+echo.
+echo 启动应用...
+echo 应用将在 http://localhost:8080 启动
+echo API文档地址: http://localhost:8080/doc.html
+echo.
+echo 注意: 请确保MySQL和RabbitMQ服务已启动
+echo.
+
+mvn spring-boot:run
+
+pause
diff --git a/qa-service/test-api.bat b/qa-service/test-api.bat
new file mode 100644
index 0000000000000000000000000000000000000000..5c560106169007f0c2b240456690b74da71952ca
--- /dev/null
+++ b/qa-service/test-api.bat
@@ -0,0 +1,37 @@
+@echo off
+echo ========================================
+echo QA智能问答系统 - API测试脚本
+echo ========================================
+echo.
+
+echo 测试1: 健康检查
+echo GET http://localhost:8080/actuator/health
+curl -X GET http://localhost:8080/actuator/health
+echo.
+echo.
+
+echo 测试2: 提交问题
+echo POST http://localhost:8080/qa/ask
+curl -X POST http://localhost:8080/qa/ask ^
+ -H "Content-Type: application/json" ^
+ -d "{\"question\":\"你好,请简单介绍一下人工智能\"}"
+echo.
+echo.
+
+echo 测试3: 快速问答
+echo GET http://localhost:8080/qa/simple?question=你好
+curl -X GET "http://localhost:8080/qa/simple?question=你好"
+echo.
+echo.
+
+echo 测试4: 获取所有记录
+echo GET http://localhost:8080/qa/records
+curl -X GET http://localhost:8080/qa/records
+echo.
+echo.
+
+echo 测试完成!
+echo 请访问 http://localhost:8080 查看Web界面
+echo 请访问 http://localhost:8080/doc.html 查看API文档
+
+pause