Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b6223b3816 | |||
| 117cec431b | |||
| d8295d14da |
@@ -0,0 +1,66 @@
|
||||
执行本地发版流程,版本号为 $ARGUMENTS。
|
||||
|
||||
按以下步骤顺序执行,任意步骤失败立即停止并报告错误。
|
||||
|
||||
## 1. 检查工作区
|
||||
|
||||
检查是否有未提交的改动(`git status`)。如果有,列出文件并询问用户是否继续。
|
||||
|
||||
## 2. 本地构建
|
||||
|
||||
```bash
|
||||
export PATH="/opt/homebrew/bin:$PATH"
|
||||
cd backend && go build ./...
|
||||
```
|
||||
|
||||
## 3. 运行测试
|
||||
|
||||
```bash
|
||||
export PATH="/opt/homebrew/bin:$PATH"
|
||||
cd backend && go test ./...
|
||||
cd client && flutter analyze --no-fatal-infos --no-fatal-warnings
|
||||
```
|
||||
|
||||
## 4. 更新 CHANGELOG.md
|
||||
|
||||
检查 CHANGELOG.md 中是否已有 `## [$ARGUMENTS]` 版本节。
|
||||
|
||||
- **已有**:直接使用,不修改。
|
||||
- **没有**:读取 `git log` 从上一个 tag 到 HEAD 的提交记录,自动生成一个版本节插入到文件顶部(位于已有版本节之前),格式如下:
|
||||
|
||||
```
|
||||
## [$ARGUMENTS] - <今天日期>
|
||||
|
||||
### 新功能
|
||||
- ...
|
||||
|
||||
### 改进
|
||||
- ...
|
||||
|
||||
### 修复
|
||||
- ...
|
||||
```
|
||||
|
||||
只保留有内容的分类(新功能/改进/修复),根据 commit message 的类型(feat/fix/refactor 等)归类。
|
||||
|
||||
## 5. 提交代码
|
||||
|
||||
将所有改动(包括 CHANGELOG.md)用以下格式提交到 main:
|
||||
|
||||
```
|
||||
chore: release v$ARGUMENTS
|
||||
```
|
||||
|
||||
## 6. 打 tag
|
||||
|
||||
```bash
|
||||
git tag v$ARGUMENTS
|
||||
```
|
||||
|
||||
## 7. 推送
|
||||
|
||||
```bash
|
||||
git push ssh://git@git.51yanmei.com:2222/wangjia/jiu.git main v$ARGUMENTS
|
||||
```
|
||||
|
||||
推送成功后提示用户:CI/CD 已触发,等待 Telegram 通知。
|
||||
@@ -5,6 +5,14 @@ All notable changes to this project will be documented in this file.
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [1.0.2] - 2026-05-25
|
||||
|
||||
### 改进
|
||||
- 添加 /release 本地发版 slash command,一键完成构建、测试、打 tag 和推送
|
||||
|
||||
### 修复
|
||||
- Web 版打印弹窗被浏览器拦截时,由静默失败改为给出明确提示,并自动上报错误
|
||||
|
||||
## [1.0.1] - 2026-05-25
|
||||
|
||||
### 改进
|
||||
|
||||
@@ -251,3 +251,55 @@ cd client && flutter test
|
||||
tx.Set("gorm:query_option", "FOR UPDATE").Where(...).First(&model)
|
||||
```
|
||||
- 适用场景:单号生成(`number_rules`)、库存扣减(`inventories`)、任何 check-then-act 模式
|
||||
|
||||
### 发版流程
|
||||
|
||||
使用 `/release <version>` slash command:
|
||||
|
||||
```
|
||||
/release 1.0.2
|
||||
```
|
||||
|
||||
执行顺序:本地 build → test → 更新 CHANGELOG.md → git commit → tag → push main+tag。
|
||||
CI/CD(Forgejo)收到 tag 后自动:编译 → 测试 → 创建 Release → 部署 EC2 → Telegram 通知。
|
||||
|
||||
**CHANGELOG 格式**(Keep a Changelog):
|
||||
```markdown
|
||||
## [1.0.2] - YYYY-MM-DD
|
||||
|
||||
### 新功能
|
||||
- ...
|
||||
|
||||
### 改进
|
||||
- ...
|
||||
|
||||
### 修复
|
||||
- ...
|
||||
```
|
||||
只保留有内容的分类。`/release` 会检查 CHANGELOG 是否已有该版本节,没有则从 git log 自动生成。
|
||||
|
||||
### 异常上报
|
||||
|
||||
客户端异常已在两处统一捕获,**无需**在每个业务层重复处理:
|
||||
- `main.dart`:`FlutterError.onError` + `runZonedGuarded` 捕获所有未处理异常
|
||||
- `api_client.dart`:Dio 拦截器自动上报所有 HTTP 5xx 错误
|
||||
|
||||
需要**手动调用** `reportError(e, st)` 的场景:
|
||||
- 技术性异常(JSON 解析失败、第三方 SDK 崩溃等)
|
||||
- **不需要**上报:`AppException` 及其子类(已知业务错误)
|
||||
|
||||
```dart
|
||||
} catch (e, st) {
|
||||
reportError(e, st); // 一行,fire-and-forget
|
||||
rethrow;
|
||||
}
|
||||
```
|
||||
|
||||
### 搜索实现(拼音)
|
||||
|
||||
商品名搜索同时支持汉字、全拼、首字母:
|
||||
- `products` 表有 `name_pinyin`(全拼)和 `name_initials`(首字母)两列
|
||||
- Create / Update / FindOrCreate 写入时自动调用 `util.ToPinyin()` 生成,**无需手动维护**
|
||||
- 启动时对 `name_pinyin = ''` 的存量数据自动回填(`backfillPinyin`)
|
||||
- 库存搜索 SQL 同时 LIKE 匹配 name、code、name_pinyin、name_initials
|
||||
- 新增其他需要拼音搜索的实体时,复用 `backend/internal/util/pinyin.go` 的 `ToPinyin()`
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@ migration:
|
||||
- platform: root
|
||||
create_revision: db50e20168db8fee486b9abf32fc912de3bc5b6a
|
||||
base_revision: db50e20168db8fee486b9abf32fc912de3bc5b6a
|
||||
- platform: web
|
||||
- platform: android
|
||||
create_revision: db50e20168db8fee486b9abf32fc912de3bc5b6a
|
||||
base_revision: db50e20168db8fee486b9abf32fc912de3bc5b6a
|
||||
|
||||
|
||||
@@ -4,13 +4,15 @@ import 'dart:typed_data';
|
||||
import 'package:web/web.dart' as web;
|
||||
import '../../models/stock_in.dart';
|
||||
import '../../models/stock_out.dart';
|
||||
import '../errors/error_reporter.dart';
|
||||
|
||||
void _openPrintWindow(String html) {
|
||||
final win = web.window.open('', '_blank');
|
||||
if (win != null) {
|
||||
win.document.write(html.toJS);
|
||||
win.document.close();
|
||||
if (win == null) {
|
||||
throw Exception('浏览器已拦截弹窗,请在地址栏允许弹窗后重试');
|
||||
}
|
||||
win.document.write(html.toJS);
|
||||
win.document.close();
|
||||
}
|
||||
|
||||
Future<void> printProductLabelImpl({
|
||||
@@ -187,7 +189,12 @@ body { width: 4in; height: 2in; overflow: hidden; background: #fff; }
|
||||
</body>
|
||||
</html>''';
|
||||
|
||||
_openPrintWindow(html);
|
||||
try {
|
||||
_openPrintWindow(html);
|
||||
} catch (e, st) {
|
||||
reportError(e, st);
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
// ── 入库单 ──────────────────────────────────────────────────────────────────
|
||||
@@ -293,7 +300,12 @@ Future<void> printStockInOrderImpl(StockInOrder order) async {
|
||||
</body>
|
||||
</html>''';
|
||||
|
||||
_openPrintWindow(html);
|
||||
try {
|
||||
_openPrintWindow(html);
|
||||
} catch (e, st) {
|
||||
reportError(e, st);
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
// ── 出库单 ──────────────────────────────────────────────────────────────────
|
||||
@@ -396,5 +408,10 @@ Future<void> printStockOutOrderImpl(StockOutOrder order) async {
|
||||
</body>
|
||||
</html>''';
|
||||
|
||||
_openPrintWindow(html);
|
||||
try {
|
||||
_openPrintWindow(html);
|
||||
} catch (e, st) {
|
||||
reportError(e, st);
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user