{"id":234,"date":"2026-01-10T07:45:08","date_gmt":"2026-01-10T07:45:08","guid":{"rendered":"https:\/\/hyc.eshachem.com\/program\/?p=234"},"modified":"2026-02-27T02:28:26","modified_gmt":"2026-02-27T02:28:26","slug":"%e4%bd%bf%e7%94%a8springboot%e8%88%87react%e4%b8%b2%e6%8e%a5gimini","status":"publish","type":"post","link":"https:\/\/hyc.eshachem.com\/program\/%e4%bd%bf%e7%94%a8springboot%e8%88%87react%e4%b8%b2%e6%8e%a5gimini\/","title":{"rendered":"\u4f7f\u7528SpringBoot\u8207React\u4e32\u63a5Gemini"},"content":{"rendered":"\n<h1 class=\"wp-block-heading\">Google AI API<\/h1>\n\n\n\n<p class=\"\">\u5148\u53bb\u9019\u500b\u7db2\u5740\u53d6\u5f97API !<\/p>\n\n\n\n<p class=\"\"><a href=\"https:\/\/aistudio.google.com\">https:\/\/aistudio.google.com<\/a><\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Gemini \u5b98\u65b9\u7d50\u69cb<\/h3>\n\n\n\n<p class=\"\">Gemini API request \u683c\u5f0f\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>{\n  \"contents\": &#91;\n    {\n      \"parts\": &#91;\n        { \"text\": \"Hello\" }\n      ]\n    }\n  ]\n}\n<\/code><\/pre>\n\n\n\n<p class=\"\">\u6240\u4ee5\u6211\u5011\u8981\u5c07\u4f7f\u7528\u8005\u7684\u8f38\u5165\uff0c\u52a0\u4e0aJSON Body\u548cHeader\u5305\u6210\u5b8c\u6574\u7684REST API\u683c\u5f0f\u50b3\u7d66gimini\uff0c\u4f5c\u70ba HTTP POST \u7684\u8acb\u6c42\u7269\u4ef6<\/p>\n\n\n\n<p class=\"\"><\/p>\n\n\n\n<h1 class=\"wp-block-heading\">SpringBoot<\/h1>\n\n\n\n<p class=\"\">\u5148\u5efa\u7acb\u4e00\u500b\u6a94\u6848\u5b58\u653eAPI\u91d1\u9470 <code>src\\main\\resources\\application.properties<\/code><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>google.gemini.api.key=AIzaSyXXXXXXX...<\/code><\/pre>\n\n\n\n<p class=\"\">\u63a5\u8457\u5efa\u7acb\u4e00\u500bservice\uff0c\u4e3b\u8981\u7684\u64cd\u4f5c\u90fd\u5728service\u4e0a !<\/p>\n\n\n\n<p class=\"\">\u64cd\u4f5c\u6d41\u7a0b:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li class=\"\">\u7a0b\u5f9e\u8a2d\u5b9a\u6a94\u8b80\u53d6 <strong>Gemini API Key<\/strong> (@Value)<\/li>\n\n\n\n<li class=\"\">\u5c07\u4f7f\u7528\u8005\u8f38\u5165\u7684 <code>prompt<\/code> \u7d44\u6210 <strong>Gemini API \u8981\u6c42\u683c\u5f0f<\/strong><\/li>\n\n\n\n<li class=\"\">\u900f\u904e <code>RestTemplate<\/code> \u547c\u53eb Google Gemini<\/li>\n\n\n\n<li class=\"\">\u89e3\u6790\u56de\u50b3 JSON<\/li>\n\n\n\n<li class=\"\">\u56de\u50b3 AI \u7522\u751f\u7684\u6587\u5b57\u7d50\u679c\uff08String\uff09<\/li>\n<\/ol>\n\n\n\n<pre class=\"wp-block-code\"><code>package demo.service;\n\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.stereotype.Service;\nimport org.springframework.web.client.RestTemplate;\nimport org.springframework.http.*;\nimport java.util.*;\n\n@Service\npublic class AIService {\n\n    @Value(\"${google.gemini.api.key}\")\n    private String apiKey;\n\n    private final String API_URL = \"https:\/\/generativelanguage.googleapis.com\/v1beta\/models\/gemini-3-flash-preview:generateContent?key=\";\n\n    public String getAiResponse(String prompt) {\n        RestTemplate restTemplate = new RestTemplate();\n\n        \/\/ 1. \u8a2d\u5b9a Header\n        HttpHeaders headers = new HttpHeaders();\n        headers.setContentType(MediaType.APPLICATION_JSON);\n\n        \/\/ 2. \u69cb\u5efa\u8acb\u6c42 Body\n        Map&lt;String, Object> textPart = Map.of(\"text\", prompt);\n        Map&lt;String, Object> parts = Map.of(\"parts\", List.of(textPart));\n        Map&lt;String, Object> contents = Map.of(\"contents\", List.of(parts));\n\n        HttpEntity&lt;Map&lt;String, Object>> entity = new HttpEntity&lt;>(contents, headers);\n\n        try {\n            \/\/ 3. \u767c\u9001\u8acb\u6c42\n            ResponseEntity&lt;Map> response = restTemplate.postForEntity(API_URL + apiKey, entity, Map.class); \/\/ \u547c\u53eb Gemini API\n\n            \/\/ 4. \u89e3\u6790 Gemini \u7684 JSON (\u8def\u5f91: candidates&#91;0].content.parts&#91;0].text)\n            Map body = response.getBody();\n            if (body != null &amp;&amp; body.containsKey(\"candidates\")) {\n                List candidates = (List) body.get(\"candidates\");\n                Map firstCandidate = (Map) candidates.get(0);\n                Map content = (Map) firstCandidate.get(\"content\");\n                List resParts = (List) content.get(\"parts\");\n                Map firstPart = (Map) resParts.get(0);\n                return firstPart.get(\"text\").toString();\n            }\n            return \"AI \u56de\u50b3\u683c\u5f0f\u7570\u5e38\";\n        } catch (Exception e) {\n            e.printStackTrace();\n            return \"AI \u670d\u52d9\u547c\u53eb\u5931\u6557: \" + e.getMessage();\n        }\n    }\n}<\/code><\/pre>\n\n\n\n<h1 class=\"wp-block-heading\">React<\/h1>\n\n\n\n<p class=\"\">\u524d\u7aef\u8981\u8ca0\u8cac\u7684\u4e8b\u5c31\u6bd4\u8f03\u7c21\u55ae:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li class=\"\">\u7ba1\u7406<strong>\u5c0d\u8a71\u72c0\u614b\uff08 useState\uff09<\/strong><\/li>\n\n\n\n<li class=\"\">\u63a5\u6536\u4f7f\u7528\u8005\u8f38\u5165\u4e26\u9001\u51fa\u81f3\u5f8c\u7aef ( fetch )<\/li>\n\n\n\n<li class=\"\">\u63a5\u6536 AI \u56de\u61c9\u4e26\u66f4\u65b0\u5c0d\u8a71\u7d00\u9304<\/li>\n\n\n\n<li class=\"\">\u63a7\u5236\u9001\u51fa\u884c\u70ba\uff08 useEffect \uff09<\/li>\n\n\n\n<li class=\"\">\u8655\u7406\u975e\u540c\u6b65\u6d41\u7a0b\u8207\u932f\u8aa4\u72c0\u614b (async \/ await )<\/li>\n\n\n\n<li class=\"\">\u7dad\u6301\u5c0d\u8a71\u53ef\u7528\u6027\uff08\u81ea\u52d5\u6372\u52d5 useEffect\uff09<\/li>\n<\/ol>\n\n\n\n<pre class=\"wp-block-code\"><code>import React, { useState, useRef, useEffect } from 'react';\nimport { FaAngleRight, FaPaperPlane } from 'react-icons\/fa';\n\ninterface Message {\n  role: 'user' | 'ai';\n  text: string;\n}\n\ninterface Props {\n  onClose: () => void;\n  medicalData: any;\n}\n\nexport default function RightPanelContent({\n  onClose,\n  medicalData\n}: Props) {\n  const &#91;messages, setMessages] = useState&lt;Message&#91;]>(&#91;\n    {\n      role: 'ai',\n      text: '\u4f60\u597d\uff01\u6211\u662f\u60a8\u7684\u91ab\u7642 AI \u52a9\u624b\uff0c\u5df2\u6e96\u5099\u597d\u5206\u6790\u75c5\u6b77\u8cc7\u6599\u3002\u8acb\u554f\u6709\u4ec0\u9ebc\u6211\u53ef\u4ee5\u5e6b\u60a8\u7684\uff1f'\n    }\n  ]);\n\n  const &#91;inputValue, setInputValue] = useState('');\n  const &#91;loading, setLoading] = useState(false);\n  const scrollRef = useRef&lt;HTMLDivElement>(null);\n\n  \/\/ \u81ea\u52d5\u6372\u52d5\u5230\u6700\u5e95\u90e8\n  useEffect(() => {\n    if (scrollRef.current) {\n      scrollRef.current.scrollTop = scrollRef.current.scrollHeight;\n    }\n  }, &#91;messages, loading]);\n\n  const handleSendMessage = async () => {\n    if (!inputValue.trim() || loading) return;\n\n    const userMessage = inputValue;\n    setMessages(prev => &#91;...prev, { role: 'user', text: userMessage }]);\n    setInputValue('');\n    setLoading(true);\n\n    try { \/\/ inlineFetch\u7684\u5beb\u6cd5\n      const response = await fetch('http:\/\/localhost:8080\/api\/ai\/chat', {\n        method: 'POST',\n        headers: { 'Content-Type': 'application\/json' },\n        body: JSON.stringify({\n          message: userMessage,\n          context: JSON.stringify(medicalData)\n        })\n      });\n\n      const result = await response.json();\n      setMessages(prev => &#91;...prev, { role: 'ai', text: result.reply }]);\n    } catch (error) {\n      setMessages(prev => &#91;\n        ...prev,\n        { role: 'ai', text: '\u9023\u7dda\u5931\u6557\uff0c\u8acb\u6aa2\u67e5\u5f8c\u7aef\u670d\u52d9\u3002' }\n      ]);\n    } finally {\n      setLoading(false);\n    }\n  };\n\n  return (\n    &lt;div>\n      {\/* Header *\/}\n      &lt;div>\n        &lt;span>AI \u667a\u80fd\u5c0d\u8a71\u52a9\u624b&lt;\/span>\n        &lt;button onClick={onClose}>\n          &lt;FaAngleRight \/>\n        &lt;\/button>\n      &lt;\/div>\n\n      {\/* \u5c0d\u8a71\u5340 *\/}\n      &lt;div ref={scrollRef}>\n        {messages.map((msg, index) => (\n          &lt;div key={index}>\n            &lt;strong>{msg.role === 'user' ? 'User' : 'AI'}:&lt;\/strong>\n            &lt;span>{msg.text}&lt;\/span>\n          &lt;\/div>\n        ))}\n\n        {loading &amp;&amp; &lt;div>AI \u6b63\u5728\u601d\u8003\u4e2d...&lt;\/div>}\n      &lt;\/div>\n\n      {\/* \u8f38\u5165\u5340 *\/}\n      &lt;div>\n        &lt;input\n          type=\"text\"\n          placeholder=\"\u8acb\u8f38\u5165\u554f\u984c...\"\n          value={inputValue}\n          onChange={e => setInputValue(e.target.value)}\n          onKeyDown={e => e.key === 'Enter' &amp;&amp; handleSendMessage()}\n        \/>\n        &lt;button onClick={handleSendMessage}>\n          &lt;FaPaperPlane \/>\n        &lt;\/button>\n      &lt;\/div>\n    &lt;\/div>\n  );\n}\n<\/code><\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Google AI API \u5148\u53bb\u9019\u500b\u7db2\u5740\u53d6\u5f97API ! https:\/\/aistudio.google.com Gemini \u5b98\u65b9\u7d50\u69cb Gemini API [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[39],"tags":[],"class_list":["post-234","post","type-post","status-publish","format-standard","hentry","category-back-end"],"_links":{"self":[{"href":"https:\/\/hyc.eshachem.com\/program\/wp-json\/wp\/v2\/posts\/234","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/hyc.eshachem.com\/program\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/hyc.eshachem.com\/program\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/hyc.eshachem.com\/program\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/hyc.eshachem.com\/program\/wp-json\/wp\/v2\/comments?post=234"}],"version-history":[{"count":2,"href":"https:\/\/hyc.eshachem.com\/program\/wp-json\/wp\/v2\/posts\/234\/revisions"}],"predecessor-version":[{"id":237,"href":"https:\/\/hyc.eshachem.com\/program\/wp-json\/wp\/v2\/posts\/234\/revisions\/237"}],"wp:attachment":[{"href":"https:\/\/hyc.eshachem.com\/program\/wp-json\/wp\/v2\/media?parent=234"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/hyc.eshachem.com\/program\/wp-json\/wp\/v2\/categories?post=234"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/hyc.eshachem.com\/program\/wp-json\/wp\/v2\/tags?post=234"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}