fix: 修复历史卦象列表无法显示及六爻解卦提示词逻辑错误
后端 Pydantic schema 添加 Hant 繁体字段支持(guaNameHant, targetGuaNameHant, spiritNameHant, relationNameHant),解决 DerivedDivinationData extra=forbid 拒绝 AI 输出的繁体字段导致 agent_output 解析失败、历史记录为空的问题。 六爻解卦提示词修复:增加静卦五行生克链分析、假破假空降权、 六冲中性判断、用神核对防捏造、空亡数据对照等硬约束。 前端 Dart model 同步添加 Hant 字段(反向兼容,缺省为空字符串)。 其他:硬币翻转动画修复、弹窗单按钮居中、起卦按钮布局调整、 繁体 l10n 清理、pre-commit 排除集成测试。
This commit is contained in:
@@ -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)
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 96 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 96 KiB After Width: | Height: | Size: 102 KiB |
@@ -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;
|
||||
|
||||
@@ -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),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
@@ -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,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -309,7 +309,7 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"divinationCostDialogConfirm": "Confirm Analysis",
|
||||
"divinationCostDialogConfirm": "Confirm",
|
||||
"toastContentCopied": "Content copied",
|
||||
"toastContentCopiedWithTitle": "{title} copied",
|
||||
"@toastContentCopiedWithTitle": {
|
||||
|
||||
@@ -785,7 +785,7 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||
}
|
||||
|
||||
@override
|
||||
String get divinationCostDialogConfirm => 'Confirm Analysis';
|
||||
String get divinationCostDialogConfirm => 'Confirm';
|
||||
|
||||
@override
|
||||
String get toastContentCopied => 'Content copied';
|
||||
|
||||
@@ -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 => '產生我的邀請碼';
|
||||
|
||||
|
||||
@@ -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),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
@@ -22,17 +22,19 @@ _WORKER_HARD_CONSTRAINTS = """\
|
||||
严禁跳过取用神直接下结论。
|
||||
严禁多核心用神混合判签;一问多意时只取一个主用神。
|
||||
严禁仅凭单一因素判吉凶。
|
||||
严禁将旬空月破机械等同凶;须辨真假及是否可解除。
|
||||
严禁将旬空月破机械等同凶;须辨真假及是否可解除;假破假空须大幅降权,不得与真破真空同等对待。
|
||||
严禁将有救应的卦直接断死,严禁表面顺利后续败坏的卦直判上吉。
|
||||
严禁脱离爻位证据给建议。
|
||||
严禁夸大确定性;吉凶并见或证据冲突时须说明边界。"""
|
||||
严禁夸大确定性;吉凶并见或证据冲突时须说明边界。
|
||||
严禁捏造或臆断未提供的数据:所有爻位属性(六亲、天干地支、五行、世应、动爻)、空亡范围、月破等事实判断,必须严格对照用户消息中给出的数据,不得凭记忆或推断添加、修改、遗漏。用神是否现于卦中,须逐爻核对六亲名称后确认,不得跳过核对直接宣称用神不现。
|
||||
静卦不得因无动爻而判为无生扶;静爻之间仍存在五行生克链,须逐一梳理原神→用神的静爻生扶路径。"""
|
||||
|
||||
_WORKER_PROCEDURE = """\
|
||||
第1步[取用神]:根据问题类型定唯一主用神并述理由。用神不现时须查伏神、飞神、变爻引出、暗动提拔,不可直接判弱判无。
|
||||
第1步[取用神]:根据问题类型定唯一主用神并述理由。取用神前须逐爻核对六亲名称,确认用神是否明现于卦中;若确实不现,再查伏神、飞神、变爻引出、暗动提拔,不可跳过核对直接判用神不现。
|
||||
|
||||
第2步[主判]:先看用神旺衰(月建日辰生扶克泄耗),再看世应与用神关系(生合冲克墓绝),再看动爻是生扶还是克耗用神,给事情定底色(偏吉/偏凶/杂局)。
|
||||
第2步[主判]:先看用神旺衰(月建日辰生扶克泄耗),再看世应与用神关系(生合冲克墓绝),再看动爻是生扶还是克耗用神,给事情定底色(偏吉/偏凶/杂局)。静卦无动爻时,须梳理静爻之间五行生克链(原神→用神、忌神→用神等)作为生扶或克耗的依据,不可因"无动爻"而忽略静爻生克。
|
||||
|
||||
第3步[修正]:必查原神忌神仇神发动对用神根气的影响;必查动变质量(回头生克、化进退空破墓绝);必查伏神飞神飞克提拔;必查合冲刑害六冲六合反吟伏吟;必查旬空月破辨真假(真空/假空、真破/假破、值日/出旬/逢合/冲起/填实);修正是对主判底色做升降,不可脱离主判单独下结论。
|
||||
第3步[修正]:必查原神忌神仇神发动对用神根气的影响;静卦须查原神静爻对用神的生扶链是否通畅。必查动变质量(回头生克、化进退空破墓绝)。必查伏神飞神飞克提拔;用神明现时无须查伏神。必查合冲刑害六冲六合反吟伏吟;六冲不等于凶:六冲主快、主动、主变,短期求事反利速决,须结合用神旺衰与问题类型综合判断,不得将六冲一律断为散耗凶象。必查旬空月破辨真假(真空/假空、真破/假破、值日/出旬/逢合/冲起/填实):假破须对照日辰或动爻是否有生扶解救,有救则大幅降权(假破近似无害),无救方可按真破论;空亡范围必须严格对照用户消息中给出的年/月/日/时空亡数据逐条核实,不得臆断或遗漏。修正是对主判底色做升降,不可脱离主判单独下结论。
|
||||
|
||||
第4步[签级]:只允许 上上签/中上签/中下签/下下签。先定底档再升降:
|
||||
- 初判:偏吉→中上签,偏凶→中下签,杂局→据用神受益或受损孰重落中上/中下。
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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 = [
|
||||
|
||||
Reference in New Issue
Block a user