news 2026/1/7 21:00:37

副业实战:一个Java程序员如何用一天时间做出赚钱的AI网站

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
副业实战:一个Java程序员如何用一天时间做出赚钱的AI网站

最近用一天时间做了个AI图片生成网站,没想到一个月后流量不错。今天跟大家分享一下整个开发过程。

为什么选Java?大家都在用Python啊

是的,我知道AI领域Python是主流,但是:

  1. 我Java最熟,用熟悉的工具效率最高
  2. Spring Boot生态完善,啥都有现成的
  3. 部署方便,一个jar包搞定
  4. 性能够用,并发处理比Python强

核心思路:Java做Web服务,调用第三方AI API生成图片,不需要自己训练模型。


技术栈选择(都是最简单的)

后端:Spring Boot 3.2 + JDK 21 数据库:PostgreSQL(存用户和订单) 缓存:Redis(存图片生成队列) 存储:AWS S3(存生成的图片) AI API:nona bananan pro 前端:Thymeleaf + Alpine.js(是的,没用React)

为什么这么选?

  • Spring Boot:开箱即用,不用折腾配置
  • PostgreSQL:免费,性能够用
  • Thymeleaf:服务端渲染,SEO友好(重要!)
  • Alpine.js:轻量级,学习成本低

一天开发时间表(真实记录)

上午 9:00 - 12:00:搭架子(3小时)

1. 初始化项目(30分钟)
# 用 Spring Initializr 生成项目curlhttps://start.spring.io/starter.zip\-ddependencies=web,data-jpa,redis,thymeleaf,security\-dtype=maven-project\-djavaVersion=21\-o nano-banana.zipunzipnano-banana.zipcdnano-banana
2. 配置文件(30分钟)
# application.ymlspring:datasource:url:jdbc:postgresql://localhost:5432/nanobanausername:${DB_USER}password:${DB_PASSWORD}redis:host:localhostport:6379servlet:multipart:max-file-size:10MBmax-request-size:10MB# 自定义配置ai:api-key:${STABILITY_API_KEY}api-url:https://api.stability.ai/v1/generationaws:s3:bucket:nano-banana-imagesregion:us-east-1
3. 核心实体类(1小时)
@Entity@Table(name="users")publicclassUser{@Id@GeneratedValue(strategy=GenerationType.IDENTITY)privateLongid;privateStringemail;privateStringpasswordHash;privateIntegercredits=2400;// 初始积分@CreationTimestampprivateLocalDateTimecreatedAt;}@Entity@Table(name="images")publicclassGeneratedImage{@Id@GeneratedValue(strategy=GenerationType.IDENTITY)privateLongid;@ManyToOneprivateUseruser;privateStringprompt;// 用户输入的提示词privateStringimageUrl;// S3存储地址privateStringstatus;// pending, completed, failed@CreationTimestampprivateLocalDateTimecreatedAt;}@Entity@Table(name="orders")publicclassOrder{@Id@GeneratedValue(strategy=GenerationType.IDENTITY)privateLongid;@ManyToOneprivateUseruser;privateBigDecimalamount;// 4.99privateIntegercredits;// 2400privateStringstatus;// paid, pending@CreationTimestampprivateLocalDateTimecreatedAt;}
4. Repository层(30分钟)
publicinterfaceUserRepositoryextendsJpaRepository<User,Long>{Optional<User>findByEmail(Stringemail);}publicinterfaceImageRepositoryextendsJpaRepository<GeneratedImage,Long>{List<GeneratedImage>findByUserOrderByCreatedAtDesc(Useruser);@Query("SELECT COUNT(i) FROM GeneratedImage i WHERE i.createdAt > :date")LongcountGeneratedToday(@Param("date")LocalDateTimedate);}publicinterfaceOrderRepositoryextendsJpaRepository<Order,Long>{List<Order>findByUserAndStatus(Useruser,Stringstatus);}

下午 13:00 - 18:00:核心功能(5小时)

5. AI图片生成服务(2小时)

这是核心中的核心!

@Service@Slf4jpublicclassImageGenerationService{@Value("${ai.api-key}")privateStringapiKey;@Value("${ai.api-url}")privateStringapiUrl;@AutowiredprivateRestTemplaterestTemplate;@AutowiredprivateS3Services3Service;@AutowiredprivateRedisTemplate<String,Object>redisTemplate;/** * 生成图片(异步) */publicCompletableFuture<String>generateImage(Stringprompt,Useruser){returnCompletableFuture.supplyAsync(()->{try{// 1. 调用AI APIHttpHeadersheaders=newHttpHeaders();headers.set("Authorization","Bearer "+apiKey);headers.setContentType(MediaType.APPLICATION_JSON);Map<String,Object>request=Map.of("text_prompts",List.of(Map.of("text",prompt)),"cfg_scale",7,"height",1024,"width",1024,"samples",1,"steps",30);HttpEntity<Map<String,Object>>entity=newHttpEntity<>(request,headers);ResponseEntity<Map>response=restTemplate.postForEntity(apiUrl+"/text-to-image",entity,Map.class);// 2. 获取生成的图片(base64)List<Map<String,String>>artifacts=(List<Map<String,String>>)response.getBody().get("artifacts");Stringbase64Image=artifacts.get(0).get("base64");// 3. 上传到S3byte[]imageBytes=Base64.getDecoder().decode(base64Image);StringfileName=UUID.randomUUID().toString()+".png";StringimageUrl=s3Service.uploadImage(fileName,imageBytes);// 4. 扣除积分user.setCredits(user.getCredits()-4);userRepository.save(user);log.info("图片生成成功: {}",imageUrl);returnimageUrl;}catch(Exceptione){log.error("图片生成失败",e);thrownewRuntimeException("生成失败,请重试");}});}/** * 检查用户积分 */publicbooleanhasEnoughCredits(Useruser){returnuser.getCredits()>=4;}}
6. S3存储服务(1小时)
@ServicepublicclassS3Service{@AutowiredprivateAmazonS3s3Client;@Value("${aws.s3.bucket}")privateStringbucketName;publicStringuploadImage(StringfileName,byte[]imageData){try{ObjectMetadatametadata=newObjectMetadata();metadata.setContentLength(imageData.length);metadata.setContentType("image/png");InputStreaminputStream=newByteArrayInputStream(imageData);s3Client.putObject(bucketName,fileName,inputStream,metadata);// 返回公开访问URLreturns3Client.getUrl(bucketName,fileName).toString();}catch(Exceptione){thrownewRuntimeException("上传失败",e);}}}
7. 支付服务(Stripe集成)(1小时)
@ServicepublicclassPaymentService{@Value("${stripe.api-key}")privateStringstripeApiKey;@AutowiredprivateOrderRepositoryorderRepository;/** * 创建支付会话 */publicStringcreateCheckoutSession(Useruser)throwsStripeException{Stripe.apiKey=stripeApiKey;SessionCreateParamsparams=SessionCreateParams.builder().setMode(SessionCreateParams.Mode.PAYMENT).setSuccessUrl("https://yourdomain.com/payment/success").setCancelUrl("https://yourdomain.com/payment/cancel").addLineItem(SessionCreateParams.LineItem.builder().setPriceData(SessionCreateParams.LineItem.PriceData.builder().setCurrency("usd").setUnitAmount(499L)// $4.99.setProductData(SessionCreateParams.LineItem.PriceData.ProductData.builder().setName("2400 Credits").build()).build()).setQuantity(1L).build()).putMetadata("userId",user.getId().toString()).build();Sessionsession=Session.create(params);// 创建订单记录Orderorder=newOrder();order.setUser(user);order.setAmount(newBigDecimal("4.99"));order.setCredits(2400);order.setStatus("pending");orderRepository.save(order);returnsession.getUrl();}/** * 处理支付回调 */publicvoidhandleWebhook(Stringpayload,StringsigHeader){// Stripe webhook验证和处理// 支付成功后给用户加积分}}
8. Controller层(1小时)
@Controller@RequestMapping("/")publicclassHomeController{@AutowiredprivateImageGenerationServiceimageService;@AutowiredprivateUserRepositoryuserRepository;/** * 首页 */@GetMappingpublicStringhome(Modelmodel,@AuthenticationPrincipalUserDetailsuserDetails){if(userDetails!=null){Useruser=userRepository.findByEmail(userDetails.getUsername()).orElseThrow();model.addAttribute("credits",user.getCredits());}return"index";}/** * 生成图片 */@PostMapping("/generate")@ResponseBodypublicResponseEntity<?>generateImage(@RequestParamStringprompt,@AuthenticationPrincipalUserDetailsuserDetails){Useruser=userRepository.findByEmail(userDetails.getUsername()).orElseThrow();// 检查积分if(!imageService.hasEnoughCredits(user)){returnResponseEntity.badRequest().body(Map.of("error","积分不足,请充值"));}// 异步生成CompletableFuture<String>future=imageService.generateImage(prompt,user);returnResponseEntity.ok(Map.of("status","processing","message","图片生成中,请稍候..."));}/** * 我的图片 */@GetMapping("/my-images")publicStringmyImages(Modelmodel,@AuthenticationPrincipalUserDetailsuserDetails){Useruser=userRepository.findByEmail(userDetails.getUsername()).orElseThrow();List<GeneratedImage>images=imageRepository.findByUserOrderByCreatedAtDesc(user);model.addAttribute("images",images);return"my-images";}}

晚上 19:00 - 22:00:前端和部署(3小时)

9. 前端页面(Thymeleaf)(1.5小时)
<!-- index.html --><!DOCTYPEhtml><htmlxmlns:th="http://www.thymeleaf.org"><head><title>Nano Banana Pro - AI Image Generator</title><metaname="description"content="Generate stunning images with AI in seconds"><scriptsrc="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script><scriptsrc="https://cdn.tailwindcss.com"></script></head><body><divx-data="imageGenerator()"class="container mx-auto px-4 py-8"><!-- Header --><headerclass="mb-8"><h1class="text-4xl font-bold">Nano Banana Pro</h1><pclass="text-gray-600">AI-Powered Image Generation</p><divclass="mt-4"><spanclass="text-lg">Credits:<strongth:text="${credits}">0</strong></span><ahref="/pricing"class="ml-4 text-blue-600">Buy More</a></div></header><!-- Generation Form --><divclass="max-w-2xl mx-auto"><textareax-model="prompt"placeholder="Describe your image..."class="w-full p-4 border rounded-lg"rows="4"></textarea><button@click="generate()":disabled="loading"class="mt-4 px-6 py-3 bg-blue-600 text-white rounded-lg"><spanx-show="!loading">Generate Image</span><spanx-show="loading">Generating...</span></button><!-- Result --><divx-show="imageUrl"class="mt-8"><img:src="imageUrl"class="w-full rounded-lg shadow-lg"><button@click="download()"class="mt-4 px-4 py-2 bg-green-600 text-white rounded">Download</button></div></div></div><script>functionimageGenerator(){return{prompt:'',loading:false,imageUrl:null,asyncgenerate(){if(!this.prompt)return;this.loading=true;try{constresponse=awaitfetch('/generate',{method:'POST',headers:{'Content-Type':'application/x-www-form-urlencoded',},body:`prompt=${encodeURIComponent(this.prompt)}`});constdata=awaitresponse.json();if(data.error){alert(data.error);return;}// 轮询检查生成状态this.pollStatus();}catch(error){alert('Generation failed');}finally{this.loading=false;}},asyncpollStatus(){// 每2秒检查一次constinterval=setInterval(async()=>{constresponse=awaitfetch('/status');constdata=awaitresponse.json();if(data.status==='completed'){this.imageUrl=data.imageUrl;clearInterval(interval);}},2000);},download(){window.open(this.imageUrl,'_blank');}}}</script></body></html>
10. Docker部署(1小时)
# Dockerfile FROM eclipse-temurin:21-jdk-alpine WORKDIR /app COPY target/*.jar app.jar EXPOSE 8080 ENTRYPOINT ["java", "-jar", "app.jar"]
# docker-compose.ymlversion:'3.8'services:app:build:.ports:-"8080:8080"environment:-DB_USER=${DB_USER}-DB_PASSWORD=${DB_PASSWORD}-STABILITY_API_KEY=${STABILITY_API_KEY}-STRIPE_API_KEY=${STRIPE_API_KEY}depends_on:-db-redisdb:image:postgres:15environment:-POSTGRES_DB=nanobana-POSTGRES_USER=${DB_USER}-POSTGRES_PASSWORD=${DB_PASSWORD}volumes:-postgres_data:/var/lib/postgresql/dataredis:image:redis:7-alpineports:-"6379:6379"volumes:postgres_data:
11. 部署到AWS(30分钟)
# 打包./mvnw clean package -DskipTests# 构建Docker镜像docker build -t nano-banana:latest.# 推送到ECRaws ecr get-login-password --region us-east-1|docker login --username AWS --password-stdin xxx.dkr.ecr.us-east-1.amazonaws.com docker tag nano-banana:latest xxx.dkr.ecr.us-east-1.amazonaws.com/nano-banana:latest docker push xxx.dkr.ecr.us-east-1.amazonaws.com/nano-banana:latest# 在EC2上运行sshec2-user@your-server docker-compose up -d

遇到的坑和解决方案

坑1:AI API超时

问题:Stability AI生成图片需要30-60秒,HTTP请求超时。

解决

// 改成异步 + WebSocket推送结果@ServicepublicclassImageGenerationService{@AutowiredprivateSimpMessagingTemplatemessagingTemplate;publicvoidgenerateImageAsync(Stringprompt,Useruser){CompletableFuture.runAsync(()->{StringimageUrl=callAIAPI(prompt);// 通过WebSocket推送给前端messagingTemplate.convertAndSendToUser(user.getEmail(),"/queue/images",Map.of("imageUrl",imageUrl));});}}

坑2:S3上传慢

问题:图片上传到S3要5-10秒。

解决

// 使用S3 Transfer Manager加速@BeanpublicTransferManagertransferManager(AmazonS3s3Client){returnTransferManagerBuilder.standard().withS3Client(s3Client).withMultipartUploadThreshold(5*1024*1024L)// 5MB.withMinimumUploadPartSize(5*1024*1024L).build();}

坑3:并发问题

问题:多个用户同时生成图片,积分扣除出现负数。

解决

// 使用Redis分布式锁@ServicepublicclassCreditService{@AutowiredprivateRedissonClientredissonClient;publicbooleandeductCredits(Useruser,intamount){RLocklock=redissonClient.getLock("user:credits:"+user.getId());try{lock.lock(5,TimeUnit.SECONDS);if(user.getCredits()<amount){returnfalse;}user.setCredits(user.getCredits()-amount);userRepository.save(user);returntrue;}finally{lock.unlock();}}}

性能优化

1. 数据库索引

CREATEINDEXidx_images_user_createdONimages(user_id,created_atDESC);CREATEINDEXidx_orders_user_statusONorders(user_id,status);

2. Redis缓存

@Cacheable(value="user",key="#email")publicUserfindByEmail(Stringemail){returnuserRepository.findByEmail(email).orElseThrow();}@CacheEvict(value="user",key="#user.email")publicvoidupdateUser(Useruser){userRepository.save(user);}

3. 图片CDN

// 使用CloudFront加速S3图片访问@Value("${cloudfront.domain}")privateStringcdnDomain;publicStringgetImageUrl(Strings3Key){return"https://"+cdnDomain+"/"+s3Key;}

运营数据(一个月后)

月访问量:18,710 注册用户:1,240 付费用户:89 (7.2% 转化率) 月收入:$444 (89 × $4.99) 服务器成本:$50/月 净利润:$394/月

关键指标

  • 用户留存率(7天):45%
  • 平均每用户生成图片:12张
  • 付费转化周期:平均3天

给独立开发者的建议

1. 不要重复造轮子

  • 用现成的AI API,别自己训练模型
  • 用成熟的支付方案(Stripe),别自己搞
  • 用云服务(AWS/Vercel),别自己运维

2. MVP先行

我第一版只有3个功能:

  • 生成图片
  • 购买积分
  • 查看历史

其他功能都是后来加的。

3. SEO从第一天开始

  • 服务端渲染(Thymeleaf)
  • 语义化HTML
  • 合理的URL结构
  • sitemap.xml

4. 监控很重要

// 加上监控@Slf4j@Aspect@ComponentpublicclassPerformanceMonitor{@Around("@annotation(org.springframework.web.bind.annotation.PostMapping)")publicObjectmonitor(ProceedingJoinPointjoinPoint)throwsThrowable{longstart=System.currentTimeMillis();try{returnjoinPoint.proceed();}finally{longduration=System.currentTimeMillis()-start;log.info("Method {} took {}ms",joinPoint.getSignature().getName(),duration);}}}

总结

用Java做AI应用完全可行,关键是:

  1. 别纠结技术栈,用你最熟的
  2. 快速上线,边做边改
  3. 关注用户需求,不是技术炫技
  4. 数据驱动,看数据做决策
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/1/7 12:33:35

RTL8852BE驱动:Linux无线网卡兼容性问题终极解决方案

RTL8852BE驱动&#xff1a;Linux无线网卡兼容性问题终极解决方案 【免费下载链接】rtl8852be Realtek Linux WLAN Driver for RTL8852BE 项目地址: https://gitcode.com/gh_mirrors/rt/rtl8852be 还在为Linux系统下的无线网卡问题而烦恼吗&#xff1f;RTL8852BE驱动为您…

作者头像 李华
网站建设 2026/1/6 8:25:26

Honor of Kings (S41) 100star 2025.12.17

Honor of Kings (S41) 100star 赛季41#&#xff0c;百星王者&#xff0c;传奇王者 15.3分的高分 干将莫邪&#xff0c;也是爆发中单&#xff0c;给对面压力很大的 芈月&#xff0c;单打上路可以&#xff0c;打团就很脆皮&#xff0c;马上就要交大招 庄周&#xff0c;我的本命…

作者头像 李华
网站建设 2026/1/6 4:23:56

LobeChat支持流式输出吗?实测大模型响应延迟表现

LobeChat支持流式输出吗&#xff1f;实测大模型响应延迟表现 在当前AI应用井喷的时代&#xff0c;用户早已不满足于“点击提问、等待十几秒后弹出一整段答案”的交互模式。我们越来越期待AI像人一样边思考边表达——前一句话刚说完&#xff0c;下一句就已经开始浮现。这种“打字…

作者头像 李华
网站建设 2026/1/7 2:20:06

LobeChat OpenAI GPT-3.5/4接入配置详解

LobeChat OpenAI GPT-3.5/4接入配置详解 在如今大模型遍地开花的时代&#xff0c;越来越多开发者不再满足于直接调用原始 API 来实现“问答机器人”——那种纯代码交互的体验&#xff0c;既不友好&#xff0c;也不适合落地到真实业务场景。人们真正需要的是一个开箱即用、界面优…

作者头像 李华
网站建设 2026/1/5 13:02:33

EmotiVoice语音合成安全性评估:防滥用机制探讨

EmotiVoice语音合成安全性评估&#xff1a;防滥用机制探讨 在虚拟主播实时互动、智能助手温柔应答的今天&#xff0c;我们正越来越难分辨声音背后的“真实身份”。一段仅5秒的社交媒体录音&#xff0c;可能足以让AI复刻出与本人几乎无异的声线&#xff1b;一句写好的文字&#…

作者头像 李华