wangjia
|
6570dfc240
|
chore: release server-v1.0.77
Deploy Server / release-deploy-server (push) Successful in 1m32s
出库列表按商品编码精确反查 + stock_out_items(shop_id,product_code) 索引
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YZ4DskSRKsSiheQonFtQvx
|
2026-06-22 22:24:43 +08:00 |
|
wangjia
|
08f9e20010
|
chore: release server-v1.0.76
Deploy Server / release-deploy-server (push) Successful in 1m30s
修复编辑入库单草稿后合计金额被清零(Update 漏累加 total)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YZ4DskSRKsSiheQonFtQvx
|
2026-06-22 20:15:38 +08:00 |
|
wangjia
|
3641f1cf16
|
chore: release server-v1.0.75
Deploy Server / release-deploy-server (push) Successful in 1m7s
出库售价(应收按售价) + 库存选择器分页上限修复 + seed 改为序列号模型
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YZ4DskSRKsSiheQonFtQvx
|
2026-06-22 13:01:25 +08:00 |
|
wangjia
|
20f9e3a410
|
chore: release server-v1.0.74
Deploy Server / release-deploy-server (push) Successful in 2m4s
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YZ4DskSRKsSiheQonFtQvx
|
2026-06-21 22:17:24 +08:00 |
|
wangjia
|
6faadf8670
|
fix(backend): 库存导入按商品编号匹配不再按名称合并 + 新增 fix-inventory-products 修复工具
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YZ4DskSRKsSiheQonFtQvx
|
2026-06-21 21:24:53 +08:00 |
|
wangjia
|
13a6777a85
|
chore: release server-v1.0.73
Deploy Server / release-deploy-server (push) Successful in 56s
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YZ4DskSRKsSiheQonFtQvx
|
2026-06-21 19:46:30 +08:00 |
|
wangjia
|
7a78448a25
|
chore: release server-v1.0.72
Deploy Server / release-deploy-server (push) Successful in 56s
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YZ4DskSRKsSiheQonFtQvx
|
2026-06-21 19:09:30 +08:00 |
|
wangjia
|
203cc7ce21
|
chore: release server-v1.0.71
Deploy Server / release-deploy-server (push) Successful in 56s
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YZ4DskSRKsSiheQonFtQvx
|
2026-06-21 18:14:27 +08:00 |
|
wangjia
|
7b67466a3e
|
chore: release server-v1.0.70
Deploy Server / release-deploy-server (push) Successful in 56s
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YZ4DskSRKsSiheQonFtQvx
|
2026-06-21 17:32:55 +08:00 |
|
wangjia
|
c1febfffec
|
feat(backend): 入库每行新建独立产品发独立序列号(product 加生产日期/批次列)
Deploy Server / release-deploy-server (push) Successful in 59s
入库 Create/Update 改为对每条明细新建独立 product(createIndependentProduct,
nextProductCode 自增 + uk_shop_code 唯一约束兜底),回填 product_id;不再按名称复用。
product 表加 production_date/batch_no。向后兼容:保留明细/库存快照列。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YZ4DskSRKsSiheQonFtQvx
|
2026-06-21 16:30:11 +08:00 |
|
wangjia
|
1a1d9aa15e
|
fix(backend): 商品编码改用最大序号生成不复用软删号 + 加 (shop_id,code) 唯一约束,根治重复编码
Deploy Server / release-deploy-server (push) Successful in 53s
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YZ4DskSRKsSiheQonFtQvx
|
2026-06-21 10:51:34 +08:00 |
|
wangjia
|
fb1e637ce4
|
fix(backend): 入库审核库存沿用明细真实快照名 + 库存列表过滤软删商品,修复历史导入占位顶替;入库/出库列表支持 keyword 搜索
Deploy Server / release-deploy-server (push) Successful in 53s
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YZ4DskSRKsSiheQonFtQvx
|
2026-06-20 16:02:02 +08:00 |
|
wangjia
|
7f3261a4a9
|
chore: release server-v1.0.66
Deploy Server / release-deploy-server (push) Successful in 51s
fix(backend): 入库/出库列表按 order_date 倒序,历史导入单按业务日期归位
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-06-20 12:50:26 +08:00 |
|
wangjia
|
50b5c9fccb
|
chore: release server-v1.0.65
Deploy Server / release-deploy-server (push) Successful in 1m5s
fix(backend): 入库/出库列表 status 支持多状态 IN 查询
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-06-20 12:19:46 +08:00 |
|
wangjia
|
15ef71734d
|
chore: release server-v1.0.64
Deploy Server / release-deploy-server (push) Successful in 55s
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-06-20 10:13:49 +08:00 |
|
wangjia
|
90d2c8668b
|
feat(backend): 历史进销存导入器 cmd/import-history + 单据明细快照列
一次性 CLI 导入旧系统 data/ 入库·出库 总表/详情表到当前库,历史只读(绝不
回放 inventories/inventory_logs)。明细忠实直存不做 SKU 归一:order/item 表
新增 creator_id(制单人)与 product_code/product_name/series/spec 快照列
(出库另补 batch_no/production_date),导入行 product_id 指向共享占位商品。
order_no 天然主键 (shop_id,order_no) 幂等去重,可重复跑。
testutil sqlite DDL 同步补列以保持 service 测试通过。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-06-20 07:05:08 +08:00 |
|
wangjia
|
7bbc944ae2
|
chore: release server-v1.0.62
Deploy Server / release-deploy-server (push) Successful in 51s
服务端安全加固:多维限流(按 IP/按门店)+ 敏感接口独立速率上限抵御 DDoS/刷接口;
登录暴力破解新增按来源 IP 锁定;反代后正确识别真实客户端 IP;
门店 custom_fields 轻量配置(录入默认值)透传保存。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Y2Wdwo7SmgBJU37cBrkhPK
|
2026-06-19 20:20:44 +08:00 |
|
wangjia
|
9b6dba4c2b
|
test(backend): 补 redeem_code 单测 + gitignore todo/ 与 gencode 产物
覆盖 NormalizeCode 边界/幂等、GenerateRedeemCode 格式/无歧义字母表
(剔除 0/O/1/I/L)/无碰撞。.gitignore 忽略全局 todo skill 本地数据
与 backend/gencode 构建产物。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Y2Wdwo7SmgBJU37cBrkhPK
|
2026-06-19 14:04:22 +08:00 |
|
wangjia
|
23dff69c62
|
feat(backend): 授权改为时长兑换券体系 + 退役 ed25519/HMAC + 平台生码工具
- 新增 license_codes 码池表 + model.LicenseCode;licenses 加 tier 档位列
- LicenseService.Redeem:单事务 FOR UPDATE 校验码未用 → 时长叠加(可叠加,0=永久)
→ 写 type/tier/max_devices → 绑设备(超限整笔回滚) → 标记已用 → 即时失效 phase 缓存
路由仍 POST /license/activate,客户端零破坏
- util.GenerateRedeemCode/NormalizeCode:JIUKU-XXXX-XXXX 短码(crypto/rand)
- cmd/gencode:平台批量生成兑换码并落库;删除 cmd/issue、cmd/genkey
- 退役 ed25519 + HMAC:删 util/license_key、GenerateKey、License 全部 config 字段
及生产启动私钥校验;trial 改直接建行(无需私钥、去 Fatal)
- tier 档位钩子默认 standard,分档消费模式后续设计
- 测试:Redeem 全场景(叠加/过期重置/永久/一码一次/无效/设备上限回滚)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-06-19 12:14:27 +08:00 |
|
wangjia
|
e41085a878
|
feat(backend): 会话安全加固 + 授权实时 phase + 首次使用自动试用
会话安全(jti 轮换 / 重用检测 / 改密吊销 / 禁用即时下线 / 清理 / 失败登录落库):
- refresh token 轮换 jti + token-family 重用检测,旧 token 重放即吊销整条会话
- 改密码、停用用户即时吊销其全部活跃会话(revoked_by 审计)
- 中间件 session JOIN user 校验,禁用/删除用户带 token 请求返回 401 USER_DISABLED
- 新增 login_attempts 失败登录落库 + 会话保留期清理 goroutine
授权实时 phase + 心跳回带:
- LicenseGuard 改为按当前 DB 实时计算 phase(30s 每店缓存),续费/过期/被改 ~30s 内对写操作生效,无需重登
- /auth/ping 回带授权概况(ShopInfoView,与 /license/info 同构),客户端一次心跳即刷新横幅/门禁
首次使用自动试用 + code-review 修复:
- 门店首次登录/续期无有效授权时自动签发 30 天 trial(快路径无锁 Count,仅首用走 FOR UPDATE 事务)
- ShopInfo 区分「确无授权」与瞬时 DB 错误,避免误降级
- trial 签发后改为在事务提交后再失效 phase 缓存(修复早于提交的竞态)
- 存量无 sid token 续期纳入显式上限,legacy 会话不再游离于并发配额之外
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-06-19 07:34:04 +08:00 |
|
wangjia
|
2d84bda99a
|
chore: release server-v1.0.58
Deploy Server / release-deploy-server (push) Successful in 48s
RefreshTokens 续登路径补发首次试用,修复自动续登老用户授权停留「未激活」。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Y2Wdwo7SmgBJU37cBrkhPK
|
2026-06-18 19:14:25 +08:00 |
|
wangjia
|
64a64e7c0a
|
feat(backend): 首次登录无有效授权时自动发放 30 天试用
未激活门店(seed/手动建店/老数据,无任何 license 行)此前被当作永久授权,
既不拦截也无提示。现在登录时若门店无有效授权,自动签发 30 天 trial,
之后照常走 grace/readonly/locked 降级链。须在 issueTokens 之前发放,
使 JWT 的 lic_exp 带上新到期日。幂等(已有有效授权不重发),签发失败
仅记日志不阻断登录。复用注册路径的发放逻辑(抽出 issueTrialLicense)。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Y2Wdwo7SmgBJU37cBrkhPK
|
2026-06-18 07:59:44 +08:00 |
|
wangjia
|
53fa259284
|
chore: release server-v1.0.56
Deploy Server / release-deploy-server (push) Successful in 40s
会话/设备管理后端:user_sessions 会话表、JWT 加 sid 校验、按平台类限并发登录、
登录失败锁定、修复禁用账号仍可凭 refresh 续期漏洞、/auth/ping、/auth/logout、
GET /sessions、DELETE /sessions/:id(管理员强制下线)。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-06-17 23:12:13 +08:00 |
|
wangjia
|
aa7099ba94
|
feat(devops): 发版拆分为 client/site/server 三条独立流水线
将单一 CI/CD 流水线拆成三条互不影响的发布流水线,各有 tag 前缀、独立版本
序列与独立 CHANGELOG:
- client(client-v*):Flutter 全平台 + version.yaml 自更新清单
- site(site-v*):web/ Eleventy 营销站,不含 web 版 app
- server(server-v*):backend Go 服务 + 共享基建 nginx/systemd
新增 3 个 workflow(deploy-client/site/server.yml)替换 deploy.yml;CI 脚本
按 part 拆分为 compile-/release-/deploy-{client,site,server}.sh,抽出公共函数
lib-forgejo.sh;compile-{macos,android,ios,windows}.sh 改去 client-v 前缀;
manual.yml 按前缀路由回滚。
跨流水线解耦:version.yaml 归 client,后端每请求实时读取(不重启、不触发
server 流水线);官网下载页的版本徽章/下载链接/更新日志时间线运行时经
/api/v1/public/release 动态拉取(API 不可达回退构建时静态内容)。为此一次性
扩展后端 changelog 字段(version.go/public.go)与 download.njk 动态渲染。
CHANGELOG.md 重命名为 CHANGELOG-client.md,新增 CHANGELOG-site/server.md;
重写 /release 命令为 /release <part> [version];同步更新 CLAUDE.md 与部署文档。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-06-17 08:21:28 +08:00 |
|
wangjia
|
eca62ba2c3
|
fix(backend): 库存导入改用 shakinm/xlsReader,按唯一编号去重,入库日期取自 Excel
extrame/xls 误读真实 .xls(3264 行只读出 455 行、编号丢失、数字被当
日期序列),换用 shakinm/xlsReader 正确读取全部行。去重改为「有商品编号
时仅按编号匹配」,避免不同批次被 NSS 回退合并;Inventory.ProductCode
存行自身唯一编号。入库时间取 Excel 入库日期列写入 created_at。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-06-17 08:04:04 +08:00 |
|
wangjia
|
5cfd1795ec
|
fix(backend): 库存导入用 Excel「入库日期」+ 同 key 增量更新/全同跳过
- 读取 Excel「入库日期」列(默认第12列,兼容入库日期/入库时间/入库日),
显式写入库存 CreatedAt,不再被 GORM 落成导入当天;新增
parseTimeWithFallback 兼容 yyyy-MM-dd / 含时分秒 / Excel 序列号
- 同 key(商品编码|仓库,回退 名+系列+规格|仓库):全字段一致则跳过,
仅差异字段增量更新;库存流水仅在 quantity 实际变化时追加
- 响应新增 skipped 计数;多租户 shop_id 隔离不变
go build / vet / test 全过。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-06-17 00:56:31 +08:00 |
|
wangjia
|
c1ba6c6d97
|
feat(backend): 二维码链接统一改为 /app/product/ 路径,消除 URL 跳变
Deploy / build-linux-web (push) Successful in 46s
Deploy / build-windows (push) Successful in 1m41s
Deploy / build-macos (push) Successful in 1m27s
Deploy / build-android (push) Successful in 58s
Deploy / build-ios (push) Successful in 6s
Deploy / release-deploy (push) Successful in 1m53s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
2026-06-15 17:55:24 +08:00 |
|
wangjia
|
2b327cd365
|
fix(backend): 修复 /app/product/ 分享无 OG 标签,描述分两行显示
Deploy / build-linux-web (push) Successful in 49s
Deploy / build-windows (push) Successful in 1m45s
Deploy / build-macos (push) Successful in 1m24s
Deploy / build-android (push) Successful in 58s
Deploy / build-ios (push) Failing after 10m6s
Deploy / release-deploy (push) Has been skipped
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
2026-06-15 17:52:41 +08:00 |
|
wangjia
|
07f8973477
|
fix(backend): 分享卡片描述补充系列(度数)信息
Deploy / build-linux-web (push) Successful in 47s
Deploy / build-windows (push) Successful in 1m44s
Deploy / build-macos (push) Successful in 1m26s
Deploy / build-android (push) Successful in 58s
Deploy / build-ios (push) Successful in 7s
Deploy / release-deploy (push) Successful in 1m50s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
2026-06-15 17:36:04 +08:00 |
|
wangjia
|
bde2d35756
|
fix(backend): 修复分享卡片 <title> 因缩进导致替换失效的问题
Deploy / build-linux-web (push) Successful in 47s
Deploy / build-windows (push) Successful in 1m45s
Deploy / build-macos (push) Successful in 1m27s
Deploy / build-android (push) Successful in 1m0s
Deploy / build-ios (push) Successful in 7s
Deploy / release-deploy (push) Successful in 1m48s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
2026-06-15 16:37:20 +08:00 |
|
wangjia
|
4ac5adac3e
|
chore: release v1.0.44
Deploy / build-linux-web (push) Successful in 47s
Deploy / build-windows (push) Successful in 1m44s
Deploy / build-macos (push) Successful in 1m24s
Deploy / build-android (push) Successful in 1m0s
Deploy / build-ios (push) Successful in 6s
Deploy / release-deploy (push) Successful in 1m56s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
2026-06-15 16:31:47 +08:00 |
|
wangjia
|
8c2dcf49a8
|
chore: release v1.0.43
Deploy / build-linux-web (push) Successful in 49s
Deploy / build-windows (push) Successful in 1m44s
Deploy / build-macos (push) Successful in 1m25s
Deploy / build-android (push) Successful in 1m9s
Deploy / build-ios (push) Successful in 6s
Deploy / release-deploy (push) Successful in 1m56s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
2026-06-15 16:11:38 +08:00 |
|
wangjia
|
a5bdb79f81
|
chore: release v1.0.40
Deploy / build-linux-web (push) Successful in 48s
Deploy / build-windows (push) Successful in 1m41s
Deploy / build-macos (push) Successful in 1m22s
Deploy / build-android (push) Successful in 1m9s
Deploy / build-ios (push) Successful in 6s
Deploy / release-deploy (push) Successful in 1m49s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
2026-06-15 14:23:03 +08:00 |
|
wangjia
|
36564571fa
|
chore: 补提测试修复、新增测试工具和设计文档
- fix(test): 同步 listInventory 签名(补 series/spec 参数)
- test(backend): 新增 import_parse_test.go
- chore(backend): 新增 cmd/test_xls 调试工具
- docs: UI 筛选组件设计规范、Gemini 项目配置
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
2026-06-15 11:17:35 +08:00 |
|
wangjia
|
eac3e8825c
|
feat(client): 库存查询新增规格/系列/入库时间;筛选组件支持搜索+滚动+标签
- 库存查询加规格、系列服务端筛选(后端 IN 条件,前端从 product-options API 加载全量选项)
- 库存查询新增「入库时间」列,导出 Excel 同步带上
- 入库单商品明细:隐藏商品编码列,生产日期从选填折叠区移至主行常显
- 筛选弹窗重构:顶部搜索框实时过滤 + 滚动列表(ConstrainedBox maxHeight)根治溢出
- 已选项以 Chip 标签置顶展示,每个标签带 × 可单独取消
- FilterableColumnHeader 筛选图标改为常驻,不再需要 hover 才显示
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
2026-06-15 11:15:43 +08:00 |
|
wangjia
|
316e9b6fc9
|
fix(backend): ImportProducts 改用 parseUploadedExcel 修复导入截断 100 条问题
旧代码直接调用 excelize.GetRows() 有内部 100 行限制;
其他 7 个导入函数在 50b139c 已修复,唯独 ImportProducts 漏网。
统一改用 parseUploadedExcel() 辅助函数,支持 .xls/.xlsx 且无行数截断。
|
2026-06-14 08:26:46 +08:00 |
|
wangjia
|
2ed0010c67
|
fix(backend): 补全多处 Scan/Count 错误未检查,私钥缺失改为 Fatal
- stock.go: ApproveStockIn 入库前库存 Scan 加错误检查
- license.go: Activate 阶段 Count 设备数加错误检查;createTrialLicense
私钥未配置由静默跳过改为 log.Fatalf,防止生产环境新注册门店无 license
- inventory.go: List 接口两处 Raw Scan 加错误检查并返回 500
- finance.go: ArBalance 汇总 Raw Scan 加错误检查并返回 500
|
2026-06-14 08:12:57 +08:00 |
|
wangjia
|
c553237f15
|
fix(db): 缩短 license_key 列至 VARCHAR(768) 避免 InnoDB 索引超限 [tsk_Qmk4wT_dkqRR]
原 size:2048 在 utf8mb4 下产生 8192 字节索引,超过 InnoDB 3072 字节上限,
全新环境 AutoMigrate 必失败。改为 VARCHAR(768)(768×4=3072 字节,恰好
在上限内),schema.sql 同步更新列类型并将 uk_license_key 改为全列索引(无
需前缀)。新增测试 TestLicenseTokenFitsColumnLimit 验证最坏情况 token 长度
确实在 768 字符以内。
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
2026-06-13 12:01:30 +08:00 |
|
wangjia
|
e0bee00ff4
|
fix: 批量改进 (#2/#23/#38/#39/#40/#47)
- Web: 隐藏 ICP 备案号(条件渲染),联系入口改为微信咨询(条件渲染,配置 wechat 字段后生效)
- backend: 拼音回填抽取为独立 cmd/backfill-pinyin 工具,不再在启动时运行
- backend: DB 连接池参数(max_idle_conns/max_open_conns)改为配置可控,默认 10/100
- backend: Inventory 反范式字段添加注释说明快照语义
- backend: 全量 handler 采用 util.RespondSuccess/RespondCreated 统一响应格式(51 处)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
2026-06-11 09:33:43 +08:00 |
|
wangjia
|
d28aba863e
|
fix(backend): license service 小修 (#45-46-48)
- #45 Activate 幂等路径 Update 加错误检查,失败时返回 error 而非静默成功
- #46 Verify 加载 license 加 shop_id 过滤(防御纵深)
- #48 删除 ErrDeviceMismatch 死代码(多设备迁移后已无单设备约束)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
2026-06-11 09:06:15 +08:00 |
|
wangjia
|
f928d5c6a3
|
fix(backend): 架构质量改进批次三 (#41-44)
- #41 CheckInventoryAvailability Scan 加错误检查,DB 故障不再误报库存不足
- #42 finance ListRecords 和 inventory Logs 补加 pageSize 上限(500)
- #43 ReconcileInventory 两条 Scan 加错误检查,DB 故障返回 500 而非误导性空结果
- #44 license Info 改用 CountDevices(licenseID) 统计当前 license 绑定数,修复跨 license 计数偏差
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
2026-06-11 08:21:42 +08:00 |
|
wangjia
|
3ab78dbf7a
|
fix(backend): 架构质量改进批次二 (#32-37)
- #32 License 激活迁移到 license_devices 表:Activate/Verify/Deactivate 全部改用
license_devices,新增 max_devices 校验和 GET /license/devices 端点;
Activate 现在校验 shop_id 防跨租户激活
- #33 checkInventory 从 StockOutHandler 移到 StockService.CheckInventoryAvailability
- #34 新增 util/response.go 统一错误响应工具(RespondError/RespondSuccess/RespondCreated)
- #35 生产模式 CORS Origin='*' 启动时 Fatal
- #36 生产模式 License 私钥未配置启动时 Fatal
- #37 新增 util/page.go ValidatePageSize,应用到 partner/product/stock_in/stock_out handler
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
2026-06-11 00:38:49 +08:00 |
|
wangjia
|
51cfe5fc6d
|
fix(backend): 修复库存 TOCTOU 竞态 + handler 白名单更新 + 对账接口
#29 ApproveStockOut:FOR UPDATE 锁定批次后再内存汇总总量,
消除预检 SUM 与加锁之间的竞态窗口,减少一次 DB 往返
#30 partner/warehouse/user/product_attr/product_option Update 方法:
- 绑定到独立 req struct,防止请求体覆盖记录 ID
- 改用 db.Model.Where("shop_id=?").Updates(map) 白名单更新,
数据库层强制 shop_id 隔离约束
#31 新增 GET /api/v1/admin/reconcile:对比 inventories 当前库存
与 inventory_logs 流水净量,返回差异行,用于发现不平账异常
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
2026-06-11 00:21:48 +08:00 |
|
wangjia
|
bc85b2c8ad
|
fix(backend): license/info 返回 phase 字段,前端横幅/弹窗依赖此字段
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
2026-06-10 09:30:25 +08:00 |
|
wangjia
|
13fc9b7aaf
|
test(backend): 授权系统端到端验证测试 (21H)
util/license_key_test.go:
- TestLicenseKeyRoundTrip: 签发→验签完整流程
- TestVerifyLicenseToken_TamperedPayload: 签名被篡改后被拒绝
- TestVerifyLicenseToken_WrongKey: 用错误公钥验签失败
- TestVerifyLicenseToken_InvalidFormat: 格式非法被拒绝
middleware/license_guard_test.go:
- TestCalcLicensePhase: 覆盖全部 phase 边界
(perpetual→normal / future→normal / <7d→grace /
7-15d→readonly / >15d→locked)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
2026-06-10 07:10:15 +08:00 |
|
wangjia
|
ebf79d0355
|
feat: LicenseGuard 中间件 + Flutter license model/repo (21D+21E)
21D — 后端:
- Claims 新增 LicenseExpiresAt (*int64 unix 秒),写入 JWT 避免每次查库
- middleware/license_guard.go: CalcLicensePhase / LicenseGuard / GetLicensePhase
- grace(0-7d): 允许通行
- readonly(7-15d): 拦截非 GET 写操作 → 403
- locked(15d+): 全部拦截 → 403
- auth.go: issueTokens 在 JWT 中嵌入 license expires_at;Login 检查 locked 拒绝登录
- router: license/* 路由豁免 LicenseGuard(锁定时仍可激活/查状态)
21E — Flutter 前端:
- models/license.dart: 新 LicenseInfo,含 phase/maxDevices,去掉旧 activatedAt
- core/device/device_id.dart: 持久化 UUID-v4 作为设备 ID(SharedPreferences)
- repositories/license_repository.dart: getInfo/activate/deactivate,激活时携带设备信息
- providers/license_provider.dart: 改接 LicenseRepository
- settings_screen.dart: activatedAt 改为显示 maxDevices
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
2026-06-10 00:52:07 +08:00 |
|
wangjia
|
c402ea0070
|
feat(backend): license_devices 表 + 注册自动签发 trial (21B+21C)
21B — DB & model:
- licenses: 扩展 license_key 为 2048 字节(容纳 Ed25519 JWT),
新增 max_devices INT DEFAULT 3,device_id/activated_at 标为 deprecated
- 新增 license_devices 表(license_id+device_id 唯一索引)
- model/license_device.go:LicenseDevice struct
- main.go AutoMigrate 加入 LicenseDevice
- testutil/setup.go 同步 SQLite DDL
21C — trial at register:
- config: 新增 Ed25519PrivateKey 配置项(LICENSE_ED25519_PRIVATE_KEY 环境变量)
- service/license.go: createTrialLicense(tx, shopID) — 签发 30d trial,
私钥未配置时静默跳过(开发/测试不影响)
- service/auth.go: Register 事务末尾调用 createTrialLicense
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
2026-06-10 00:42:56 +08:00 |
|
wangjia
|
40a80467ce
|
feat(backend): Ed25519 license signing utilities (21A)
- util/license_key.go: LicensePayload struct, GenerateEd25519KeyPair,
IssueLicenseToken (sign), VerifyLicenseToken (verify offline)
- cmd/genkey: one-shot keypair generator with demo sign+verify
- config: add Ed25519PublicKey field (LICENSE_ED25519_PUBLIC_KEY env)
Token format: base64url(header).base64url(payload).base64url(ed25519-sig)
Private key stored in Bitwarden; public key embedded in config/client.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
2026-06-10 00:28:55 +08:00 |
|
wangjia
|
fab7bd21dd
|
feat: #13 公开商品 API 补充 code/barcode/batch.quantity,报错反馈携带商品和门店信息
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
2026-06-08 22:39:59 +08:00 |
|
wangjia
|
4e64e5eb47
|
feat: 入库单/商品详情展示产地保质期储存方式,商品详情显示建议零售价
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
2026-06-08 07:57:10 +08:00 |
|