Commit Graph
55 Commits
Author SHA1 Message Date
yeuimu bea0bf14d8 feat(api): order public goods by country > mid-category > style > priority 2026-09-02 10:27:23 +08:00
yeuimu 3ccddc5982 feat(api): backfill category/country sort order from reference table 2026-09-02 10:22:49 +08:00
yeuimu dc90712c4f feat(api): add sort_order column to categories 2026-09-02 10:16:29 +08:00
yeuimu 007ad2c831 deploy(v2): h5 build with base /v2/h5, admin dockerfile & nginx route, shared compose 2026-09-01 16:26:22 +08:00
yeuimu 62f968c1c1 chore(api): add miniprogram static assets to /app/public (served at /assets/miniprogram) 2026-08-31 19:02:23 +08:00
yeuimu 674d38a9a7 fix(api,admin): stop test fixture junk leaking to shared dev DB; badge text x个配置
清理标签筛选里的「Upd {ts}-v2」空分组与原产品库树里的
「Tree Cat {ts}」垃圾分类——两者均为集成测试夹具残留:
- tag-groups spec「改名」用例把分组改成 -v2 后缀,afterAll 按原名
  删除永远漏掉它(累积 31 个空分组);修复:改名前把 v2 名一并登记
- origin-goods spec getTree 用例的 finally 先删 good,被
  goodOriginGood 外键限制挡住且错误被 .catch 吞掉,分类/商品/国家/
  链接全部残留;修复:先删引用行再删 good
- 已清库:31 个 Upd 空分组、Tree Cat 分类 + Tree Good 商品 +
  Tree Country 国家 + 2 条 tree-a/b 链接;复跑两 spec 复核零残留

左树一族一行徽标文案按用户反馈去掉「N 条链接」,仅显示「M 个配置」
(memberCount 字段保留在 DTO,悬浮弹层行为不变)

验证:tag-groups + origin-goods spec 13/13 绿且零残留;admin
vue-tsc 干净 + vitest 22/22
2026-08-30 03:15:18 +08:00
yeuimu a756c8c3c8 fix(admin,api): always show family composition badge N条链接·M个配置
按用户反馈(PLTM006 明明能查到配置数却不显示):左树徽标此前只在
同分类内 ≥2 个配置时才出现,单配置族一行上什么都不显示,而右树同族
链接都挂着绿色已配置徽标,两边数字对不上,反复造成「配置数没显示」
的误解。

- goods API:GOOD_INCLUDE 的 family 带 _count.originGoods,
  GoodDto.originGood.family 增加 memberCount(族成员链接数)
- admin:distinctByFamily 记录 linkCount;一族一行的徽标常显
  「N 条链接 · M 个配置」(链接数=族成员数,配置数=该族官网商品数),
  悬浮仍列出全部配置可打开
- 文档同步徽标语义

效果:PLTM006 行显示「4 条链接 · 1 个配置」(真值:4 条链接仅配了
1 个官网商品);PLTF026 的「4 条链接 · 4 个配置」同屏可对比,其中
4 个配置为历史同国家重复配置,建议另行清理。

验证:admin vue-tsc 干净 + vitest 22/22;api goods spec 18/18;
Prisma _count 查询实测返回 originGoods=4
2026-08-30 03:07:16 +08:00
yeuimu 93ac525c55 refactor(api): parsing out of runtime — pure-mirror sync, explicit organize, auto aggregate recompute
解析去运行时化(三层架构,plans/refactor/organize-script-refactor.md):
- 同步 = 纯镜像:upsertOriginGood 不再写解析列、不再自动挂族;详情同步仍触发重算
- 整理 = 显式人工动作(OrganizeService):解析列回填 → 派生标签(人工接管永不
  覆盖)→ auto-group 建族 → 全量重算;入口 CLI(pnpm --filter @inkreach/api
  organize)+ POST /product-families/organize + 后台「整理」按钮
- 重算 = 纯结构化聚合:不再按名称重派生标签(防上游改名倒灌,回归测试覆盖);
  矩阵维度只认标签/CUSTOM 显式标签,未整理成员不进矩阵;工艺=不打印时
  印花数量以单面占位(纯结构化规则);「恢复自动」走整理的单链接派生
- 派生默认补齐(脚本层假设):名称无单/双面且工艺非不打印 → 印花数量单面印花
- goods 服务建品/更新后仅镜像标签+重算(不派生);含商品名入库规范化
  (normalizeGoodName,管理员输入边界质检)
- organize.service.spec 由 tag-sync spec 迁移 + 防倒灌回归;sync/recompute/
  public/families spec 全部适配;API 173/173,admin typecheck+22/22
2026-08-30 01:27:01 +08:00
yeuimu b5875d33b2 fix(sync): drop local size-code column (P/M/G/GG) from SDS size charts
巴西/西语市场上游尺码表带一个名为「尺码」的列,值是本地尺码码
P/M/G/GG(=S/M/L/XL),与行首尺码重复且非测量值;解析时整列丢弃。
其他非数字参考列(身高/体重/建议体重)保留。存量 2 条详情已修复
并重算受影响族。
2026-08-29 08:55:16 +08:00
yeuimu 6498a198ef fix(parser): normalize half/full-width bracket mix in link name segment 1
SDS 上游存在 '美国(不包邮)…' 混用括号:先归一为全角再配对,
否则国家/物流/品名三字段全部解析失败
2026-08-29 02:36:26 +08:00
yeuimu 1e07c62fc8 feat(admin): country drag-sort + always-available member detail sync
- Country 加 sort_order(默认 0 保持 id 序);PATCH /countries/sort 批量保存
  顺序(对齐 /tags/sort 模式);findAll 与公开 /public/countries 均按
  sortOrder 排序
- CountriesView 表格改为可拖拽行列表:拖动松开即全量保存新顺序,失败回滚
- 商品编辑弹窗成员展开面板:同步详情按钮常驻(已同步显示 重新同步详情),
  不再只在未同步态出现
- goods.service.spec 的 FamilyRecomputeService mock 补齐 syncFamilyTags 等
  方法(全量并行时其他套件的扫名归族会把本套件夹具收进族,create/update
  会调用到,mock 缺方法导致偶发 TypeError)
- api 164/164、admin typecheck+22/22+构建全绿
2026-08-28 20:00:56 +08:00
yeuimu 63b436fe41 test(public): cover home-goods family dedupe 2026-08-28 18:44:24 +08:00
yeuimu 8a052773cd feat(public): family-first contract — goodId=familyId, strict 5-dim price matrix
公开契约族化(前端只需知道款号/族):
- GET /public/goods 一族一条(goodId=族ID,price=族起价,分页作用于分组后);
  无族商品(自定义)不进任何公开端点(列表/首页/分类树/标签统计)
- GET /public/goods/:goodId 仅认族 ID;公共字段取代表 Good,变体=全体成员并集,
  尺码表/包装规格=族物化并集;旧 SDS 链接 ID 寻址 404
- priceMatrix 严格五维:尺码×颜色×印花数量×工艺×物流;维度来源改为链接级
  标签(人工接管按人工标签),弃用原始 craftLabel;CUSTOM 成员尊重显式标签
- 名称派生补裸「单面/双面」写法(直喷双面→双面印花+直喷,18 条存量链接修复)
- family_price_overrides 加 print_count 列(五键唯一),PUT/DELETE/校验五键化
- admin 编辑弹窗矩阵消费适配(SKU 列直读 printCount,成员格子三维匹配,
  改价 payload 带 printCount)
- 存量 339 族已全量重算;api 162/162、admin 22/22、双端构建绿
2026-08-28 18:43:30 +08:00
yeuimu e018f16d0b fix(api): drop helmet CSP upgrade-insecure-requests so Swagger UI loads over plain http 2026-08-28 17:50:39 +08:00
yeuimu f28f51155f feat(goods): per-member detail tabs in edit dialog; revert right-tree to raw names; split configured/family badges; add GET /origin-goods/:id 2026-08-28 16:55:07 +08:00
yeuimu 10197c47a8 refactor(admin): split GoodsView into dialog components; fix(GoodsView): raw member names, no primary badges, member price grid; feat(tag): 光板 maps to 不打印 2026-08-28 16:40:48 +08:00
yeuimu 08201f18a4 feat(product-family): link-level tags (origin_good_tags) with manual override + member detail enrichment 2026-08-28 16:00:46 +08:00
yeuimu ee336968a0 feat(product-family): derive tags per link name (印花数量/工艺/物流 rules) 2026-08-28 15:33:11 +08:00
yeuimu f5a3b8c840 merge: bring in public media/variant dedup from origin/develop (local only) 2026-08-28 15:15:17 +08:00
yeuimu eedfbb344e feat(product-family): auto-derive logistics/craft tags from family members 2026-08-28 14:57:25 +08:00
yeuimu 4eadb67c36 feat(product-family): replace legacy good merge with family mechanism in config view and public detail 2026-08-28 14:14:58 +08:00
yeuimu 6418aa7302 feat(api): public good detail family block behind gray-release flag 2026-08-28 13:40:19 +08:00
yeuimu b4b9fbbe60 feat(api): good family_id column with backfill 2026-08-28 13:34:42 +08:00
yeuimu ab450c350e fix(api): tolerant member attach in auto-group under concurrent cleanup 2026-08-28 12:52:22 +08:00
yeuimu 502b7eba6e docs: update references for product families; fix controller import and test isolation 2026-08-28 12:50:42 +08:00
yeuimu c8531bfe08 feat(api): category-based family grouping, tree family info and custom goods family attribution 2026-08-28 12:43:58 +08:00
yeuimu d63f1a6f35 feat(api): product families backfill script 2026-08-28 12:36:39 +08:00
yeuimu d20a933e34 feat(api): sync hooks for parsing, auto-attach and family recompute 2026-08-28 12:33:33 +08:00
yeuimu f68898dea4 feat(api): product families module with CRUD, auto-group, members, custom members and price overrides 2026-08-28 12:30:57 +08:00
yeuimu de0f5509b1 feat(api): add family recompute service with union and price matrix 2026-08-28 12:27:29 +08:00
yeuimu c07b28def7 feat(api): add product_families schema and migration 2026-08-28 12:24:24 +08:00
yeuimu 60946b5f64 feat(api): add origin good name parser 2026-08-28 12:23:35 +08:00
yeuimu 270e516f94 fix(public): media images are objects ({id,url,sortOrder}) - handle both shapes in mergeMedia 2026-08-28 11:22:53 +08:00
yeuimu d826c5bdd4 feat(public): merge secondary media gallery images with URL dedup 2026-08-28 11:19:27 +08:00
yeuimu 457792b729 feat(public): dedupe merged variants and merge specs/options from secondary origins 2026-08-28 11:05:34 +08:00
yeuimu cbdb507be9 fix(api): allow pageSize up to 1000 so admin goods tree can load full list 2026-08-28 09:36:15 +08:00
yeuimu f06dfffbda feat(public): resolve goods by secondary sds id and merge variants 2026-08-27 18:20:25 +08:00
yeuimu d9ecd04747 feat(origin-goods): include merged secondary references in tree stats 2026-08-27 18:18:29 +08:00
yeuimu 9c279ae393 feat(goods): support merged secondary origin goods in create/update/detail 2026-08-27 18:15:56 +08:00
yeuimu 848eed0b6f feat(db): add good_origin_goods junction table for merged origin goods 2026-08-27 18:09:00 +08:00
yeuimu 6c61a4e871 feat(deploy): production deployment setup and fixes
- Debian-based api image (bookworm-slim), docker/debian mirrors, prisma
  binaryTargets for openssl 3.0
- nginx: admin SPA under /admin, TLS via acme.sh (ZeroSSL) + auto-renewal
  cron, http->https redirect
- prisma: add origin_goods.delisted migration, sync missing schema
  (good_image/tag_font_color/good_tags), fix users.createdAt Timestamptz
- api: CORS wildcard reflection, helmet CORP cross-origin, price
  backfill in persistProductDetail, categoryIcon ancestor fallback,
  mediaByColor per-color gallery in public goods detail
- admin: /admin base path (vite + router)
- import-data.mjs: udt_name casting, serial sequence advance fix
2026-08-26 14:23:09 +08:00
yeuimu be0b90e68f feat(security): HttpOnly cookie sessions, token revocation, and RBAC
- Add User.role (enum Role/ADMIN) and User.tokenVersion with migration
- Login now issues short-lived access token (30m default) + 7d refresh
  token, both embedding tokenVersion and a typ discriminator
- Tokens delivered via HttpOnly SameSite cookies (ir_at, ir_rt scoped
  to /auth); refresh token never leaves the cookie
- New endpoints: POST /auth/refresh (rotation), GET /auth/me,
  POST /auth/logout (bumps tokenVersion, revoking all tokens)
- JWT strategy accepts bearer or cookie, rejects refresh tokens, and
  verifies tokenVersion + user existence on every request
- Global RolesGuard: authenticated routes require ADMIN unless widened
  via @Roles(...)
- Admin SPA: session fully cookie-based, no token in localStorage;
  router guard restores session via /auth/me; axios auto-refreshes once
  on 401; stale localStorage keys cleaned up
2026-08-22 12:04:56 +08:00
yeuimu 9c1106586a fix(security): harden auth, upload, and API configuration
- Lock public registration to first-user bootstrap (403 afterwards)
- Require JwtAuthGuard on upload + whitelist png/jpg/webp/gif (SVG/XSS blocked)
- Add global throttling (login/register 5/min, upload 10/min)
- Add helmet security headers; serve uploads with nosniff
- Replace permissive CORS (origin:true+credentials) with CORS_ORIGINS whitelist
- Disable Swagger outside development; sanitize 500 error responses
- Enforce 32+ char JWT_SECRET; make token expiry configurable (TOKEN_EXPIRES_IN)
- Re-check user in DB on every JWT validation (revocation on user delete)
- Dummy bcrypt compare to prevent login user-enumeration via timing
- Map malformed BigInt inputs to 400 instead of 500
- Widen .gitignore to .env* and add apps/api/.env.example
- Disable Nuxt devtools and sourcemaps
2026-08-22 11:55:13 +08:00
yeuimu b04623ebdd feat(goods): add editable custom products 2026-08-21 14:45:42 +08:00
yeuimu a4151607c5 fix(sync): hydrate all origin product details 2026-08-21 14:05:15 +08:00
yeuimu 4cc99f3f23 fix(docs): describe tags as grouped array 2026-08-21 11:44:25 +08:00
yeuimu 8b38d6fef7 refactor(api): group public tag filters 2026-08-21 11:34:57 +08:00
yeuimu 437d9ca93b refactor(api): pair public tag filters with groups 2026-08-21 10:24:35 +08:00
yeuimu d375df810d feat(admin): manage and sync product details 2026-08-21 10:18:57 +08:00
yeuimu 7d09077f1d feat(api): add mini program catalog endpoints and product details 2026-08-21 02:11:20 +08:00