springboot项目调用本地和在线deepseek详解

deepseek 日益火爆,是不是很多小伙伴跟我一样很好奇,都想在自己本地部署一个 deepseek 来玩一玩呢?当然使用 ollama 部署本地 deepseek 很方便,但是有没有小伙伴想跟我一样,自己使用java写一个脚本来实现下呢?这样不仅可以直接部署到你的项目中,而且遇到你不会想问题,直接切到 deepseek 菜单,直接提问就可以调用,是不是很酷炫呢?那么废话不多说,咱们开始吧!

下图是我自己设计的 JS智能问答系统:
这是我项目里deepseek截图

1. Controller 层

package com.management.web.controller.myai;

import com.management.ai.service.IOllamaModelService;
import com.management.ai.service.IOllamaService;
import com.management.ai.service.IOllamaSessionService;
import com.management.common.annotation.Log;
import com.management.common.core.controller.BaseController;
import com.management.common.core.domain.AjaxResult;
import com.management.common.core.domain.ai.OllamaModel;
import com.management.common.core.domain.ai.OllamaQianWen;
import com.management.common.core.domain.ai.OllamaSession;
import com.management.common.enums.BusinessStatus;
import com.management.common.enums.BusinessType;
import com.management.common.enums.OperationResultEnum;
import com.management.common.utils.ShiroUtils;
import com.management.common.utils.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.util.ObjectUtils;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

import javax.validation.Valid;
import java.util.List;

@Controller
@RequestMapping("/ollama/ai")
public class OllamaController extends BaseController {
	//调用本地 deepseek 地址
    private String LOCALHOST_BASE_URL = "http://localhost:11434/api/generate";
    private String LOCALHOST_USE_MODEL = "deepseek-r1:14b";
    //调用在线 deepseek 地址
    private String ONLINE_BASE_URL = "https://api.deepseek.com/chat/completions";
    private String ONLINE_USE_MODEL = "deepseek-chat";
    private String ONLINE_APIKEY = "你deepseek的APIKEY";
    @Autowired
    private IOllamaService ollamaService;
    @Autowired
    private IOllamaSessionService ollamaSessionService;
    @Autowired
    private IOllamaModelService ollamaModelService;

    private String prefix = "ai";
	
	//前端页面
    @GetMapping()
    public String toIndex(ModelMap modelMap) {
        return prefix + "/index";
    }

    /**
     * 查询会话列表
     */
    @PostMapping("/list")
    @ResponseBody
    public List<OllamaSession> list(OllamaSession ollamaSession) {
        return ollamaSessionService.selectOllamaSessionList(ollamaSession);
    }

    /**
     * 查询会话列表
     */
    @PostMapping("/getHistoryList")
    @ResponseBody
    public List<OllamaQianWen> getHistoryList(OllamaQianWen ollamaQianWen) {
        return ollamaService.selectListBySessionId(ollamaQianWen);
    }

    /**
     * 跳转新增会话
     */
    @GetMapping("/add")
    public String add(ModelMap modelMap) {
        List<OllamaModel> modelList = ollamaModelService.queryModelList();
        modelMap.put("modelList", modelList);
        return prefix + "/add";
    }

    /**
     * 新增会话
     */
    @Log(title = "新增会话", businessType = BusinessType.INSERT)
    @PostMapping("/add")
    @ResponseBody
    public AjaxResult addSave(@ModelAttribute @Valid OllamaSession ollamaSession, BindingResult result) {
        // 如果有错误,则返回错误信息
        if (result.hasErrors()) {
            return AjaxResult.error(result.getFieldError().getDefaultMessage());
        }
        String[] modelArr = ollamaSession.getMainAiModel().split("——");
        ollamaSession.setMainAiModel(modelArr[0]);
        ollamaSession.setUseModel(modelArr[1]);
        String msg = ollamaModelService.verificationModel(ollamaSession);
        if (!BusinessStatus.SUCCESS.toString().equals(msg)) {
            return AjaxResult.error(msg);
        }
        return toAjax(ollamaSessionService.addSave(ollamaSession));
    }

    /**
     * 跳转修改会话
     */
    @GetMapping("/edit/{id}")
    public String edit(@PathVariable("id") Long id, ModelMap modelMap) {
        OllamaSession ollamaSession = ollamaSessionService.selectOllamaSessionById(id);
        ollamaSession.setMainAiModel(ollamaSession.getMainAiModel() + "——" + ollamaSession.getUseModel());
        modelMap.put("ollamaSession", ollamaSession);
        List<OllamaModel> modelList = ollamaModelService.queryModelList();
        modelMap.put("modelList", modelList);
        return prefix + "/edit";
    }

    /**
     * 修改会话
     */
    @Log(title = "修改会话", businessType = BusinessType.UPDATE)
    @PostMapping("/edit")
    @ResponseBody
    public AjaxResult editSave(@ModelAttribute @Valid OllamaSession ollamaSession, BindingResult result) {
        // 如果有错误,则返回错误信息
        if (result.hasErrors()) {
            return AjaxResult.error(result.getFieldError().getDefaultMessage());
        }
        String[] modelArr = ollamaSession.getMainAiModel().split("——");
        ollamaSession.setMainAiModel(modelArr[0]);
        ollamaSession.setUseModel(modelArr[1]);
        String msg = ollamaModelService.verificationModel(ollamaSession);
        if (!BusinessStatus.SUCCESS.toString().equals(msg)) {
            return AjaxResult.error(msg);
        }
        return toAjax(ollamaSessionService.updateOllamaSession(ollamaSession));
    }

    /**
     * 删除会话
     */
    @Log(title = "删除会话", businessType = BusinessType.DELETE)
    @PostMapping( "/remove")
    @ResponseBody
    public AjaxResult remove(String ids) {
        return toAjax(ollamaSessionService.deleteSessionByIds(ids));
    }
	
	/*
		普通调用 deepseek
			即非流式输出 deepseek 答案,即问完问题后,deepseek 在后端加载完数据后,一次性输出给前端,缺点:不美观、交互性不好
	*/
    @PostMapping("/chat/v1")
    @ResponseBody
    public OllamaQianWen chatV1(OllamaQianWen ollamaQianWen) {
        OllamaQianWen responseBody = null;
        if (StringUtils.isEmpty(ollamaQianWen.getPrompt()) || ObjectUtils.isEmpty(ollamaQianWen.getSessionId())) {
            return responseBody;
        }
        OllamaModel ollamaModel = getOllamaModel(ollamaQianWen);
        ollamaQianWen.setIsOnline(ollamaModel.getIsOnline());
        ollamaQianWen.setRequestUrl(ollamaModel.getRequestUrl());
        ollamaQianWen.setUseModel(ollamaModel.getUseModel());
        responseBody = ollamaService.invokeDeepSeekV1(ollamaQianWen, ONLINE_APIKEY);
        return responseBody;
    }
	
	/*
		流式调用 deepseek
			即流式输出 deepseek 答案,deepseek 反馈多少,前端页面展示多少,优点:交互性好,缺点:不好控制前端展示格式
	*/
    @PostMapping(value = "/chat/v2", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    @ResponseBody
    public SseEmitter chatV2(@RequestBody OllamaQianWen ollamaQianWen) {
    	// 使用 SseEmitter 流式输出
        SseEmitter responseBody = null;
        if (StringUtils.isEmpty(ollamaQianWen.getPrompt()) || ObjectUtils.isEmpty(ollamaQianWen.getSessionId())) {
            return responseBody;
        }
        //-------------------由于我要收集 deepseek 问题和答案,所以需要存储到数据库中---------------------
        String ip = ShiroUtils.getIp();
        Long loginUserId = ShiroUtils.getLoginUserId();
        OllamaModel ollamaModel = getOllamaModel(ollamaQianWen);
        ollamaQianWen.setIsOnline(ollamaModel.getIsOnline());
        ollamaQianWen.setRequestUrl(ollamaModel.getRequestUrl());
        ollamaQianWen.setUseModel(ollamaModel.getUseModel());
        ollamaQianWen.setUserId(loginUserId);
        ollamaQianWen.setRequestIp(ip);
        //----------------------------------------
        responseBody = ollamaService.invokeDeepSeekV2(ollamaQianWen, ONLINE_APIKEY);
        return responseBody;
    }

    private OllamaModel getOllamaModel(OllamaQianWen ollamaQianWen) {
        //根据 sessionId 查询出使用的模型
        OllamaSession ollamaSession = ollamaSessionService.selectOllamaSessionById(ollamaQianWen.getSessionId());
        OllamaModel ollamaModel = ollamaModelService.selectByModel(ollamaSession.getMainAiModel(), ollamaSession.getUseModel());
        if (ObjectUtils.isEmpty(ollamaModel)) {
            throw new RuntimeException(OperationResultEnum.OLLAMA_MODEL_NOT_EXIST.getInfo());
        }
        return ollamaModel;
    }
}

2. Service 层

package com.management.ai.service;

import com.management.common.core.domain.ai.OllamaQianWen;
import com.management.common.core.service.IBaseService;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import reactor.core.publisher.Flux;
import java.util.List;

public interface IOllamaService extends IBaseService<OllamaQianWen> {

    /**
     * 普通响应
     */
    OllamaQianWen invokeDeepSeekV1(OllamaQianWen ollamaQianWen, String apiKey);

    /**
     * 根据会话ID查询出所有历史会话数据
     * @param ollamaQianWen
     * @return
     */
    List<OllamaQianWen> selectListBySessionId(OllamaQianWen ollamaQianWen);

    /**
     * 流式响应
     */
    SseEmitter invokeDeepSeekV2(OllamaQianWen ollamaQianWen, String apiKey);
}

3. ServiceImpl 层

package com.management.ai.service.impl;

import com.alibaba.fastjson.JSON;
import com.google.gson.Gson;
import com.management.ai.domain.model.*;
import com.management.ai.mapper.OllamaMapper;
import com.management.ai.service.IOllamaService;
import com.management.common.annotation.Anonymous;
import com.management.common.constant.Global;
import com.management.common.core.domain.ai.OllamaQianWen;
import com.management.common.core.service.impl.BaseServiceImpl;
import com.management.common.enums.BusinessStatus;
import com.management.common.utils.ShiroUtils;
import com.management.common.utils.StringUtils;
import okhttp3.*;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import reactor.core.publisher.Mono;
import tk.mybatis.mapper.entity.Example;

import java.io.IOException;
import java.util.*;
import java.util.concurrent.ThreadPoolExecutor;

@Service
@Transactional(readOnly = true)
public class OllamaServiceImpl extends BaseServiceImpl<OllamaMapper, OllamaQianWen> implements IOllamaService {

    private final OkHttpClient client = new OkHttpClient();
    private final Gson gson = new Gson();
    @Autowired
    private ThreadPoolExecutor threadPoolExecutor;
    @Autowired
    private OllamaMapper ollamaMapper;

    /**
     * 根据会话ID查询出所有历史会话数据
     *
     * @param ollamaQianWen
     * @return
     */
    @Override
    public List<OllamaQianWen> selectListBySessionId(OllamaQianWen ollamaQianWen) {
        if (ObjectUtils.isEmpty(ollamaQianWen.getSessionId())) {
            return new ArrayList<>();
        }
        Long loginUserId = ShiroUtils.getLoginUserId();
        Example example = new Example(OllamaQianWen.class);
        example.orderBy("requestTime");
        example.createCriteria()
                .andEqualTo("userId", loginUserId)
                .andEqualTo("sessionId", ollamaQianWen.getSessionId())
                .andEqualTo("isValid", Global.TRUE);
        return ollamaMapper.selectByExample(example);
    }

    /**
     * 普通响应(非流式请求)
     */
    @Override
    @Transactional(readOnly = false)
    @Anonymous
    public OllamaQianWen invokeDeepSeekV1(OllamaQianWen ollamaQianWen, String apiKey) {
        Request request = createRequestBody(ollamaQianWen, apiKey);
        //设置请求时间
        ollamaQianWen.setRequestTime(new Date());
        StringBuilder tempRes = new StringBuilder();
        ExceptionModel exceptionModel = new ExceptionModel();
        try (Response response = client.newCall(request).execute()) {
            //如果响应成功,并且返回体有内容,就输出内容,否则表示响应失败
            if (response.isSuccessful() && response.body() != null) {
                //解析响应流
                ResponseBody responseBody = response.body();
                String line;
                while ((line = responseBody.source().readUtf8Line()) != null) {
                    logger.info("响应数据:【" + line + "】");
                    //调用本地 deepseek
                    if (!ollamaQianWen.getIsOnline()) {
                        LocalhostDeepSeekResponseModel jsonParse = gson.fromJson(line, LocalhostDeepSeekResponseModel.class);
                        logger.info("LocalhostDeepSeekResponseModel响应数据:【" + jsonParse.toString() + "】");
                        String responseResult = jsonParse.getResponse();
                        if (StringUtils.isNotBlank(responseResult) && !"stop".equals(jsonParse.getResponse())) {
                            tempRes.append(responseResult);
                        }
                    } else { //调用在线deepseek API
                        if (line.contains("data: ") && !line.contains("[DONE]")) {
                            DeepSeekResponseModel jsonParse = gson.fromJson(line.replace("data: ", ""), DeepSeekResponseModel.class);
                            logger.info("DeepSeekResponseModel响应数据:【" + jsonParse.toString() + "】");
                            List<DeepSeekResponseModel.Choices> choicesList = jsonParse.getChoices();
                            for (DeepSeekResponseModel.Choices temp : choicesList) {
                                String content = temp.getDelta().getContent();
                                if (StringUtils.isNotBlank(content) && !"stop".equals(temp.getFinishReason())) {
                                    tempRes.append(temp.getDelta().getContent());
                                }
                            }
                        }
                    }
                    tempRes = new StringBuilder(tempRes.toString().replace("<think></think>", ""));
                    logger.info("响应结果:" + tempRes);
                    ollamaQianWen.setResponseTime(new Date());
                }
            }
        } catch (IOException e) {
            exceptionModel.setIsSuccess(Global.FALSE);
            exceptionModel.setErrorMessage(e.getMessage());
            logger.error(e.getMessage());
        }
        ollamaQianWen.setResponseResult(tempRes.toString());
        //异步新增
        asynAddOllamaQianWen(ollamaQianWen, request.toString(), exceptionModel);
        return ollamaQianWen;
    }

    /**
     * 流式响应
     */
    @Override
    @Transactional(readOnly = false)
    public SseEmitter invokeDeepSeekV2(OllamaQianWen ollamaQianWen, String apiKey) {
        SseEmitter emitter = new SseEmitter(0L); // 0L 代表无超时
        String request = createRequestBody2(ollamaQianWen, apiKey);
        //设置请求时间
        ollamaQianWen.setRequestTime(new Date());
        StringBuilder tempRes = new StringBuilder();
        ExceptionModel exceptionModel = new ExceptionModel();
        WebClient webClient = WebClient.builder().build();
        Subject subject = SecurityUtils.getSubject();
        try {
            subject.execute(() -> {
                webClient.post()
                        .uri(ollamaQianWen.getRequestUrl())
                        .bodyValue(request)
                        .headers(httpHeaders -> {
                            httpHeaders.setContentType(org.springframework.http.MediaType.APPLICATION_JSON_UTF8);
                            // 使用 Bearer Token 认证
                            httpHeaders.setBearerAuth(apiKey);
                        })
                        .retrieve() //获取响应体
                        .onStatus(httpStatus -> httpStatus.is4xxClientError() || httpStatus.is5xxServerError(), clientResponse -> {
                            logger.info("Error Response: " + clientResponse.statusCode());
                            return Mono.error(new RuntimeException("请求失败"));
                        })
                        .bodyToFlux(String.class)   //获取响应体的内容
                        .flatMap(line -> processStreamData(line, ollamaQianWen.getIsOnline()))   //解析数据
                        .doOnTerminate(() -> {
                            logger.info("流处理结束,最终响应数据:" + tempRes.toString());
                            ollamaQianWen.setResponseResult(tempRes.toString());
                            ollamaQianWen.setResponseTime(new Date());
                            //异步新增
                            asynAddOllamaQianWen(ollamaQianWen, request, exceptionModel);
                        })  //结束时触发
                        .doOnNext(data -> {
                            try {
                                if (StringUtils.isNotBlank(data)) {
                                    emitter.send(new MySseEventBuilderImpl().data(data)); // 发送数据到前端
                                    logger.info("流式输出:" + data);
                                    tempRes.append(data);
                                }
                            } catch (IOException e) {
                                emitter.completeWithError(e);
                                exceptionModel.setIsSuccess(Global.FALSE);
                                exceptionModel.setErrorMessage(e.getMessage());
                                logger.error(e.getMessage());
                            }
                        })
                        .doOnComplete(emitter::complete)
                        .doOnError(e -> {
                            logger.error(e.getMessage());
                            exceptionModel.setIsSuccess(Global.FALSE);
                            exceptionModel.setErrorMessage(e.getMessage());
                        })
                        .subscribe(); // 启动异步请求
            });
        } catch (Exception e) {
            emitter.completeWithError(e);
            exceptionModel.setIsSuccess(Global.FALSE);
            exceptionModel.setErrorMessage(e.getMessage());
            logger.error(e.getMessage());
        }
        return emitter;
    }
	
	 /**
     * 异步新增 ollamaQianWen 
     * @param ollamaQianWen
     * @param request
     * @param exceptionModel
     */
    @Transactional(readOnly = false)
    public void asynAddOllamaQianWen(OllamaQianWen ollamaQianWen, String request, ExceptionModel exceptionModel) {
        threadPoolExecutor.execute(() -> {
            ollamaQianWen.setRequestParams(request);
            ollamaQianWen.setUseModel(ollamaQianWen.getUseModel());
            ollamaQianWen.setRequestUrl(ollamaQianWen.getRequestUrl());
            //不为空表示请求成功
            if (exceptionModel.getIsSuccess()) {
                ollamaQianWen.setResponseStatus(BusinessStatus.SUCCESS.toString());
                ollamaQianWen.setResponseResult(ollamaQianWen.getResponseResult());
            } else {
                ollamaQianWen.setResponseStatus(BusinessStatus.FAIL.toString());
                ollamaQianWen.setResponseResult(exceptionModel.getErrorMessage());
            }
            logger.info("异步新增ollamaQianWen : " + ollamaQianWen.toString());
            add(ollamaQianWen, ollamaQianWen.getUserId());
        });
    }

    private String createRequestBody2(OllamaQianWen ollamaQianWen, String apiKey) {
        String jsonBody = null;
        if (ollamaQianWen.getIsOnline()) {
            // 构建请求体
            DeepSeekRequestModel.Message message = DeepSeekRequestModel.Message.builder().role("user").content(ollamaQianWen.getPrompt()).build();
            DeepSeekRequestModel requestBody = DeepSeekRequestModel.builder().model(ollamaQianWen.getUseModel()).messages(Collections.singletonList(message)).stream(Global.TRUE).build();

            //构建请求体json:{"messages":[{"content":"你好,DeepSeek!","role":"user"}],"model":"deepseek-chat"}
            jsonBody = JSON.toJSONString(requestBody);
        } else {
            LocalhostDeepSeekRequestModel requestBody = LocalhostDeepSeekRequestModel.builder().model(ollamaQianWen.getUseModel()).prompt(ollamaQianWen.getPrompt()).temperature(0.7F).maxTokens(500).build();
            //构建请求体json:{"prompt": "你好","model":"deepseek-chat","temperature":"0.7","max_tokens":"500"}
            jsonBody = JSON.toJSONString(requestBody);
        }
        logger.info("构建请求体【" + jsonBody + "】");
        return jsonBody;
    }

    /**
     * 解析数据
     */
    private Mono<String> processStreamData(String line, Boolean isOnline) {
        logger.info("line响应数据:【" + line + "】");
        String responseResult = null;
        try {
            if (isOnline) {
                if (!line.contains("[DONE]")) {
                    DeepSeekResponseModel jsonParse = gson.fromJson(line.replaceAll("data:\\s*", ""), DeepSeekResponseModel.class);
                    logger.info("DeepSeekResponseModel响应数据:【" + jsonParse.toString() + "】");
                    List<DeepSeekResponseModel.Choices> choicesList = jsonParse.getChoices();
                    for (DeepSeekResponseModel.Choices temp : choicesList) {
                        String content = temp.getDelta().getContent();
                        if (StringUtils.isNotBlank(content) && !"stop".equals(temp.getFinishReason())) {
                            responseResult = temp.getDelta().getContent();
                        }
                    }
                }
            }else {
                LocalhostDeepSeekResponseModel jsonParse = gson.fromJson(line, LocalhostDeepSeekResponseModel.class);
                logger.info("LocalhostDeepSeekResponseModel响应数据:【" + jsonParse.toString() + "】");
                responseResult = jsonParse.getResponse();
            }
            if (StringUtils.isNotBlank(responseResult)) {
                return Mono.just(responseResult);
            }
        } catch (Exception e) {
            return Mono.just("Error parsing JSON");
        }
        return Mono.empty();
    }

    /**
     * 封装请求参数
     *
     * @return
     */
    private Request createRequestBody(OllamaQianWen ollamaQianWen, String apiKey) {
        Request request = null;
        if (ollamaQianWen.getIsOnline()) {
            // 构建请求体
            DeepSeekRequestModel.Message message = DeepSeekRequestModel.Message.builder().role("user").content(ollamaQianWen.getPrompt()).build();
            DeepSeekRequestModel requestBody = DeepSeekRequestModel.builder().model(ollamaQianWen.getUseModel()).messages(Collections.singletonList(message)).stream(Global.TRUE).build();

            //构建请求体json:{"messages":[{"content":"你好,DeepSeek!","role":"user"}],"model":"deepseek-chat"}
            String jsonBody = JSON.toJSONString(requestBody);
            // 创建HTTP请求
            request = new Request.Builder().url(ollamaQianWen.getRequestUrl()).post(RequestBody.create(jsonBody, MediaType.get("application/json"))).addHeader("Authorization", "Bearer " + apiKey).build();
        } else {
            LocalhostDeepSeekRequestModel requestBody = LocalhostDeepSeekRequestModel.builder().model(ollamaQianWen.getUseModel()).prompt(ollamaQianWen.getPrompt()).temperature(0.7F).maxTokens(500).build();

            //构建请求体json:{"prompt": "你好","model":"deepseek-chat","temperature":"0.7","max_tokens":"500"}
            String jsonBody = JSON.toJSONString(requestBody);
            // 创建HTTP请求
            request = new Request.Builder().url(ollamaQianWen.getRequestUrl()).post(RequestBody.create(jsonBody, MediaType.get("application/json"))).build();
        }
        logger.info("构建请求体【" + request + "】");
        return request;
    }
}

4. 实体类

OllamaQianWen

package com.management.common.core.domain.ai;

import com.management.common.annotation.Excel;
import com.management.common.core.domain.BaseEntity;
import com.management.common.enums.BusinessStatus;
import lombok.Data;
import lombok.EqualsAndHashCode;

import javax.persistence.*;
import java.util.Date;

/**
 - Ollama千问
 -  3. @author management
 - @date 2025-03-02
 */
@Data
@EqualsAndHashCode(callSuper = true)
@Table(name = "ollama_qian_wen")
public class OllamaQianWen extends BaseEntity {

    /** ID */
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY, generator = "select RIGHT(uuid_short(), 19)")
    private Long id;

    /** 会话ID */
    @Excel(name = "会话ID")
    private Long sessionId;

    /** 用户ID */
    @Excel(name = "用户ID")
    private Long userId;

    @Excel(name = "请求IP")
    private String requestIp;

    @Excel(name = "请求时间")
    private Date requestTime;

    @Excel(name = "请求接口")
    private String requestUrl;

    @Excel(name = "使用模型")
    private String useModel;

    @Excel(name = "请求问题")
    private String prompt;

    @Excel(name = "请求参数")
    private String requestParams;

    @Excel(name = "响应结果")
    private String responseResult;

    @Excel(name = "响应时间")
    private Date responseTime;

    @Transient
    private Boolean isOnline;

    @Transient
    private String apiKey;

    /**
     * 响应成功状态(0-响应失败;1-响应成功;2-响应中断;3-响应等待)
     */
    @Excel(name = "响应成功状态")
    private String responseStatus;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public Long getUserId() {
        return userId;
    }

    public void setUserId(Long userId) {
        this.userId = userId;
    }

    public String getRequestIp() {
        return requestIp;
    }

    public void setRequestIp(String requestIp) {
        this.requestIp = requestIp;
    }

    public Date getRequestTime() {
        return requestTime;
    }

    public void setRequestTime(Date requestTime) {
        this.requestTime = requestTime;
    }

    public String getRequestUrl() {
        return requestUrl;
    }

    public void setRequestUrl(String requestUrl) {
        this.requestUrl = requestUrl;
    }

    public String getUseModel() {
        return useModel;
    }

    public void setUseModel(String useModel) {
        this.useModel = useModel;
    }

    public String getRequestParams() {
        return requestParams;
    }

    public void setRequestParams(String requestParams) {
        this.requestParams = requestParams;
    }

    public String getResponseResult() {
        return responseResult;
    }

    public void setResponseResult(String responseResult) {
        this.responseResult = responseResult;
    }

    public Date getResponseTime() {
        return responseTime;
    }

    public void setResponseTime(Date responseTime) {
        this.responseTime = responseTime;
    }

    public String getResponseStatus() {
        return responseStatus;
    }

    public void setResponseStatus(String responseStatus) {
        this.responseStatus = responseStatus;
    }

    public Long getSessionId() {
        return sessionId;
    }

    public void setSessionId(Long sessionId) {
        this.sessionId = sessionId;
    }

    public String getPrompt() {
        return prompt;
    }

    public void setPrompt(String prompt) {
        this.prompt = prompt;
    }

    public Boolean getIsOnline() {
        return isOnline;
    }

    public void setIsOnline(Boolean isOnline) {
        this.isOnline = isOnline;
    }

    public String getApiKey() {
        return apiKey;
    }

    public void setApiKey(String apiKey) {
        this.apiKey = apiKey;
    }

    @Override
    public String toString() {
        return "OllamaQianWen{" +
                "id=" + id +
                ", sessionId=" + sessionId +
                ", userId=" + userId +
                ", requestIp='" + requestIp + '\'' +
                ", requestTime=" + requestTime +
                ", requestUrl='" + requestUrl + '\'' +
                ", useModel='" + useModel + '\'' +
                ", prompt='" + prompt + '\'' +
                ", requestParams='" + requestParams + '\'' +
                ", responseResult='" + responseResult + '\'' +
                ", responseTime=" + responseTime +
                ", isOnline=" + isOnline +
                ", apiKey='" + apiKey + '\'' +
                ", responseStatus='" + responseStatus + '\'' +
                '}';
    }
}

OllamaModel

package com.management.common.core.domain.ai;

import com.management.common.annotation.Excel;
import com.management.common.core.domain.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;

import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.validation.constraints.NotBlank;
import java.util.Date;

@Data
@EqualsAndHashCode(callSuper = true)
@Table(name = "ollama_model")
public class OllamaModel extends BaseEntity {

    /** ID */
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY, generator = "select RIGHT(uuid_short(), 19)")
    private Long id;

    @NotBlank(message = "AI大模型不能为空")
    @Excel(name = "AI大模型")
    private String mainAiModel;

    @NotBlank(message = "使用模型不能为空")
    @Excel(name = "使用模型")
    private String useModel;

    @Excel(name = "在线/本地")
    private Boolean isOnline = false;

    @Excel(name = "请求接口")
    private String requestUrl;

    @Excel(name = "是否开发完成(0:未完成;1-已完成)")
    private Boolean isFinish;

    @Excel(name = "是否启用(0:不启用;1-启用)")
    private Boolean isOpen;

    @Excel(name = "启用时间")
    private Date startTime;

    @Excel(name = "结束时间")
    private Date endTime;
}

OllamaSession

package com.management.common.core.domain.ai;

import com.management.common.annotation.Excel;
import com.management.common.core.domain.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;

import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.validation.constraints.NotBlank;

@Data
@EqualsAndHashCode(callSuper = true)
@Table(name = "ollama_session")
public class OllamaSession extends BaseEntity {

    /** ID */
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY, generator = "select RIGHT(uuid_short(), 19)")
    private Long id;

    /**
     * 用户ID
     */
    @Excel(name = "用户ID")
    private Long userId;

    @NotBlank(message = "会话标题不能为空")
    @Excel(name = "会话标题")
    private String sessionTitle;

    @NotBlank(message = "AI大模型不能为空")
    @Excel(name = "AI大模型")
    private String mainAiModel;

    @Excel(name = "使用模型")
    private String useModel;

    @Excel(name = "在线/本地")
    private Boolean isOnline;

    @Excel(name = "用途")
    private String purpose;
}

DeepSeekRequestModel

package com.management.ai.domain.model;

import lombok.Builder;
import lombok.Data;
import java.util.List;

/**
 * @author 吴嘉诚
 * @date 2025/2/24 10:08
 * @desc
 */
@Data
@Builder
public class DeepSeekRequestModel {

    /**
     * 所用DeepSeek模型
     */
    private String model;
    private Boolean stream;
    private List<Message> messages;

    /**
     * 消息体
     */
    @Data
    @Builder
    public static class Message {
        private String role;
        private String content;
    }
}

DeepSeekResponseModel

package com.management.ai.domain.model;

import com.google.gson.annotations.SerializedName;
import lombok.Builder;
import lombok.Data;

import java.util.List;

@Data
@Builder
public class DeepSeekResponseModel {
    private String id;
    private String object;
    private String created;
    @SerializedName("system_fingerprint")
    private String systemFingerprint;
    private List<Choices> choices;
    private Usage usage;

    @Data
    @Builder
    public static class Choices {
        private Integer index;
        private Delta delta;
        private String logprobs;
        @SerializedName("finish_reason")
        private String finishReason;
    }

    @Data
    @Builder
    public static class Delta {
        private String content;
    }

    @Data
    @Builder
    public static class Usage {
        @SerializedName("prompt_tokens")
        private Integer promptTokens;
        @SerializedName("completion_tokens")
        private Integer completionTokens;
        @SerializedName("total_tokens")
        private Integer totalTokens;
        @SerializedName("prompt_tokens_details")
        private PromptTokensDetails promptTokensDetails;
        @SerializedName("prompt_cache_hit_tokens")
        private Integer promptCacheHitTokens;
        @SerializedName("prompt_cache_miss_tokens")
        private Integer promptCacheMissTokens;
    }

    @Data
    @Builder
    public static class PromptTokensDetails {
        @SerializedName("cached_tokens")
        private Integer cachedTokens;
    }
}

ExceptionModel

package com.management.ai.domain.model;

import com.management.common.constant.Global;

/**
 * @author 吴嘉诚
 * @date 2025/3/10 22:10
 * @desc
 */
public class ExceptionModel {

    private boolean isSuccess = Global.TRUE;
    private String errorMessage;

    public boolean getIsSuccess() {
        return isSuccess;
    }

    public void setIsSuccess(boolean isSuccess) {
        this.isSuccess = isSuccess;
    }

    public String getErrorMessage() {
        return errorMessage;
    }

    public void setErrorMessage(String errorMessage) {
        this.errorMessage = errorMessage;
    }
}
  • LocalhostDeepSeekRequestModel
package com.management.ai.domain.model;

import lombok.Builder;
import lombok.Data;

@Data
@Builder
public class LocalhostDeepSeekRequestModel {
    private String model;
    private String prompt;
    private Float temperature;
    private Integer maxTokens;
}

LocalhostDeepSeekResponseModel

package com.management.ai.domain.model;

import com.google.gson.annotations.SerializedName;
import lombok.Builder;
import lombok.Data;

import java.util.Date;
import java.util.List;

@Data
@Builder
public class LocalhostDeepSeekResponseModel {

    private String model;
    private Date createdAt;
    private String response;
    private Boolean done;
    @SerializedName("done_reason")
    private String doneReason;
    private List<String> context;
    @SerializedName("total_duration")
    private String totalDuration;
    @SerializedName("load_duration")
    private String loadDuration;
    @SerializedName("prompt_eval_count")
    private Integer promptEvalCount;
    @SerializedName("prompt_eval_duration")
    private String promptEvalDuration;
    @SerializedName("eval_count")
    private Integer evalCount;
    @SerializedName("eval_duration")
    private String evalDuration;
}

Message

package com.management.ai.domain;

import com.fasterxml.jackson.annotation.JsonProperty;

public class Message {
    @JsonProperty("role")
    private String role;

    @JsonProperty("content")
    private String content;

    public Message(String role, String content) {
        this.role = role;
        this.content = content;
    }

    public String getRole() {
        return role;
    }

    public void setRole(String role) {
        this.role = role;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }
}

以上就是所有的后端代码啦,大家可以去试试咯!!!在自己项目中部署一个流式输出的、又能灵活调用本地和在线 deepseek 、问题和答案还能保留到本地的数据库,太酷了!!!
我以后也会继续分享一些实用的代码,有需要的可以自取哦,麻烦关注三连,谢谢!!!

Logo

欢迎加入DeepSeek 技术社区。在这里,你可以找到志同道合的朋友,共同探索AI技术的奥秘。

更多推荐