Merge branch 'dev' into happi/dev/database-update

This commit is contained in:
Happi (哈比)
2026-03-07 14:29:22 +08:00
7 changed files with 91 additions and 19 deletions

View File

@@ -1400,7 +1400,7 @@ flowchart LR
<li>无法精确控制重建范围</li> <li>无法精确控制重建范围</li>
</ul> </ul>
<p><strong>实际案例UUTalk</strong></p> <p><strong>实际案例:</strong></p>
<pre><code class="language-dart">// 整个 Container 都会重建 <pre><code class="language-dart">// 整个 Container 都会重建
Obx(() =&gt; Container( Obx(() =&gt; Container(
@@ -1636,7 +1636,7 @@ Get.put&lt;BaseController&gt;(ChatController());
final controller = Get.find&lt;UserController&gt;(); // 找到 ChatController类型不匹配 final controller = Get.find&lt;UserController&gt;(); // 找到 ChatController类型不匹配
</code></pre> </code></pre>
<p><strong>运行时错误案例UUTalk 实际问题)</strong></p> <p><strong>运行时错误案例:</strong></p>
<ul> <ul>
<li>Controller 未初始化</li> <li>Controller 未初始化</li>
<li>类型转换错误</li> <li>类型转换错误</li>
@@ -1683,16 +1683,16 @@ ChatViewModel
<li>重构安全IDE 自动提示依赖变化</li> <li>重构安全IDE 自动提示依赖变化</li>
<li>团队协作:依赖关系明确,不会互相影响</li> <li>团队协作:依赖关系明确,不会互相影响</li>
</ul> </ul>
<h4>GetX + Obx vs Riverpod来自 UUTalk 项目的实践教训</h4> <h4>GetX + Obx vs Riverpod实践教训</h4>
<div style="background-color: #fff3cd; border-left: 4px solid #ffc107; padding: 15px; margin: 20px 0;"> <div style="background-color: #fff3cd; border-left: 4px solid #ffc107; padding: 15px; margin: 20px 0;">
<p><strong>真实案例警示</strong></p> <p><strong>典型问题示例</strong></p>
<p>以下内容基于 <strong>UUTalk 项目</strong>的实际经验,展示了 GetX + Obx 在大型项目中暴露的严重问题</p> <p>以下内容展示了 GetX + Obx 在大型项目中暴露的典型问题,作为选型参考</p>
</div> </div>
<h5>1. 状态管理混乱</h5> <h5>1. 状态管理混乱</h5>
<p><strong>UUTalk 项目的 GetX + Obx 问题代码chat_list_controller.dart</strong></p> <p><strong>GetX + Obx 问题代码示例chat_list_controller.dart</strong></p>
<pre><code class="language-dart">class ChatListController extends GetxController { <pre><code class="language-dart">class ChatListController extends GetxController {
var lastClickedSpecialChatId = (-1).obs; var lastClickedSpecialChatId = (-1).obs;
@@ -1728,7 +1728,7 @@ ChatViewModel
<h5>2. 过度嵌套和性能问题</h5> <h5>2. 过度嵌套和性能问题</h5>
<p><strong>UUTalk 项目的 Obx 嵌套地狱home_view.dart</strong></p> <p><strong>Obx 嵌套地狱示例home_view.dart</strong></p>
<pre><code class="language-dart">body: Obx(() =&gt; AnimatedPadding( <pre><code class="language-dart">body: Obx(() =&gt; AnimatedPadding(
child: GetBuilder( child: GetBuilder(
@@ -1765,7 +1765,7 @@ ChatViewModel
<h5>3. Controller 过于臃肿</h5> <h5>3. Controller 过于臃肿</h5>
<p><strong>UUTalk 项目的巨型 Controller</strong></p> <p><strong>巨型 Controller 反模式示例</strong></p>
<pre><code class="language-dart">// chat_list_controller.dart <pre><code class="language-dart">// chat_list_controller.dart
import 'dart:async'; import 'dart:async';
@@ -1811,7 +1811,7 @@ class ChatListController extends GetxController
<h5>4. 没有编译时安全</h5> <h5>4. 没有编译时安全</h5>
<p><strong>UUTalk 项目的运行时陷阱:</strong></p> <p><strong>GetX 运行时陷阱:</strong></p>
<pre><code class="language-dart">// 通过字符串查找 Controller运行时才知道对错 <pre><code class="language-dart">// 通过字符串查找 Controller运行时才知道对错
Get.find&lt;ChatListController&gt;(); Get.find&lt;ChatListController&gt;();
@@ -1839,7 +1839,7 @@ class ChatListController extends GetxController {
<h5>5. 难以测试</h5> <h5>5. 难以测试</h5>
<p><strong>UUTalk 项目的测试困境:</strong></p> <p><strong>GetX 测试困境:</strong></p>
<pre><code class="language-dart">// 测试时必须初始化整个 GetX 框架 <pre><code class="language-dart">// 测试时必须初始化整个 GetX 框架
testWidgets('test chat list', (tester) async { testWidgets('test chat list', (tester) async {
@@ -1865,7 +1865,7 @@ testWidgets('test chat list', (tester) async {
</ul> </ul>
</div> </div>
<h5>GetX + Obx vs Riverpod 实际对比(基于 UUTalk 项目经验)</h5> <h5>GetX + Obx vs Riverpod 实际对比</h5>
<table> <table>
<thead> <thead>
@@ -1933,7 +1933,7 @@ testWidgets('test chat list', (tester) async {
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 20px; margin: 20px 0;"> <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 20px; margin: 20px 0;">
<div style="background-color: #f8d7da; border: 2px solid #dc3545; padding: 15px; border-radius: 8px;"> <div style="background-color: #f8d7da; border: 2px solid #dc3545; padding: 15px; border-radius: 8px;">
<p><strong>GetX + ObxUUTalk 现状)</strong></p> <p><strong>GetX + Obx</strong></p>
<p><strong>状态混乱:</strong></p> <p><strong>状态混乱:</strong></p>
<pre><code class="language-dart">class ChatListController extends GetxController { <pre><code class="language-dart">class ChatListController extends GetxController {
@@ -1999,10 +1999,10 @@ class ChatListViewModel extends StateNotifier&lt;ChatListState&gt; {
</div> </div>
<h5>UUTalk 项目的痛点总结</h5> <h5>GetX + Obx 痛点总结</h5>
<div style="background-color: #fff3cd; border-left: 4px solid #ffc107; padding: 15px; margin: 20px 0;"> <div style="background-color: #fff3cd; border-left: 4px solid #ffc107; padding: 15px; margin: 20px 0;">
<p> UUTalk 项目的实际经验中,我们总结出 GetX + Obx 的核心问题:</p> <p>实践经验中,我们总结出 GetX + Obx 的核心问题:</p>
<ol> <ol>
<li><strong>"快速开发"变成"技术债"</strong>:初期确实快,但 6 个月后代码无法维护</li> <li><strong>"快速开发"变成"技术债"</strong>:初期确实快,但 6 个月后代码无法维护</li>
<li><strong>"响应式"变成"性能杀手"</strong>Obx 嵌套导致过度重建,页面卡顿</li> <li><strong>"响应式"变成"性能杀手"</strong>Obx 嵌套导致过度重建,页面卡顿</li>
@@ -2014,7 +2014,7 @@ class ChatListViewModel extends StateNotifier&lt;ChatListState&gt; {
<div style="background-color: #d4edda; border-left: 4px solid #28a745; padding: 15px; margin: 20px 0;"> <div style="background-color: #d4edda; border-left: 4px solid #28a745; padding: 15px; margin: 20px 0;">
<p><strong>Riverpod 的技术优势</strong></p> <p><strong>Riverpod 的技术优势</strong></p>
<p>基于 UUTalk 项目的实践教训,我们选择 Riverpod 作为新架构的状态管理方案,因为它从根本上解决了 GetX 的所有问题:</p> <p>基于以上实践教训,我们选择 Riverpod 作为新架构的状态管理方案,因为它从根本上解决了 GetX 的所有问题:</p>
<ul> <ul>
<li><strong>编译时安全</strong>:不会再有运行时崩溃</li> <li><strong>编译时安全</strong>:不会再有运行时崩溃</li>
<li><strong>结构化状态</strong>@freezed 强制统一状态结构</li> <li><strong>结构化状态</strong>@freezed 强制统一状态结构</li>

View File

@@ -47,6 +47,7 @@ if [ "$FORMAT" = "aab" ]; then
cd "$APP_DIR" cd "$APP_DIR"
flutter build appbundle \ flutter build appbundle \
--release \ --release \
--no-pub \
--dart-define-from-file=config/config.json \ --dart-define-from-file=config/config.json \
--split-debug-info="$DEBUG_INFO_DIR" \ --split-debug-info="$DEBUG_INFO_DIR" \
--obfuscate --obfuscate
@@ -57,6 +58,7 @@ else
cd "$APP_DIR" cd "$APP_DIR"
flutter build apk \ flutter build apk \
--release \ --release \
--no-pub \
--dart-define-from-file=config/config.json \ --dart-define-from-file=config/config.json \
--split-debug-info="$DEBUG_INFO_DIR" \ --split-debug-info="$DEBUG_INFO_DIR" \
--obfuscate --obfuscate

View File

@@ -41,6 +41,7 @@ cd "$APP_DIR"
flutter build ipa \ flutter build ipa \
--release \ --release \
--no-pub \
--dart-define-from-file=config/config.json \ --dart-define-from-file=config/config.json \
--split-debug-info="$DEBUG_INFO_DIR" \ --split-debug-info="$DEBUG_INFO_DIR" \
--obfuscate --obfuscate

View File

@@ -37,6 +37,7 @@ cd "$APP_DIR"
flutter build macos \ flutter build macos \
--release \ --release \
--no-pub \
--dart-define-from-file=config/config.json \ --dart-define-from-file=config/config.json \
--split-debug-info="$DEBUG_INFO_DIR" \ --split-debug-info="$DEBUG_INFO_DIR" \
--obfuscate --obfuscate

View File

@@ -39,6 +39,7 @@ cd "$APP_DIR"
flutter build windows \ flutter build windows \
--release \ --release \
--no-pub \
--dart-define-from-file=config/config.json \ --dart-define-from-file=config/config.json \
--split-debug-info="$DEBUG_INFO_DIR" \ --split-debug-info="$DEBUG_INFO_DIR" \
--obfuscate --obfuscate

60
scripts/pre-commit Normal file
View File

@@ -0,0 +1,60 @@
#!/bin/bash
# Git pre-commit hook提交前自动 formatanalyze 有 error 则拦截提交
#
# 安装bash scripts/setup.shsetup.sh 自动把本文件复制到 .git/hooks/pre-commit
# 手动安装cp scripts/pre-commit .git/hooks/pre-commit && chmod +x .git/hooks/pre-commit
set -euo pipefail
ROOT_DIR="$(git rev-parse --show-toplevel)"
DART="$(command -v dart 2>/dev/null || echo "")"
if [[ -z "$DART" ]]; then
echo "[pre-commit] dart not found, skipping checks."
exit 0
fi
# ── pubspec.lock 保护 ────────────────────────────────────────────────────────
# flutter run / flutter build 会在未经授权的情况下触发 pub get 并修改 lock。
# 规则lock 有未暂存的改动(意外修改)→ 自动还原;
# 开发者主动 git add pubspec.lock 后(暂存)→ 正常放行。
LOCK_UNSTAGED=$(git diff --name-only pubspec.lock 2>/dev/null || true)
LOCK_STAGED=$(git diff --cached --name-only pubspec.lock 2>/dev/null || true)
if [[ -n "$LOCK_UNSTAGED" && -z "$LOCK_STAGED" ]]; then
git checkout HEAD -- pubspec.lock
echo "[pre-commit] pubspec.lock was auto-modified by flutter, restored to HEAD."
echo " To intentionally update: run 'dart pub get' or 'dart pub upgrade',"
echo " then 'git add pubspec.lock' before committing."
fi
# ── dart format + analyze ────────────────────────────────────────────────────
STAGED_DART=$(git diff --cached --name-only --diff-filter=ACM | grep '\.dart$' || true)
if [[ -z "$STAGED_DART" ]]; then
exit 0
fi
echo "[pre-commit] formatting staged dart files..."
echo "$STAGED_DART" | xargs dart format --line-length=80
echo "$STAGED_DART" | xargs git add
echo "[pre-commit] running dart analyze..."
cd "$ROOT_DIR"
ANALYZE_OUTPUT=$(dart analyze 2>&1)
if echo "$ANALYZE_OUTPUT" | grep -q "^ error"; then
echo "[pre-commit] dart analyze found errors, commit blocked."
echo "$ANALYZE_OUTPUT" | grep "^ error"
exit 1
fi
if echo "$ANALYZE_OUTPUT" | grep -q "^ warning"; then
echo "[pre-commit] dart analyze found warnings, commit blocked."
echo "$ANALYZE_OUTPUT" | grep "^ warning"
exit 1
fi
echo "[pre-commit] all checks passed."
exit 0

View File

@@ -28,13 +28,13 @@ echo "=== customer-im-client 环境初始化 ==="
echo "" echo ""
# ---- Step 1: 安装全局工具 ---- # ---- Step 1: 安装全局工具 ----
echo "[1/6] 安装全局工具 (melos + mason_cli)..." echo "[1/7] 安装全局工具 (melos + mason_cli)..."
dart pub global activate melos dart pub global activate melos
dart pub global activate mason_cli dart pub global activate mason_cli
echo "" echo ""
# ---- Step 2: 配置 PATH ---- # ---- Step 2: 配置 PATH ----
echo "[2/6] 配置 PATH..." echo "[2/7] 配置 PATH..."
if [[ "$SHELL" == */zsh ]]; then if [[ "$SHELL" == */zsh ]]; then
SHELL_CONFIG="$HOME/.zshrc" SHELL_CONFIG="$HOME/.zshrc"
elif [[ "$SHELL" == */bash ]]; then elif [[ "$SHELL" == */bash ]]; then
@@ -78,12 +78,19 @@ if [ -f "$ROOT_DIR/mason-lock.json" ]; then
fi fi
# ---- Step 5: 注册 mason bricks ---- # ---- Step 5: 注册 mason bricks ----
echo "[5/6] mason get (注册本地 bricks)..." echo "[5/7] mason get (注册本地 bricks)..."
cd "$ROOT_DIR" && "$PUB_CACHE_BIN/mason" get cd "$ROOT_DIR" && "$PUB_CACHE_BIN/mason" get
echo "" echo ""
echo "[6/7] mason add clean_plugin_sdk bricks..."
if ! grep -qE '^\s*clean_plugin_sdk:' "$ROOT_DIR/mason.yaml" 2>/dev/null; then
cd "$ROOT_DIR" && "$PUB_CACHE_BIN/mason" add clean_plugin_sdk --path ./mason/bricks/clean_plugin_sdk
else
echo " clean_plugin_sdk 已在 mason.yaml 中,跳過"
fi
# ---- Step 6: 首次代码生成 ---- # ---- Step 6: 首次代码生成 ----
echo "[6/6] melos run gen (首次代码生成)..." echo "[7/7] melos run gen (首次代码生成)..."
cd "$ROOT_DIR" && "$PUB_CACHE_BIN/melos" run gen cd "$ROOT_DIR" && "$PUB_CACHE_BIN/melos" run gen
echo "" echo ""