{"id":10021103,"date":"2025-12-05T23:16:27","date_gmt":"2025-12-05T15:16:27","guid":{"rendered":"https:\/\/itmax.cn\/?p=10021103"},"modified":"2025-12-05T23:16:27","modified_gmt":"2025-12-05T15:16:27","slug":"%e5%9f%ba%e4%ba%8e-qoder-%e5%92%8c-analyticdb-supabase-%e5%bf%ab%e9%80%9f%e6%9e%84%e5%bb%baai%e5%8e%9f%e7%94%9f%e7%a7%bb%e5%8a%a8%e7%ab%af-app","status":"publish","type":"post","link":"https:\/\/itmax.cn\/?p=10021103","title":{"rendered":"\u57fa\u4e8e Qoder \u548c AnalyticDB Supabase \u5feb\u901f\u6784\u5efaAI\u539f\u751f\u79fb\u52a8\u7aef APP"},"content":{"rendered":"<p>\u672c\u6587\u4ecb\u7ecd\u5982\u4f55\u5229\u7528Qoder\u3001\u4e91\u539f\u751f\u6570\u636e\u4ed3\u5e93 AnalyticDB PostgreSQL \u7248Supabase\u548c\u901a\u4e49\u5343\u95ee\u56fe\u50cf\u7f16\u8f91\u6a21\u578b\uff08Qwen Image Edit\uff09\uff0c\u5feb\u901f\u642d\u5efa\u4e00\u4e2a\u65e0\u9700\u4f20\u7edf\u540e\u7aef\u7684AI\u624b\u529e\u751f\u56feFlutter\u5e94\u7528\u3002\u5185\u5bb9\u6db5\u76d6\u4ece\u524d\u7aef\u4ee3\u7801\u81ea\u52a8\u751f\u6210\u3001\u540e\u7aef\u5373\u670d\u52a1\uff08BaaS\uff09\u914d\u7f6e\uff0c\u5230AI\u6a21\u578b\u96c6\u6210\uff0c\u9002\u5408\u5e0c\u671b\u5feb\u901f\u9a8c\u8bc1AI\u539f\u751f\u5e94\u7528\u539f\u578b\u5e76\u5b9e\u73b0\u654f\u6377\u5f00\u53d1\u7684\u5f00\u53d1\u8005\u3002<\/p>\n<h2>\u4e00\u3001\u6982\u8ff0<\/h2>\n<p>\u5728AI\u539f\u751f\u5e94\u7528\u5f00\u53d1\u7684\u65f6\u4ee3\uff0c\u4f20\u7edf\u7684\u540e\u7aef\u67b6\u6784\u6b63\u5728\u88ab\u91cd\u65b0\u5b9a\u4e49\u3002\u672c\u65b9\u6848\u91c7\u7528\u8f7b\u91cf\u3001\u654f\u6377\u7684\u67b6\u6784\uff0c\u901a\u8fc7\u7ec4\u5408\u4ee5\u4e0b\u6838\u5fc3\u6280\u672f\uff0c\u5b9e\u73b0\u5168\u7a0b\u65e0\u9700\u81ea\u5efa\u4f20\u7edf\u540e\u7aef\uff0c\u5373\u53ef\u5feb\u901f\u6784\u5efaAI\u624b\u529e\u751f\u56feFlutter\u5e94\u7528\u3002<\/p>\n<ul>\n<li>\u524d\u7aef\uff1a\u7531Qoder\u6839\u636e\u9700\u6c42\u81ea\u52a8\u751f\u6210Flutter\u4ee3\u7801\uff0c\u8d1f\u8d23\u754c\u9762\u4e0e\u4ea4\u4e92\u3002Qoder\u4f5c\u4e3aAI\u9a71\u52a8\u7684IDE Agent\uff0c\u80fd\u591f\u6839\u636e\u9700\u6c42\u81ea\u52a8\u751f\u6210\u9ad8\u8d28\u91cf\u7684Flutter\u4ee3\u7801\u3002\u4f7f\u7528Flutter\u63d2\u4ef6\u521b\u5efaEmpty Project\u540e\uff0c\u60a8\u53ea\u9700\u63cf\u8ff0\u6838\u5fc3\u529f\u80fd\uff0c\u914d\u5408\u51e0\u8f6e\u8c03\u8bd5\uff0c\u5c31\u80fd\u5f97\u5230\u53ef\u8fd0\u884c\u7684\u79fb\u52a8\u7aef\u5e94\u7528\u3002<\/li>\n<li>\u540e\u7aef\u5373\u670d\u52a1\uff08BaaS\uff09\uff1aAnalyticDB Supabase\u63d0\u4f9b\u6570\u636e\u5b58\u50a8\u3001\u5bf9\u8c61\u5b58\u50a8\u548c\u8fb9\u7f18\u51fd\u6570\u80fd\u529b\uff0c\u7b80\u5316\u4e86\u4f20\u7edf\u540e\u7aef\u5f00\u53d1\u7684\u590d\u6742\u6027\u3002<\/li>\n<li>AI\u80fd\u529b\u96c6\u6210\uff1aAnalyticDB Supabase Edge Function\u63a5\u5165\u901a\u4e49\u5343\u95ee\u56fe\u50cf\u7f16\u8f91\u6a21\u578b\uff0c\u5b9e\u73b0\u56fe\u7247\u7f16\u8f91\u3002<\/li>\n<\/ul>\n<h2>\u4e8c\u3001\u524d\u63d0\u6761\u4ef6<\/h2>\n<ul>\n<li>\u5df2\u521b\u5efaSupabase\u9879\u76ee\u3002<\/li>\n<li>\u5df2\u4e3a\u4e91\u539f\u751f\u6570\u636e\u4ed3\u5e93AnalyticDB PostgreSQL\u7248Supabase\u9879\u76ee\u5f00\u901a\u516c\u7f51\u8bbf\u95ee\u3002<\/li>\n<li>\u5df2\u83b7\u53d6\u963f\u91cc\u4e91\u767e\u70bcAPI Key\uff0c\u7528\u4e8e\u8c03\u7528\u901a\u4e49\u5343\u95ee\u56fe\u50cf\u7f16\u8f91\u6a21\u578b\u3002<\/li>\n<\/ul>\n<h2>\u4e09\u3001\u64cd\u4f5c\u6b65\u9aa4<\/h2>\n<h3>\u6b65\u9aa4\u4e00\uff1a\u751f\u6210Flutter\u5e94\u7528\u4ee3\u7801<\/h3>\n<p>1.\u73af\u5883\u51c6\u5907\u3002<\/p>\n<ul>\n<li>\u5b89\u88c5Qoder\u4e0eFlutter\u63d2\u4ef6\u3002<\/li>\n<li>\u5b89\u88c5Flutter\u73af\u5883\u3002<\/li>\n<\/ul>\n<p>2.\u521b\u5efaFlutter\u9879\u76ee\u3002<br \/>\u5728VS Code\u4e2d\u4f7f\u7528\u5feb\u6377\u952eCommand + Shift + P\uff08Mac\uff09\u6216Ctrl + Shift + P\uff08Windows\/Linux\uff09\uff0c\u641c\u7d22\u201cflutter\u201d\uff0c\u9009\u62e9Flutter: New Project\u3002<br \/>3.\u4f7f\u7528Qoder\u751f\u6210\u4ee3\u7801\u3002<br \/>\u5411Qoder\u63cf\u8ff0\u529f\u80fd\u9700\u6c42\uff0c\u5e76\u8c03\u8bd5\u751f\u6210\u4ee3\u7801\u3002\u672c\u6587\u6e90\u4ee3\u7801\u793a\u4f8b\u8bf7\u53c2\u89c1adb-supabase-flutter-demo\u3002\u529f\u80fd\u9700\u6c42\u63cf\u8ff0\u793a\u4f8b\u5982\u4e0b\uff1a<\/p>\n<pre><code>build a flutter image edit app, powered by supabase, using edge function invoke image model to edit image by uploaded by users<\/code><\/pre>\n<h3>\u6b65\u9aa4\u4e8c\uff1a\u914d\u7f6eAnalyticDB Supabase<\/h3>\n<p>1.\u914d\u7f6eAPI\u8bbf\u95ee\u3002\u5728\u9879\u76ee\u6839\u76ee\u5f55\u4e0b\u65b0\u589e.env\u6587\u4ef6\uff0c\u590d\u5236\u4ee5\u4e0b\u4fe1\u606f\u5e76\u5c06\u76f8\u5173\u914d\u7f6e\u66ff\u6362\u4e3a\u5b9e\u9645\u503c\u3002\u914d\u7f6e\u4fe1\u606f\u83b7\u53d6\u8bf7\u53c2\u89c1\u83b7\u53d6API Keys\u3002<\/p>\n<pre><code>SUPABASE_URL=https:\/\/sbp-xxxxx.supabase.opentrust.net\nSUPABASE_SERVICE_KEY=xxxxxxxx<\/code><\/pre>\n<p>2.\u8bbe\u8ba1\u6570\u636e\u5e93\u8868\u7ed3\u6784\u3002<br \/>\u767b\u5f55Supabase Dashboard\uff0c\u521b\u5efa\u6570\u636e\u5e93\u8868\u3002\u6b64\u8868\u7528\u4e8e\u5b58\u50a8\u7528\u6237\u7f16\u8f91\u56fe\u7247\u7684\u8bb0\u5f55\uff0c\u5305\u62ec\u539f\u59cb\u56fe\u7247URL\u3001\u7f16\u8f91\u540e\u56fe\u7247URL\u3001\u7528\u6237\u8f93\u5165\u7684\u63d0\u793a\u8bcd\u7b49\u5173\u952e\u4fe1\u606f<\/p>\n<pre><code>CREATE TABLE public.edited_images (\n    id TEXT PRIMARY KEY,\n    prompt TEXT NOT NULL,\n    original_image_url TEXT NOT NULL,\n    edited_image_url TEXT NOT NULL,\n    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()\n);<\/code><\/pre>\n<p>3.\u521b\u5efa\u5bf9\u8c61\u5b58\u50a8\u6876\u3002<\/p>\n<ul>\n<li>\u5728Supabase Dashboard\u4fa7\u8fb9\u680f\uff0c\u5355\u51fbStorage\u3002<\/li>\n<li>\u521b\u5efa\u4e00\u4e2a\u540d\u4e3aimages\u7684\u5b58\u50a8\u6876\uff0c\u7528\u4e8e\u5b58\u50a8\u7528\u6237\u4e0a\u4f20\u7684\u56fe\u7247\u6570\u636e\u3002<\/li>\n<\/ul>\n<h3>\u6b65\u9aa4\u4e09\uff1a\u96c6\u6210AI\u670d\u52a1<\/h3>\n<p>1.\u914d\u7f6e\u5b89\u5168\u5bc6\u94a5\u3002<\/p>\n<blockquote><p>\u8bf4\u660e\uff1a\u5728AnalyticDB Supabase\u4e2d\uff0c\u963f\u91cc\u4e91\u63d0\u4f9b\u539f\u751f\u7684Edge Function Secrets\u914d\u7f6e\u4e0e\u96c6\u4e2d\u7ba1\u7406\u80fd\u529b\uff0c\u53ef\u5c06AI API Token\uff08\u5982DashScope\u548c\u767e\u70bc\uff09\u5b89\u5168\u5730\u5b58\u653e\u5728\u51fd\u6570\u8fd0\u884c\u73af\u5883\u7684\u5bc6\u94a5\u5e93\u4e2d\uff0c\u901a\u8fc7Deno.env.get\u8bfb\u53d6\uff0c\u907f\u514d\u786c\u7f16\u7801\u6216\u5ba2\u6237\u7aef\u66b4\u9732\u3002<\/p><\/blockquote>\n<ul>\n<li>\u5728Supabase Dashboard\u4fa7\u8fb9\u680f\uff0c\u5355\u51fbEdge Function&gt;Secrets\u3002<\/li>\n<li>\u914d\u7f6eBAILIAN_API_KEY\uff0c\u5176\u503c\u4e3a\u524d\u63d0\u6761\u4ef6\u4e2d\u83b7\u53d6\u7684\u963f\u91cc\u4e91\u767e\u70bcAPI Key\u3002<\/li>\n<\/ul>\n<p>2.\u90e8\u7f72Edge Function\u3002<br \/>\u5728Supabase Dashboard\u4fa7\u8fb9\u680f\uff0c\u5355\u51fbEdge Function&gt;Functions\u3002<br \/>\u5355\u51fb\u9875\u9762\u53f3\u4e0a\u89d2\u7684Deploy a new function\uff0c\u5728\u4e0b\u62c9\u9009\u9879\u4e2d\u9009\u62e9Via Editor\u3002<br \/>\u521b\u5efa\u5e76\u90e8\u7f72\u540d\u4e3awan\u7684function\u3002<br \/>\u4ee3\u7801\u793a\u4f8b\u5982\u4e0b\uff0c\u8bf7\u6839\u636e\u7f51\u7edc\u8bbf\u95ee\u65b9\u5f0f\u66ff\u6362BASE_URL\u3002\u79c1\u7f51\u8bbf\u95ee\uff0c\u8bf7\u53c2\u89c1\u901a\u8fc7\u7ec8\u7aef\u8282\u70b9\u79c1\u7f51\u8bbf\u95ee\u963f\u91cc\u4e91\u767e\u70bc\u5e73\u53f0\uff1b\u516c\u7f51\u8bbf\u95ee\uff0c\u8bf7\u53c2\u89c1\u56fe\u50cf\u7f16\u8f91-\u901a\u4e49\u5343\u95ee\u3002<\/p>\n<pre><code>const DASHSCOPE_API_KEY = Deno.env.get('BAILIAN_API_KEY');\nconst BASE_URL = 'https:\/\/vpc-cn-beijing.dashscope.aliyuncs.com\/api\/v1';\nasync function callImageEditAPI(image_url, prompt) {\n  const messages = [\n    {\n      role: &quot;user&quot;,\n      content: [\n        {\n          image: image_url\n        },\n        {\n          text: prompt\n        }\n      ]\n    }\n  ];\n  const payload = {\n    model: &quot;qwen-image-edit&quot;,\n    input: {\n      messages\n    },\n    parameters: {\n      negative_prompt: &quot;&quot;,\n      watermark: false\n    }\n  };\n  try {\n    const response = await fetch(`${BASE_URL}\/services\/aigc\/multimodal-generation\/generation`, {\n      method: 'POST',\n      headers: {\n        'Authorization': `Bearer ${DASHSCOPE_API_KEY}`,\n        'Content-Type': 'application\/json'\n      },\n      body: JSON.stringify(payload)\n    });\n    if (!response.ok) {\n      console.error(`Request failed: ${response.status} ${response.statusText}`);\n      return null;\n    }\n    const data = await response.json();\n    return data.output?.choices?.[0]?.message?.content ?? null;\n  } catch (error) {\n    console.error(&quot;Request error:&quot;, error.message);\n    return null;\n  }\n}\nDeno.serve(async (req)=&gt;{\n  try {\n    const { image_url, prompt } = await req.json();\n    if (!image_url || !prompt) {\n      return new Response(JSON.stringify({\n        error: &quot;Missing image_url or prompt&quot;\n      }), {\n        status: 400,\n        headers: {\n          'Content-Type': 'application\/json'\n        }\n      });\n    }\n    const result = await callImageEditAPI(image_url, prompt);\n    return new Response(JSON.stringify({\n      message: result\n    }), {\n      headers: {\n        'Content-Type': 'application\/json',\n        'Connection': 'keep-alive'\n      }\n    });\n  } catch (error) {\n    console.error(&quot;Server error:&quot;, error);\n    return new Response(JSON.stringify({\n      error: &quot;Internal server error&quot;\n    }), {\n      status: 500,\n      headers: {\n        'Content-Type': 'application\/json'\n      }\n    });\n  }\n});<\/code><\/pre>\n<h2>\u56db\u3001\u5de5\u4f5c\u6d41\u7a0b<\/h2>\n<ul>\n<li>\u4e0a\u4f20\u539f\u56fe\uff1a\u7528\u6237\u9009\u62e9\u56fe\u7247\u540e\uff0c\u524d\u7aef\u5c06\u5176\u4e0a\u4f20\u81f3Supabase Storage\u7684images\u5b58\u50a8\u6876\uff0c\u5e76\u751f\u6210\u7b7e\u540dURL\u3002<\/li>\n<li>\u8c03\u7528\u7f16\u8f91\uff1a\u524d\u7aef\u5c06\u7b7e\u540dURL\u4e0e\u7f16\u8f91\u6307\u4ee4\uff08prompt\uff09\u53d1\u9001\u7ed9Edge Function\u3002Edge Function\u5229\u7528BAILIAN_API_KEY\u8c03\u7528\u901a\u4e49\u5343\u95ee\u56fe\u50cf\u7f16\u8f91\u6a21\u578b\uff0c\u5904\u7406\u56fe\u7247\u5e76\u83b7\u53d6\u751f\u6210\u56fe\u7684URL\u3002<\/li>\n<li>\u5199\u5165\u5386\u53f2\u8bb0\u5f55\uff1a\u524d\u7aef\u5c06\u539f\u59cb\u56fe\u7247URL\u3001\u7f16\u8f91\u540e\u56fe\u7247URL\u53caprompt\u7b49\u4fe1\u606f\u5199\u5165edited_images\u6570\u636e\u5e93\u8868\uff0c\u4f5c\u4e3a\u5386\u53f2\u8bb0\u5f55\u3002<\/li>\n<\/ul>\n<h2>\u4e94\u3001\u6d4b\u8bd5\u4e0e\u9a8c\u8bc1<\/h2>\n<p>\u4f9d\u6b21\u6267\u884c\u4ee5\u4e0b\u547d\u4ee4\uff0c\u5b89\u88c5\u4f9d\u8d56\u5e76\u542f\u52a8\u5e94\u7528\u3002<\/p>\n<pre><code>flutter pub get\nflutter run<\/code><\/pre>\n<p>\u542f\u52a8\u5e94\u7528\u540e\uff0c\u60a8\u53ef\u5728\u8bbe\u5907\u6216\u6a21\u62df\u5668\u4e0a\u4f53\u9a8cAI\u624b\u529e\u751f\u56fe\u529f\u80fd\u3002<br \/><strong>\u63d0\u793a\u8bcd\u793a\u4f8b<\/strong><\/p>\n<pre><code>\u7ed8\u5236\u56fe\u4e2d\u89d2\u8272\u76841\/7\u6bd4\u4f8b\u7684\u5546\u4e1a\u5316\u624b\u529e\uff0c\u5199\u5b9e\u98ce\u683c\uff0c\u771f\u5b9e\u73af\u5883\uff0c\u624b\u529e\u653e\u5728\u7535\u8111\u684c\u4e0a,\u7535\u8111\u5c4f\u5e55\u91cc\u7684\u5185\u5bb9\u4e3a\u8be5\u624b\u529e\u7684C4D\u5efa\u6a21\u8fc7\u7a0b\uff0c\u7535\u8111\u5c4f\u5e55\u65c1\u653e\u7740\u5370\u6709\u539f\u753b\u7684\u5851\u6599\u73a9\u5177\u5305\u88c5\u76d2\uff0c\u7535\u8111\u684c\u4e0a\u8fd8\u6709\u5236\u4f5c\u624b\u529e\u7684\u5de5\u5177\uff0c\u5982\u753b\u7b14\uff0c\u989c\u6599\uff0c\u5c0f\u5200\u7b49\u3002<\/code><\/pre>\n<p><strong>\u6d4b\u8bd5\u793a\u4f8b<\/strong><br \/><img decoding=\"async\"   referrerpolicy=\"no-referrer\" src=\"https:\/\/segmentfault.com\/img\/remote\/1460000047449724\" alt=\"\u56fe\u7247\" title=\"\u56fe\u7247\"><br \/><strong>\u6548\u679c\u793a\u4f8b<\/strong><br \/><img decoding=\"async\"   referrerpolicy=\"no-referrer\" src=\"https:\/\/segmentfault.com\/img\/remote\/1460000047449725\" alt=\"\u56fe\u7247\" title=\"\u56fe\u7247\" loading=\"lazy\"><\/p>\n<h2>\u4e86\u89e3\u66f4\u591a<\/h2>\n<p>\u6b22\u8fce\u626b\u63cf\u4e0b\u65b9\u7fa4\u7801\u6216<strong>\u641c\u7d22\u9489\u9489\u7fa4\u53f7\uff08101930027031\uff09<\/strong>\u5165\u7fa4\u9886\u53d6\u514d\u8d39\u8bd5\u7528\uff01<br \/><img decoding=\"async\"   referrerpolicy=\"no-referrer\" src=\"https:\/\/segmentfault.com\/img\/remote\/1460000047449726\" alt=\"\u56fe\u7247\" title=\"\u56fe\u7247\" loading=\"lazy\"><\/p>\n<p><a href=\"https:\/\/segmentfault.com\/a\/1190000047450525\">https:\/\/segmentfault.com\/a\/1190000047450525<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u672c\u6587\u4ecb\u7ecd\u5982\u4f55\u5229\u7528Qoder\u3001\u4e91\u539f\u751f\u6570\u636e\u4ed3\u5e93 AnalyticDB PostgreSQL \u7248Supabase\u548c\u901a [&hellip;]<\/p>\n","protected":false},"author":7,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[4],"tags":[],"class_list":["post-10021103","post","type-post","status-publish","format-standard","hentry","category-share"],"jetpack_sharing_enabled":true,"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/itmax.cn\/index.php?rest_route=\/wp\/v2\/posts\/10021103","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/itmax.cn\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/itmax.cn\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/itmax.cn\/index.php?rest_route=\/wp\/v2\/users\/7"}],"replies":[{"embeddable":true,"href":"https:\/\/itmax.cn\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=10021103"}],"version-history":[{"count":0,"href":"https:\/\/itmax.cn\/index.php?rest_route=\/wp\/v2\/posts\/10021103\/revisions"}],"wp:attachment":[{"href":"https:\/\/itmax.cn\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=10021103"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/itmax.cn\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=10021103"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/itmax.cn\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=10021103"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}