浏览代码

1.0 登录注册,对话功能

zrh1922741520 1 周之前
父节点
当前提交
0d12c5fd8a

+ 5 - 0
.spring-api-helper.json

@@ -0,0 +1,5 @@
+{
+  "apiKey": "APS-4oIOfrCNjvkRtC9QVyVItlLNbXFkby16",
+  "projectId": "6353915",
+  "projectName": "智养问道"
+}

+ 12 - 0
mvnw

@@ -143,6 +143,13 @@
 			<version>0.11.5</version>
 			<scope>runtime</scope>
 		</dependency>
+		
+		<!-- Spring Security 依赖 -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-security</artifactId>
+        </dependency>
+	
 	</dependencies>
 	<dependencyManagement>
 		<dependencies>
@@ -165,6 +172,11 @@
 			<name>Spring Milestones</name>
 			<url>https://maven.aliyun.com/repository/public/</url>
 		</repository>
+		<repository>
+			<id>jcenter</id>
+			<name>JCenter</name>
+			<url>https://jcenter.bintray.com/</url>
+		</repository>
 	</repositories>
 	<build>
 		<plugins>

+ 31 - 0
src/main/java/ai/huisuan/back/config/SecurityConfig.java

@@ -0,0 +1,31 @@
+package ai.huisuan.back.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.security.web.SecurityFilterChain;
+
+@Configuration
+public class SecurityConfig {
+
+    @Bean
+    public PasswordEncoder passwordEncoder() {
+        return new BCryptPasswordEncoder();
+    }
+
+    @Bean
+    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
+        http
+            .csrf(csrf -> csrf.disable())
+            .authorizeHttpRequests(auth -> auth
+                .requestMatchers("/v1/auth/**").permitAll() // 允许访问登录和注册接口
+                .anyRequest().authenticated())
+            .formLogin(form -> form.disable()) // 禁用默认登录页面
+            .logout(logout -> logout
+                .logoutUrl("/logout")
+                .logoutSuccessUrl("/login-page")); // 确保与 loginPage 一致
+        return http.build();
+    }
+}

+ 0 - 1
src/main/java/ai/huisuan/back/controller/ChatController.java

@@ -11,7 +11,6 @@ import org.springframework.web.bind.annotation.RestController;
 import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
 
 import java.io.IOException;
-import java.util.List;
 
 /**
  * @author 对话接口

+ 37 - 0
src/main/java/ai/huisuan/back/controller/LoginController.java

@@ -0,0 +1,37 @@
+package ai.huisuan.back.controller;
+
+import ai.huisuan.back.model.LoginRequest;
+import ai.huisuan.back.model.RegisterRequest;
+import ai.huisuan.back.model.ResponseModel;
+import ai.huisuan.back.service.LoginService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+
+@RestController
+@RequestMapping("/v1/auth")
+public class LoginController {
+
+    @Autowired
+    private LoginService loginService;
+
+    @PostMapping("/login")
+    public ResponseEntity<ResponseModel> login(@RequestBody LoginRequest loginRequest) {
+        boolean success = loginService.login(loginRequest.getUsername(), loginRequest.getPassword(), loginRequest.getIp());
+        if (success) {
+            return ResponseEntity.ok(new ResponseModel("登录成功", 200));
+        } else {
+            return ResponseEntity.status(401).body(new ResponseModel("用户名或密码错误", 401));
+        }
+    }
+
+    @PostMapping("/register")
+    public ResponseEntity<ResponseModel> register(@RequestBody RegisterRequest registerRequest) {
+        boolean success = loginService.registerUser(registerRequest.getUsername(), registerRequest.getPassword(), registerRequest.getPhone());
+        if (success) {
+            return ResponseEntity.ok(new ResponseModel("注册成功", 200));
+        } else {
+            return ResponseEntity.status(400).body(new ResponseModel("用户名已存在", 400));
+        }
+    }
+}

+ 1 - 1
src/main/java/ai/huisuan/back/entity/UserInfo.java

@@ -31,7 +31,7 @@ public class UserInfo {
     private Instant createdAt;
 
     @Column(name = "phone")
-    private Integer phone;
+    private String phone;
 
     @Lob
     @Column(name = "role")

+ 4 - 2
src/main/java/ai/huisuan/back/entity/UserLoginInfo.java

@@ -15,6 +15,7 @@ import java.time.Instant;
 @Table(name = "user_login_info")
 public class UserLoginInfo {
     @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
     @Column(name = "id", nullable = false)
     private Integer id;
 
@@ -28,7 +29,8 @@ public class UserLoginInfo {
     private Instant lastLoginTime;
 
     @Size(max = 45)
-    @Column(name = "last_login_ip", length = 45)
-    private String lastLoginIp;
+    @NotNull
+    @Column(name = "login_ip", length = 45)
+    private String loginIp;
 
 }

+ 0 - 2
src/main/java/ai/huisuan/back/model/ChatRequest.java

@@ -3,8 +3,6 @@ package ai.huisuan.back.model;
 
 import lombok.Data;
 
-import java.util.List;
-
 
 /**
  * @author 对话接口请求DTO

+ 0 - 1
src/main/java/ai/huisuan/back/model/ChatResponse.java

@@ -5,7 +5,6 @@ import lombok.Data;
 import lombok.NoArgsConstructor;
 
 import java.time.Instant;
-import java.util.List;
 @Builder
 @Data
 @AllArgsConstructor

+ 0 - 1
src/main/java/ai/huisuan/back/model/History.java

@@ -4,7 +4,6 @@ package ai.huisuan.back.model;
 import lombok.Builder;
 import lombok.Data;
 
-import java.time.Instant;
 import java.util.List;
 
 @Data

+ 10 - 0
src/main/java/ai/huisuan/back/model/LoginRequest.java

@@ -0,0 +1,10 @@
+package ai.huisuan.back.model;
+
+import lombok.Data;
+
+@Data
+public class LoginRequest {
+    private String username;
+    private String password;
+    private String ip;
+}

+ 0 - 1
src/main/java/ai/huisuan/back/model/OllamaMessage.java

@@ -4,7 +4,6 @@ import lombok.AllArgsConstructor;
 import lombok.Data;
 import lombok.NoArgsConstructor;
 
-import java.time.Instant;
 @NoArgsConstructor
 @AllArgsConstructor
 @Data

+ 18 - 0
src/main/java/ai/huisuan/back/model/RegisterRequest.java

@@ -0,0 +1,18 @@
+package ai.huisuan.back.model;
+
+import lombok.Data;
+
+@Data
+public class RegisterRequest {
+    private String username;
+    private String password;
+    private String phone;
+
+    public String getPhone() {
+        return phone;
+    }
+
+    public void setPhone(String phone) {
+        this.phone = phone;
+    }
+}

+ 11 - 0
src/main/java/ai/huisuan/back/model/ResponseModel.java

@@ -0,0 +1,11 @@
+package ai.huisuan.back.model;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+
+@AllArgsConstructor
+@Data
+public class ResponseModel {
+    private String message;
+    private int status;
+}

+ 9 - 0
src/main/java/ai/huisuan/back/repository/UserLoginInfoRepository.java

@@ -0,0 +1,9 @@
+package ai.huisuan.back.repository;
+
+import ai.huisuan.back.entity.UserLoginInfo;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public interface UserLoginInfoRepository extends JpaRepository<UserLoginInfo, Integer> {
+}

+ 11 - 0
src/main/java/ai/huisuan/back/repository/UserRepository.java

@@ -0,0 +1,11 @@
+package ai.huisuan.back.repository;
+
+import ai.huisuan.back.entity.UserInfo;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public interface UserRepository extends JpaRepository<UserInfo, Integer> {
+    UserInfo findByUserName(String userName);
+    boolean existsByUserName(String userName);
+}

+ 1 - 2
src/main/java/ai/huisuan/back/service/ChatService.java

@@ -3,12 +3,10 @@ package ai.huisuan.back.service;
 // ChatService.java (Service Layer Interface)
 
 
-import ai.huisuan.back.entity.HistoryRecordInfo;
 import ai.huisuan.back.model.ChatRequest;
 import ai.huisuan.back.model.ChatResponse;
 import com.fasterxml.jackson.core.JsonProcessingException;
 import okhttp3.Request;
-import org.springframework.transaction.annotation.Transactional;
 import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
 
 import java.io.IOException;
@@ -22,4 +20,5 @@ public interface ChatService {
     ChatResponse completions(ChatRequest requestDTO) throws IOException;
 
     SseEmitter streamChat(ChatRequest requestDTO);
+
 }

+ 5 - 1
src/main/java/ai/huisuan/back/service/LoginService.java

@@ -1,4 +1,8 @@
 package ai.huisuan.back.service;
 
-public class LoginService {
+public interface LoginService {
+    boolean login(String username, String password, String ip);
+    boolean registerUser(String username, String password, String phone);
 }
+
+

+ 26 - 8
src/main/java/ai/huisuan/back/service/impl/ChatServiceImpl.java

@@ -1,5 +1,7 @@
 package ai.huisuan.back.service.impl;
 
+import ai.huisuan.back.entity.DialogueSessionInfo;
+import ai.huisuan.back.entity.HistoryRecordInfo;
 import ai.huisuan.back.model.*;
 import ai.huisuan.back.repository.DialogueSessionsRepository;
 import ai.huisuan.back.repository.HistoryRecordsRepository;
@@ -20,9 +22,6 @@ import java.io.IOException;
 import java.io.InputStreamReader;
 import java.time.Duration;
 import java.time.Instant;
-import java.time.format.DateTimeFormatter;
-import java.util.Date;
-import java.util.UUID;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 
@@ -45,7 +44,6 @@ public class ChatServiceImpl implements ChatService {
     private static final Duration READ_TIMEOUT = Duration.ofSeconds(600);
     private static final Duration WRITE_TIMEOUT = Duration.ofSeconds(300);
     private static final Duration SSE_TIMEOUT = Duration.ofMinutes(10);
-    private final DateTimeFormatter formatter = DateTimeFormatter.ISO_DATE_TIME;
 
     public ChatServiceImpl(ObjectMapper objectMapper,
                            DialogueSessionsRepository dialogueSessionsRepository,
@@ -62,6 +60,16 @@ public class ChatServiceImpl implements ChatService {
 
     @Override
     public Request buildOllamaRequest(ChatRequest requestDTO,boolean stream) throws JsonProcessingException {
+        // 保存数据
+        HistoryRecordInfo historyRecordInfo = new HistoryRecordInfo();
+        historyRecordInfo.setContent(requestDTO.getQuestion().getContent());
+        historyRecordInfo.setRole(requestDTO.getQuestion().getRole());
+        historyRecordInfo.setTimestamp(Instant.now());
+        DialogueSessionInfo dialogueSessionInfo = dialogueSessionsRepository.findById(requestDTO.getSessionId()).orElse(null);
+        historyRecordInfo.setSession(dialogueSessionInfo);
+        historyRecordsRepository.save(historyRecordInfo);
+
+        // 构建请求体
         List<OllamaMessage> ollamaMessages = new ArrayList<>();
 
         List<Message> messages = requestDTO.getHistory().getMessages();
@@ -98,12 +106,22 @@ public class ChatServiceImpl implements ChatService {
             History history =chatRequest.getHistory();
             history.getMessages().add(chatRequest.getQuestion());
             history.getMessages().add(new Message());
-
             ChatResponse responseDTO = new ChatResponse();
             responseDTO.setAnswer(message);
             responseDTO.setModel(message.getRole());
             responseDTO.setCreatedAt(message.getTimestamp());
             responseDTO.setHistory(history);
+
+            // 保存数据到数据库
+            HistoryRecordInfo historyRecordInfo = new HistoryRecordInfo();
+            historyRecordInfo.setContent(responseBody);
+            historyRecordInfo.setRole(responseDTO.getAnswer().getRole());
+            historyRecordInfo.setTimestamp(Instant.now());
+            DialogueSessionInfo dialogueSessionInfo = dialogueSessionsRepository.findById(chatRequest.getSessionId()).orElse(null);
+            historyRecordInfo.setSession(dialogueSessionInfo);
+            historyRecordsRepository.save(historyRecordInfo);
+
+            // 返回结果
             return responseDTO;
     }
 
@@ -130,6 +148,7 @@ public class ChatServiceImpl implements ChatService {
         return emitter;
     }
 
+
     private void executeStreamingRequest(SseEmitter emitter, ChatRequest requestDTO) throws JsonProcessingException {
         Request request = buildOllamaRequest(requestDTO,true);
         client.newCall(request).enqueue(new Callback() {
@@ -146,7 +165,7 @@ public class ChatServiceImpl implements ChatService {
                     OllamaChatResponse chunk = objectMapper.readValue(line, OllamaChatResponse.class);
                     System.out.println(chunk);
                     fullResponse.append(chunk.getMessage().getContent());
-                    processChunk( requestDTO, emitter, chunk);
+                    processChunk(emitter, chunk);
                     if (chunk.isDone()) {
                         break;
                     }
@@ -164,8 +183,7 @@ public class ChatServiceImpl implements ChatService {
         });
     }
 
-    private void processChunk( ChatRequest requestDTO,
-                              SseEmitter emitter, OllamaChatResponse chunk) throws IOException {
+    private void processChunk(SseEmitter emitter, OllamaChatResponse chunk) throws IOException {
         ChatStreamResponse chatStreamResponse = new ChatStreamResponse();
         chatStreamResponse.setContent(chunk.getMessage().getContent());
         chatStreamResponse.setDone(chunk.isDone());

+ 56 - 0
src/main/java/ai/huisuan/back/service/impl/LoginServiceImpl.java

@@ -0,0 +1,56 @@
+package ai.huisuan.back.service.impl;
+
+import ai.huisuan.back.entity.UserLoginInfo;
+import ai.huisuan.back.entity.UserInfo;
+import ai.huisuan.back.repository.UserRepository;
+import ai.huisuan.back.repository.UserLoginInfoRepository;
+import ai.huisuan.back.service.LoginService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.stereotype.Service;
+import jakarta.transaction.Transactional;
+
+import java.time.Instant;
+
+@Service
+public class LoginServiceImpl implements LoginService {
+
+    @Autowired
+    private UserRepository userRepository;
+
+    @Autowired
+    private UserLoginInfoRepository userLoginInfoRepository;
+
+    @Autowired
+    private PasswordEncoder passwordEncoder;
+
+    @Override
+    @Transactional
+    public boolean login(String username, String password, String ip) {
+        UserInfo user = userRepository.findByUserName(username);
+        if (user != null && passwordEncoder.matches(password, user.getPasswordHash())) {
+            UserLoginInfo loginInfo = new UserLoginInfo();
+            loginInfo.setUser(user);
+            loginInfo.setLastLoginTime(Instant.now());
+            loginInfo.setLoginIp(ip); // 设置登录 IP
+            userLoginInfoRepository.save(loginInfo);
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public boolean registerUser(String username, String password, String phone) {
+        if (userRepository.existsByUserName(username)) {
+            return false;
+        }
+        UserInfo newUser = new UserInfo();
+        newUser.setUserName(username);
+        newUser.setPasswordHash(passwordEncoder.encode(password));
+        newUser.setCreatedAt(Instant.now());
+        newUser.setPhone(phone);
+        newUser.setRole("普通用户");
+        userRepository.save(newUser);
+        return true;
+    }
+}

+ 0 - 13
src/test/java/ai/huisuan/back/BackApplicationTests.java

@@ -1,13 +0,0 @@
-package ai.huisuan.back;
-
-import org.junit.jupiter.api.Test;
-import org.springframework.boot.test.context.SpringBootTest;
-
-@SpringBootTest
-class BackApplicationTests {
-
-    @Test
-    void contextLoads() {
-    }
-
-}