<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>开源项目 on 方糖猫Blog</title>
    <link>https://ftmao.com/blog/tags/%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE/</link>
    <description>AI 视频、AI Agent 与工程实践笔记</description>
    <generator>Hugo</generator>
    <language>zh-CN</language>
    <lastBuildDate>Mon, 13 Jul 2026 18:00:00 +0800</lastBuildDate><atom:link href="https://ftmao.com/blog/tags/%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE/index.xml" rel="self" type="application/rss+xml" /><item>
      <title>GeekCast vs OpenCut：两款视频编辑项目的功能对比</title>
      <link>https://ftmao.com/blog/posts/geekcast-vs-opencut/</link>
      <pubDate>Mon, 13 Jul 2026 18:00:00 +0800</pubDate>
      <guid>https://ftmao.com/blog/posts/geekcast-vs-opencut/</guid>
      <description>&lt;h1 id=&#34;geekcast-vs-opencut两款视频编辑项目的功能对比&#34;&gt;GeekCast vs OpenCut：两款视频编辑项目的功能对比&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;我个人最近开发的GeekCast &lt;a href=&#34;https://gitee.com/soulnew/geekcast&#34;&gt;GeekCast&lt;/a&gt;（我开发的极客口播自动化剪辑工具）, 用AI对比了开源项目 OpenCut 的功能、架构与定位差异。GeekCast 已开源，地址：https://gitee.com/soulnew/geekcast。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;一项目定位对比&#34;&gt;一、项目定位对比&lt;/h2&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;维度&lt;/th&gt;
					&lt;th&gt;OpenCut&lt;/th&gt;
					&lt;th&gt;GeekCast Engine&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;产品形态&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;通用开源非线性视频编辑器（NLE），面向普通创作者&lt;/td&gt;
					&lt;td&gt;垂直的&amp;quot;口播自动化剪辑工具&amp;quot;，面向技术博主、独立开发者、播客主&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;核心假设&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;用户在时间线上手动剪辑&lt;/td&gt;
					&lt;td&gt;&lt;strong&gt;视频即文本&lt;/strong&gt;：用户像编辑文档一样删除气口、静音、语气词&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;目标平台&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;Web + Desktop + Mobile（重写版目标）&lt;/td&gt;
					&lt;td&gt;Web 管理台 + Java API + Python Worker，本地或私有部署&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;开源协议&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;MIT&lt;/td&gt;
					&lt;td&gt;已开源（Gitee）&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id=&#34;二功能对比&#34;&gt;二、功能对比&lt;/h2&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;功能&lt;/th&gt;
					&lt;th&gt;OpenCut（当前重写版）&lt;/th&gt;
					&lt;th&gt;OpenCut（classic）&lt;/th&gt;
					&lt;th&gt;GeekCast&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;视频导入/素材管理&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;❌ 未实现&lt;/td&gt;
					&lt;td&gt;✅ 支持&lt;/td&gt;
					&lt;td&gt;✅ 统一 &lt;code&gt;public/assets&lt;/code&gt; 目录，支持原始素材、B-roll、脱水视频&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;时间线编辑&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;❌ 无时间线模型&lt;/td&gt;
					&lt;td&gt;✅ 传统时间线&lt;/td&gt;
					&lt;td&gt;✅ 文本化 timeline：&lt;code&gt;MAIN_VOICE&lt;/code&gt; / &lt;code&gt;B_ROLL&lt;/code&gt; / &lt;code&gt;REMOTION_TEMPLATE&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;词级删除/气口分析&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;❌ 无&lt;/td&gt;
					&lt;td&gt;一般 NLE 手动处理&lt;/td&gt;
					&lt;td&gt;✅ Whisper 转录 + VAD，支持词级删除与多种语义块&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;AI 自动化&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;❌ 仅 README 规划&lt;/td&gt;
					&lt;td&gt;有限&lt;/td&gt;
					&lt;td&gt;✅ 转录 → LLM 生成 Master Config → 向量匹配 B-roll → WanX 生图&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;B-roll 覆盖&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;❌ 无&lt;/td&gt;
					&lt;td&gt;基础轨道叠加&lt;/td&gt;
					&lt;td&gt;✅ KEN_BURNS、PIP、FULL_SCREEN、SLIDE_IN、ZOOM_POP、MOSAIC 等 10 种展示方式&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;字幕/特效&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;❌ 无&lt;/td&gt;
					&lt;td&gt;基础字幕&lt;/td&gt;
					&lt;td&gt;✅ Remotion 模板 + 字幕动效（打字机、渐变边框、BURST 等）&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;音效&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;❌ 无&lt;/td&gt;
					&lt;td&gt;基础音频轨道&lt;/td&gt;
					&lt;td&gt;✅ segment 级音效配置，FFmpeg &lt;code&gt;adelay+amix&lt;/code&gt; 混合&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;渲染导出&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;❌ 无渲染管线&lt;/td&gt;
					&lt;td&gt;客户端/云端渲染&lt;/td&gt;
					&lt;td&gt;✅ Remotion CLI 渲染 + FFmpeg 后处理&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;横竖屏分辨率&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;❌ 未涉及&lt;/td&gt;
					&lt;td&gt;通用&lt;/td&gt;
					&lt;td&gt;✅ &lt;code&gt;outputResolution&lt;/code&gt; 统一配置，支持 16:9 与 9:16&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;插件系统&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;📋 规划中 plugin-first&lt;/td&gt;
					&lt;td&gt;有限&lt;/td&gt;
					&lt;td&gt;❌ 无插件机制，特效/模板硬编码&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;MCP / AI Agent 接口&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;📋 规划中&lt;/td&gt;
					&lt;td&gt;无&lt;/td&gt;
					&lt;td&gt;❌ 无&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;Headless / 批量渲染&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;📋 规划中&lt;/td&gt;
					&lt;td&gt;无&lt;/td&gt;
					&lt;td&gt;⚠️ Worker 任务队列可批量，但无公开 API/Scripting&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;移动端&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;📋 规划中&lt;/td&gt;
					&lt;td&gt;有 Web/移动端&lt;/td&gt;
					&lt;td&gt;❌ 不支持&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id=&#34;三技术架构对比&#34;&gt;三、技术架构对比&lt;/h2&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;维度&lt;/th&gt;
					&lt;th&gt;OpenCut（重写版）&lt;/th&gt;
					&lt;th&gt;GeekCast&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;前端&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;React 19 + TanStack Router/Start + Tailwind v4 + shadcn/ui&lt;/td&gt;
					&lt;td&gt;Vue 3 + Vite + Pinia&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;后端&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;Elysia + Cloudflare Workers&lt;/td&gt;
					&lt;td&gt;Spring Boot 3 + Java 17 + MySQL + RocketMQ&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;桌面端&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;Rust + GPUI（Zed 同款）&lt;/td&gt;
					&lt;td&gt;无&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;核心渲染&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;未开始（目标：Rust core 跨 Web/Desktop/Mobile）&lt;/td&gt;
					&lt;td&gt;Python Worker + Remotion (React/TypeScript/Node) + FFmpeg&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;工程化&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;moonrepo + proto + Bun + Rust 工具链&lt;/td&gt;
					&lt;td&gt;Maven/Gradle + Python pip + Node npm&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;部署&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;Cloudflare Pages + Workers，边缘原生&lt;/td&gt;
					&lt;td&gt;本地/私有服务器，文件系统共享&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id=&#34;四opencut-优劣势&#34;&gt;四、OpenCut 优劣势&lt;/h2&gt;
&lt;h3 id=&#34;41-优势&#34;&gt;4.1 优势&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;开源 + 通用&lt;/strong&gt;：MIT 协议，目标是&amp;quot;人人都能用的开源 Premiere/CapCut&amp;quot;，生态潜力大。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;跨端野心&lt;/strong&gt;：Rust core + Web/Desktop/Mobile 一套代码，若做成可避免重复开发。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;AI/Agent 原生&lt;/strong&gt;：规划了 MCP Server、Headless 模式、Scripting Tab，未来可能天然适配 AI 工作流。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;现代化工具链&lt;/strong&gt;：moonrepo、GPUI、TanStack、Elysia 都是当前较新的技术选型。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;42-劣势&#34;&gt;4.2 劣势&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;当前几乎空白&lt;/strong&gt;：本地仓库只有 3 个空壳应用（Web 首页 Hello World、API 3 个健康检查端点、Desktop 一个窗口壳），没有实际剪辑能力。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;工程风险高&lt;/strong&gt;：Rust core 跨 Web(WASM)/Desktop/Mobile 的共享渲染、插件 ABI/WASM、移动端适配都是高难度目标，落地周期极长。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;经典版割裂&lt;/strong&gt;：README 自己都说当前能用的是另一个仓库 &lt;code&gt;opencut-classic&lt;/code&gt;，重写版与经典版暂不兼容。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;五geekcast-优劣势&#34;&gt;五、GeekCast 优劣势&lt;/h2&gt;
&lt;h3 id=&#34;51-优势&#34;&gt;5.1 优势&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;端到端可运行&lt;/strong&gt;：从上传口播 → Whisper 转录 → 气口分析 → 精剪 → B-roll/特效/音效 → Remotion 渲染，整条链路已实现。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;文本驱动降低门槛&lt;/strong&gt;：不需要用户懂非线性剪辑，删除气口像删文字一样。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;AI 链路完整&lt;/strong&gt;：LLM 生成剧本/时间线、CLIP 向量匹配 B-roll、WanX 生图、提示词增强，自动化程度高。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;视觉能力丰富&lt;/strong&gt;：Remotion 模板、字幕动效、B-roll 多种展示方式、竖屏适配，成片风格统一。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;强契约文化&lt;/strong&gt;：&lt;code&gt;ai_work/design/&lt;/code&gt; 里有大量跨层契约、边界值、问题复盘，适合长期维护。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;52-劣势&#34;&gt;5.2 劣势&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;领域狭窄&lt;/strong&gt;：只擅长&amp;quot;口播 → 短视频&amp;quot;这一类内容，无法做通用 Vlog、剧情片、多机位等复杂剪辑。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;本地/Windows 中心化&lt;/strong&gt;：很多路径、Chrome Headless、资产目录都是本地假设，难以直接 SaaS 化或多租户。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;扩展性差&lt;/strong&gt;：没有插件系统，新增特效/模板需要改 Remotion 代码并重新部署 Worker。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;AI 瓶颈明显&lt;/strong&gt;：LLM 输出 token 限制、AAC padding、B-roll 节奏等问题都依赖后处理和 prompt 调优。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;六结论与可借鉴点&#34;&gt;六、结论与可借鉴点&lt;/h2&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;你的场景&lt;/th&gt;
					&lt;th&gt;建议&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;想做一个通用、开源、社区化的视频编辑器&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;OpenCut 的方向更匹配，但要接受它目前只是脚手架，可能需要从 classic 版或自己重新设计核心。&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;想快速落地&amp;quot;口播/播客&amp;quot;自动剪辑&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;GeekCast 的架构和契约更成熟，可直接作为参考甚至基础。&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;GeekCast 想补齐 AI Agent / 批量渲染 / 插件能力&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;可以借鉴 OpenCut 规划中的 MCP Server、Headless CLI、plugin-first 架构，把 Remotion 模板做成可插拔配置。&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;OpenCut 想做出口播自动化功能&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;可以参考 GeekCast 的&amp;quot;视频即文本&amp;quot;工作流、Whisper+VAD 气口模型、Remotion+FFmpeg 渲染管线。&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;简而言之：&lt;strong&gt;OpenCut 是&amp;quot;想做通用编辑器的宏大蓝图&amp;quot;，GeekCast 是&amp;quot;在口播自动剪辑这个细分领域已经跑通的工程实现&amp;quot;&lt;/strong&gt;。两者目前不在同一个完成度层级上，但互相有不少可借鉴的设计理念。&lt;/p&gt;
&lt;hr&gt;
&lt;blockquote&gt;
&lt;p&gt;如果你在视频自动化、AI 剪辑或多模态生成方面有想法，欢迎在评论区交流。&lt;/p&gt;
&lt;/blockquote&gt;
</description>
    </item>
    <item>
      <title>starmall：一个微信小程序电商系统的全栈实战——预约、支付与订单架构解析</title>
      <link>https://ftmao.com/blog/posts/starmall-wechat-miniprogram-ecommerce-fullstack/</link>
      <pubDate>Tue, 20 Jul 2021 10:00:00 +0800</pubDate>
      <guid>https://ftmao.com/blog/posts/starmall-wechat-miniprogram-ecommerce-fullstack/</guid>
      <description>&lt;h1 id=&#34;starmall一个微信小程序电商系统的全栈实战预约支付与订单架构解析&#34;&gt;starmall：一个微信小程序电商系统的全栈实战——预约、支付与订单架构解析&lt;/h1&gt;
&lt;p&gt;这是我 2021 年做的一套完整电商系统 &lt;strong&gt;starmall&lt;/strong&gt;（六爻星辰）：微信小程序端 + Java 后端 + PC 管理后台，支持商品购买、老师咨询时段预约、择吉日查询、在线咨询等业务。整理开源出来，写篇文章做一次完整的技术复盘。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;前端（uni-app 小程序）：&lt;a href=&#34;https://gitee.com/soulnew/starmall&#34;&gt;https://gitee.com/soulnew/starmall&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;后端（Java + 管理端）：&lt;a href=&#34;https://gitee.com/soulnew/crm_server&#34;&gt;https://gitee.com/soulnew/crm_server&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;技术栈：Spring Boot 2.2 + MyBatis-Plus + MySQL + Redis / uni-app + uView / Vue 2 + Element UI（管理端）&lt;/li&gt;
&lt;li&gt;说明：后端是在开源商城 &lt;strong&gt;CRMEB Java 版&lt;/strong&gt; 基础上二次开发的，分销/秒杀/拼团/砍价等营销能力来自底座，预约系统、择吉日、名片、咨询聊天等是自研部分&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;一整体架构&#34;&gt;一、整体架构&lt;/h2&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph TD
    MP[&amp;#34;微信小程序端&amp;lt;br/&amp;gt;starmall uni-app + uView&amp;#34;] --&amp;gt;|&amp;#34;https://ftmao.com/api/front/**&amp;#34;| API[&amp;#34;SpringBoot 单体后端&amp;lt;br/&amp;gt;62 个 Mapper / 23 个前台 Controller&amp;#34;]
    H5[&amp;#34;H5 / App&amp;lt;br/&amp;gt;uni-app 多端编译&amp;#34;] --&amp;gt; API
    ADMIN[&amp;#34;PC 管理后台&amp;lt;br/&amp;gt;Vue 2 + Element UI + ECharts&amp;#34;] --&amp;gt;|&amp;#34;后台接口&amp;#34;| API
    API --&amp;gt; MYSQL[&amp;#34;MySQL 5.7&amp;lt;br/&amp;gt;62 张表 Druid 连接池&amp;#34;]
    API --&amp;gt; REDIS[&amp;#34;Redis&amp;lt;br/&amp;gt;token / 订单缓存 / 任务队列 / 配置缓存&amp;#34;]
    API --&amp;gt; WX[&amp;#34;微信开放平台&amp;lt;br/&amp;gt;登录 / 支付 V2 / 订阅消息&amp;#34;]
    API --&amp;gt; QN[&amp;#34;七牛云 OSS&amp;lt;br/&amp;gt;图片上传&amp;#34;]
    API --&amp;gt; QY[&amp;#34;企业微信机器人&amp;lt;br/&amp;gt;部署结果通知&amp;#34;]
    MP --&amp;gt;|&amp;#34;wss://... websocket&amp;#34;| CHAT[&amp;#34;咨询聊天服务&amp;#34;]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;代码规模：后端约 850 个 Java 文件、12.6 万行；小程序前端业务代码约 1.2 万行；数据库初始化 SQL 全量在仓库里，共 62 张表。后端按业务分包：&lt;code&gt;front&lt;/code&gt;（小程序接口）、&lt;code&gt;order/payment&lt;/code&gt;、&lt;code&gt;marketing&lt;/code&gt;、&lt;code&gt;seckill&lt;/code&gt;、&lt;code&gt;task&lt;/code&gt;（定时任务）、&lt;code&gt;wechat&lt;/code&gt;，以及自研的 &lt;code&gt;appoint&lt;/code&gt;（预约）、&lt;code&gt;lunar&lt;/code&gt;（黄历）等模块。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;二功能介绍&#34;&gt;二、功能介绍&lt;/h2&gt;
&lt;h3 id=&#34;21-商品购买全流程&#34;&gt;2.1 商品购买全流程&lt;/h3&gt;
&lt;p&gt;标准的商城闭环：商品详情（多规格 SKU）→ 立即购买/加购物车 → 确认订单（优惠券、积分抵扣、运费计算）→ 创建订单 → 微信支付 → 轮询支付结果 → 收货评价 → 退款/售后。&lt;strong&gt;支付是真实对接的微信支付 V2&lt;/strong&gt;——后端手工实现 XML 报文 + MD5 签名调统一下单（&lt;code&gt;WeChatPayServiceImpl&lt;/code&gt;，746 行），前端弹起 &lt;code&gt;wx.requestPayment&lt;/code&gt;，不是 mock。&lt;/p&gt;
&lt;p&gt;此外还支持：余额支付、运费模板（满额包邮/指定城市包邮/按件数重量体积三种首续费）、物流查询、自提核销码、再次下单。营销玩法（秒杀、砍价、拼团、优惠券）来自 CRMEB 底座，一应俱全。&lt;/p&gt;
&lt;h3 id=&#34;22-老师咨询时段预约自研核心&#34;&gt;2.2 老师咨询时段预约（自研核心）&lt;/h3&gt;
&lt;p&gt;这是这个项目区别于普通商城的地方——卖的不只是实物商品，还有&amp;quot;老师的时间&amp;quot;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;商品分类为&amp;quot;老师类&amp;quot;时，商品详情页出现一个&lt;strong&gt;时段选择器&lt;/strong&gt;（每天 6 个时段：9/11/13/15/17/19 点）；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;teacher_plan&lt;/code&gt; 表按「日期 × 时段」预生成号源，被约走的时段前端置灰；&lt;/li&gt;
&lt;li&gt;下单时携带 &lt;code&gt;teacherPlanId&lt;/code&gt;，&lt;strong&gt;创建订单的同一个事务里&lt;/strong&gt;把该时段标记为已约——号源占用和订单创建要么一起成功要么一起回滚，不会出现&amp;quot;钱付了时段没了&amp;quot;；&lt;/li&gt;
&lt;li&gt;号源由定时任务 &lt;code&gt;TeacherPlanTask&lt;/code&gt; 每 8 小时自动向前补齐未来 8 天，并清理过期的空闲号源。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;23-其他自研功能&#34;&gt;2.3 其他自研功能&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;择吉日查询&lt;/strong&gt;：4.7 万行黄历数据入库（&lt;code&gt;lunar&lt;/code&gt; 系列表），按&amp;quot;宜&amp;quot;事反查吉日，比如查&amp;quot;最近适合搬家的日子&amp;quot;；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;在线咨询&lt;/strong&gt;：基于 WebSocket 长连接的实时聊天，支持文字/语音/图片消息（详见第三章「长连接聊天」）；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;老师名片&lt;/strong&gt;、文章资讯、签到积分、充值、小程序订阅消息提醒；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;管理后台&lt;/strong&gt;：商品/订单/营销/分销/财务/短信/按钮级权限/数据统计大屏（ECharts）。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;三后端能力全景&#34;&gt;三、后端能力全景&lt;/h2&gt;
&lt;p&gt;后端虽然是 SpringBoot 单体，但能力面相当完整：前台 23 个 Controller、62 个 MyBatis Mapper，按业务分包组织：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph TD
    subgraph M1[&amp;#34;业务模块&amp;#34;]
        A1[&amp;#34;front 前台接口&amp;lt;br/&amp;gt;23 个 Controller&amp;#34;]
        A2[&amp;#34;order / payment&amp;lt;br/&amp;gt;订单与支付&amp;#34;]
        A3[&amp;#34;marketing / seckill / bargain / combination&amp;lt;br/&amp;gt;优惠券 秒杀 砍价 拼团&amp;#34;]
        A4[&amp;#34;appoint / lunar / businessCard&amp;lt;br/&amp;gt;预约 择吉日 名片（自研）&amp;#34;]
        A5[&amp;#34;finance / statistics / export&amp;lt;br/&amp;gt;充值提现 数据统计 Excel 导出&amp;#34;]
        A6[&amp;#34;wechat / wechatMessage / sms&amp;lt;br/&amp;gt;微信生态与短信&amp;#34;]
        A7[&amp;#34;task&amp;lt;br/&amp;gt;定时任务体系&amp;#34;]
    end
    subgraph M2[&amp;#34;公共层&amp;#34;]
        B1[&amp;#34;拦截器链&amp;lt;br/&amp;gt;前台 Token / 后台 Token+权限 / Swagger&amp;#34;]
        B2[&amp;#34;工具层 RedisUtil / WxPayUtil / CrmebUtil&amp;#34;]
        B3[&amp;#34;ThreadLocal 上下文传递当前用户&amp;#34;]
    end
    A1 --&amp;gt; B1
    A2 --&amp;gt; B2
    B1 --&amp;gt; B3&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;商城主链路之外，后端还有一批容易被忽略的能力：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;微信公众号消息自动回复&lt;/strong&gt;：&lt;code&gt;wechatMessage&lt;/code&gt; 模块支持文字/语音/图片/图文四种回复类型，关注、取关、扫码等事件都有对应处理；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;模板消息与订阅消息&lt;/strong&gt;：小程序订阅消息、公众号模板消息的增删查与群发，且发送走异步任务（&lt;code&gt;AsyncWeChatProgramTempMessage&lt;/code&gt; 等），不阻塞主流程；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;渠道二维码与素材管理&lt;/strong&gt;：带参数的推广二维码、微信素材的上传管理；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;数据导出与统计&lt;/strong&gt;：POI 实现的 Excel 导出、交易/商品/用户多维统计接口（配合管理端 ECharts 大屏）；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;短信与物流&lt;/strong&gt;：短信验证码/通知、快递物流轨迹查询；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;接口文档&lt;/strong&gt;：Swagger 在线文档 + 仓库内置 admin/app/公共三份离线接口文档。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;31-长连接聊天websocket架构&#34;&gt;3.1 长连接聊天（WebSocket）架构&lt;/h3&gt;
&lt;p&gt;在线咨询是这个系统的&amp;quot;第二前台&amp;quot;，采用的是 &lt;strong&gt;WebSocket 长连接 + HTTP 接口&lt;/strong&gt;的组合方案。部署上两个服务跑在同一台服务器，由 nginx 按路径前缀分流：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph TD
    MP[&amp;#34;小程序聊天页&amp;lt;br/&amp;gt;pages/chat&amp;#34;] --&amp;gt;|&amp;#34;wss://.../websocket/userId/sessionId&amp;#34;| NGX[&amp;#34;nginx 按路径分流&amp;#34;]
    MP --&amp;gt;|&amp;#34;HTTP /chat/session/**  /chat/msg/**&amp;#34;| NGX
    MP --&amp;gt;|&amp;#34;HTTP /api/front/**&amp;#34;| NGX
    NGX --&amp;gt; CHAT[&amp;#34;聊天服务&amp;lt;br/&amp;gt;WebSocket 长连接 + 会话/消息接口&amp;#34;]
    NGX --&amp;gt; MALL[&amp;#34;商城后端 crm_server&amp;lt;br/&amp;gt;SpringBoot 18080&amp;#34;]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;从开源的小程序端实现和接口契约，可以看到这套长连接聊天的几个设计要点：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;连接即身份&lt;/strong&gt;：WebSocket 握手 URL 直接携带 &lt;code&gt;userId/sessionId&lt;/code&gt;，连接建立的同时就完成了身份识别与会话路由，不需要额外的登录帧；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;实时走长连接，持久化走 HTTP&lt;/strong&gt;：收发消息走 WebSocket（消息体为 JSON）；会话绑定（&lt;code&gt;session/bindSession&lt;/code&gt;）、会话列表、删除会话、历史消息拉取（&lt;code&gt;msg/msgList&lt;/code&gt;）都走 &lt;code&gt;/chat/&lt;/code&gt; 前缀的 HTTP 接口——长连接管&amp;quot;实时推送&amp;quot;，HTTP 管&amp;quot;可查询、可回溯&amp;quot;，职责分得很干净；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;统一的消息类型协议&lt;/strong&gt;：&lt;code&gt;contentType&lt;/code&gt; 1=文字、2=语音（带 &lt;code&gt;contentDuration&lt;/code&gt; 时长）、3=图片（先经 &lt;code&gt;user/upload/image&lt;/code&gt; 上传拿到 URL，再把 URL 作为消息内容发出）；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;语音消息的移动端交互&lt;/strong&gt;：按住说话（&lt;code&gt;touchstart&lt;/code&gt;/&lt;code&gt;touchend&lt;/code&gt; 控制录音起止）、上滑取消（&lt;code&gt;touchmove&lt;/code&gt;/&lt;code&gt;touchcancel&lt;/code&gt;），体验和微信原生聊天对齐；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;发送体验乐观化&lt;/strong&gt;：消息发出后先本地 push 进消息列表立即上屏，&lt;code&gt;send&lt;/code&gt; 的 fail 回调再 toast 兜底提示。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;需要如实说明：聊天服务端的代码没有随这两个仓库开源（它部署在同一台服务器上，由 nginx 分流），仓库里能看到的生产实现是小程序端；&lt;code&gt;crm_server&lt;/code&gt; 里的 &lt;code&gt;com/im/SocketServer.java&lt;/code&gt; 只是当时练手的原生 TCP Socket demo（12345 端口），并非线上实现。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;四技术亮点&#34;&gt;四、技术亮点&lt;/h2&gt;
&lt;h3 id=&#34;41-下单幂等两段式--唯一键兜底&#34;&gt;4.1 下单幂等：两段式 + 唯一键兜底&lt;/h3&gt;
&lt;p&gt;小程序网络环境下用户双击、重试、断网重发是常态，下单接口必须幂等。这里用了一个两段式设计：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph TD
    A[&amp;#34;order/confirm 确认订单页&amp;#34;] --&amp;gt; B[&amp;#34;订单数据序列化存 Redis&amp;lt;br/&amp;gt;key = md5 当前时间&amp;lt;br/&amp;gt;有效期 60 分钟&amp;#34;]
    B --&amp;gt; C[&amp;#34;order/create/orderKey 创建订单&amp;#34;]
    C --&amp;gt; D{&amp;#34;第一步:查库判重&amp;lt;br/&amp;gt;StoreOrder.unique = orderKey&amp;#34;}
    D --&amp;gt;|&amp;#34;已存在&amp;#34;| E[&amp;#34;直接返回已有订单&amp;lt;br/&amp;gt;不重复创建&amp;#34;]
    D --&amp;gt;|&amp;#34;不存在&amp;#34;| F{&amp;#34;第二步:校验 Redis 缓存&amp;lt;br/&amp;gt;是否过期/被用过&amp;#34;}
    F --&amp;gt;|&amp;#34;过期&amp;#34;| G[&amp;#34;拒绝 提示重新确认&amp;#34;]
    F --&amp;gt;|&amp;#34;有效&amp;#34;| H[&amp;#34;事务内创建订单&amp;lt;br/&amp;gt;成功后删除缓存&amp;#34;]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;&lt;code&gt;unique&lt;/code&gt; 字段是最后的兜底——即使 Redis 挂了、缓存丢了，数据库唯一约束也保证同一个 orderKey 不会建出两张单。&lt;/p&gt;
&lt;h3 id=&#34;42-原子扣库存防超卖&#34;&gt;4.2 原子扣库存防超卖&lt;/h3&gt;
&lt;p&gt;库存扣减没有用&amp;quot;先查后改&amp;quot;，而是一条 SQL 完成：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// setSql + 条件改写，数据库层面原子完成&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;stock &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; stock &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt; n   且要求   stock &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt; n &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;=&lt;/span&gt; 0&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;数据库单行更新的原子性天然挡住并发超卖，不需要分布式锁。秒杀/砍价/拼团场景更复杂：同一事务内要扣&lt;strong&gt;活动表 + 活动 SKU 表 + 商品表 + 商品 SKU 表&lt;/strong&gt;四份库存，任何一步失败整体回滚。&lt;/p&gt;
&lt;h3 id=&#34;43-编程式事务包住整个建单过程&#34;&gt;4.3 编程式事务包住整个建单过程&lt;/h3&gt;
&lt;p&gt;创建订单是个多写操作：扣库存、占预约时段、写订单主表、写订单详情、核销优惠券、写订单日志。代码用 &lt;code&gt;TransactionTemplate&lt;/code&gt; 编程式事务（而不是 &lt;code&gt;@Transactional&lt;/code&gt; 注解）把这一串操作包起来——好处是事务边界精确可控，不会把整个方法（包括不需要事务的远程调用）都圈进去。订单核心逻辑集中在 &lt;code&gt;OrderUtils.java&lt;/code&gt;（1500+ 行）和 &lt;code&gt;OrderServiceImpl.java&lt;/code&gt;（近 1000 行）。&lt;/p&gt;
&lt;h3 id=&#34;44-支付回调防重--异步化&#34;&gt;4.4 支付回调：防重 + 异步化&lt;/h3&gt;
&lt;p&gt;微信支付的回调处理（&lt;code&gt;CallbackServiceImpl.weChat&lt;/code&gt;，448 行）有几个关键点：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;解析 XML、校验 &lt;code&gt;return_code&lt;/code&gt;/&lt;code&gt;result_code&lt;/code&gt;，按 &lt;code&gt;attach&lt;/code&gt; 里携带的 userId + type 区分是订单支付还是充值；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;已支付直接返回 SUCCESS&lt;/strong&gt;——微信会多次重推回调，幂等是回调接口的生死线；&lt;/li&gt;
&lt;li&gt;事务内完成 &lt;code&gt;updatePaid&lt;/code&gt;（置订单已支付）+ 扣积分 + 拼团状态处理；&lt;/li&gt;
&lt;li&gt;后续的&amp;quot;慢动作&amp;quot;——发短信、推订阅消息、分销结算——&lt;strong&gt;不放在回调里同步做&lt;/strong&gt;，而是 &lt;code&gt;lPush&lt;/code&gt; 进 Redis 队列异步执行，回调接口尽快给微信返回，避免超时重推。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;45-没有-mqredis-list--定时任务的务实替代&#34;&gt;4.5 没有 MQ：Redis List + 定时任务的务实替代&lt;/h3&gt;
&lt;p&gt;项目没有引入 RabbitMQ/RocketMQ，而是用 &lt;strong&gt;Redis List 当队列 + Spring &lt;code&gt;@Scheduled&lt;/code&gt; 定时任务消费&lt;/strong&gt;，撑起了一整套异步任务体系：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph LR
    A[&amp;#34;下单成功&amp;lt;br/&amp;gt;lPush ORDER_AUTO_CANCEL_KEY&amp;#34;] --&amp;gt; Q[&amp;#34;Redis List 队列&amp;#34;]
    P[&amp;#34;支付成功回调&amp;lt;br/&amp;gt;lPush 后续动作&amp;#34;] --&amp;gt; Q
    Q --&amp;gt; T1[&amp;#34;OrderAutoCancelTask&amp;lt;br/&amp;gt;每分钟消费&amp;lt;br/&amp;gt;超时未支付自动取消 + 回滚库存&amp;#34;]
    Q --&amp;gt; T2[&amp;#34;自动收货 / 自动好评&amp;#34;]
    Q --&amp;gt; T3[&amp;#34;优惠券过期 / 砍价截止&amp;lt;br/&amp;gt;佣金解冻&amp;#34;]
    Q --&amp;gt; T4[&amp;#34;TeacherPlanTask&amp;lt;br/&amp;gt;每8小时补齐号源&amp;#34;]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;&lt;code&gt;task/order/&lt;/code&gt; 下共有 8 个定时任务。对这个体量的系统，省掉一个 MQ 的部署运维成本，换来的是完全够用的可靠性——这是小项目技术选型上我很认可的一个取舍。&lt;/p&gt;
&lt;h3 id=&#34;46-鉴权链路uuid-token--redis--threadlocal&#34;&gt;4.6 鉴权链路：UUID Token + Redis + ThreadLocal&lt;/h3&gt;
&lt;p&gt;没有用 JWT，而是更传统的方案：登录后签发 UUID 存 Redis（按模块区分 key 前缀），拦截器 &lt;code&gt;FrontTokenInterceptor&lt;/code&gt; → &lt;code&gt;CheckFrontToken&lt;/code&gt; → &lt;code&gt;TokenManagerImpl&lt;/code&gt; 逐级校验，通过后把当前用户放进 &lt;code&gt;ThreadLocal&lt;/code&gt; 供全链路取用。管理端是 Token + 权限双拦截器。好处是&lt;strong&gt;服务端可随时踢人下线&lt;/strong&gt;（删 Redis key 即可），这是纯 JWT 做不到的。&lt;/p&gt;
&lt;h3 id=&#34;47-其他值得提的实现&#34;&gt;4.7 其他值得提的实现&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;订单快照&lt;/strong&gt;：下单时把购物车内容序列化成 JSON 存进 &lt;code&gt;StoreOrderInfo.info&lt;/code&gt;，之后商品改价改名都不影响历史订单的展示；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;运费计算引擎&lt;/strong&gt;：&lt;code&gt;getOrderPriceGroup&lt;/code&gt; 支持满额包邮、指定城市包邮（件数 + 金额双条件）、按件数/重量/体积三种首续计费模式；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;XSS 转义&lt;/strong&gt;：用户输入统一 &lt;code&gt;escapeHtml4&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;部署脚本&lt;/strong&gt;：&lt;code&gt;start.sh&lt;/code&gt; nohup 启动 + 轮询日志判断启动成功 + 企业微信机器人推送部署结果——单机部署也搞出了仪式感。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;五复盘与遗憾&#34;&gt;五、复盘与遗憾&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;底座与自研的边界&lt;/strong&gt;：秒杀/拼团/砍价/分销这些&amp;quot;重&amp;quot;能力直接复用 CRMEB 底座，把精力集中在业务差异化（预约、择吉日、聊天）上——对个人开发者来说这是正确的杠杆；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;单体的局限&lt;/strong&gt;：所有东西在一个 SpringBoot 应用里，定时任务、支付、前台接口互相影响，量大了必然要拆；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;原子扣库存的返回值没检查&lt;/strong&gt;：&lt;code&gt;setSql&lt;/code&gt; 扣减失败（库存不足）时影响的行数为 0，但代码没有校验这个返回值，极端情况下会漏判超卖——这是现在回头看想补的一个洞；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;聊天服务端未开源&lt;/strong&gt;：如第三章所述，长连接聊天的服务端实现没有随仓库开源，小程序端和接口契约是完整的，算是开源的一点遗憾。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;六写在最后&#34;&gt;六、写在最后&lt;/h2&gt;
&lt;p&gt;这个项目让我完整走了一遍&amp;quot;真实收钱&amp;quot;的系统该有的样子：幂等、事务、防超卖、回调防重、异步补偿——每一个点背后都是电商系统趟过的坑。代码在 &lt;a href=&#34;https://gitee.com/soulnew/starmall&#34;&gt;https://gitee.com/soulnew/starmall&lt;/a&gt; 和 &lt;a href=&#34;https://gitee.com/soulnew/crm_server&#34;&gt;https://gitee.com/soulnew/crm_server&lt;/a&gt;，建库 SQL、接口文档都在仓库里，希望能给做小程序商城的朋友一个可参照的完整样本。&lt;/p&gt;
</description>
    </item>
    <item>
      <title>jphotoshop：用 Java Swing 手写一个 Photoshop——架构与算法全解析</title>
      <link>https://ftmao.com/blog/posts/jphotoshop-java-swing-photoshop-clone/</link>
      <pubDate>Fri, 18 May 2018 10:00:00 +0800</pubDate>
      <guid>https://ftmao.com/blog/posts/jphotoshop-java-swing-photoshop-clone/</guid>
      <description>&lt;h1 id=&#34;jphotoshop用-java-swing-手写一个-photoshop架构与算法全解析&#34;&gt;jphotoshop：用 Java Swing 手写一个 Photoshop——架构与算法全解析&lt;/h1&gt;
&lt;p&gt;把多年前的一个练手项目整理开源了：&lt;strong&gt;jphotoshop&lt;/strong&gt;，一个用纯 Java Swing 手写的 Photoshop 模拟器。不写不知道，写一个&amp;quot;简化版 PS&amp;quot;几乎把计算机图形学、数据结构和设计模式都过了一遍，这篇文章既是功能介绍，也是一次技术复盘。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;开源地址：&lt;a href=&#34;https://gitee.com/soulnew/jphotoshop&#34;&gt;https://gitee.com/soulnew/jphotoshop&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;技术栈：Java + Swing（Swing Application Framework / JSR-296），NetBeans Ant 工程&lt;/li&gt;
&lt;li&gt;规模：197 个 Java 文件，约 1.8 万行（自研约 1.4 万行），开发周期约半年&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;一功能概览一个简化版-ps能做什么&#34;&gt;一、功能概览：一个&amp;quot;简化版 PS&amp;quot;能做什么&lt;/h2&gt;
&lt;p&gt;UI 形态上和老版 PS 一致：&lt;strong&gt;MDI 多文档界面&lt;/strong&gt;——每张图片一个内部编辑窗口，图层面板、导航缩放面板都是可拖动的浮动内窗。&lt;/p&gt;
&lt;h3 id=&#34;11-图层系统&#34;&gt;1.1 图层系统&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;多图层编辑：图像图层、&lt;strong&gt;文字图层&lt;/strong&gt;、浮动选区层（拖动选区内容时的临时层）；&lt;/li&gt;
&lt;li&gt;图层面板带 40×30 &lt;strong&gt;实时缩略图&lt;/strong&gt;，支持拖拽换序（基于 Swing TransferHandler 的 DnD）、复制粘贴为新图层；&lt;/li&gt;
&lt;li&gt;透明区域用经典的灰白棋盘格打底。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;12-选区工具7-个&#34;&gt;1.2 选区工具（7 个）&lt;/h3&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;工具&lt;/th&gt;
					&lt;th&gt;说明&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;魔棒&lt;/td&gt;
					&lt;td&gt;容差可调（默认 23），支持多连通区域一次选中&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;色彩范围&lt;/td&gt;
					&lt;td&gt;全图相似色选取&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;多边形套索&lt;/td&gt;
					&lt;td&gt;逐点勾勒&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;自由套索&lt;/td&gt;
					&lt;td&gt;鼠标轨迹勾勒&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;磁性套索&lt;/td&gt;
					&lt;td&gt;局部窗口内吸附边缘（完成度不高）&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;贝塞尔选区&lt;/td&gt;
					&lt;td&gt;双击闭合时自动做曲线拟合，生成平滑选区&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;移动工具&lt;/td&gt;
					&lt;td&gt;把选区内容提成浮动层，拖动实时合成，松手落回底层&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;选区还支持：&lt;strong&gt;羽化&lt;/strong&gt;（可调半径）、反选、平移/缩放（AffineTransform）、以及标志性的&lt;strong&gt;蚂蚁线动画&lt;/strong&gt;。&lt;/p&gt;
&lt;h3 id=&#34;13-滤镜24-个分-5-大类&#34;&gt;1.3 滤镜（24 个，分 5 大类）&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;模糊&lt;/strong&gt;：均值平滑、高斯模糊（3×3 高斯核 &lt;code&gt;{1,2,1;2,4,2;1,2,1}/16&lt;/code&gt;）、中值滤波；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;锐化&lt;/strong&gt;：Sharpen / SharpenMore（拉普拉斯卷积核 &lt;code&gt;{0,-1,0;-1,5,-1;0,-1,0}&lt;/code&gt;）；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;艺术&lt;/strong&gt;：曝光过度、冰晶、熔岩、暗调（平方衰减）、漫画等 8 种风格化效果；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;简单&lt;/strong&gt;：反色、怀旧（sepia 矩阵 &lt;code&gt;0.393/0.769/0.189...&lt;/code&gt;）、灰度；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;扭曲&lt;/strong&gt;：挤压、球面化、正/反旋转、正弦波浪、摩尔纹——扭曲类全部带&lt;strong&gt;多段滑块实时预览&lt;/strong&gt;，取消可还原。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;14-图像调整与文字&#34;&gt;1.4 图像调整与文字&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;HSB 调整&lt;/strong&gt;：先把整图 RGB 转成 HSB 三维 float 数组，色相/饱和度/亮度三个滑块实时调节再转回 RGB；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;对比度&lt;/strong&gt;调整；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;文字工具&lt;/strong&gt;：点击图片任意位置就地出现透明输入框 + 字体选择面板，完成后生成为独立的文字图层。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;15-文件与工程&#34;&gt;1.5 文件与工程&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;打开 jpg / png / gif / bmp；&lt;/li&gt;
&lt;li&gt;自定义 &lt;strong&gt;&lt;code&gt;.jps&lt;/code&gt; 工程格式&lt;/strong&gt;（类似 PSD）：整个图层模型实现 &lt;code&gt;Serializable&lt;/code&gt;，用 &lt;code&gt;ObjectOutputStream&lt;/code&gt; 套 &lt;code&gt;GZIPOutputStream&lt;/code&gt; 序列化保存，可以随时回来继续编辑；&lt;/li&gt;
&lt;li&gt;复制 / 剪切 / 粘贴、导航缩放窗口、中文界面。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;二架构总览&#34;&gt;二、架构总览&lt;/h2&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph TD
    A[&amp;#34;JPhotoShop 入口&amp;lt;br/&amp;gt;EDT 中构建主窗口&amp;#34;] --&amp;gt; B[&amp;#34;JDesktopPane MDI 主框架&amp;#34;]
    B --&amp;gt; C[&amp;#34;EditFrame 编辑窗口&amp;lt;br/&amp;gt;每张图一个&amp;#34;]
    B --&amp;gt; D[&amp;#34;LayerControl 图层面板&amp;#34;]
    B --&amp;gt; E[&amp;#34;ViewSizeFrame 导航/缩放&amp;#34;]
    C --&amp;gt; F[&amp;#34;EditModel&amp;lt;br/&amp;gt;像素模型 + MemoryImageSource 渲染&amp;#34;]
    F --&amp;gt; G[&amp;#34;ImageModel&amp;lt;br/&amp;gt;LinkedList 图层栈&amp;#34;]
    G --&amp;gt; H[&amp;#34;ImagePaintLayer / TextPaintLayer / TempImageLayer&amp;#34;]
    H --&amp;gt; I[&amp;#34;LayerPlug 混合样式&amp;lt;br/&amp;gt;Alpha 合成&amp;#34;]
    C --&amp;gt; J[&amp;#34;ToolManager&amp;lt;br/&amp;gt;工具代理/状态切换&amp;#34;]
    J --&amp;gt; K[&amp;#34;Tool 抽象类&amp;lt;br/&amp;gt;魔棒/套索/贝塞尔/移动/文字...&amp;#34;]
    C --&amp;gt; L[&amp;#34;FilterManager&amp;lt;br/&amp;gt;properties + 反射装配&amp;#34;]
    L --&amp;gt; M[&amp;#34;PicFilter 滤镜即 Action&amp;#34;]
    M --&amp;gt; N[&amp;#34;Math 包&amp;lt;br/&amp;gt;CoverOp 卷积 / FeatherSide 羽化&amp;lt;br/&amp;gt;FindSide 轮廓追踪 / PointConnection 并查集&amp;#34;]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;整体分四层：&lt;strong&gt;UI 层&lt;/strong&gt;（MDI 窗口与面板）、&lt;strong&gt;模型层&lt;/strong&gt;（图层栈与像素数据）、&lt;strong&gt;工具/滤镜层&lt;/strong&gt;（操作入口）、&lt;strong&gt;算法层&lt;/strong&gt;（&lt;code&gt;jphotoshop.Math&lt;/code&gt; 包）。工具和滤镜都通过 properties 配置 + 反射装配，下面详细说。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;三技术亮点&#34;&gt;三、技术亮点&lt;/h2&gt;
&lt;h3 id=&#34;31-自绘像素管线int--memoryimagesource&#34;&gt;3.1 自绘像素管线：int[][] + MemoryImageSource&lt;/h3&gt;
&lt;p&gt;核心存储没有用 &lt;code&gt;BufferedImage&lt;/code&gt;，而是直接操作 &lt;strong&gt;&lt;code&gt;int[][]&lt;/code&gt; ARGB 二维数组&lt;/strong&gt;——所有算法（卷积、混合、羽化）都在裸数组上做，直观且没有额外封装开销。&lt;/p&gt;
&lt;p&gt;显示侧用了一个当年很典型的 Swing 高性能技巧：&lt;code&gt;MemoryImageSource&lt;/code&gt; + &lt;code&gt;setAnimated(true)&lt;/code&gt;，像素变化后调 &lt;code&gt;newPixels()&lt;/code&gt; &lt;strong&gt;增量刷新&lt;/strong&gt;屏幕图像，避免每次重绘都重建整个 Image 对象。编辑、拖选区、调滤镜滑块时的实时反馈都靠它撑着。&lt;/p&gt;
&lt;h3 id=&#34;32-链式图层合成缓存&#34;&gt;3.2 链式图层合成缓存&lt;/h3&gt;
&lt;p&gt;多图层合成是最频繁的操作，朴素实现是每次重绘把所有层从头合一遍。这里的做法是给每个图层挂一个&amp;quot;&lt;strong&gt;本层与下面所有层合成结果的缓存&lt;/strong&gt; &lt;code&gt;cacheData&lt;/code&gt;&amp;quot;，层与层之间用 &lt;code&gt;backLayer&lt;/code&gt; 引用连成链：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph LR
    BG[&amp;#34;背景层&amp;lt;br/&amp;gt;棋盘格&amp;#34;] --&amp;gt;|&amp;#34;合成&amp;#34;| L1[&amp;#34;图层1&amp;lt;br/&amp;gt;cacheData&amp;#34;]
    L1 --&amp;gt;|&amp;#34;合成&amp;#34;| L2[&amp;#34;图层2&amp;lt;br/&amp;gt;cacheData&amp;#34;]
    L2 --&amp;gt;|&amp;#34;合成&amp;#34;| L3[&amp;#34;图层3&amp;lt;br/&amp;gt;cacheData&amp;#34;]
    L3 --&amp;gt; OUT[&amp;#34;最终显示图像&amp;#34;]
    style L2 fill:#f96
    L2 -. &amp;#34;仅从此层向上重建&amp;#34; .-&amp;gt; L3&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;某一层像素变化时，&lt;strong&gt;只从该层向上依次 &lt;code&gt;reBuild&lt;/code&gt;&lt;/strong&gt;，下面的层缓存不动。层数多、图大时，这个收益非常明显。&lt;/p&gt;
&lt;h3 id=&#34;33-手写-alpha-混合&#34;&gt;3.3 手写 Alpha 混合&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;AlphaLayerStyle&lt;/code&gt; 实现了逐像素的经典混合公式 &lt;code&gt;C = α·Cf + (1-α)·Cb&lt;/code&gt;，并且处理了&lt;strong&gt;任意偏移（含负坐标裁剪）&lt;/strong&gt;——浮动选区拖到画布边缘外也能正确合成。羽化后的选区掩码是 short 型 alpha 权重，抠图粘贴时按权重衰减，边缘不会出现生硬的锯齿。&lt;/p&gt;
&lt;h3 id=&#34;34-魔棒一条完整的算法链&#34;&gt;3.4 魔棒：一条完整的算法链&lt;/h3&gt;
&lt;p&gt;魔棒是整个项目里算法含量最高的功能，完整链路分三步：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph TD
    A[&amp;#34;用户点击图片某点&amp;#34;] --&amp;gt; B[&amp;#34;Flood Fill 泛洪填充&amp;lt;br/&amp;gt;8邻域 + 容差判断&amp;lt;br/&amp;gt;ΔR+ΔG+ΔB ＜ range&amp;#34;]
    B --&amp;gt; C[&amp;#34;PointConnection 并查集&amp;lt;br/&amp;gt;id 数组 + sz 数组按秩合并&amp;lt;br/&amp;gt;分离多个互不连通的区域&amp;#34;]
    C --&amp;gt; D[&amp;#34;FindSide 8方向轮廓追踪&amp;lt;br/&amp;gt;沿区域边界走一圈&amp;lt;br/&amp;gt;每3步抽稀路径点&amp;#34;]
    D --&amp;gt; E[&amp;#34;GeneralPath 选区路径&amp;#34;]
    E --&amp;gt; F[&amp;#34;蚂蚁线动画&amp;lt;br/&amp;gt;Timer 500ms 切换两条相位差3的虚线&amp;#34;]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;几个细节：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;泛洪填充用 &lt;code&gt;LinkedList&lt;/code&gt; 当队列，容差判断用曼哈顿距离（&lt;code&gt;|ΔR|+|ΔG|+|ΔB|&lt;/code&gt;），比欧氏距离省开方；&lt;/li&gt;
&lt;li&gt;一次点击可能选中多个互不连通的区域（比如图片里分散的几块蓝天），用&lt;strong&gt;并查集&lt;/strong&gt;把连通域分组，每个域独立生成轮廓；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;轮廓追踪&lt;/strong&gt;把面状的 boolean 掩码转成线状的路径点列，这一步直接决定蚂蚁线的平滑程度；&lt;/li&gt;
&lt;li&gt;蚂蚁线动画是两条相位差为 3 的虚线 &lt;code&gt;BasicStroke&lt;/code&gt; 用 500ms 的 &lt;code&gt;Timer&lt;/code&gt; 交替绘制——朴素但效果完全对味。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;35-通用卷积引擎-coverop&#34;&gt;3.5 通用卷积引擎 CoverOp&lt;/h3&gt;
&lt;p&gt;所有卷积类滤镜（模糊、锐化背后是同一个引擎 &lt;code&gt;CoverOp&lt;/code&gt;，提供三种重载：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;全图卷积；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;掩码限定区域卷积&lt;/strong&gt;——滤镜只在选区内生效，这是 PS 的基本体验；&lt;/li&gt;
&lt;li&gt;short alpha 掩码卷积——配合羽化选区做渐变处理。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;RGB 三通道分离卷积 + 边界 clamp，滤镜类只需要给一个卷积核就完事。新增一个模糊变体的成本是十几行代码。&lt;/p&gt;
&lt;h3 id=&#34;36-贝塞尔选区schneider-曲线拟合&#34;&gt;3.6 贝塞尔选区：Schneider 曲线拟合&lt;/h3&gt;
&lt;p&gt;自由手绘的选区轨迹是抖动的折线，直接当选区很难看。贝塞尔选区工具在双击闭合时调用 JHotDraw 的 &lt;code&gt;Bezier.fitCurve&lt;/code&gt;（Schneider 的经典拟合算法），把鼠标点列拟合成平滑的三次贝塞尔曲线段——这就是 PS 里&amp;quot;钢笔工具&amp;quot;的底层原理。&lt;/p&gt;
&lt;h3 id=&#34;37-声明式装配properties--反射的原始插件化&#34;&gt;3.7 声明式装配：properties + 反射的原始&amp;quot;插件化&amp;quot;&lt;/h3&gt;
&lt;p&gt;工具和滤镜都&lt;strong&gt;不写死在代码里&lt;/strong&gt;，而是从 properties 读类名、&lt;code&gt;Class.forName&lt;/code&gt; 反射实例化：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-properties&#34; data-lang=&#34;properties&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# resource/filters.properties&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;filters.Kinds&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;模糊,锐化,艺术效果,简单,扭曲&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;模糊&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;Smooth,GaussBlur,Median&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;加一个新滤镜 = 写一个类 + 改一行配置，菜单自动按分类分组生成。工具栏同理（&lt;code&gt;ToolManager.initTool&lt;/code&gt;）。项目里还专门有一个 &lt;code&gt;plug&lt;/code&gt; 包在探索真正的外部插件机制（虽然只是个雏形）。在 2007 年没有 OSGi、没有 IoC 框架依赖的纯 Swing 工程里，这是最务实的可扩展方案。&lt;/p&gt;
&lt;h3 id=&#34;38-工具架构proxy--state-的混合&#34;&gt;3.8 工具架构：Proxy + State 的混合&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;Tool&lt;/code&gt; 抽象类把底层 MouseEvent 翻译成语义化回调（&lt;code&gt;click / pressed / dragged / release / rightClick / clickDouble / move&lt;/code&gt;），子类只关心语义。&lt;code&gt;ToolManager&lt;/code&gt; 本身也是 &lt;code&gt;Tool&lt;/code&gt; 的子类，作为&lt;strong&gt;代理&lt;/strong&gt;把事件转发给当前激活的工具——选中哪个工具，事件流就切到哪个状态机上。每个工具还可以自带 &lt;code&gt;getPlugPanel()&lt;/code&gt; 参数面板，选中时挂到选项栏上，和 PS 的交互完全一致。&lt;/p&gt;
&lt;h3 id=&#34;39-扭曲滤镜的预览-取消模式&#34;&gt;3.9 扭曲滤镜的&amp;quot;预览-取消&amp;quot;模式&lt;/h3&gt;
&lt;p&gt;扭曲类滤镜（球面、旋转、波浪……）都是坐标重映射：对每个目标像素做逆极坐标变换，找它在原图的取样点。为了支持滑块&lt;strong&gt;实时预览又能取消还原&lt;/strong&gt;，&lt;code&gt;ContortFilter&lt;/code&gt; 在打开对话框时先备份 &lt;code&gt;tempData&lt;/code&gt;，滑块每次变化都在备份基础上重算，点取消直接把备份拷回——一个廉价的 Memento 模式。配套的 &lt;code&gt;MultiSlider&lt;/code&gt; 多段滑块控件也是自绘的。&lt;/p&gt;
&lt;h3 id=&#34;310-滤镜即-action&#34;&gt;3.10 滤镜即 Action&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;PicFilter extends AbstractAction&lt;/code&gt;，滤镜本身就是可以挂进菜单/工具栏的 Action：&lt;code&gt;actionPerformed&lt;/code&gt; 里自动取当前窗口、当前图层、当前选区掩码，还处理了&lt;strong&gt;选区坐标系到图层坐标系的换算&lt;/strong&gt;（&lt;code&gt;start -= layerStart&lt;/code&gt;），执行完触发 &lt;code&gt;rePaintEdit&lt;/code&gt; 刷新。框架把脏活都干了，滤镜作者只需要关注像素算法。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;四诚实的复盘留下了什么遗憾&#34;&gt;四、诚实的复盘：留下了什么遗憾&lt;/h2&gt;
&lt;p&gt;开源的代码就是当年的样子，包括未完成的部分，如实列出：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;撤销/重做是空架子&lt;/strong&gt;：&lt;code&gt;UndoRedoManager&lt;/code&gt; 的骨架搭好了，但 &lt;code&gt;propertyChange&lt;/code&gt; 三个分支全是空的——这是当时没啃下来的硬骨头，现在看应该用 Command 模式重做，而不是当初设想的 property 监听方案；&lt;/li&gt;
&lt;li&gt;历史面板、通道面板、外部插件机制都只有雏形；&lt;/li&gt;
&lt;li&gt;磁性套索完成度不高；&lt;/li&gt;
&lt;li&gt;若干小 bug，比如 &lt;code&gt;FindSide&lt;/code&gt; 里找最近点写成了找最远点（&lt;code&gt;if (temp &amp;gt; MinLength)&lt;/code&gt; 应为 &lt;code&gt;&amp;lt;&lt;/code&gt;）、&lt;code&gt;&amp;quot;undo&amp;quot;.endsWith(...)&lt;/code&gt; 应为 &lt;code&gt;startsWith&lt;/code&gt;、几处类名 typo（&lt;code&gt;WaveFiler&lt;/code&gt;、&lt;code&gt;getSize&lt;/code&gt; 拼写）——权当留给读者的&amp;quot;考古彩蛋&amp;quot;。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;五写在最后&#34;&gt;五、写在最后&lt;/h2&gt;
&lt;p&gt;写 jphotoshop 的半年收获远超预期：并查集、轮廓追踪、卷积、贝塞尔拟合这些算法不再是教科书上的名词，而是要亲手调到能跑的东西；合成缓存、声明式装配、Action 抽象这些设计，也都是被真实需求逼出来的。如果你也想扎实练一次&amp;quot;算法 + 工程&amp;quot;的综合功，手写一个图像编辑器是我能想到的最好题目之一。&lt;/p&gt;
&lt;p&gt;源码在 &lt;a href=&#34;https://gitee.com/soulnew/jphotoshop&#34;&gt;https://gitee.com/soulnew/jphotoshop&lt;/a&gt;，欢迎 Star、Fork，更欢迎把那个空着的 &lt;code&gt;UndoRedoManager&lt;/code&gt; 补上。&lt;/p&gt;
</description>
    </item>
    
  </channel>
</rss>
