Browse Source

(*)增加ftl模板的解析和异常处理;

linxiaobin 5 months ago
parent
commit
b60652d442

+ 7 - 0
pom.xml

@@ -102,6 +102,13 @@
             <artifactId>jakarta.servlet-api</artifactId>
             <artifactId>jakarta.servlet-api</artifactId>
             <scope>provided</scope>
             <scope>provided</scope>
         </dependency>
         </dependency>
+
+        <!-- freemarker 模板引擎 -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-freemarker</artifactId>
+            <version>3.2.6</version>
+        </dependency>
     </dependencies>
     </dependencies>
 
 
 
 

+ 26 - 0
src/main/java/com/yango/javaailangchain4j/config/FreeMarkerConfig.java

@@ -0,0 +1,26 @@
+package com.yango.javaailangchain4j.config;
+
+import freemarker.core.TemplateClassResolver;
+import freemarker.template.ObjectWrapper;
+import org.springframework.context.annotation.Bean;
+
+@org.springframework.context.annotation.Configuration
+public class FreeMarkerConfig {
+
+    @Bean(name = "freeMarkerConfiguration")
+    public freemarker.template.Configuration freeMarkerConfiguration() {
+        freemarker.template.Configuration configuration = new freemarker.template.Configuration(freemarker.template.Configuration.VERSION_2_3_32);
+        configuration.setNamingConvention(freemarker.template.Configuration.AUTO_DETECT_NAMING_CONVENTION);
+        configuration.setObjectWrapper(ObjectWrapper.DEFAULT_WRAPPER);
+        configuration.setDefaultEncoding("UTF-8");
+        configuration.setNumberFormat("0.##");
+        configuration.setTemplateExceptionHandler(freemarker.template.TemplateExceptionHandler.RETHROW_HANDLER);
+        configuration.setLogTemplateExceptions(false);
+        configuration.setWrapUncheckedExceptions(true);
+        configuration.setFallbackOnNullLoopVariable(false);
+        configuration.setAPIBuiltinEnabled(false);
+        configuration.setNewBuiltinClassResolver(TemplateClassResolver.SAFER_RESOLVER);
+
+        return configuration;
+    }
+}

+ 40 - 3
src/main/java/com/yango/javaailangchain4j/controller/GpsresEvaluationController.java

@@ -3,10 +3,16 @@ package com.yango.javaailangchain4j.controller;
 
 
 import com.yango.javaailangchain4j.assistant.SeparateChatAssistant;
 import com.yango.javaailangchain4j.assistant.SeparateChatAssistant;
 import com.yango.javaailangchain4j.dto.EvaluationPsren;
 import com.yango.javaailangchain4j.dto.EvaluationPsren;
+import com.yango.javaailangchain4j.service.TemplateProcessingService;
 import com.yango.javaailangchain4j.utils.Result;
 import com.yango.javaailangchain4j.utils.Result;
+import freemarker.template.TemplateException;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.bind.annotation.*;
 
 
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
 @RestController
 @RestController
 @RequestMapping("/api")
 @RequestMapping("/api")
 @CrossOrigin(origins = "*")
 @CrossOrigin(origins = "*")
@@ -14,17 +20,48 @@ public class GpsresEvaluationController {
     @Autowired
     @Autowired
     private SeparateChatAssistant separateChatAssistant;
     private SeparateChatAssistant separateChatAssistant;
 
 
+    @Autowired
+    private TemplateProcessingService templateProcessingService;
+
+
+    // // fixme:从数据库获取模板的方法(你需要根据实际情况实现)
+    private String getTemplateFromDatabase(String stepName) {
+        // 临时返回示例模板字符串
+        return "现在学生在${courseName}课程中的${stepName}环节,Goal是${goal}。\n"
+                + "学生的答案是${studentAnswer},\n"
+                + "标准答案是${standardAnswer}。\n"
+                + "答案不要再把学生答案和标准答案复述一遍了,以纯文本格式返回。\n"
+                + "根据标准答案和学生答案,给出该环节的评分和建议,总分${score}分。";
+    }
+
     @CrossOrigin(origins = "*")
     @CrossOrigin(origins = "*")
     @PostMapping( "/psrenEvalution")
     @PostMapping( "/psrenEvalution")
-    public Result Pevaluation(@RequestBody EvaluationPsren evaluationPsren) {
+    public Result Pevaluation(@RequestBody EvaluationPsren evaluationPsren) throws IOException, TemplateException {
         String courseName = evaluationPsren.getCourseName();
         String courseName = evaluationPsren.getCourseName();
         String stepName = evaluationPsren.getStepName();
         String stepName = evaluationPsren.getStepName();
         String goal = evaluationPsren.getGoal();
         String goal = evaluationPsren.getGoal();
         String standardAnswer = evaluationPsren.getStandardAnswer();
         String standardAnswer = evaluationPsren.getStandardAnswer();
         String studentAnswer = evaluationPsren.getStudentAnswer();
         String studentAnswer = evaluationPsren.getStudentAnswer();
         int score = evaluationPsren.getScore();
         int score = evaluationPsren.getScore();
-        String userMessage = String.format("现在学生在%s课程中的%s环节,Goal是%s,学生的答案是%s,标准答案是%s,答案不要再把学生答案和标准答案复述一遍了,以纯文本格式返回" +
-                "根据标准答案和学生答案,给出该环节的评分和建议,总分%d分",courseName,stepName,goal,studentAnswer,standardAnswer, score);
+        /*String userMessage = String.format("现在学生在%s课程中的%s环节,Goal是%s,学生的答案是%s,标准答案是%s,答案不要再把学生答案和标准答案复述一遍了,以纯文本格式返回" +
+                "根据标准答案和学生答案,给出该环节的评分和建议,总分%d分",courseName,stepName,goal,studentAnswer,standardAnswer, score);*/
+
+        // 准备模板数据
+        Map<String, Object> templateData = new HashMap<>();
+        templateData.put("courseName", courseName);
+        templateData.put("stepName", stepName);
+        templateData.put("goal", goal);
+        templateData.put("studentAnswer", studentAnswer);
+        templateData.put("standardAnswer", standardAnswer);
+        templateData.put("score", score);
+
+        // fixme:从数据库读取的模板字符串
+        String templateString = getTemplateFromDatabase(stepName); // 你的数据库读取方法
+
+        // 使用模板处理服务处理模板
+        String userMessage = templateProcessingService.processTemplate(
+                templateString, templateData, "evaluationTemplate-" + stepName);
+
         String s = separateChatAssistant.chat5(
         String s = separateChatAssistant.chat5(
                 evaluationPsren.getMemoryId(),
                 evaluationPsren.getMemoryId(),
                 evaluationPsren.getCourseName(),
                 evaluationPsren.getCourseName(),

+ 16 - 0
src/main/java/com/yango/javaailangchain4j/exception/GlobalExceptionHandler.java

@@ -0,0 +1,16 @@
+package com.yango.javaailangchain4j.exception;
+
+import com.yango.javaailangchain4j.utils.Result;
+import com.yango.javaailangchain4j.utils.ResultCode;
+import org.springframework.util.StringUtils;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+
+@RestControllerAdvice
+public class GlobalExceptionHandler {
+    @ExceptionHandler(Exception.class)
+    public Result handleException(Exception e) {
+        e.printStackTrace();
+        return Result.fail(ResultCode.FAIL, StringUtils.hasLength(e.getMessage()) ? e.getMessage() : "操作失败");
+    }
+}

+ 50 - 0
src/main/java/com/yango/javaailangchain4j/service/TemplateProcessingService.java

@@ -0,0 +1,50 @@
+package com.yango.javaailangchain4j.service;
+
+import freemarker.template.Configuration;
+import freemarker.template.Template;
+import freemarker.template.TemplateException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.stereotype.Service;
+import org.springframework.ui.freemarker.FreeMarkerTemplateUtils;
+
+import java.io.IOException;
+import java.util.Map;
+
+@Service
+public class TemplateProcessingService {
+
+    @Autowired
+    @Qualifier("freeMarkerConfiguration")
+    private Configuration freemarkerConfig;
+
+    /**
+     * 使用FreeMarker模板引擎处理模板字符串
+     *
+     * @param templateString 模板字符串
+     * @param templateData 模板数据
+     * @param templateName 模板名称,用于调试
+     * @return 处理后的字符串
+     * @throws IOException IO异常
+     * @throws TemplateException 模板异常
+     */
+    public String processTemplate(String templateString, Map<String, Object> templateData, String templateName)
+            throws IOException, TemplateException {
+        Template template = new Template(templateName, templateString, freemarkerConfig);
+        return FreeMarkerTemplateUtils.processTemplateIntoString(template, templateData);
+    }
+
+    /**
+     * 使用FreeMarker模板引擎处理模板字符串(使用默认模板名称)
+     *
+     * @param templateString 模板字符串
+     * @param templateData 模板数据
+     * @return 处理后的字符串
+     * @throws IOException IO异常
+     * @throws TemplateException 模板异常
+     */
+    public String processTemplate(String templateString, Map<String, Object> templateData)
+            throws IOException, TemplateException {
+        return processTemplate(templateString, templateData, "defaultTemplate");
+    }
+}

+ 6 - 0
src/main/resources/templates/evaluation-template.ftl

@@ -0,0 +1,6 @@
+现在学生在${courseName}课程中的${stepName}环节,
+Goal是${goal},
+学生的答案是${studentAnswer},
+标准答案是${standardAnswer},
+答案不要再把学生答案和标准答案复述一遍了,
+以纯文本格式返回根据标准答案和学生答案,给出该环节的评分和建议,总分${score}分

+ 145 - 0
src/test/java/com/yango/javaailangchain4j/service/TemplateProcessingServiceTest.java

@@ -0,0 +1,145 @@
+package com.yango.javaailangchain4j.service;
+
+import freemarker.core.TemplateClassResolver;
+import freemarker.template.Configuration;
+import freemarker.template.ObjectWrapper;
+import freemarker.template.TemplateException;
+import freemarker.template.TemplateExceptionHandler;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.springframework.ui.freemarker.FreeMarkerTemplateUtils;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.ArgumentMatchers.*;
+import static org.mockito.Mockito.*;
+
+class TemplateProcessingServiceTest {
+
+    @Mock
+    private Configuration freemarkerConfig;
+
+    @InjectMocks
+    private TemplateProcessingService templateProcessingService;
+
+    @BeforeEach
+    void setUp() {
+        MockitoAnnotations.openMocks(this);
+
+        // 设置更多 Configuration 属性,避免执行时报错
+        when(freemarkerConfig.getIncompatibleImprovements())
+                .thenReturn(Configuration.VERSION_2_3_32);
+        // 自动检测命名约定
+        when(freemarkerConfig.getNamingConvention())
+                .thenReturn(Configuration.AUTO_DETECT_NAMING_CONVENTION);
+
+        when(freemarkerConfig.getLocale()).thenReturn(java.util.Locale.getDefault());
+        when(freemarkerConfig.getTemplateExceptionHandler())
+                .thenReturn(TemplateExceptionHandler.RETHROW_HANDLER);
+
+        // 设置必要的配置避免执行时报错
+        when(freemarkerConfig.getObjectWrapper())
+                .thenReturn(ObjectWrapper.DEFAULT_WRAPPER);
+        when(freemarkerConfig.getDefaultEncoding())
+                .thenReturn("UTF-8");
+        when(freemarkerConfig.getWrapUncheckedExceptions())
+                .thenReturn(true);
+        when(freemarkerConfig.getFallbackOnNullLoopVariable())
+                .thenReturn(false);
+        when(freemarkerConfig.getNewBuiltinClassResolver())
+                .thenReturn(TemplateClassResolver.SAFER_RESOLVER);
+        when(freemarkerConfig.getNumberFormat())
+                .thenReturn("0.##");
+    }
+
+    @Test
+    void testProcessTemplateWithCustomTemplateName() throws IOException, TemplateException {
+        // 准备测试数据
+        String templateString = "Hello ${name}!";
+        Map<String, Object> templateData = new HashMap<>();
+        templateData.put("name", "World");
+        String templateName = "testTemplate";
+
+        // 执行测试
+        String result = templateProcessingService.processTemplate(templateString, templateData, templateName);
+
+        // 验证结果
+        assertNotNull(result);
+    }
+
+    @Test
+    void testProcessTemplateWithDefaultTemplateName() throws IOException, TemplateException {
+        // 准备测试数据
+        String templateString = "Hello ${name}!";
+        Map<String, Object> templateData = new HashMap<>();
+        templateData.put("name", "World");
+
+        // 执行测试
+        String result = templateProcessingService.processTemplate(templateString, templateData);
+
+        // 验证结果
+        assertNotNull(result);
+    }
+
+    @Test
+    void testProcessTemplateWithMultipleVariables() throws IOException, TemplateException {
+        // 准备测试数据
+        String templateString = "学生${studentName}在${courseName}课程中获得了${score}分";
+        Map<String, Object> templateData = new HashMap<>();
+        templateData.put("studentName", "张三");
+        templateData.put("courseName", "数学");
+        templateData.put("score", 95);
+
+        // 执行测试
+        String result = templateProcessingService.processTemplate(templateString, templateData);
+
+        // 验证结果包含预期内容
+        assertNotNull(result);
+    }
+
+    @Test
+    void testProcessTemplateWithEmptyTemplate() throws IOException, TemplateException {
+        // 准备测试数据
+        String templateString = "";
+        Map<String, Object> templateData = new HashMap<>();
+
+        // 执行测试
+        String result = templateProcessingService.processTemplate(templateString, templateData);
+
+        // 验证结果
+        assertEquals("", result);
+    }
+
+    @Test
+    void testProcessTemplateWithNullData() throws IOException, TemplateException {
+        // 准备测试数据
+        String templateString = "Static text";
+
+        // 执行测试
+        String result = templateProcessingService.processTemplate(templateString, null);
+
+        // 验证结果
+        assertEquals("Static text", result);
+    }
+
+    @Test
+    void testProcessTemplateWithNewlines() throws IOException, TemplateException {
+        // 准备测试数据
+        String templateString = "Line 1\nLine 2\n${variable}";
+        Map<String, Object> templateData = new HashMap<>();
+        templateData.put("variable", "Line 3");
+
+        // 执行测试
+        String result = templateProcessingService.processTemplate(templateString, templateData);
+
+        // 验证结果包含换行符
+        assertNotNull(result);
+        assertTrue(result.contains("\n"));
+    }
+}