Repository navigation
v0.2.6
v0.2.6
日期:2026-10-16
这是 v0.2.5 之后的可选商品描述。设备码、授权范围、买家注册、买入开关、支付成功条件、店面缓存、文本压缩范围、Caddy 匹配和安装回滚都没有改。没有新路由,也没有新错误码。compose.yaml 与 v0.2.5 相同。install.sh 与 v0.2.5 相同。
v0.2.5 的商品只有标题、状态、封面和目录字段。公开详情上的说明是用标题、价格和库存拼出来的,商家不能写一段给买家看的正文。这一版给商品增加可选纯文本 description。创建和 PATCH 可以写它。公开详情、商品 Markdown、meta description、JSON-LD 和关键词 q 使用同一份值。新订单行另存下单时的副本。已有商品和已有订单不回填。
同一时间仓库文档也重写了:README.md、README_EN.md、架构图,以及 txpearl.cn、eileenpeng.com 的案例。这些文件不进运行行为。只换文档不会带上商品描述。
生产应用镜像是 docker.io/<用户名>/merclink:0.2.6。正式版同时推送 0.2.6 和 latest。要换应用镜像。进程启动时会执行新迁移 017_product_description.sql。不需要换 compose.yaml。
中文
这一版能做什么
products增加可空列description。纯文本,先去掉首尾空白,内部换行保留。长度用字符串length,1 到 2000 个字符。空字符串、非字符串和超长都是validation_error。省略后为空,空着仍可上架。JSON、页面和订单快照里的空描述都是null,不写进fields。description成为保留系统字段名,和title、price、stock一样。不能放进商品fields,也不能新建、停用或复用这个目录字段 key。拒绝仍是validation_error,不是新错误码。PATCH省略description不改已保存的值;传null清空。省略fields仍不改自定义属性。不能为了改描述去重传fields,也不能用描述改价格。- 公开商品 JSON 和商家商品 JSON 都在
title旁返回description。没有描述时是null。不要从fields读这个键。GET /api/v1/products的q同时匹配标题、这段描述和目录文本字段,不要求catalog_id。描述不参与数字、单选或是否过滤。买家 Skill、未开启买入时的 Skill 和商家 Skill 已改成这套读写说明。 - 内置商品详情在价格和可售状态下面展示这段文字。换行保留,内容转义,不渲染 Markdown 或 HTML。没有描述就不留空块。首页卡片和商品列表仍只显示封面、标题和价格。商品 Markdown 把描述单独成段,不压成一行。
- 有描述时,商品页 meta description 和 JSON-LD
description先把连续空白收成一个空格,再截到 150 个字符。没有描述时仍用标题、价格和库存状态拼成的句子。不另存 SEO 文案。 - 新订单行写入
order_items.description_snapshot,从下单时锁定的商品行复制。当时为空就写null,不放进fields_snapshot。之后修改商品描述不改这一行。旧订单行保持null,不回填。订单 JSON 仍只返回行的id、variant_id、qty和amount,不返回这段快照。 - 内置商品页和起始店面的商品页声明槽位
product.description。槽位有值时转义后输出,换行变成<br>;没有描述时槽位为空。首页和商品列表模板不声明这个槽位。已经接受的发布不会被改写。已发布且没有这个槽位的自定义店面保持原样,页面不会自动补一块描述。商家要在模板里声明product.description后再发布,自定义店面才会出现这段文字。 - 仓库首页改成带首图的说明,并增加英文 README、架构图和两个在线店铺案例。Mermaid 总览改放在
docs/ARCHITECTURE.md。这只影响仓库阅读,不影响已部署进程。
这一版不做
- 不渲染描述里的 Markdown 或 HTML,不加后台表单,也不按规格写描述。
- 不把描述放进首页卡片、商品列表或店铺简介。
- 不自动把已有
fields.description搬进新列,也不停用那个 key。 - 不改设备码流程、scope、买家注册或支付成功条件。只有订单状态
paid表示支付成功。调用方仍不能传价格。 - 不改店面 HTML 的
no-store,也不改 CSS、JS 的 10 分钟新鲜期和之后最多 24 小时的后台校验。 - 不改公开图片按 id 缓存一年的规则。缺失图片仍是
404。 - 不改 Caddy 的五行
Content-Type匹配,也不改镜像标签。仍是caddy:2.11.7-alpine。 - 不改一键安装询问的字段,不安装系统包,也不默认打开买入。安装和升级未完成时的回滚与 v0.2.5 相同。
- 不新增路由、错误码或依赖。不激活店面,不补写历史发布,也不把 Docker Hub 用户名、令牌、域名或证书写进仓库。
升级注意
商品描述在应用镜像和数据库迁移里。compose.yaml 和 install.sh 与 v0.2.5 相同。不要删 .env,也不要删 caddy_data。不要改已经应用过的迁移;新文件只是 017_product_description.sql。
升级前先查目录字段。只要有一行 product_fields.key = 'description',迁移会抛出 product_fields.key description already exists 并整段回滚,新进程起不来。这一版不复制、也不停用那个 key。先导出值,再用现有字段停用流程去掉该 key,然后再升级。停用可能让缺值商品下架。没有这个 key 时,迁移只给 products 和 order_items 各加一列可空文本,不回填。
一键安装过的机器,等这个正式版发布后执行:
curl -fsSL -o /tmp/merclink-install.sh https://github.com/ptonlix/MercLink/releases/latest/download/install.sh && bash /tmp/merclink-install.sh upgradeupgrade 会下载这个 tag 的 compose.yaml,保留已有密钥和后来手工打开的买入配置,然后 pull。它不打印已保存的密码。这次 compose.yaml 没有变化。升级如果没有完成,部署文件和版本号会回到升级前;已经换上的容器要再执行 docker compose up -d 才和文件一致。新容器要能跑完 017 才会正常服务。
手工部署的机器不要再跑一键安装。把 MERCLINK_IMAGE 改为 docker.io/<用户名>/merclink:0.2.6,然后执行:
docker compose pull && docker compose up -d不要加 --build。拉取完成且进程起来之后,旧商品的 description 仍是 null,公开页和上架行为与 v0.2.5 一致,直到创建或 PATCH 写入描述。用商家接口写入后,内置商品详情会显示这段文字。还没有接受发布时,重新下载的起始店面商品页带有 product.description。已经激活的自定义店面不会因此改版。
回滚时把应用镜像改回 docker.io/<用户名>/merclink:0.2.5,再 pull。不要把 latest 当作回滚点。latest 会在这个正式版发布时移到 0.2.6。0.2.5 不读这两列。迁移已经成功时,不要为了回滚删掉 products.description 或 order_items.description_snapshot;删掉会丢掉新写入的描述。订单金额、库存和支付状态不依赖这两列。
English
Date: 2026-10-16
This is the optional product description after v0.2.5. The device-code flow, authorization scopes, buyer registration, purchase switch, the rule that only paid means payment success, storefront caching, the set of compressed types, the Caddy matchers, and install rollback are unchanged. There is no new route and no new error code. compose.yaml is the same file as v0.2.5. install.sh is the same file as v0.2.5.
In v0.2.5 a product has a title, status, cover, and catalog fields. The public detail blurb is assembled from the title, price, and stock. A merchant cannot write body text for buyers. This version adds an optional plain-text description. Create and PATCH can set it. The public detail page, product Markdown, meta description, JSON-LD, and keyword q use that same value. A new order line stores a copy from order time. Existing products and existing orders are not backfilled.
The repository docs were rewritten in the same range: README.md, README_EN.md, the architecture figure, and case notes for txpearl.cn and eileenpeng.com. Those files do not change runtime behavior. Replacing only the docs does not ship product descriptions.
The production image is docker.io/<namespace>/merclink:0.2.6. The stable release also publishes latest. The application image has to be replaced. Process startup applies the new migration 017_product_description.sql. compose.yaml does not need to change.
What this version does
productsgains a nullabledescriptioncolumn. The text is plain, end whitespace is removed, and internal line breaks stay. Length is JavaScript stringlength, from 1 to 2000 characters. An empty string, a non-string, and an over-long value arevalidation_error. Omitting it leaves the description empty, and publishing does not require it. An empty description isnullin JSON, on the page, and in the order snapshot. It is not stored infields.descriptionis now a reserved system field name, liketitle,price, andstock. It cannot be placed in productfields, and it cannot be created, retired, or reused as a catalog field key. Rejection staysvalidation_error; there is no new error code. OmittingdescriptiononPATCHleaves the saved value unchanged;nullclears it. Omittingfieldsstill leaves custom attributes unchanged. Do not resendfieldsonly to change the description, and do not use the description to change a price.- Public product JSON and merchant product JSON both return
descriptionbesidetitle. It isnullwhen absent. Do not read this key fromfields.qonGET /api/v1/productsmatches the title, this description, and catalog text fields, and does not requirecatalog_id. The description does not take part in number, single-select, or boolean filters. The buyer Skill, the purchase-disabled Skill, and the merchant Skill now describe this read and write behavior. - The built-in product detail shows the text under the price and availability. Line breaks are kept, the text is escaped, and Markdown or HTML is not rendered. An empty description leaves no empty block. Home cards and the product list still show only the cover, title, and price. Product Markdown keeps the description as its own paragraph and does not collapse it to one line.
- When a description exists, the product meta description and JSON-LD
descriptioncollapse consecutive whitespace to one space, then truncate to 150 characters. Without a description they still use the sentence built from title, price, and stock. No separate SEO copy is stored. - A new order line writes
order_items.description_snapshot, copied from the product row locked at order time. An empty description is stored asnulland is not placed infields_snapshot. A later edit to the product description does not change that line. Old order lines staynulland are not backfilled. Order JSON still returns onlyid,variant_id,qty, andamountfor a line. It does not return this snapshot. - The built-in product page and the starter storefront product page declare
product.description. A filled slot is escaped and line breaks become<br>; an empty description leaves the slot empty. The home and product-list templates do not declare this slot. An accepted release is not rewritten. A published custom storefront that does not declare the slot stays as it is, and the page does not grow a description block on its own. The merchant declaresproduct.descriptionin the template and publishes again before a custom storefront shows the text. - The repository home page is now a hero-led introduction, with an English README, an architecture figure, and two live-store case notes. The Mermaid overview moves to
docs/ARCHITECTURE.md. This changes how the repository reads. It does not change a deployed process.
What this version does not do
- It does not render Markdown or HTML inside a description, add an admin form, or store a description per variant.
- It does not put the description on home cards, the product list, or the store profile.
- It does not copy an existing
fields.descriptioninto the new column, and it does not retire that key. - It does not change the device-code flow, scopes, buyer registration, or the payment-success rule. Only order status
paidmeans payment succeeded. The caller still cannot send a price. - It does not change
no-storeon storefront HTML, or the 10-minute freshness window and later 24-hour background revalidation for CSS and JS. - It does not change the one-year cache for a public image id. A missing image is still
404. - It does not change Caddy's five
Content-Typelines or the image tag. It remainscaddy:2.11.7-alpine. - It does not change what one-click install asks for, it does not install system packages, and it does not turn purchase on by default. Rollback of an unfinished install or upgrade stays as it was in v0.2.5.
- It does not add a route, an error code, or a dependency. It does not activate a storefront, backfill old releases, or store a Docker Hub username, token, domain, or certificate in the repository.
Upgrade note
The description ships in the application image and in a database migration. compose.yaml and install.sh match v0.2.5. Do not delete .env or caddy_data. Do not edit a migration that has already been applied. The new file is only 017_product_description.sql.
Check catalog fields before upgrading. If any row has product_fields.key = 'description', the migration raises product_fields.key description already exists and rolls the whole step back. The new process does not stay up. This version does not copy or retire that key. Export the values, remove the key with the existing field-retirement flow, then upgrade. Retirement can unpublish products that lose a required value. When the key is absent, the migration only adds one nullable text column to products and one to order_items. It does not backfill.
After this stable release is published, a host installed by the script should run:
curl -fsSL -o /tmp/merclink-install.sh https://github.com/ptonlix/MercLink/releases/latest/download/install.sh && bash /tmp/merclink-install.sh upgradeupgrade downloads compose.yaml for this tag, keeps generated secrets and any later purchase settings, then pulls. It does not print stored passwords. This compose.yaml is unchanged. If this upgrade does not finish, the deploy files and version pin return to their previous values. Containers already recreated from the new image match those files only after another docker compose up -d. The new container serves traffic only after 017 finishes.
Do not run a fresh one-click install over a deployment that was created by hand. Set MERCLINK_IMAGE to docker.io/<namespace>/merclink:0.2.6, then run:
docker compose pull && docker compose up -dDo not add --build. After the pull, once the process is up, existing products still have description null. Public pages and publishing stay as they were in v0.2.5 until a create or PATCH writes a description. After that write, the built-in product detail shows the text. When no release has been accepted, a newly downloaded starter product page contains product.description. An already active custom storefront is not republished by this upgrade.
To roll back, set the application image to docker.io/<namespace>/merclink:0.2.5 and pull again. Do not use latest as the rollback point. latest moves to 0.2.6 when this stable release is published. 0.2.5 does not read the two new columns. After the migration has succeeded, do not drop products.description or order_items.description_snapshot just to roll back. Dropping them discards descriptions written by this version. Order amounts, stock, and payment status do not depend on those columns.