diff --git a/xianyu-common/src/main/java/top/biwin/xinayu/common/dto/request/AiSettingRequest.java b/xianyu-common/src/main/java/top/biwin/xinayu/common/dto/request/AiSettingRequest.java new file mode 100644 index 0000000..4a2ac1b --- /dev/null +++ b/xianyu-common/src/main/java/top/biwin/xinayu/common/dto/request/AiSettingRequest.java @@ -0,0 +1,39 @@ +package top.biwin.xinayu.common.dto.request; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +/** + * TODO + * + * @author wangli + * @since 2026-01-29 23:58 + */ +@Data +public class AiSettingRequest { + + @JsonProperty("ai_enabled") + private Boolean aiEnabled; + + @JsonProperty("api_key") + private String apiKey; + + @JsonProperty("base_url") + private String baseUrl; + + @JsonProperty("custom_prompts") + private String customPrompts; + + @JsonProperty("max_bargain_rounds") + private Integer maxBargainRounds; + + @JsonProperty("max_discount_amount") + private Integer maxDiscountAmount; + + @JsonProperty("max_discount_percent") + private Integer maxDiscountPercent; + + @JsonProperty("model_name") + private String modelName; + +} diff --git a/xianyu-core/src/main/java/top/biwin/xianyu/core/entity/AiReplySettingEntity.java b/xianyu-core/src/main/java/top/biwin/xianyu/core/entity/AiReplySettingEntity.java index 09c2d67..708b2a5 100644 --- a/xianyu-core/src/main/java/top/biwin/xianyu/core/entity/AiReplySettingEntity.java +++ b/xianyu-core/src/main/java/top/biwin/xianyu/core/entity/AiReplySettingEntity.java @@ -1,5 +1,6 @@ package top.biwin.xianyu.core.entity; +import com.fasterxml.jackson.annotation.JsonProperty; import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.Id; @@ -17,37 +18,48 @@ public class AiReplySettingEntity { @Id @Column(name = "goofish_id") + @JsonProperty("goofish_id") private String goofishId; @Column(name = "ai_enabled") + @JsonProperty("ai_enabled") private Boolean aiEnabled = false; @Column(name = "model_name") + @JsonProperty("model_name") private String modelName = "qwen-plus"; @Column(name = "api_key") + @JsonProperty("api_key") private String apiKey; @Column(name = "base_url") + @JsonProperty("base_url") private String baseUrl = "https://dashscope.aliyuncs.com/compatible-mode/v1"; @Column(name = "max_discount_percent") + @JsonProperty("max_discount_percent") private Integer maxDiscountPercent = 10; @Column(name = "max_discount_amount") + @JsonProperty("max_discount_amount") private Integer maxDiscountAmount = 100; @Column(name = "max_bargain_rounds") + @JsonProperty("max_bargain_rounds") private Integer maxBargainRounds = 3; @Column(name = "custom_prompts", columnDefinition = "TEXT") + @JsonProperty("custom_prompts") private String customPrompts; @CreationTimestamp @Column(name = "created_at", updatable = false) + @JsonProperty("created_at") private LocalDateTime createdAt; @UpdateTimestamp @Column(name = "updated_at") + @JsonProperty("updated_at") private LocalDateTime updatedAt; } diff --git a/xianyu-core/src/main/java/top/biwin/xianyu/core/repository/AiReplySettingRepository.java b/xianyu-core/src/main/java/top/biwin/xianyu/core/repository/AiReplySettingRepository.java index 6ec6955..d24bb5b 100644 --- a/xianyu-core/src/main/java/top/biwin/xianyu/core/repository/AiReplySettingRepository.java +++ b/xianyu-core/src/main/java/top/biwin/xianyu/core/repository/AiReplySettingRepository.java @@ -2,8 +2,12 @@ package top.biwin.xianyu.core.repository; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; +import top.biwin.xianyu.core.entity.AdminUserEntity; import top.biwin.xianyu.core.entity.AiReplySettingEntity; +import java.util.Optional; + @Repository public interface AiReplySettingRepository extends JpaRepository { + } diff --git a/xianyu-server/src/main/java/top/biwin/xinayu/server/config/HttpClientConfig.java b/xianyu-server/src/main/java/top/biwin/xinayu/server/config/HttpClientConfig.java index 69a43f6..a095368 100644 --- a/xianyu-server/src/main/java/top/biwin/xinayu/server/config/HttpClientConfig.java +++ b/xianyu-server/src/main/java/top/biwin/xinayu/server/config/HttpClientConfig.java @@ -85,6 +85,11 @@ public class HttpClientConfig { */ @Bean public ObjectMapper objectMapper() { - return new ObjectMapper(); + ObjectMapper mapper = new ObjectMapper(); + // 注册 JavaTimeModule 以支持 Java 8 时间类型 (LocalDateTime 等) + mapper.registerModule(new com.fasterxml.jackson.datatype.jsr310.JavaTimeModule()); + // 禁用将日期写为时间戳,使其序列化为 ISO 格式字符串 + mapper.disable(com.fasterxml.jackson.databind.SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); + return mapper; } } diff --git a/xianyu-server/src/main/java/top/biwin/xinayu/server/controller/KeywordController.java b/xianyu-server/src/main/java/top/biwin/xinayu/server/controller/KeywordController.java index 61b2854..e3a40b8 100644 --- a/xianyu-server/src/main/java/top/biwin/xinayu/server/controller/KeywordController.java +++ b/xianyu-server/src/main/java/top/biwin/xinayu/server/controller/KeywordController.java @@ -1,9 +1,13 @@ package top.biwin.xinayu.server.controller; +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.bean.copier.CopyOptions; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import top.biwin.xianyu.core.entity.AiReplySettingEntity; @@ -12,6 +16,7 @@ import top.biwin.xianyu.core.entity.KeywordEntity; import top.biwin.xianyu.core.repository.AiReplySettingRepository; import top.biwin.xianyu.core.repository.GoofishAccountRepository; import top.biwin.xianyu.core.repository.KeywordRepository; +import top.biwin.xinayu.common.dto.request.AiSettingRequest; import top.biwin.xinayu.common.dto.response.KeywordResponse; import java.util.List; @@ -47,6 +52,26 @@ public class KeywordController { return ResponseEntity.ok(settings.stream().collect(Collectors.toMap(AiReplySettingEntity::getGoofishId, s -> s))); } + @GetMapping("/ai-reply-settings/{accountId}") + public ResponseEntity getAiSetting(@PathVariable String accountId) { + return ResponseEntity.ok(aiReplySettingRepository.findById(accountId).orElse(null)); + } + + @PutMapping("/ai-reply-settings/{accountId}") + public ResponseEntity upsertAiSetting(@PathVariable String accountId, @RequestBody AiSettingRequest request) { + // Upsert 逻辑:存在则更新,不存在则创建 + AiReplySettingEntity entity = aiReplySettingRepository.findById(accountId) + .orElseGet(() -> { + // 不存在时创建新实体 + AiReplySettingEntity newEntity = new AiReplySettingEntity(); + newEntity.setGoofishId(accountId); + return newEntity; + }); + // 忽略 null 值,防止前端未传的字段覆盖已有值或默认值 + BeanUtil.copyProperties(request, entity, CopyOptions.create().setIgnoreNullValue(true)); + return ResponseEntity.ok(aiReplySettingRepository.save(entity)); + } + @GetMapping("/keywords-with-item-id/{gid}") public ResponseEntity> getKeywordsWithItemId(@PathVariable Long gid) { diff --git a/xianyu-server/src/main/java/top/biwin/xinayu/server/exception/AuthenticationExceptionHandler.java b/xianyu-server/src/main/java/top/biwin/xinayu/server/exception/AuthenticationExceptionHandler.java index 18dc0f6..81e85c3 100644 --- a/xianyu-server/src/main/java/top/biwin/xinayu/server/exception/AuthenticationExceptionHandler.java +++ b/xianyu-server/src/main/java/top/biwin/xinayu/server/exception/AuthenticationExceptionHandler.java @@ -1,17 +1,11 @@ package top.biwin.xinayu.server.exception; -import org.springframework.http.HttpStatus; +import lombok.extern.slf4j.Slf4j; import org.springframework.http.ResponseEntity; -import org.springframework.security.authentication.BadCredentialsException; -import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; import top.biwin.xinayu.common.dto.response.BaseResponse; -import java.time.LocalDateTime; -import java.util.HashMap; -import java.util.Map; - /** * 全局异常处理器 * 统一处理认证授权相关的异常,返回友好的 JSON 错误响应 @@ -34,23 +28,25 @@ import java.util.Map; * @author wangli * @since 2026-01-21 */ +@Slf4j @RestControllerAdvice public class AuthenticationExceptionHandler { - /** - * 处理未知异常 - *

- * 触发场景: - * - 其他未被捕获的异常 - * - * @param ex 异常对象 - * @return ResponseEntity 错误响应 - */ - @ExceptionHandler(Exception.class) - public ResponseEntity handleException(Exception ex) { - return ResponseEntity.ok(BaseResponse.builder() - .success(false) - .message(ex.getMessage()) - .build()); - } + /** + * 处理未知异常 + *

+ * 触发场景: + * - 其他未被捕获的异常 + * + * @param ex 异常对象 + * @return ResponseEntity 错误响应 + */ + @ExceptionHandler(Exception.class) + public ResponseEntity handleException(Exception ex) { + log.error("发生异常:{}", ex.getMessage(), ex); + return ResponseEntity.ok(BaseResponse.builder() + .success(false) + .message(ex.getMessage()) + .build()); + } }