diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 29d4808..4e6c3cb 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -23,7 +23,7 @@ repos: - id: backend-pytest name: backend pytest - entry: uv run pytest backend/tests + entry: uv run pytest backend/tests -m "not integration" language: system pass_filenames: false files: ^(backend/|pyproject\.toml|uv\.lock) diff --git a/apps/assets/images/qigua/hua.jpg b/apps/assets/images/qigua/hua.jpg index 61ebd64..c5cdb50 100644 Binary files a/apps/assets/images/qigua/hua.jpg and b/apps/assets/images/qigua/hua.jpg differ diff --git a/apps/assets/images/qigua/zi.jpg b/apps/assets/images/qigua/zi.jpg index e958092..123cf20 100644 Binary files a/apps/assets/images/qigua/zi.jpg and b/apps/assets/images/qigua/zi.jpg differ diff --git a/apps/lib/features/divination/data/models/divination_backend_models.dart b/apps/lib/features/divination/data/models/divination_backend_models.dart index 5813f18..3f67a36 100644 --- a/apps/lib/features/divination/data/models/divination_backend_models.dart +++ b/apps/lib/features/divination/data/models/divination_backend_models.dart @@ -132,9 +132,11 @@ class DerivedDivinationData { required this.binaryCode, required this.changedBinaryCode, required this.guaName, + this.guaNameHant = '', required this.upperName, required this.lowerName, required this.targetGuaName, + this.targetGuaNameHant = '', required this.worldPosition, required this.responsePosition, required this.hasChangingYao, @@ -154,9 +156,11 @@ class DerivedDivinationData { binaryCode: _requiredString(json, 'binaryCode'), changedBinaryCode: _requiredString(json, 'changedBinaryCode'), guaName: _requiredString(json, 'guaName'), + guaNameHant: json['guaNameHant'] as String? ?? '', upperName: _requiredString(json, 'upperName'), lowerName: _requiredString(json, 'lowerName'), targetGuaName: _requiredString(json, 'targetGuaName'), + targetGuaNameHant: json['targetGuaNameHant'] as String? ?? '', worldPosition: _requiredInt(json, 'worldPosition'), responsePosition: _requiredInt(json, 'responsePosition'), hasChangingYao: _requiredBool(json, 'hasChangingYao'), @@ -176,9 +180,11 @@ class DerivedDivinationData { final String binaryCode; final String changedBinaryCode; final String guaName; + final String guaNameHant; final String upperName; final String lowerName; final String targetGuaName; + final String targetGuaNameHant; final int worldPosition; final int responsePosition; final bool hasChangingYao; @@ -256,7 +262,9 @@ class YaoBackendLine { const YaoBackendLine({ required this.position, required this.spiritName, + this.spiritNameHant = '', required this.relationName, + this.relationNameHant = '', required this.tiganName, required this.elementName, required this.isYang, @@ -268,7 +276,9 @@ class YaoBackendLine { return YaoBackendLine( position: _requiredInt(json, 'position'), spiritName: _requiredString(json, 'spiritName'), + spiritNameHant: json['spiritNameHant'] as String? ?? '', relationName: _requiredString(json, 'relationName'), + relationNameHant: json['relationNameHant'] as String? ?? '', tiganName: _requiredString(json, 'tiganName'), elementName: _requiredString(json, 'elementName'), isYang: _requiredBool(json, 'isYang'), @@ -279,7 +289,9 @@ class YaoBackendLine { final int position; final String spiritName; + final String spiritNameHant; final String relationName; + final String relationNameHant; final String tiganName; final String elementName; final bool isYang; diff --git a/apps/lib/features/divination/presentation/screens/auto_divination_screen.dart b/apps/lib/features/divination/presentation/screens/auto_divination_screen.dart index 6405b90..db2675e 100644 --- a/apps/lib/features/divination/presentation/screens/auto_divination_screen.dart +++ b/apps/lib/features/divination/presentation/screens/auto_divination_screen.dart @@ -599,15 +599,18 @@ class _YaoPickerCard extends StatelessWidget { ], ), const SizedBox(height: AppSpacing.lg), - FilledButton( - onPressed: canShake ? onStartShake : null, - style: FilledButton.styleFrom( - backgroundColor: isSpinning - ? colors.surfaceContainerHighest - : colors.primary, - fixedSize: const Size(120, 40), + Align( + alignment: Alignment.center, + child: FilledButton( + onPressed: canShake ? onStartShake : null, + style: FilledButton.styleFrom( + backgroundColor: isSpinning + ? colors.surfaceContainerHighest + : colors.primary, + minimumSize: const Size(120, 40), + ), + child: Text(buttonText), ), - child: Text(buttonText), ), ], ), diff --git a/apps/lib/features/divination/presentation/screens/manual_divination_screen.dart b/apps/lib/features/divination/presentation/screens/manual_divination_screen.dart index 16cce23..5fc6cfc 100644 --- a/apps/lib/features/divination/presentation/screens/manual_divination_screen.dart +++ b/apps/lib/features/divination/presentation/screens/manual_divination_screen.dart @@ -704,19 +704,21 @@ class _FlippingCoinState extends State<_FlippingCoin> animation: _animation, builder: (context, child) { final angle = _animation.value * pi; - return Transform( - alignment: Alignment.center, - transform: Matrix4.identity() - ..setEntry(3, 2, 0.001) - ..rotateY(angle), - child: ClipOval( - child: Image.asset( - _showHua - ? 'assets/images/qigua/hua.jpg' - : 'assets/images/qigua/zi.jpg', + return ClipOval( + child: Transform( + alignment: Alignment.center, + transform: Matrix4.identity() + ..setEntry(3, 2, 0.001) + ..rotateY(angle), + child: SizedBox( width: 80, height: 80, - fit: BoxFit.cover, + child: Image.asset( + _showHua + ? 'assets/images/qigua/hua.jpg' + : 'assets/images/qigua/zi.jpg', + fit: BoxFit.cover, + ), ), ), ); diff --git a/apps/lib/l10n/app_en.arb b/apps/lib/l10n/app_en.arb index d22ece9..4a0c168 100644 --- a/apps/lib/l10n/app_en.arb +++ b/apps/lib/l10n/app_en.arb @@ -309,7 +309,7 @@ } } }, - "divinationCostDialogConfirm": "Confirm Analysis", + "divinationCostDialogConfirm": "Confirm", "toastContentCopied": "Content copied", "toastContentCopiedWithTitle": "{title} copied", "@toastContentCopiedWithTitle": { diff --git a/apps/lib/l10n/app_localizations_en.dart b/apps/lib/l10n/app_localizations_en.dart index 6ff22b3..811d252 100644 --- a/apps/lib/l10n/app_localizations_en.dart +++ b/apps/lib/l10n/app_localizations_en.dart @@ -785,7 +785,7 @@ class AppLocalizationsEn extends AppLocalizations { } @override - String get divinationCostDialogConfirm => 'Confirm Analysis'; + String get divinationCostDialogConfirm => 'Confirm'; @override String get toastContentCopied => 'Content copied'; diff --git a/apps/lib/l10n/app_localizations_zh.dart b/apps/lib/l10n/app_localizations_zh.dart index 1387797..3f45bc1 100644 --- a/apps/lib/l10n/app_localizations_zh.dart +++ b/apps/lib/l10n/app_localizations_zh.dart @@ -1165,15 +1165,6 @@ class AppLocalizationsZhHant extends AppLocalizationsZh { @override String get appTitle => '覓爻籤問'; - @override - String get welcomeLogin => '歡迎登入'; - - @override - String get loginSubtitle => '請使用信箱登入'; - - @override - String get loginSubtitleEmail => '請使用信箱登入'; - @override String get emailHint => '請輸入信箱地址'; @@ -1227,12 +1218,6 @@ class AppLocalizationsZhHant extends AppLocalizationsZh { @override String get disclaimer => '免責聲明'; - @override - String get icp => '粵ICP備2025428416號-1A'; - - @override - String get invalidPhone => '請輸入正確的手機號碼'; - @override String get invalidEmail => '請輸入正確的信箱地址'; @@ -1242,17 +1227,6 @@ class AppLocalizationsZhHant extends AppLocalizationsZh { @override String get agreementRequired => '請先勾選協議'; - @override - String get codeSent => '驗證碼已傳送,請注意查收'; - - @override - String get loginSuccess => '登入成功'; - - @override - String helloUser(String name) { - return '您好,$name'; - } - @override String get startJourney => '開始您的卦象之旅'; @@ -1283,33 +1257,9 @@ class AppLocalizationsZhHant extends AppLocalizationsZh { @override String get notify => '訊息通知'; - @override - String get featurePending => '該功能暫未接入資料'; - @override String get logout => '登出'; - @override - String get defaultUserName => '用戶'; - - @override - String get historyQuestion1 => '今年轉崗是否合適?'; - - @override - String get historyQuestion2 => '最近感情是否能推進?'; - - @override - String get historyQuestion3 => '本季度投資節奏如何?'; - - @override - String get guaName1 => '天雷無妄'; - - @override - String get guaName2 => '澤火革'; - - @override - String get guaName3 => '風地觀'; - @override String get welcomeDialogTitle => '歡迎使用覓爻籤問'; @@ -1341,18 +1291,6 @@ class AppLocalizationsZhHant extends AppLocalizationsZh { @override String get readAllFirst => '請先閱讀完整內容'; - @override - String get signBest => '上上籤'; - - @override - String get signGood => '中上籤'; - - @override - String get signNormal => '中下籤'; - - @override - String get signBad => '下下籤'; - @override String get language => '語言'; @@ -1362,12 +1300,6 @@ class AppLocalizationsZhHant extends AppLocalizationsZh { @override String get settingsSectionGeneral => '偏好設定'; - @override - String get settingsSectionQuickAccess => '一級選單'; - - @override - String get settingsSectionAccount => '帳戶操作'; - @override String get settingsSectionPrivacy => '隱私設定'; @@ -1395,53 +1327,18 @@ class AppLocalizationsZhHant extends AppLocalizationsZh { @override String get settingsGeneralTitle => '通用設定'; - @override - String settingsGeneralSubtitle(String currentLanguage) { - return '語言:$currentLanguage,其餘欄位按 profiles.settings 結構預留'; - } - @override String get settingsPrivacyAndNotificationTitle => '隱私與通知'; - @override - String get settingsPrivacyAndNotificationSubtitle => - '分組管理 privacy 與 notification 佔位設定'; - @override String get settingsLegalCenterTitle => '關於與協議'; - @override - String get settingsLegalCenterSubtitle => '查看關於我們、隱私政策與服務條款'; - @override String get settingsCoinCenterTitle => '點數中心'; - @override - String settingsCoinCenterSubtitle(int balance) { - return '目前餘額 $balance 點數,查看套餐與儲值入口'; - } - @override String get settingsCoinHeroSubtitle => '點數可用於後續起卦與相關服務消費'; - @override - String get settingsAiLanguageHint => - '該欄位將對齊 profiles.settings.preferences.ai_language,後續接入真實偏好設定。'; - - @override - String get settingsTimezone => '時區'; - - @override - String get settingsTimezoneHint => - '該欄位將對齊 profiles.settings.preferences.timezone,後續提供時區選擇。'; - - @override - String get settingsCountry => '國家/地區'; - - @override - String get settingsCountryHint => - '該欄位將對齊 profiles.settings.preferences.country,後續提供國家或地區選擇。'; - @override String get settingsPrivacyProfileVisibility => '資料可見性'; @@ -1468,15 +1365,6 @@ class AppLocalizationsZhHant extends AppLocalizationsZh { String get settingsNotificationHint => '這些選項會落到 profiles.settings.notification 下,目前先提供介面佔位。'; - @override - String get settingsVersion => '目前版本'; - - @override - String get settingsVersionHint => '版本資訊和更多設定說明會在後續接入真實資料。'; - - @override - String get settingsTapToView => '點擊查看'; - @override String get settingsComingSoon => '即將上線'; @@ -1488,12 +1376,6 @@ class AppLocalizationsZhHant extends AppLocalizationsZh { @override String get settingsCurrentValue => '目前值'; - @override - String get settingsVersionLabel => '設定版本'; - - @override - String get settingsLogoutSubtitle => '登出目前登入帳戶'; - @override String get settingsLogoutDialogTitle => '確認登出?'; @@ -1503,12 +1385,6 @@ class AppLocalizationsZhHant extends AppLocalizationsZh { @override String get settingsDeleteAccountTitle => '刪除帳號'; - @override - String get settingsDeleteAccountSubtitle => '永久刪除帳號及相關個人資料'; - - @override - String get settingsDeleteAccountWarningTitle => '刪除前請確認'; - @override String get settingsDeleteAccountWarningBody => '刪除帳號後,個人資料、歷史記錄、點數資訊等相關資料將被永久刪除,且不可恢復。'; @@ -1517,15 +1393,6 @@ class AppLocalizationsZhHant extends AppLocalizationsZh { String get settingsDeleteAccountReRegisterNotice => '重要提示:同一信箱刪除後重新註冊,已消耗積分不會重置或返還。'; - @override - String get settingsDeleteAccountScopeProfile => '個人資料和帳號資訊會被刪除'; - - @override - String get settingsDeleteAccountScopeHistory => '歷史解卦記錄會被刪除'; - - @override - String get settingsDeleteAccountScopePoints => '點數帳戶與流水記錄會被刪除'; - @override String get settingsDeleteAccountDialogTitle => '確認永久刪除帳號?'; @@ -1535,9 +1402,6 @@ class AppLocalizationsZhHant extends AppLocalizationsZh { @override String get settingsDeleteAccountAction => '確認刪除帳號'; - @override - String get settingsDeleteAccountProcessing => '正在刪除...'; - @override String settingsDeleteAccountWaitAction(int seconds) { return '請等待 $seconds 秒後確認刪除'; @@ -1546,12 +1410,6 @@ class AppLocalizationsZhHant extends AppLocalizationsZh { @override String get settingsCancel => '取消'; - @override - String get settingsLogoutConfirmHint => '再次點擊確認登出'; - - @override - String get settingsLogoutConfirmAction => '再次點擊確認登出'; - @override String get settingsEditProfileAction => '編輯'; @@ -1576,18 +1434,12 @@ class AppLocalizationsZhHant extends AppLocalizationsZh { @override String get settingsBioHint => '一句話介紹你自己'; - @override - String get settingsAvatarPickerHint => '支援 PNG / JPG / WEBP,建議上傳清晰正方形頭像'; - @override String get settingsAvatarChooseFromAlbum => '從相簿選擇頭像'; @override String get settingsAvatarUploading => '上傳中...'; - @override - String get settingsAvatarUploadSuccess => '頭像上傳成功'; - @override String get settingsAvatarPickPermissionHint => '無法開啟相簿,請在系統設定中允許照片存取權限'; @@ -1634,9 +1486,6 @@ class AppLocalizationsZhHant extends AppLocalizationsZh { @override String get english => '英文'; - @override - String get chinese => '中文'; - @override String get dialogConfirm => '確定'; @@ -1646,18 +1495,6 @@ class AppLocalizationsZhHant extends AppLocalizationsZh { @override String get agreementAnd => '和'; - @override - String get aboutUsContent => - '你好,歡迎來到覓爻籤問,這是一個借助於AI解讀傳統六爻卦象的平臺,為用戶了解中國傳統易學文化提供一個窗口。\n\n六爻卦象源於《周易》深邃的哲學體系,是古人探索世界執行規律的一種獨特方法。古人認為宇宙萬物相互關聯,在您起卦時,您的心念與時空資訊會凝結 成卦象的方式呈現出來。得到卦象後,再結合《易經》中的爻辭和某些特定規律,如五行生剋、干支沖合等,分析各要素間的發展趨勢,最終斷定出事物可能的走向。\n\n覓爻籤問就是基於這樣的思路而開發出來的平臺,它的核心價值在於幫助您跳出局限思維,從事物全域和演變趨勢的角度看清現況的矛盾 、潛在機會和風險點,為您的判斷和行動提供多一個維度的參考資訊,讓您能更理性、更周全地做決定。用AI解鎖古老智慧,讓覓爻籤問成為您探索趨勢、明晰方向的現代助手吧!\n\n特別提醒\n卦象解讀結果均由AI生成,僅供娛樂參考,切不可作為商業、醫療等專業領域的決策依據。理性看待卦象,自由掌握人生。\n\n粵ICP備2025428416號-1A'; - - @override - String get privacyContent => - '尊敬的用戶:\n歡迎使用覓爻籤問 APP(以下簡稱「覓爻」)。我們深知您的隱私對您至關重要,因此非常重視保護您的個人資訊。本隱私政策將向您說明我們在您使用服務時如何收集、使用、儲存和共享您的個人資訊,以及您如何存取和管理這些資訊。\n\n一、我們收集哪些您的個人資訊\n1. 您主動提供的資訊:帳號註冊資訊、個人資料資訊、解卦相關資訊。\n2. 我們自動收集的資訊:裝置資訊、日誌資訊。\n\n二、我們如何使用您的個人資訊\n1. 提供和優化服務:利用您提供的卦象問題、解卦方式等資訊生成解卦結果,並持續優化演算法。\n2. 帳號管理和服務運營:用於登入驗證、帳號安全監測、服務改進。\n3. 與您溝通和聯繫:用於服務通知、用戶回饋與客服支援。\n\n三、我們如何儲存您的個人資訊\n1. 儲存地點:原則上儲存於中華人民共和國境內。\n2. 儲存期限:僅在符合法律要求及實現服務目的所必需的最短時間範圍內儲存,超期後刪除或匿名化處理。\n\n四、我們如何共享您的個人資訊\n除以下情況外,我們不會與第三方共享您的個人資訊:獲得您的明確同意;與服務提供商合作;法律要求或保護合法權益;涉及企業收購、合併或破產。\n\n五、您的權利\n您有權存取、更正、刪除個人資訊,也可以申請註銷帳號。帳號註銷後,相關資料可能無法恢復。\n\n六、未成年人保護\n如果您是未滿 14 週歲的未成年人,請在父母或法定監護人的指導下使用服務,並確保事先獲得其同意。\n\n七、您的個人資訊安全\n我們採取合理的安全措施和技術手段,保護您的個人資訊免遭未經授權的存取、公開披露、使用、修改、損壞或丟失,包括加密、存取控制、安全審計和監控等措施。\n\n八、本隱私政策的更新\n我們可能會根據業務發展、法律法規變化或服務調整適時更新本隱私政策,並通過顯著方式通知重大變更。\n\n九、如何聯繫我們\n如果您對本隱私政策有任何疑問、意見或建議,可通過信箱 xuyunlong@xunmee.com 與我們聯繫。\n\n洵覓科技(深圳)有限公司\n2025年6月1日'; - - @override - String get termsContent => - '第一章 總則\n1. 歡迎使用覓爻籤問 APP。覓爻由洵覓科技(深圳)有限公司開發、運營和維護,旨在為用戶提供實際、有趣的解卦體驗。\n2. 用戶在使用覓爻服務之前,應仔細閱讀並充分理解本服務條款。通過下載、安裝、註冊、登入或使用等任一方式開始使用覓爻,即表示用戶已充分理解並完全接受本服務條款。\n3. 如用戶不同意本服務條款的任何內容,請不要進行後續操作。\n\n第二章 服務說明\n覓爻提供基於人工智慧技術的解卦服務,包括手動起卦、自動起卦等基礎功能。因系統維護、故障、不可抗力或其他合理原因導致的服務中斷或暫停,不視為違約。\n\n第三章 用戶帳號與資訊安全\n用戶應確保註冊資格合法,提供真實、準確、完整、有效的資料,並妥善保管帳號及身份驗證資訊。覓爻會按照隱私政策收集、使用和保護必要的個人資訊。\n\n第四章 智慧財產權聲明\n覓爻整體內容及相關商標、標識、網域名稱等智慧財產權均受法律保護。未經書面許可,用戶不得複製、修改、出租、出借、出售、傳播或通過反向工程、反編譯、反彙編等方式取得原始碼。\n\n第五章 用戶行為規範\n用戶不得發布違法違規內容,不得侵犯他人合法權益,不得破壞服務正常執行,不得進行未經授權的商業活動。對於違反規範的行為,覓爻有權採取警告、限制功能、封禁帳號等措施,並保留追究法律責任的權利。\n\n第六章 法律責任與免責條款\n如果用戶違反本服務條款導致洵覓科技或關聯公司遭受損失,用戶應承擔賠償責任。解卦結果僅供參考,不能作為實際決策的唯一依據;因依賴解卦結果產生的後果,由用戶自行承擔風險。\n\n第七章 爭議解決\n本服務條款適用中華人民共和國法律。因本服務條款引起的爭議,應先友好協商;協商不成的,任一方有權向洵覓科技公司註冊地有管轄權的人民法院提起訴訟。\n\n第八章 其他條款\n覓爻可以通過聯繫方式、系統訊息、站內信、公告等方式向用戶送達通知。若用戶需要聯繫洵覓科技,可通過信箱 xuyunlong@xunmee.com 提交請求或回饋。\n\n洵覓科技(深圳)有限公司\n2025年6月1日'; - @override String get disclaimerContent => '免責聲明內容展示佔位。'; @@ -1724,14 +1561,6 @@ class AppLocalizationsZhHant extends AppLocalizationsZh { @override String get divinationStartButton => '開始起卦'; - @override - String divinationCoinBalance(int balance) { - return '目前可用銅幣:$balance 枚'; - } - - @override - String get divinationRefreshBalance => '重新整理餘額'; - @override String get divinationRecommendManual => '推薦使用手動起卦,解卦更準確!準備三枚一樣的銅錢或硬幣,點擊這裡查看手動起卦教程。'; @@ -1858,9 +1687,6 @@ class AppLocalizationsZhHant extends AppLocalizationsZh { @override String get divinationIAcknowledge => '我知道了'; - @override - String get divinationClose => '關閉'; - @override String get divinationModify => '修改'; @@ -1922,9 +1748,6 @@ class AppLocalizationsZhHant extends AppLocalizationsZh { @override String get resultAIAnalysis => 'AI解卦'; - @override - String get resultShare => '分享'; - @override String get resultBasicInfo => '基本資訊'; @@ -1998,32 +1821,6 @@ class AppLocalizationsZhHant extends AppLocalizationsZh { @override String get followUpQuotaUsed => '本次會話追問次數已用完'; - @override - String get followUpInputHint => '輸入您想繼續追問的問題'; - - @override - String get followUpHoldToSpeak => '按住說話'; - - @override - String get followUpRecording => '鬆開發送'; - - @override - String get followUpRecordingHint => '上滑取消'; - - @override - String get followUpTranscribing => '語音轉文字中...'; - - @override - String get followUpGenerating => '正在生成回覆...'; - - @override - String get followUpStepWorker => '正在分析卦象並生成回覆'; - - @override - String followUpStepGeneric(String stepName) { - return '正在處理:$stepName'; - } - @override String get errorAudioUnsupportedFormat => '音頻格式不支援,請使用 wav'; @@ -2138,12 +1935,6 @@ class AppLocalizationsZhHant extends AppLocalizationsZh { @override String get termRiChong => '日沖'; - @override - String get guaNameSimplifiedChars => '风泽观讼师谦随蛊临贲剥复无颐恒壮晋损渐归丰兑涣节济'; - - @override - String get guaNameTraditionalChars => '風澤觀訟師謙隨蠱臨賁剝復無頤恆壯晉損漸歸豐兌渙節濟'; - @override String get manualScreenTitle => '手動起卦'; @@ -2214,20 +2005,6 @@ class AppLocalizationsZhHant extends AppLocalizationsZh { @override String get autoShakeComplete => '點擊頁面底部開始解卦'; - @override - String get autoTryShakePhone => '您也可以試試搖晃手機來起卦'; - - @override - String autoSimBalance(int balance) { - return '目前可用銅幣:$balance 枚'; - } - - @override - String get autoGuideTitle => '自動起卦教程'; - - @override - String get autoGuideInstruction => '搖晃手機或點擊按鈕,連續搖 6 次即可形成完整卦象。'; - @override String get dateTab => '日期'; @@ -2240,27 +2017,15 @@ class AppLocalizationsZhHant extends AppLocalizationsZh { @override String get cancel => '取消'; - @override - String get coinFaceGuideTitle => '字花對照說明'; - - @override - String get coinFaceGuideDescription => '左側為花面,右側為字面。'; - @override String get settingsInviteTitle => '我的邀請'; - @override - String get settingsInviteSubtitle => '邀請好友,共同獲得獎勵'; - @override String get settingsInviteMyCode => '我的邀請碼'; @override String get settingsInviteCopySuccess => '邀請碼已複製'; - @override - String get settingsInviteCopied => '已複製'; - @override String get settingsInviteCopy => '複製'; @@ -2272,9 +2037,6 @@ class AppLocalizationsZhHant extends AppLocalizationsZh { @override String get settingsInviteBindCode => '綁定邀請碼'; - @override - String get settingsInviteBindHint => '輸入好友的邀請碼'; - @override String get settingsInviteBindPlaceholder => '6位邀請碼'; @@ -2284,12 +2046,6 @@ class AppLocalizationsZhHant extends AppLocalizationsZh { @override String get settingsInviteBindSuccess => '邀請碼綁定成功'; - @override - String get settingsInviteBindFailed => '邀請碼綁定失敗'; - - @override - String get settingsInviteGenerateTitle => '產生我的邀請碼'; - @override String get settingsInviteGenerateButton => '產生我的邀請碼'; diff --git a/apps/lib/shared/widgets/app_modal_dialog.dart b/apps/lib/shared/widgets/app_modal_dialog.dart index c9e2dc4..ab8ef7e 100644 --- a/apps/lib/shared/widgets/app_modal_dialog.dart +++ b/apps/lib/shared/widgets/app_modal_dialog.dart @@ -97,54 +97,74 @@ class AppModalDialog extends StatelessWidget { ), ), const SizedBox(height: AppSpacing.lg), - Row( - children: actions - .map((action) { - final child = action.primary - ? FilledButton( - onPressed: action.onPressed, - style: FilledButton.styleFrom( - backgroundColor: action.destructive - ? colors.error - : colors.primary, - foregroundColor: action.destructive - ? colors.onError - : colors.onPrimary, - minimumSize: const Size.fromHeight(44), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular( - AppRadius.full, + if (actions.length == 1) + Center( + child: FilledButton( + onPressed: actions[0].onPressed, + style: FilledButton.styleFrom( + backgroundColor: actions[0].destructive + ? colors.error + : colors.primary, + foregroundColor: actions[0].destructive + ? colors.onError + : colors.onPrimary, + minimumSize: const Size.fromHeight(44), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(AppRadius.full), + ), + ), + child: Text(actions[0].label), + ), + ) + else + Row( + children: actions + .map((action) { + final child = action.primary + ? FilledButton( + onPressed: action.onPressed, + style: FilledButton.styleFrom( + backgroundColor: action.destructive + ? colors.error + : colors.primary, + foregroundColor: action.destructive + ? colors.onError + : colors.onPrimary, + minimumSize: const Size.fromHeight(44), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular( + AppRadius.full, + ), ), ), - ), - child: Text(action.label), - ) - : OutlinedButton( - onPressed: action.onPressed, - style: OutlinedButton.styleFrom( - foregroundColor: colors.onSurface, - side: BorderSide(color: colors.outline), - minimumSize: const Size.fromHeight(44), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular( - AppRadius.full, + child: Text(action.label), + ) + : OutlinedButton( + onPressed: action.onPressed, + style: OutlinedButton.styleFrom( + foregroundColor: colors.onSurface, + side: BorderSide(color: colors.outline), + minimumSize: const Size.fromHeight(44), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular( + AppRadius.full, + ), ), ), - ), - child: Text(action.label), - ); + child: Text(action.label), + ); - return Expanded( - child: Padding( - padding: const EdgeInsets.symmetric( - horizontal: AppSpacing.xs, + return Expanded( + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: AppSpacing.xs, + ), + child: child, ), - child: child, - ), - ); - }) - .toList(growable: false), - ), + ); + }) + .toList(growable: false), + ), ], ), ), diff --git a/backend/src/core/agentscope/prompts/agent_prompt.py b/backend/src/core/agentscope/prompts/agent_prompt.py index 85c570f..5b8da31 100644 --- a/backend/src/core/agentscope/prompts/agent_prompt.py +++ b/backend/src/core/agentscope/prompts/agent_prompt.py @@ -22,17 +22,19 @@ _WORKER_HARD_CONSTRAINTS = """\ 严禁跳过取用神直接下结论。 严禁多核心用神混合判签;一问多意时只取一个主用神。 严禁仅凭单一因素判吉凶。 -严禁将旬空月破机械等同凶;须辨真假及是否可解除。 +严禁将旬空月破机械等同凶;须辨真假及是否可解除;假破假空须大幅降权,不得与真破真空同等对待。 严禁将有救应的卦直接断死,严禁表面顺利后续败坏的卦直判上吉。 严禁脱离爻位证据给建议。 -严禁夸大确定性;吉凶并见或证据冲突时须说明边界。""" +严禁夸大确定性;吉凶并见或证据冲突时须说明边界。 +严禁捏造或臆断未提供的数据:所有爻位属性(六亲、天干地支、五行、世应、动爻)、空亡范围、月破等事实判断,必须严格对照用户消息中给出的数据,不得凭记忆或推断添加、修改、遗漏。用神是否现于卦中,须逐爻核对六亲名称后确认,不得跳过核对直接宣称用神不现。 +静卦不得因无动爻而判为无生扶;静爻之间仍存在五行生克链,须逐一梳理原神→用神的静爻生扶路径。""" _WORKER_PROCEDURE = """\ -第1步[取用神]:根据问题类型定唯一主用神并述理由。用神不现时须查伏神、飞神、变爻引出、暗动提拔,不可直接判弱判无。 +第1步[取用神]:根据问题类型定唯一主用神并述理由。取用神前须逐爻核对六亲名称,确认用神是否明现于卦中;若确实不现,再查伏神、飞神、变爻引出、暗动提拔,不可跳过核对直接判用神不现。 -第2步[主判]:先看用神旺衰(月建日辰生扶克泄耗),再看世应与用神关系(生合冲克墓绝),再看动爻是生扶还是克耗用神,给事情定底色(偏吉/偏凶/杂局)。 +第2步[主判]:先看用神旺衰(月建日辰生扶克泄耗),再看世应与用神关系(生合冲克墓绝),再看动爻是生扶还是克耗用神,给事情定底色(偏吉/偏凶/杂局)。静卦无动爻时,须梳理静爻之间五行生克链(原神→用神、忌神→用神等)作为生扶或克耗的依据,不可因"无动爻"而忽略静爻生克。 -第3步[修正]:必查原神忌神仇神发动对用神根气的影响;必查动变质量(回头生克、化进退空破墓绝);必查伏神飞神飞克提拔;必查合冲刑害六冲六合反吟伏吟;必查旬空月破辨真假(真空/假空、真破/假破、值日/出旬/逢合/冲起/填实);修正是对主判底色做升降,不可脱离主判单独下结论。 +第3步[修正]:必查原神忌神仇神发动对用神根气的影响;静卦须查原神静爻对用神的生扶链是否通畅。必查动变质量(回头生克、化进退空破墓绝)。必查伏神飞神飞克提拔;用神明现时无须查伏神。必查合冲刑害六冲六合反吟伏吟;六冲不等于凶:六冲主快、主动、主变,短期求事反利速决,须结合用神旺衰与问题类型综合判断,不得将六冲一律断为散耗凶象。必查旬空月破辨真假(真空/假空、真破/假破、值日/出旬/逢合/冲起/填实):假破须对照日辰或动爻是否有生扶解救,有救则大幅降权(假破近似无害),无救方可按真破论;空亡范围必须严格对照用户消息中给出的年/月/日/时空亡数据逐条核实,不得臆断或遗漏。修正是对主判底色做升降,不可脱离主判单独下结论。 第4步[签级]:只允许 上上签/中上签/中下签/下下签。先定底档再升降: - 初判:偏吉→中上签,偏凶→中下签,杂局→据用神受益或受损孰重落中上/中下。 diff --git a/backend/src/schemas/domain/divination.py b/backend/src/schemas/domain/divination.py index ea04fe3..b126f9a 100644 --- a/backend/src/schemas/domain/divination.py +++ b/backend/src/schemas/domain/divination.py @@ -29,7 +29,9 @@ class YaoDetail(BaseModel): position: int = Field(ge=1, le=6) spirit_name: str = Field(alias="spiritName", min_length=1) + spirit_name_hant: str = Field(alias="spiritNameHant", default="") relation_name: str = Field(alias="relationName", min_length=1) + relation_name_hant: str = Field(alias="relationNameHant", default="") tigan_name: str = Field(alias="tiganName", min_length=1) element_name: str = Field(alias="elementName", min_length=1) is_yang: bool = Field(alias="isYang") @@ -42,6 +44,7 @@ class FushenDetail(BaseModel): position: int = Field(ge=1, le=6) relation_name: str = Field(alias="relationName", min_length=1) + relation_name_hant: str = Field(alias="relationNameHant", default="") tigan_name: str = Field(alias="tiganName", min_length=1) element_name: str = Field(alias="elementName", min_length=1) @@ -94,9 +97,11 @@ class DerivedDivinationData(BaseModel): alias="changedBinaryCode", min_length=6, max_length=6 ) gua_name: str = Field(alias="guaName", min_length=2) + gua_name_hant: str = Field(alias="guaNameHant", default="") upper_name: str = Field(alias="upperName", min_length=1) lower_name: str = Field(alias="lowerName", min_length=1) target_gua_name: str = Field(alias="targetGuaName", min_length=2) + target_gua_name_hant: str = Field(alias="targetGuaNameHant", default="") world_position: int = Field(alias="worldPosition", ge=1, le=6) response_position: int = Field(alias="responsePosition", ge=1, le=6) has_changing_yao: bool = Field(alias="hasChangingYao") diff --git a/backend/tests/integration/conftest.py b/backend/tests/integration/conftest.py index 5b60d35..c3c6892 100644 --- a/backend/tests/integration/conftest.py +++ b/backend/tests/integration/conftest.py @@ -14,6 +14,18 @@ from core.config.settings import config from core.db.session import AsyncSessionLocal +def pytest_configure(config): # noqa: ARG001 + config.addinivalue_line( + "markers", "integration: integration test requiring live backend" + ) + + +def pytest_collection_modifyitems(items): + for item in items: + if "integration" in item.nodeid: + item.add_marker(pytest.mark.integration) + + @pytest.fixture(scope="session") def api_base_url() -> str: return os.environ.get("ERYAO_TEST_BASE_URL", "http://localhost:5775") diff --git a/pyproject.toml b/pyproject.toml index e33b13b..3d8c74c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -44,6 +44,7 @@ testpaths = ["backend/tests"] addopts = "-q --import-mode=importlib" asyncio_mode = "auto" pythonpath = ["backend/src"] +markers = ["integration: integration test requiring live backend and workers"] [dependency-groups] dev = [