{"repo": "chenguojun06-star/fz66666", "files": {"cloudbaserc.json": {"92136382831779e22f23b72a08f4e137bfa17dd3": {"t": 1785745313.773528, "n": 3154, "h": "d7faedb3f54e9911", "c": "{\n \"version\": \"2.0\",\n \"envId\": \"{{TCB_ENV_ID}}\",\n \"framework\": {\n \"name\": \"fashion-supplychain\",\n \"plugins\": {\n \"backend\": {\n \"use\": \"@cloudbase/framework-plugin-container\",\n \"inputs\": {\n \"serviceName\": \"fashion-backend\",\n \"servicePath\": \"/api\",\n \"localPath\": \"./backend\",\n \"dockerfilePath\": \"./backend/Dockerfile\",\n \"containerPort\": 8088,\n \"initialDelaySeconds\": 300,\n \"minNum\": 1,\n \"maxNum\": 5,\n \"cpu\": 1,\n \"mem\": 2,\n \"envVariables\": {\n \"SPRING_PROFILES_ACTIVE\": \"prod\",\n \"WECHAT_TRUST_ALL_CERTS\": \"true\",\n \"APP_AUTH_JWT_SECRET\": \"{{JWT_SECRET}}\",\n \"APP_DB_HOST\": \"{{DB_HOST}}\",\n \"APP_DB_NAME\": \"{{DB_NAME}}\",\n \"APP_DB_USERNAME\": \"{{DB_USERNAME}}\",\n \"APP_DB_PASSWORD\": \"{{DB_PASSWORD}}\",\n \"APP_COS_SECRET_ID\": \"{{COS_SECRET_ID}}\",\n \"APP_COS_SECRET_KEY\": \"{{COS_SECRET_KEY}}\",\n \"APP_COS_BUCKET\": \"{{COS_BUCKET}}\",\n \"APP_COS_REGION\": \"{{COS_REGION}}\",\n \"DEEPSEEK_API_KEY\": \"{{DEEPSEEK_API_KEY}}\",\n \"AGNES_API_KEY\": \"{{AGNES_API_KEY}}\",\n \"AGNES_MODEL\": \"{{AGNES_MODEL:agnes-2.5-flash}}\",\n \"AGNES2_MODEL\": \"{{AGNES2_MODEL:agnes-2.5-flash}}\",\n \"AI_MODEL_VISION\": \"{{AI_MODEL_VISION:agnes-2.5-flash}}\",\n \"VOYAGE_API_KEY\": \"{{VOYAGE_API_KEY}}\",\n \"QDRANT_URL\": \"{{QDRANT_URL}}\",\n \"QDRANT_API_KEY\": \"{{QDRANT_API_KEY}}\",\n \"WECHAT_MP_APPID\": \"{{WECHAT_MP_APPID}}\",\n \"WECHAT_MP_SECRET\": \"{{WECHAT_MP_SECRET}}\",\n \"APP_SECURITY_PII_ENCRYPTION_KEY\": \"{{PII_ENCRYPTION_KEY}}\",\n \"AI_OPENAI_COMPAT_ENABLED\": \"{{AI_OPENAI_COMPAT_ENABLED}}\",\n \"AI_OPENAI_COMPAT_API_KEY\": \"{{AI_OPENAI_COMPAT_API_KEY}}\",\n \"AI_OPENAI_COMPAT_TENANT_ID\": \"{{AI_OPENAI_COMPAT_TENANT_ID}}\",\n \"AI_IM_AI_FEISHU_ENABLED\": \"true\",\n \"AI_IM_AI_FEISHU_VERIFICATION_TOKEN\": \"dxZCfyFE816hLOrPbpE3pzdHJpmcXAMQ\",\n \"AI_IM_AI_FEISHU_ENCRYPT_KEY\": \"7qZik8wobG3FJVt8rrg97esIHfRRV6dc\"\n }\n }\n },\n \"frontend\": {\n \"use\": \"@cloudbase/framework-plugin-container\",\n \"inputs\": {\n \"serviceName\": \"fashion-admin\",\n \"servicePath\": \"/\",\n \"localPath\": \"./frontend\",\n \"dockerfilePath\": \"./frontend/Dockerfile\",\n \"containerPort\": 80,\n \"initialDelaySeconds\": 30,\n \"minNum\": 1,\n \"maxNum\": 3,\n \"cpu\": 0.5,\n \"mem\": 1,\n \"envVariables\": {\n \"BACKEND_URL\": \"https://api.webyszl.cn\"\n }\n }\n },\n \"h5\": {\n \"use\": \"@cloudbase/framework-plugin-container\",\n \"inputs\": {\n \"serviceName\": \"fashion-h5\",\n \"servicePath\": \"/h5\",\n \"localPath\": \"./h5-web\",\n \"dockerfilePath\": \"./h5-web/Dockerfile\",\n \"containerPort\": 80,\n \"initialDelaySeconds\": 30,\n \"minNum\": 1,\n \"maxNum\": 3,\n \"cpu\": 0.25,\n \"mem\": 0.5\n }\n }\n }\n }\n}"}, "e3eca03c3b9734ba801c15ce6bafd09fa6b8584a": {"t": 1785749953.5917158, "n": 3154, "h": "d7faedb3f54e9911", "c": "{\n \"version\": \"2.0\",\n \"envId\": \"{{TCB_ENV_ID}}\",\n \"framework\": {\n \"name\": \"fashion-supplychain\",\n \"plugins\": {\n \"backend\": {\n \"use\": \"@cloudbase/framework-plugin-container\",\n \"inputs\": {\n \"serviceName\": \"fashion-backend\",\n \"servicePath\": \"/api\",\n \"localPath\": \"./backend\",\n \"dockerfilePath\": \"./backend/Dockerfile\",\n \"containerPort\": 8088,\n \"initialDelaySeconds\": 300,\n \"minNum\": 1,\n \"maxNum\": 5,\n \"cpu\": 1,\n \"mem\": 2,\n \"envVariables\": {\n \"SPRING_PROFILES_ACTIVE\": \"prod\",\n \"WECHAT_TRUST_ALL_CERTS\": \"true\",\n \"APP_AUTH_JWT_SECRET\": \"{{JWT_SECRET}}\",\n \"APP_DB_HOST\": \"{{DB_HOST}}\",\n \"APP_DB_NAME\": \"{{DB_NAME}}\",\n \"APP_DB_USERNAME\": \"{{DB_USERNAME}}\",\n \"APP_DB_PASSWORD\": \"{{DB_PASSWORD}}\",\n \"APP_COS_SECRET_ID\": \"{{COS_SECRET_ID}}\",\n \"APP_COS_SECRET_KEY\": \"{{COS_SECRET_KEY}}\",\n \"APP_COS_BUCKET\": \"{{COS_BUCKET}}\",\n \"APP_COS_REGION\": \"{{COS_REGION}}\",\n \"DEEPSEEK_API_KEY\": \"{{DEEPSEEK_API_KEY}}\",\n \"AGNES_API_KEY\": \"{{AGNES_API_KEY}}\",\n \"AGNES_MODEL\": \"{{AGNES_MODEL:agnes-2.5-flash}}\",\n \"AGNES2_MODEL\": \"{{AGNES2_MODEL:agnes-2.5-flash}}\",\n \"AI_MODEL_VISION\": \"{{AI_MODEL_VISION:agnes-2.5-flash}}\",\n \"VOYAGE_API_KEY\": \"{{VOYAGE_API_KEY}}\",\n \"QDRANT_URL\": \"{{QDRANT_URL}}\",\n \"QDRANT_API_KEY\": \"{{QDRANT_API_KEY}}\",\n \"WECHAT_MP_APPID\": \"{{WECHAT_MP_APPID}}\",\n \"WECHAT_MP_SECRET\": \"{{WECHAT_MP_SECRET}}\",\n \"APP_SECURITY_PII_ENCRYPTION_KEY\": \"{{PII_ENCRYPTION_KEY}}\",\n \"AI_OPENAI_COMPAT_ENABLED\": \"{{AI_OPENAI_COMPAT_ENABLED}}\",\n \"AI_OPENAI_COMPAT_API_KEY\": \"{{AI_OPENAI_COMPAT_API_KEY}}\",\n \"AI_OPENAI_COMPAT_TENANT_ID\": \"{{AI_OPENAI_COMPAT_TENANT_ID}}\",\n \"AI_IM_AI_FEISHU_ENABLED\": \"true\",\n \"AI_IM_AI_FEISHU_VERIFICATION_TOKEN\": \"dxZCfyFE816hLOrPbpE3pzdHJpmcXAMQ\",\n \"AI_IM_AI_FEISHU_ENCRYPT_KEY\": \"7qZik8wobG3FJVt8rrg97esIHfRRV6dc\"\n }\n }\n },\n \"frontend\": {\n \"use\": \"@cloudbase/framework-plugin-container\",\n \"inputs\": {\n \"serviceName\": \"fashion-admin\",\n \"servicePath\": \"/\",\n \"localPath\": \"./frontend\",\n \"dockerfilePath\": \"./frontend/Dockerfile\",\n \"containerPort\": 80,\n \"initialDelaySeconds\": 30,\n \"minNum\": 1,\n \"maxNum\": 3,\n \"cpu\": 0.5,\n \"mem\": 1,\n \"envVariables\": {\n \"BACKEND_URL\": \"https://api.webyszl.cn\"\n }\n }\n },\n \"h5\": {\n \"use\": \"@cloudbase/framework-plugin-container\",\n \"inputs\": {\n \"serviceName\": \"fashion-h5\",\n \"servicePath\": \"/h5\",\n \"localPath\": \"./h5-web\",\n \"dockerfilePath\": \"./h5-web/Dockerfile\",\n \"containerPort\": 80,\n \"initialDelaySeconds\": 30,\n \"minNum\": 1,\n \"maxNum\": 3,\n \"cpu\": 0.25,\n \"mem\": 0.5\n }\n }\n }\n }\n}"}, "260347e68db7ecd42554f892d362755a81e6df07": {"t": 1785749954.6667893, "n": 2958, "h": "7d5d9fff4061f6be", "c": "{\n \"version\": \"2.0\",\n \"envId\": \"{{TCB_ENV_ID}}\",\n \"framework\": {\n \"name\": \"fashion-supplychain\",\n \"plugins\": {\n \"backend\": {\n \"use\": \"@cloudbase/framework-plugin-container\",\n \"inputs\": {\n \"serviceName\": \"fashion-backend\",\n \"servicePath\": \"/api\",\n \"localPath\": \"./backend\",\n \"dockerfilePath\": \"./backend/Dockerfile\",\n \"containerPort\": 8088,\n \"initialDelaySeconds\": 300,\n \"minNum\": 1,\n \"maxNum\": 5,\n \"cpu\": 1,\n \"mem\": 2,\n \"envVariables\": {\n \"SPRING_PROFILES_ACTIVE\": \"prod\",\n \"WECHAT_TRUST_ALL_CERTS\": \"true\",\n \"APP_AUTH_JWT_SECRET\": \"{{JWT_SECRET}}\",\n \"APP_DB_HOST\": \"{{DB_HOST}}\",\n \"APP_DB_NAME\": \"{{DB_NAME}}\",\n \"APP_DB_USERNAME\": \"{{DB_USERNAME}}\",\n \"APP_DB_PASSWORD\": \"{{DB_PASSWORD}}\",\n \"APP_COS_SECRET_ID\": \"{{COS_SECRET_ID}}\",\n \"APP_COS_SECRET_KEY\": \"{{COS_SECRET_KEY}}\",\n \"APP_COS_BUCKET\": \"{{COS_BUCKET}}\",\n \"APP_COS_REGION\": \"{{COS_REGION}}\",\n \"DEEPSEEK_API_KEY\": \"{{DEEPSEEK_API_KEY}}\",\n \"AGNES_API_KEY\": \"{{AGNES_API_KEY}}\",\n \"VOYAGE_API_KEY\": \"{{VOYAGE_API_KEY}}\",\n \"QDRANT_URL\": \"{{QDRANT_URL}}\",\n \"QDRANT_API_KEY\": \"{{QDRANT_API_KEY}}\",\n \"WECHAT_MP_APPID\": \"{{WECHAT_MP_APPID}}\",\n \"WECHAT_MP_SECRET\": \"{{WECHAT_MP_SECRET}}\",\n \"APP_SECURITY_PII_ENCRYPTION_KEY\": \"{{PII_ENCRYPTION_KEY}}\",\n \"AI_OPENAI_COMPAT_ENABLED\": \"{{AI_OPENAI_COMPAT_ENABLED}}\",\n \"AI_OPENAI_COMPAT_API_KEY\": \"{{AI_OPENAI_COMPAT_API_KEY}}\",\n \"AI_OPENAI_COMPAT_TENANT_ID\": \"{{AI_OPENAI_COMPAT_TENANT_ID}}\",\n \"AI_IM_AI_FEISHU_ENABLED\": \"true\",\n \"AI_IM_AI_FEISHU_VERIFICATION_TOKEN\": \"dxZCfyFE816hLOrPbpE3pzdHJpmcXAMQ\",\n \"AI_IM_AI_FEISHU_ENCRYPT_KEY\": \"7qZik8wobG3FJVt8rrg97esIHfRRV6dc\"\n }\n }\n },\n \"frontend\": {\n \"use\": \"@cloudbase/framework-plugin-container\",\n \"inputs\": {\n \"serviceName\": \"fashion-admin\",\n \"servicePath\": \"/\",\n \"localPath\": \"./frontend\",\n \"dockerfilePath\": \"./frontend/Dockerfile\",\n \"containerPort\": 80,\n \"initialDelaySeconds\": 30,\n \"minNum\": 1,\n \"maxNum\": 3,\n \"cpu\": 0.5,\n \"mem\": 1,\n \"envVariables\": {\n \"BACKEND_URL\": \"https://api.webyszl.cn\"\n }\n }\n },\n \"h5\": {\n \"use\": \"@cloudbase/framework-plugin-container\",\n \"inputs\": {\n \"serviceName\": \"fashion-h5\",\n \"servicePath\": \"/h5\",\n \"localPath\": \"./h5-web\",\n \"dockerfilePath\": \"./h5-web/Dockerfile\",\n \"containerPort\": 80,\n \"initialDelaySeconds\": 30,\n \"minNum\": 1,\n \"maxNum\": 3,\n \"cpu\": 0.25,\n \"mem\": 0.5\n }\n }\n }\n }\n}"}, "4d454c0ea46f46c64a4b5e6e9ead7cf0843b7890": {"t": 1785749955.7756293, "n": 2050, "h": "cef5193b6963e416", "c": "{\n \"version\": \"2.0\",\n \"envId\": \"{{TCB_ENV_ID}}\",\n \"services\": [\n {\n \"name\": \"fashion-backend\",\n \"dockerfile\": \"./backend/Dockerfile\",\n \"containerPort\": 8088,\n \"InitialDelaySeconds\": 300,\n \"PeriodSeconds\": 30,\n \"TimeoutSeconds\": 10,\n \"FailureThreshold\": 5,\n \"minNum\": 1,\n \"maxNum\": 5,\n \"cpu\": 1,\n \"mem\": 2,\n \"docker\": {\n \"buildType\": \"Dockerfile\",\n \"dockerfilePath\": \"./backend/Dockerfile\"\n },\n \"envVariables\": {\n \"SPRING_PROFILES_ACTIVE\": \"prod\",\n \"WECHAT_TRUST_ALL_CERTS\": \"true\",\n \"APP_AUTH_JWT_SECRET\": \"{{JWT_SECRET}}\",\n \"APP_DB_HOST\": \"{{DB_HOST}}\",\n \"APP_DB_NAME\": \"{{DB_NAME}}\",\n \"APP_DB_USERNAME\": \"{{DB_USERNAME}}\",\n \"APP_DB_PASSWORD\": \"{{DB_PASSWORD}}\",\n \"APP_COS_SECRET_ID\": \"{{COS_SECRET_ID}}\",\n \"APP_COS_SECRET_KEY\": \"{{COS_SECRET_KEY}}\",\n \"APP_COS_BUCKET\": \"{{COS_BUCKET}}\",\n \"APP_COS_REGION\": \"{{COS_REGION}}\",\n \"DEEPSEEK_API_KEY\": \"{{DEEPSEEK_API_KEY}}\",\n \"AGNES_API_KEY\": \"{{AGNES_API_KEY}}\",\n \"VOYAGE_API_KEY\": \"{{VOYAGE_API_KEY}}\",\n \"QDRANT_URL\": \"{{QDRANT_URL}}\",\n \"QDRANT_API_KEY\": \"{{QDRANT_API_KEY}}\",\n \"WECHAT_MP_APPID\": \"{{WECHAT_MP_APPID}}\",\n \"WECHAT_MP_SECRET\": \"{{WECHAT_MP_SECRET}}\",\n \"APP_SECURITY_PII_ENCRYPTION_KEY\": \"{{PII_ENCRYPTION_KEY}}\",\n \"AI_OPENAI_COMPAT_ENABLED\": \"{{AI_OPENAI_COMPAT_ENABLED}}\",\n \"AI_OPENAI_COMPAT_API_KEY\": \"{{AI_OPENAI_COMPAT_API_KEY}}\",\n \"AI_OPENAI_COMPAT_TENANT_ID\": \"{{AI_OPENAI_COMPAT_TENANT_ID}}\",\n \"AI_IM_AI_FEISHU_ENABLED\": \"true\",\n \"AI_IM_AI_FEISHU_VERIFICATION_TOKEN\": \"dxZCfyFE816hLOrPbpE3pzdHJpmcXAMQ\",\n \"AI_IM_AI_FEISHU_ENCRYPT_KEY\": \"7qZik8wobG3FJVt8rrg97esIHfRRV6dc\"\n }\n }\n ],\n \"static\": [\n {\n \"name\": \"fashion-h5\",\n \"localPath\": \"./h5-web/dist\",\n \"cloudPath\": \"/h5\"\n },\n {\n \"name\": \"fashion-admin\",\n \"localPath\": \"./frontend/dist\",\n \"cloudPath\": \"/\"\n }\n ]\n}\n"}, "9016c9bd9aad954ab25b55fac14ed0295cda5550": {"t": 1785749957.903767, "n": 1966, "h": "6f9aba996428b19a", "c": "{\n \"version\": \"2.0\",\n \"envId\": \"{{TCB_ENV_ID}}\",\n \"services\": [\n {\n \"name\": \"fashion-backend\",\n \"dockerfile\": \"./backend/Dockerfile\",\n \"containerPort\": 8088,\n \"InitialDelaySeconds\": 150,\n \"minNum\": 1,\n \"maxNum\": 5,\n \"cpu\": 1,\n \"mem\": 2,\n \"docker\": {\n \"buildType\": \"Dockerfile\",\n \"dockerfilePath\": \"./backend/Dockerfile\"\n },\n \"envVariables\": {\n \"SPRING_PROFILES_ACTIVE\": \"prod\",\n \"WECHAT_TRUST_ALL_CERTS\": \"true\",\n \"APP_AUTH_JWT_SECRET\": \"{{JWT_SECRET}}\",\n \"APP_DB_HOST\": \"{{DB_HOST}}\",\n \"APP_DB_NAME\": \"{{DB_NAME}}\",\n \"APP_DB_USERNAME\": \"{{DB_USERNAME}}\",\n \"APP_DB_PASSWORD\": \"{{DB_PASSWORD}}\",\n \"APP_COS_SECRET_ID\": \"{{COS_SECRET_ID}}\",\n \"APP_COS_SECRET_KEY\": \"{{COS_SECRET_KEY}}\",\n \"APP_COS_BUCKET\": \"{{COS_BUCKET}}\",\n \"APP_COS_REGION\": \"{{COS_REGION}}\",\n \"DEEPSEEK_API_KEY\": \"{{DEEPSEEK_API_KEY}}\",\n \"AGNES_API_KEY\": \"{{AGNES_API_KEY}}\",\n \"VOYAGE_API_KEY\": \"{{VOYAGE_API_KEY}}\",\n \"QDRANT_URL\": \"{{QDRANT_URL}}\",\n \"QDRANT_API_KEY\": \"{{QDRANT_API_KEY}}\",\n \"WECHAT_MP_APPID\": \"{{WECHAT_MP_APPID}}\",\n \"WECHAT_MP_SECRET\": \"{{WECHAT_MP_SECRET}}\",\n \"APP_SECURITY_PII_ENCRYPTION_KEY\": \"{{PII_ENCRYPTION_KEY}}\",\n \"AI_OPENAI_COMPAT_ENABLED\": \"{{AI_OPENAI_COMPAT_ENABLED}}\",\n \"AI_OPENAI_COMPAT_API_KEY\": \"{{AI_OPENAI_COMPAT_API_KEY}}\",\n \"AI_OPENAI_COMPAT_TENANT_ID\": \"{{AI_OPENAI_COMPAT_TENANT_ID}}\",\n \"AI_IM_AI_FEISHU_ENABLED\": \"true\",\n \"AI_IM_AI_FEISHU_VERIFICATION_TOKEN\": \"dxZCfyFE816hLOrPbpE3pzdHJpmcXAMQ\",\n \"AI_IM_AI_FEISHU_ENCRYPT_KEY\": \"7qZik8wobG3FJVt8rrg97esIHfRRV6dc\"\n }\n }\n ],\n \"static\": [\n {\n \"name\": \"fashion-h5\",\n \"localPath\": \"./h5-web/dist\",\n \"cloudPath\": \"/h5\"\n },\n {\n \"name\": \"fashion-admin\",\n \"localPath\": \"./frontend/dist\",\n \"cloudPath\": \"/\"\n }\n ]\n}\n"}, "49b31a363b2a1b237b97d53d638aad2dbcd3279f": {"t": 1785749959.2831678, "n": 1966, "h": "8b9aaf48e647f684", "c": "{\n \"version\": \"2.0\",\n \"envId\": \"{{TCB_ENV_ID}}\",\n \"services\": [\n {\n \"name\": \"fashion-backend\",\n \"dockerfile\": \"./backend/Dockerfile\",\n \"containerPort\": 8088,\n \"initialDelaySeconds\": 120,\n \"minNum\": 1,\n \"maxNum\": 5,\n \"cpu\": 1,\n \"mem\": 2,\n \"docker\": {\n \"buildType\": \"Dockerfile\",\n \"dockerfilePath\": \"./backend/Dockerfile\"\n },\n \"envVariables\": {\n \"SPRING_PROFILES_ACTIVE\": \"prod\",\n \"WECHAT_TRUST_ALL_CERTS\": \"true\",\n \"APP_AUTH_JWT_SECRET\": \"{{JWT_SECRET}}\",\n \"APP_DB_HOST\": \"{{DB_HOST}}\",\n \"APP_DB_NAME\": \"{{DB_NAME}}\",\n \"APP_DB_USERNAME\": \"{{DB_USERNAME}}\",\n \"APP_DB_PASSWORD\": \"{{DB_PASSWORD}}\",\n \"APP_COS_SECRET_ID\": \"{{COS_SECRET_ID}}\",\n \"APP_COS_SECRET_KEY\": \"{{COS_SECRET_KEY}}\",\n \"APP_COS_BUCKET\": \"{{COS_BUCKET}}\",\n \"APP_COS_REGION\": \"{{COS_REGION}}\",\n \"DEEPSEEK_API_KEY\": \"{{DEEPSEEK_API_KEY}}\",\n \"AGNES_API_KEY\": \"{{AGNES_API_KEY}}\",\n \"VOYAGE_API_KEY\": \"{{VOYAGE_API_KEY}}\",\n \"QDRANT_URL\": \"{{QDRANT_URL}}\",\n \"QDRANT_API_KEY\": \"{{QDRANT_API_KEY}}\",\n \"WECHAT_MP_APPID\": \"{{WECHAT_MP_APPID}}\",\n \"WECHAT_MP_SECRET\": \"{{WECHAT_MP_SECRET}}\",\n \"APP_SECURITY_PII_ENCRYPTION_KEY\": \"{{PII_ENCRYPTION_KEY}}\",\n \"AI_OPENAI_COMPAT_ENABLED\": \"{{AI_OPENAI_COMPAT_ENABLED}}\",\n \"AI_OPENAI_COMPAT_API_KEY\": \"{{AI_OPENAI_COMPAT_API_KEY}}\",\n \"AI_OPENAI_COMPAT_TENANT_ID\": \"{{AI_OPENAI_COMPAT_TENANT_ID}}\",\n \"AI_IM_AI_FEISHU_ENABLED\": \"true\",\n \"AI_IM_AI_FEISHU_VERIFICATION_TOKEN\": \"dxZCfyFE816hLOrPbpE3pzdHJpmcXAMQ\",\n \"AI_IM_AI_FEISHU_ENCRYPT_KEY\": \"7qZik8wobG3FJVt8rrg97esIHfRRV6dc\"\n }\n }\n ],\n \"static\": [\n {\n \"name\": \"fashion-h5\",\n \"localPath\": \"./h5-web/dist\",\n \"cloudPath\": \"/h5\"\n },\n {\n \"name\": \"fashion-admin\",\n \"localPath\": \"./frontend/dist\",\n \"cloudPath\": \"/\"\n }\n ]\n}\n"}, "45a0163991a4d9c2e1add3c194206884f6ecd03a": {"t": 1785749960.358283, "n": 1932, "h": "d8be46df05863e91", "c": "{\n \"version\": \"2.0\",\n \"envId\": \"{{TCB_ENV_ID}}\",\n \"services\": [\n {\n \"name\": \"fashion-backend\",\n \"dockerfile\": \"./backend/Dockerfile\",\n \"containerPort\": 8088,\n \"minNum\": 1,\n \"maxNum\": 5,\n \"cpu\": 1,\n \"mem\": 2,\n \"docker\": {\n \"buildType\": \"Dockerfile\",\n \"dockerfilePath\": \"./backend/Dockerfile\"\n },\n \"envVariables\": {\n \"SPRING_PROFILES_ACTIVE\": \"prod\",\n \"WECHAT_TRUST_ALL_CERTS\": \"true\",\n \"APP_AUTH_JWT_SECRET\": \"{{JWT_SECRET}}\",\n \"APP_DB_HOST\": \"{{DB_HOST}}\",\n \"APP_DB_NAME\": \"{{DB_NAME}}\",\n \"APP_DB_USERNAME\": \"{{DB_USERNAME}}\",\n \"APP_DB_PASSWORD\": \"{{DB_PASSWORD}}\",\n \"APP_COS_SECRET_ID\": \"{{COS_SECRET_ID}}\",\n \"APP_COS_SECRET_KEY\": \"{{COS_SECRET_KEY}}\",\n \"APP_COS_BUCKET\": \"{{COS_BUCKET}}\",\n \"APP_COS_REGION\": \"{{COS_REGION}}\",\n \"DEEPSEEK_API_KEY\": \"{{DEEPSEEK_API_KEY}}\",\n \"AGNES_API_KEY\": \"{{AGNES_API_KEY}}\",\n \"VOYAGE_API_KEY\": \"{{VOYAGE_API_KEY}}\",\n \"QDRANT_URL\": \"{{QDRANT_URL}}\",\n \"QDRANT_API_KEY\": \"{{QDRANT_API_KEY}}\",\n \"WECHAT_MP_APPID\": \"{{WECHAT_MP_APPID}}\",\n \"WECHAT_MP_SECRET\": \"{{WECHAT_MP_SECRET}}\",\n \"APP_SECURITY_PII_ENCRYPTION_KEY\": \"{{PII_ENCRYPTION_KEY}}\",\n \"AI_OPENAI_COMPAT_ENABLED\": \"{{AI_OPENAI_COMPAT_ENABLED}}\",\n \"AI_OPENAI_COMPAT_API_KEY\": \"{{AI_OPENAI_COMPAT_API_KEY}}\",\n \"AI_OPENAI_COMPAT_TENANT_ID\": \"{{AI_OPENAI_COMPAT_TENANT_ID}}\",\n \"AI_IM_AI_FEISHU_ENABLED\": \"true\",\n \"AI_IM_AI_FEISHU_VERIFICATION_TOKEN\": \"dxZCfyFE816hLOrPbpE3pzdHJpmcXAMQ\",\n \"AI_IM_AI_FEISHU_ENCRYPT_KEY\": \"7qZik8wobG3FJVt8rrg97esIHfRRV6dc\"\n }\n }\n ],\n \"static\": [\n {\n \"name\": \"fashion-h5\",\n \"localPath\": \"./h5-web/dist\",\n \"cloudPath\": \"/h5\"\n },\n {\n \"name\": \"fashion-admin\",\n \"localPath\": \"./frontend/dist\",\n \"cloudPath\": \"/\"\n }\n ]\n}\n"}, "fb7445b2abf78a0442d8fc132ec96513b2399050": {"t": 1785749961.8656561, "n": 1934, "h": "89ed0346fe79a2dd", "c": "{\n \"version\": \"2.0\",\n \"envId\": \"{{TCB_ENV_ID}}\",\n \"services\": [\n {\n \"name\": \"fashion-backend\",\n \"dockerfile\": \"./backend/Dockerfile\",\n \"containerPort\": 8088,\n \"minNum\": 1,\n \"maxNum\": 5,\n \"cpu\": 1,\n \"mem\": 2,\n \"docker\": {\n \"buildType\": \"Dockerfile\",\n \"dockerfilePath\": \"./backend/Dockerfile\"\n },\n \"envVariables\": {\n \"SPRING_PROFILES_ACTIVE\": \"prod\",\n \"WECHAT_TRUST_ALL_CERTS\": \"true\",\n \"APP_AUTH_JWT_SECRET\": \"{{JWT_SECRET}}\",\n \"APP_DB_HOST\": \"{{DB_HOST}}\",\n \"APP_DB_NAME\": \"{{DB_NAME}}\",\n \"APP_DB_USERNAME\": \"{{DB_USERNAME}}\",\n \"APP_DB_PASSWORD\": \"{{DB_PASSWORD}}\",\n \"APP_COS_SECRET_ID\": \"{{COS_SECRET_ID}}\",\n \"APP_COS_SECRET_KEY\": \"{{COS_SECRET_KEY}}\",\n \"APP_COS_BUCKET\": \"{{COS_BUCKET}}\",\n \"APP_COS_REGION\": \"{{COS_REGION}}\",\n \"DEEPSEEK_API_KEY\": \"{{DEEPSEEK_API_KEY}}\",\n \"DOUBAO_API_KEY\": \"{{DOUBAO_API_KEY}}\",\n \"VOYAGE_API_KEY\": \"{{VOYAGE_API_KEY}}\",\n \"QDRANT_URL\": \"{{QDRANT_URL}}\",\n \"QDRANT_API_KEY\": \"{{QDRANT_API_KEY}}\",\n \"WECHAT_MP_APPID\": \"{{WECHAT_MP_APPID}}\",\n \"WECHAT_MP_SECRET\": \"{{WECHAT_MP_SECRET}}\",\n \"APP_SECURITY_PII_ENCRYPTION_KEY\": \"{{PII_ENCRYPTION_KEY}}\",\n \"AI_OPENAI_COMPAT_ENABLED\": \"{{AI_OPENAI_COMPAT_ENABLED}}\",\n \"AI_OPENAI_COMPAT_API_KEY\": \"{{AI_OPENAI_COMPAT_API_KEY}}\",\n \"AI_OPENAI_COMPAT_TENANT_ID\": \"{{AI_OPENAI_COMPAT_TENANT_ID}}\",\n \"AI_IM_AI_FEISHU_ENABLED\": \"true\",\n \"AI_IM_AI_FEISHU_VERIFICATION_TOKEN\": \"dxZCfyFE816hLOrPbpE3pzdHJpmcXAMQ\",\n \"AI_IM_AI_FEISHU_ENCRYPT_KEY\": \"7qZik8wobG3FJVt8rrg97esIHfRRV6dc\"\n }\n }\n ],\n \"static\": [\n {\n \"name\": \"fashion-h5\",\n \"localPath\": \"./h5-web/dist\",\n \"cloudPath\": \"/h5\"\n },\n {\n \"name\": \"fashion-admin\",\n \"localPath\": \"./frontend/dist\",\n \"cloudPath\": \"/\"\n }\n ]\n}\n"}, "956c9f0e06bd2fa961a921da326c30029b68938a": {"t": 1785749962.9523864, "n": 1734, "h": "e1f979c607b4c7bb", "c": "{\n \"version\": \"2.0\",\n \"envId\": \"{{TCB_ENV_ID}}\",\n \"services\": [\n {\n \"name\": \"fashion-backend\",\n \"dockerfile\": \"./backend/Dockerfile\",\n \"containerPort\": 8088,\n \"minNum\": 1,\n \"maxNum\": 5,\n \"cpu\": 1,\n \"mem\": 2,\n \"docker\": {\n \"buildType\": \"Dockerfile\",\n \"dockerfilePath\": \"./backend/Dockerfile\"\n },\n \"envVariables\": {\n \"SPRING_PROFILES_ACTIVE\": \"prod\",\n \"WECHAT_TRUST_ALL_CERTS\": \"true\",\n \"APP_AUTH_JWT_SECRET\": \"{{JWT_SECRET}}\",\n \"APP_DB_HOST\": \"{{DB_HOST}}\",\n \"APP_DB_NAME\": \"{{DB_NAME}}\",\n \"APP_DB_USERNAME\": \"{{DB_USERNAME}}\",\n \"APP_DB_PASSWORD\": \"{{DB_PASSWORD}}\",\n \"APP_COS_SECRET_ID\": \"{{COS_SECRET_ID}}\",\n \"APP_COS_SECRET_KEY\": \"{{COS_SECRET_KEY}}\",\n \"APP_COS_BUCKET\": \"{{COS_BUCKET}}\",\n \"APP_COS_REGION\": \"{{COS_REGION}}\",\n \"DEEPSEEK_API_KEY\": \"{{DEEPSEEK_API_KEY}}\",\n \"DOUBAO_API_KEY\": \"{{DOUBAO_API_KEY}}\",\n \"VOYAGE_API_KEY\": \"{{VOYAGE_API_KEY}}\",\n \"QDRANT_URL\": \"{{QDRANT_URL}}\",\n \"QDRANT_API_KEY\": \"{{QDRANT_API_KEY}}\",\n \"WECHAT_MP_APPID\": \"{{WECHAT_MP_APPID}}\",\n \"WECHAT_MP_SECRET\": \"{{WECHAT_MP_SECRET}}\",\n \"APP_SECURITY_PII_ENCRYPTION_KEY\": \"{{PII_ENCRYPTION_KEY}}\",\n \"AI_OPENAI_COMPAT_ENABLED\": \"{{AI_OPENAI_COMPAT_ENABLED}}\",\n \"AI_OPENAI_COMPAT_API_KEY\": \"{{AI_OPENAI_COMPAT_API_KEY}}\",\n \"AI_OPENAI_COMPAT_TENANT_ID\": \"{{AI_OPENAI_COMPAT_TENANT_ID}}\"\n }\n }\n ],\n \"static\": [\n {\n \"name\": \"fashion-h5\",\n \"localPath\": \"./h5-web/dist\",\n \"cloudPath\": \"/h5\"\n },\n {\n \"name\": \"fashion-admin\",\n \"localPath\": \"./frontend/dist\",\n \"cloudPath\": \"/\"\n }\n ]\n}\n"}, "1ca8b8f7f9cdd4597b35367dcb6be1e89c77b1e4": {"t": 1785749964.0457792, "n": 1526, "h": "b89773a21c9bf00d", "c": "{\n \"version\": \"2.0\",\n \"envId\": \"{{TCB_ENV_ID}}\",\n \"services\": [\n {\n \"name\": \"fashion-backend\",\n \"dockerfile\": \"./backend/Dockerfile\",\n \"containerPort\": 8088,\n \"minNum\": 1,\n \"maxNum\": 5,\n \"cpu\": 1,\n \"mem\": 2,\n \"docker\": {\n \"buildType\": \"Dockerfile\",\n \"dockerfilePath\": \"./backend/Dockerfile\"\n },\n \"envVariables\": {\n \"SPRING_PROFILES_ACTIVE\": \"prod\",\n \"WECHAT_TRUST_ALL_CERTS\": \"true\",\n \"APP_AUTH_JWT_SECRET\": \"{{JWT_SECRET}}\",\n \"APP_DB_HOST\": \"{{DB_HOST}}\",\n \"APP_DB_NAME\": \"{{DB_NAME}}\",\n \"APP_DB_USERNAME\": \"{{DB_USERNAME}}\",\n \"APP_DB_PASSWORD\": \"{{DB_PASSWORD}}\",\n \"APP_COS_SECRET_ID\": \"{{COS_SECRET_ID}}\",\n \"APP_COS_SECRET_KEY\": \"{{COS_SECRET_KEY}}\",\n \"APP_COS_BUCKET\": \"{{COS_BUCKET}}\",\n \"APP_COS_REGION\": \"{{COS_REGION}}\",\n \"DEEPSEEK_API_KEY\": \"{{DEEPSEEK_API_KEY}}\",\n \"DOUBAO_API_KEY\": \"{{DOUBAO_API_KEY}}\",\n \"VOYAGE_API_KEY\": \"{{VOYAGE_API_KEY}}\",\n \"QDRANT_URL\": \"{{QDRANT_URL}}\",\n \"QDRANT_API_KEY\": \"{{QDRANT_API_KEY}}\",\n \"WECHAT_MP_APPID\": \"{{WECHAT_MP_APPID}}\",\n \"WECHAT_MP_SECRET\": \"{{WECHAT_MP_SECRET}}\",\n \"APP_SECURITY_PII_ENCRYPTION_KEY\": \"{{PII_ENCRYPTION_KEY}}\"\n }\n }\n ],\n \"static\": [\n {\n \"name\": \"fashion-h5\",\n \"localPath\": \"./h5-web/dist\",\n \"cloudPath\": \"/h5\"\n },\n {\n \"name\": \"fashion-admin\",\n \"localPath\": \"./frontend/dist\",\n \"cloudPath\": \"/\"\n }\n ]\n}\n"}, "4893a2ef7839c1499308898109716c4f8ca8a213": {"t": 1785749969.022853, "n": 1457, "h": "52700e8c9a1d134d", "c": "{\n \"version\": \"2.0\",\n \"envId\": \"{{TCB_ENV_ID}}\",\n \"services\": [\n {\n \"name\": \"fashion-backend\",\n \"dockerfile\": \"./backend/Dockerfile\",\n \"containerPort\": 8088,\n \"minNum\": 1,\n \"maxNum\": 5,\n \"cpu\": 1,\n \"mem\": 2,\n \"docker\": {\n \"buildType\": \"Dockerfile\",\n \"dockerfilePath\": \"./backend/Dockerfile\"\n },\n \"envVariables\": {\n \"SPRING_PROFILES_ACTIVE\": \"prod\",\n \"WECHAT_TRUST_ALL_CERTS\": \"true\",\n \"APP_AUTH_JWT_SECRET\": \"{{JWT_SECRET}}\",\n \"APP_DB_HOST\": \"{{DB_HOST}}\",\n \"APP_DB_NAME\": \"{{DB_NAME}}\",\n \"APP_DB_USERNAME\": \"{{DB_USERNAME}}\",\n \"APP_DB_PASSWORD\": \"{{DB_PASSWORD}}\",\n \"APP_COS_SECRET_ID\": \"{{COS_SECRET_ID}}\",\n \"APP_COS_SECRET_KEY\": \"{{COS_SECRET_KEY}}\",\n \"APP_COS_BUCKET\": \"{{COS_BUCKET}}\",\n \"APP_COS_REGION\": \"{{COS_REGION}}\",\n \"DEEPSEEK_API_KEY\": \"{{DEEPSEEK_API_KEY}}\",\n \"DOUBAO_API_KEY\": \"{{DOUBAO_API_KEY}}\",\n \"VOYAGE_API_KEY\": \"{{VOYAGE_API_KEY}}\",\n \"QDRANT_URL\": \"{{QDRANT_URL}}\",\n \"QDRANT_API_KEY\": \"{{QDRANT_API_KEY}}\",\n \"WECHAT_MP_APPID\": \"{{WECHAT_MP_APPID}}\",\n \"WECHAT_MP_SECRET\": \"{{WECHAT_MP_SECRET}}\"\n }\n }\n ],\n \"static\": [\n {\n \"name\": \"fashion-h5\",\n \"localPath\": \"./h5-web/dist\",\n \"cloudPath\": \"/h5\"\n },\n {\n \"name\": \"fashion-admin\",\n \"localPath\": \"./frontend/dist\",\n \"cloudPath\": \"/\"\n }\n ]\n}\n"}, "c5ff9274b0de31b599715f7fc2aa76b4bf7f0c74": {"t": 1785749970.1310608, "n": 1355, "h": "327aacc25772a5d6", "c": "{\n \"version\": \"2.0\",\n \"envId\": \"{{TCB_ENV_ID}}\",\n \"services\": [\n {\n \"name\": \"fashion-backend\",\n \"dockerfile\": \"./backend/Dockerfile\",\n \"containerPort\": 8088,\n \"minNum\": 1,\n \"maxNum\": 5,\n \"cpu\": 1,\n \"mem\": 2,\n \"docker\": {\n \"buildType\": \"Dockerfile\",\n \"dockerfilePath\": \"./backend/Dockerfile\"\n },\n \"envVariables\": {\n \"SPRING_PROFILES_ACTIVE\": \"prod\",\n \"WECHAT_TRUST_ALL_CERTS\": \"true\",\n \"APP_AUTH_JWT_SECRET\": \"{{JWT_SECRET}}\",\n \"APP_DB_HOST\": \"{{DB_HOST}}\",\n \"APP_DB_NAME\": \"{{DB_NAME}}\",\n \"APP_DB_USERNAME\": \"{{DB_USERNAME}}\",\n \"APP_DB_PASSWORD\": \"{{DB_PASSWORD}}\",\n \"APP_COS_SECRET_ID\": \"{{COS_SECRET_ID}}\",\n \"APP_COS_SECRET_KEY\": \"{{COS_SECRET_KEY}}\",\n \"APP_COS_BUCKET\": \"{{COS_BUCKET}}\",\n \"APP_COS_REGION\": \"{{COS_REGION}}\",\n \"DEEPSEEK_API_KEY\": \"{{DEEPSEEK_API_KEY}}\",\n \"DOUBAO_API_KEY\": \"{{DOUBAO_API_KEY}}\",\n \"VOYAGE_API_KEY\": \"{{VOYAGE_API_KEY}}\",\n \"QDRANT_URL\": \"{{QDRANT_URL}}\",\n \"QDRANT_API_KEY\": \"{{QDRANT_API_KEY}}\"\n }\n }\n ],\n \"static\": [\n {\n \"name\": \"fashion-h5\",\n \"localPath\": \"./h5-web/dist\",\n \"cloudPath\": \"/h5\"\n },\n {\n \"name\": \"fashion-admin\",\n \"localPath\": \"./frontend/dist\",\n \"cloudPath\": \"/\"\n }\n ]\n}\n"}, "e701be03d01d12a792e286729420a3e2dadf973b": {"t": 1785749971.2254527, "n": 1119, "h": "f94db0707c47bd0e", "c": "{\n \"version\": \"2.0\",\n \"envId\": \"{{TCB_ENV_ID}}\",\n \"services\": [\n {\n \"name\": \"fashion-backend\",\n \"dockerfile\": \"./backend/Dockerfile\",\n \"containerPort\": 8088,\n \"minNum\": 1,\n \"maxNum\": 5,\n \"cpu\": 1,\n \"mem\": 2,\n \"docker\": {\n \"buildType\": \"Dockerfile\",\n \"dockerfilePath\": \"./backend/Dockerfile\"\n },\n \"envVariables\": {\n \"SPRING_PROFILES_ACTIVE\": \"prod\",\n \"WECHAT_TRUST_ALL_CERTS\": \"true\",\n \"APP_AUTH_JWT_SECRET\": \"{{JWT_SECRET}}\",\n \"APP_DB_HOST\": \"{{DB_HOST}}\",\n \"APP_DB_NAME\": \"{{DB_NAME}}\",\n \"APP_DB_USERNAME\": \"{{DB_USERNAME}}\",\n \"APP_DB_PASSWORD\": \"{{DB_PASSWORD}}\",\n \"APP_COS_SECRET_ID\": \"{{COS_SECRET_ID}}\",\n \"APP_COS_SECRET_KEY\": \"{{COS_SECRET_KEY}}\",\n \"APP_COS_BUCKET\": \"{{COS_BUCKET}}\",\n \"APP_COS_REGION\": \"{{COS_REGION}}\"\n }\n }\n ],\n \"static\": [\n {\n \"name\": \"fashion-h5\",\n \"localPath\": \"./h5-web/dist\",\n \"cloudPath\": \"/h5\"\n },\n {\n \"name\": \"fashion-admin\",\n \"localPath\": \"./frontend/dist\",\n \"cloudPath\": \"/\"\n }\n ]\n}\n"}, "8cf66d2861f0cd29ad6f5cca6aed03fa700d0901": {"t": 1785749972.2951632, "n": 1077, "h": "e8ee40f14345cbb3", "c": "{\n \"version\": \"2.0\",\n \"envId\": \"{{TCB_ENV_ID}}\",\n \"services\": [\n {\n \"name\": \"fashion-backend\",\n \"dockerfile\": \"./backend/Dockerfile\",\n \"containerPort\": 8088,\n \"minNum\": 1,\n \"maxNum\": 5,\n \"cpu\": 1,\n \"mem\": 2,\n \"docker\": {\n \"buildType\": \"Dockerfile\",\n \"dockerfilePath\": \"./backend/Dockerfile\"\n },\n \"envVariables\": {\n \"SPRING_PROFILES_ACTIVE\": \"prod\",\n \"APP_AUTH_JWT_SECRET\": \"{{JWT_SECRET}}\",\n \"APP_DB_HOST\": \"{{DB_HOST}}\",\n \"APP_DB_NAME\": \"{{DB_NAME}}\",\n \"APP_DB_USERNAME\": \"{{DB_USERNAME}}\",\n \"APP_DB_PASSWORD\": \"{{DB_PASSWORD}}\",\n \"APP_COS_SECRET_ID\": \"{{COS_SECRET_ID}}\",\n \"APP_COS_SECRET_KEY\": \"{{COS_SECRET_KEY}}\",\n \"APP_COS_BUCKET\": \"{{COS_BUCKET}}\",\n \"APP_COS_REGION\": \"{{COS_REGION}}\"\n }\n }\n ],\n \"static\": [\n {\n \"name\": \"fashion-h5\",\n \"localPath\": \"./h5-web/dist\",\n \"cloudPath\": \"/h5\"\n },\n {\n \"name\": \"fashion-admin\",\n \"localPath\": \"./frontend/dist\",\n \"cloudPath\": \"/\"\n }\n ]\n}\n"}, "8cac5f58a452b3c354e16fc82c389f4912fe2ed5": {"t": 1785749973.5188007, "n": 1077, "h": "b6a839446273c8a3", "c": "{\n \"version\": \"2.0\",\n \"envId\": \"{{TCB_ENV_ID}}\",\n \"services\": [\n {\n \"name\": \"fashion-backend\",\n \"dockerfile\": \"./backend/Dockerfile\",\n \"containerPort\": 8080,\n \"minNum\": 1,\n \"maxNum\": 5,\n \"cpu\": 1,\n \"mem\": 2,\n \"docker\": {\n \"buildType\": \"Dockerfile\",\n \"dockerfilePath\": \"./backend/Dockerfile\"\n },\n \"envVariables\": {\n \"SPRING_PROFILES_ACTIVE\": \"prod\",\n \"APP_AUTH_JWT_SECRET\": \"{{JWT_SECRET}}\",\n \"APP_DB_HOST\": \"{{DB_HOST}}\",\n \"APP_DB_NAME\": \"{{DB_NAME}}\",\n \"APP_DB_USERNAME\": \"{{DB_USERNAME}}\",\n \"APP_DB_PASSWORD\": \"{{DB_PASSWORD}}\",\n \"APP_COS_SECRET_ID\": \"{{COS_SECRET_ID}}\",\n \"APP_COS_SECRET_KEY\": \"{{COS_SECRET_KEY}}\",\n \"APP_COS_BUCKET\": \"{{COS_BUCKET}}\",\n \"APP_COS_REGION\": \"{{COS_REGION}}\"\n }\n }\n ],\n \"static\": [\n {\n \"name\": \"fashion-h5\",\n \"localPath\": \"./h5-web/dist\",\n \"cloudPath\": \"/h5\"\n },\n {\n \"name\": \"fashion-admin\",\n \"localPath\": \"./frontend/dist\",\n \"cloudPath\": \"/\"\n }\n ]\n}\n"}, "65e5b81123568f44fc9098a0c003378f12852fe2": {"t": 1785749974.6060681, "n": 972, "h": "59e63bd1ef0c6267", "c": "{\n \"version\": \"2.0\",\n \"envId\": \"{{TCB_ENV_ID}}\",\n \"services\": [\n {\n \"name\": \"fashion-backend\",\n \"dockerfile\": \"./backend/Dockerfile\",\n \"containerPort\": 8080,\n \"minNum\": 1,\n \"maxNum\": 5,\n \"cpu\": 1,\n \"mem\": 2,\n \"docker\": {\n \"buildType\": \"Dockerfile\",\n \"dockerfilePath\": \"./backend/Dockerfile\"\n },\n \"envVariables\": {\n \"SPRING_PROFILES_ACTIVE\": \"prod\",\n \"APP_AUTH_JWT_SECRET\": \"{{JWT_SECRET}}\",\n \"APP_DB_HOST\": \"{{DB_HOST}}\",\n \"APP_DB_NAME\": \"{{DB_NAME}}\",\n \"APP_DB_USERNAME\": \"{{DB_USERNAME}}\",\n \"APP_DB_PASSWORD\": \"{{DB_PASSWORD}}\",\n \"APP_COS_SECRET_ID\": \"{{COS_SECRET_ID}}\",\n \"APP_COS_SECRET_KEY\": \"{{COS_SECRET_KEY}}\",\n \"APP_COS_BUCKET\": \"{{COS_BUCKET}}\",\n \"APP_COS_REGION\": \"{{COS_REGION}}\"\n }\n }\n ],\n \"static\": [\n {\n \"name\": \"fashion-h5\",\n \"localPath\": \"./h5-web/dist\",\n \"cloudPath\": \"/h5\"\n }\n ]\n}\n"}, "bf0d8e1358f851dea63091cdab4e485562dd8725": {"t": 1785749975.6847663, "n": 2554, "h": "794b87bf8666262b", "c": "{\n \"envId\": \"{{ENV_ID}}\",\n \"framework\": {\n \"name\": \"fashion-supplychain\",\n \"plugins\": {\n \"backend\": {\n \"use\": \"@cloudbase/framework-plugin-container\",\n \"inputs\": {\n \"serviceName\": \"backend\",\n \"servicePath\": \"/backend\",\n \"localPath\": \"./backend\",\n \"containerPort\": 8088,\n \"cpu\": 2,\n \"mem\": 4,\n \"minNum\": 1,\n \"maxNum\": 4,\n \"policyType\": \"cpu\",\n \"policyThreshold\": 60,\n \"envVariables\": {\n \"PORT\": \"8088\",\n \"SPRING_DATASOURCE_URL\": \"jdbc:mysql://{{MYSQL_HOST}}:3306/fashion_supplychain?useUnicode=true&characterEncoding=UTF-8&useSSL=true&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&connectTimeout=5000&socketTimeout=10000&tcpKeepAlive=true\",\n \"SPRING_DATASOURCE_USERNAME\": \"{{MYSQL_USER}}\",\n \"SPRING_DATASOURCE_PASSWORD\": \"{{MYSQL_PASSWORD}}\",\n \"SPRING_REDIS_HOST\": \"dgfbkffb.my-redis.j6jq9qgw.o7t59w4f.com\",\n \"SPRING_REDIS_PORT\": \"6379\",\n \"APP_AUTH_JWT_SECRET\": \"{{JWT_SECRET}}\",\n \"WECHAT_MP_APPID\": \"{{WECHAT_APPID}}\",\n \"WECHAT_MP_SECRET\": \"{{WECHAT_SECRET}}\",\n \"WECHAT_MINI_PROGRAM_MOCK_ENABLED\": \"false\",\n \"FLYWAY_ENABLED\": \"true\",\n \"SPRING_PROFILES_ACTIVE\": \"prod\",\n \"FASHION_DB_INITIALIZER_ENABLED\": \"false\",\n \"APP_CORS_ALLOWED_ORIGIN_PATTERNS\": \"https://*.tcloudbase.com,https://*.service.tcloudbase.com,https://*.ap-shanghai.service.tcloudbase.com\",\n \"COS_SECRET_ID\": \"{{COS_SECRET_ID}}\",\n \"COS_SECRET_KEY\": \"{{COS_SECRET_KEY}}\",\n \"COS_REGION\": \"{{COS_REGION}}\",\n \"COS_BUCKET\": \"{{COS_BUCKET}}\",\n \"SERPAPI_ENABLED\": \"true\",\n \"QWEN_VL_API_KEY\": \"{{QWEN_VL_API_KEY}}\",\n \"DOUBAO_API_KEY\": \"{{DOUBAO_API_KEY}}\",\n \"APP_QRCODE_HMAC_SECRET\": \"{{QRCODE_HMAC_SECRET}}\",\n \"APP_QRCODE_REQUIRE_SIGNATURE\": \"true\"\n },\n \"customLogs\": \"stdout\",\n \"healthPath\": \"/actuator/health/liveness\"\n }\n },\n \"frontend\": {\n \"use\": \"@cloudbase/framework-plugin-container\",\n \"inputs\": {\n \"serviceName\": \"frontend\",\n \"servicePath\": \"/\",\n \"localPath\": \"./frontend\",\n \"containerPort\": 80,\n \"cpu\": 0.5,\n \"mem\": 1,\n \"minNum\": 1,\n \"maxNum\": 2,\n \"envVariables\": {\n \"BACKEND_URL\": \"http://backend:8088\"\n }\n }\n }\n }\n }\n}\n"}, "94f4aebfc1a4ac8252fd599f34112b1d8933c02a": {"t": 1785749976.7376614, "n": 2573, "h": "7937ea1a9bed18ae", "c": "{\n \"envId\": \"{{ENV_ID}}\",\n \"framework\": {\n \"name\": \"fashion-supplychain\",\n \"plugins\": {\n \"backend\": {\n \"use\": \"@cloudbase/framework-plugin-container\",\n \"inputs\": {\n \"serviceName\": \"backend\",\n \"servicePath\": \"/backend\",\n \"localPath\": \"./backend\",\n \"containerPort\": 8088,\n \"cpu\": 2,\n \"mem\": 4,\n \"minNum\": 1,\n \"maxNum\": 4,\n \"policyType\": \"cpu\",\n \"policyThreshold\": 60,\n \"envVariables\": {\n \"PORT\": \"8088\",\n \"SPRING_DATASOURCE_URL\": \"jdbc:mysql://{{MYSQL_HOST}}:3306/fashion_supplychain?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&connectTimeout=5000&socketTimeout=10000&tcpKeepAlive=true\",\n \"SPRING_DATASOURCE_USERNAME\": \"{{MYSQL_USER}}\",\n \"SPRING_DATASOURCE_PASSWORD\": \"{{MYSQL_PASSWORD}}\",\n \"SPRING_REDIS_HOST\": \"dgfbkffb.my-redis.j6jq9qgw.o7t59w4f.com\",\n \"SPRING_REDIS_PORT\": \"6379\",\n \"APP_AUTH_JWT_SECRET\": \"{{JWT_SECRET}}\",\n \"WECHAT_MP_APPID\": \"{{WECHAT_APPID}}\",\n \"WECHAT_MP_SECRET\": \"{{WECHAT_SECRET}}\",\n \"WECHAT_MINI_PROGRAM_MOCK_ENABLED\": \"false\",\n \"FLYWAY_ENABLED\": \"true\",\n \"SPRING_PROFILES_ACTIVE\": \"prod\",\n \"FASHION_DB_INITIALIZER_ENABLED\": \"false\",\n \"APP_CORS_ALLOWED_ORIGIN_PATTERNS\": \"https://*.tcloudbase.com,https://*.service.tcloudbase.com,https://*.ap-shanghai.service.tcloudbase.com\",\n \"COS_SECRET_ID\": \"{{COS_SECRET_ID}}\",\n \"COS_SECRET_KEY\": \"{{COS_SECRET_KEY}}\",\n \"COS_REGION\": \"{{COS_REGION}}\",\n \"COS_BUCKET\": \"{{COS_BUCKET}}\",\n \"SERPAPI_ENABLED\": \"true\",\n \"QWEN_VL_API_KEY\": \"{{QWEN_VL_API_KEY}}\",\n \"DOUBAO_API_KEY\": \"7a8b0840-0a70-4fc1-a765-bb10c67beb06\",\n \"APP_QRCODE_HMAC_SECRET\": \"{{QRCODE_HMAC_SECRET}}\",\n \"APP_QRCODE_REQUIRE_SIGNATURE\": \"true\"\n },\n \"customLogs\": \"stdout\",\n \"healthPath\": \"/actuator/health/liveness\"\n }\n },\n \"frontend\": {\n \"use\": \"@cloudbase/framework-plugin-container\",\n \"inputs\": {\n \"serviceName\": \"frontend\",\n \"servicePath\": \"/\",\n \"localPath\": \"./frontend\",\n \"containerPort\": 80,\n \"cpu\": 0.5,\n \"mem\": 1,\n \"minNum\": 1,\n \"maxNum\": 2,\n \"envVariables\": {\n \"BACKEND_URL\": \"http://backend:8088\"\n }\n }\n }\n }\n }\n}\n"}, "d74935f72791358c9b608ef2939d28f89c2f7fbd": {"t": 1785749977.7932262, "n": 2457, "h": "0b4e89fe05118e48", "c": "{\n \"envId\": \"{{ENV_ID}}\",\n \"framework\": {\n \"name\": \"fashion-supplychain\",\n \"plugins\": {\n \"backend\": {\n \"use\": \"@cloudbase/framework-plugin-container\",\n \"inputs\": {\n \"serviceName\": \"backend\",\n \"servicePath\": \"/backend\",\n \"localPath\": \"./backend\",\n \"containerPort\": 8088,\n \"cpu\": 2,\n \"mem\": 4,\n \"minNum\": 1,\n \"maxNum\": 4,\n \"policyType\": \"cpu\",\n \"policyThreshold\": 60,\n \"envVariables\": {\n \"PORT\": \"8088\",\n \"SPRING_DATASOURCE_URL\": \"jdbc:mysql://{{MYSQL_HOST}}:3306/fashion_supplychain?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&connectTimeout=5000&socketTimeout=10000&tcpKeepAlive=true\",\n \"SPRING_DATASOURCE_USERNAME\": \"{{MYSQL_USER}}\",\n \"SPRING_DATASOURCE_PASSWORD\": \"{{MYSQL_PASSWORD}}\",\n \"SPRING_REDIS_HOST\": \"dgfbkffb.my-redis.j6jq9qgw.o7t59w4f.com\",\n \"SPRING_REDIS_PORT\": \"6379\",\n \"APP_AUTH_JWT_SECRET\": \"{{JWT_SECRET}}\",\n \"WECHAT_MP_APPID\": \"{{WECHAT_APPID}}\",\n \"WECHAT_MP_SECRET\": \"{{WECHAT_SECRET}}\",\n \"WECHAT_MINI_PROGRAM_MOCK_ENABLED\": \"false\",\n \"FLYWAY_ENABLED\": \"true\",\n \"SPRING_PROFILES_ACTIVE\": \"prod\",\n \"FASHION_DB_INITIALIZER_ENABLED\": \"false\",\n \"APP_CORS_ALLOWED_ORIGIN_PATTERNS\": \"https://*.tcloudbase.com,https://*.service.tcloudbase.com,https://*.ap-shanghai.service.tcloudbase.com\",\n \"COS_SECRET_ID\": \"{{COS_SECRET_ID}}\",\n \"COS_SECRET_KEY\": \"{{COS_SECRET_KEY}}\",\n \"COS_REGION\": \"{{COS_REGION}}\",\n \"COS_BUCKET\": \"{{COS_BUCKET}}\",\n \"SERPAPI_ENABLED\": \"true\",\n \"QWEN_VL_API_KEY\": \"{{QWEN_VL_API_KEY}}\",\n \"DOUBAO_API_KEY\": \"7a8b0840-0a70-4fc1-a765-bb10c67beb06\"\n },\n \"customLogs\": \"stdout\",\n \"healthPath\": \"/actuator/health/liveness\"\n }\n },\n \"frontend\": {\n \"use\": \"@cloudbase/framework-plugin-container\",\n \"inputs\": {\n \"serviceName\": \"frontend\",\n \"servicePath\": \"/\",\n \"localPath\": \"./frontend\",\n \"containerPort\": 80,\n \"cpu\": 0.5,\n \"mem\": 1,\n \"minNum\": 1,\n \"maxNum\": 2,\n \"envVariables\": {\n \"BACKEND_URL\": \"http://backend:8088\"\n }\n }\n }\n }\n }\n}\n"}, "029844acda5c091b5a594ea030aef7895aa831c6": {"t": 1785749978.875513, "n": 2387, "h": "4b14515dd791616a", "c": "{\n \"envId\": \"{{ENV_ID}}\",\n \"framework\": {\n \"name\": \"fashion-supplychain\",\n \"plugins\": {\n \"backend\": {\n \"use\": \"@cloudbase/framework-plugin-container\",\n \"inputs\": {\n \"serviceName\": \"backend\",\n \"servicePath\": \"/backend\",\n \"localPath\": \"./backend\",\n \"containerPort\": 8088,\n \"cpu\": 2,\n \"mem\": 4,\n \"minNum\": 1,\n \"maxNum\": 4,\n \"policyType\": \"cpu\",\n \"policyThreshold\": 60,\n \"envVariables\": {\n \"PORT\": \"8088\",\n \"SPRING_DATASOURCE_URL\": \"jdbc:mysql://{{MYSQL_HOST}}:3306/fashion_supplychain?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&connectTimeout=5000&socketTimeout=10000&tcpKeepAlive=true\",\n \"SPRING_DATASOURCE_USERNAME\": \"{{MYSQL_USER}}\",\n \"SPRING_DATASOURCE_PASSWORD\": \"{{MYSQL_PASSWORD}}\",\n \"SPRING_REDIS_HOST\": \"dgfbkffb.my-redis.j6jq9qgw.o7t59w4f.com\",\n \"SPRING_REDIS_PORT\": \"6379\",\n \"APP_AUTH_JWT_SECRET\": \"{{JWT_SECRET}}\",\n \"WECHAT_MP_APPID\": \"{{WECHAT_APPID}}\",\n \"WECHAT_MP_SECRET\": \"{{WECHAT_SECRET}}\",\n \"WECHAT_MINI_PROGRAM_MOCK_ENABLED\": \"false\",\n \"FLYWAY_ENABLED\": \"true\",\n \"SPRING_PROFILES_ACTIVE\": \"prod\",\n \"FASHION_DB_INITIALIZER_ENABLED\": \"false\",\n \"APP_CORS_ALLOWED_ORIGIN_PATTERNS\": \"https://*.tcloudbase.com,https://*.service.tcloudbase.com,https://*.ap-shanghai.service.tcloudbase.com\",\n \"COS_SECRET_ID\": \"{{COS_SECRET_ID}}\",\n \"COS_SECRET_KEY\": \"{{COS_SECRET_KEY}}\",\n \"COS_REGION\": \"{{COS_REGION}}\",\n \"COS_BUCKET\": \"{{COS_BUCKET}}\",\n \"SERPAPI_ENABLED\": \"true\",\n \"QWEN_VL_API_KEY\": \"{{QWEN_VL_API_KEY}}\"\n },\n \"customLogs\": \"stdout\",\n \"healthPath\": \"/actuator/health/liveness\"\n }\n },\n \"frontend\": {\n \"use\": \"@cloudbase/framework-plugin-container\",\n \"inputs\": {\n \"serviceName\": \"frontend\",\n \"servicePath\": \"/\",\n \"localPath\": \"./frontend\",\n \"containerPort\": 80,\n \"cpu\": 0.5,\n \"mem\": 1,\n \"minNum\": 1,\n \"maxNum\": 2,\n \"envVariables\": {\n \"BACKEND_URL\": \"http://backend:8088\"\n }\n }\n }\n }\n }\n}\n"}, "fe45a6442ae73581b550b593691ff9808801d5a5": {"t": 1785749979.981822, "n": 2333, "h": "365d68f84ced12c2", "c": "{\n \"envId\": \"{{ENV_ID}}\",\n \"framework\": {\n \"name\": \"fashion-supplychain\",\n \"plugins\": {\n \"backend\": {\n \"use\": \"@cloudbase/framework-plugin-container\",\n \"inputs\": {\n \"serviceName\": \"backend\",\n \"servicePath\": \"/backend\",\n \"localPath\": \"./backend\",\n \"containerPort\": 8088,\n \"cpu\": 2,\n \"mem\": 4,\n \"minNum\": 1,\n \"maxNum\": 4,\n \"policyType\": \"cpu\",\n \"policyThreshold\": 60,\n \"envVariables\": {\n \"PORT\": \"8088\",\n \"SPRING_DATASOURCE_URL\": \"jdbc:mysql://{{MYSQL_HOST}}:3306/fashion_supplychain?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&connectTimeout=5000&socketTimeout=10000&tcpKeepAlive=true\",\n \"SPRING_DATASOURCE_USERNAME\": \"{{MYSQL_USER}}\",\n \"SPRING_DATASOURCE_PASSWORD\": \"{{MYSQL_PASSWORD}}\",\n \"SPRING_REDIS_HOST\": \"dgfbkffb.my-redis.j6jq9qgw.o7t59w4f.com\",\n \"SPRING_REDIS_PORT\": \"6379\",\n \"APP_AUTH_JWT_SECRET\": \"{{JWT_SECRET}}\",\n \"WECHAT_MP_APPID\": \"{{WECHAT_APPID}}\",\n \"WECHAT_MP_SECRET\": \"{{WECHAT_SECRET}}\",\n \"WECHAT_MINI_PROGRAM_MOCK_ENABLED\": \"false\",\n \"FLYWAY_ENABLED\": \"true\",\n \"SPRING_PROFILES_ACTIVE\": \"prod\",\n \"FASHION_DB_INITIALIZER_ENABLED\": \"false\",\n \"APP_CORS_ALLOWED_ORIGIN_PATTERNS\": \"https://*.tcloudbase.com,https://*.service.tcloudbase.com,https://*.ap-shanghai.service.tcloudbase.com\",\n \"COS_SECRET_ID\": \"{{COS_SECRET_ID}}\",\n \"COS_SECRET_KEY\": \"{{COS_SECRET_KEY}}\",\n \"COS_REGION\": \"{{COS_REGION}}\",\n \"COS_BUCKET\": \"{{COS_BUCKET}}\",\n \"SERPAPI_ENABLED\": \"true\"\n },\n \"customLogs\": \"stdout\",\n \"healthPath\": \"/actuator/health/liveness\"\n }\n },\n \"frontend\": {\n \"use\": \"@cloudbase/framework-plugin-container\",\n \"inputs\": {\n \"serviceName\": \"frontend\",\n \"servicePath\": \"/\",\n \"localPath\": \"./frontend\",\n \"containerPort\": 80,\n \"cpu\": 0.5,\n \"mem\": 1,\n \"minNum\": 1,\n \"maxNum\": 2,\n \"envVariables\": {\n \"BACKEND_URL\": \"http://backend:8088\"\n }\n }\n }\n }\n }\n}\n"}, "8bb2076c82e219ecf8dcaa787165a03823b89348": {"t": 1785749981.0676723, "n": 2379, "h": "127277d1ca56b543", "c": "{\n \"envId\": \"{{ENV_ID}}\",\n \"framework\": {\n \"name\": \"fashion-supplychain\",\n \"plugins\": {\n \"backend\": {\n \"use\": \"@cloudbase/framework-plugin-container\",\n \"inputs\": {\n \"serviceName\": \"backend\",\n \"servicePath\": \"/backend\",\n \"localPath\": \"./backend\",\n \"containerPort\": 8088,\n \"cpu\": 2,\n \"mem\": 4,\n \"minNum\": 1,\n \"maxNum\": 4,\n \"policyType\": \"cpu\",\n \"policyThreshold\": 60,\n \"envVariables\": {\n \"PORT\": \"8088\",\n \"SPRING_DATASOURCE_URL\": \"jdbc:mysql://{{MYSQL_HOST}}:3306/fashion_supplychain?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&connectTimeout=5000&socketTimeout=10000&tcpKeepAlive=true\",\n \"SPRING_DATASOURCE_USERNAME\": \"{{MYSQL_USER}}\",\n \"SPRING_DATASOURCE_PASSWORD\": \"{{MYSQL_PASSWORD}}\",\n \"SPRING_REDIS_HOST\": \"dgfbkffb.my-redis.j6jq9qgw.o7t59w4f.com\",\n \"SPRING_REDIS_PORT\": \"6379\",\n \"APP_AUTH_JWT_SECRET\": \"{{JWT_SECRET}}\",\n \"WECHAT_MP_APPID\": \"{{WECHAT_APPID}}\",\n \"WECHAT_MP_SECRET\": \"{{WECHAT_SECRET}}\",\n \"WECHAT_MINI_PROGRAM_MOCK_ENABLED\": \"false\",\n \"FLYWAY_ENABLED\": \"true\",\n \"SPRING_PROFILES_ACTIVE\": \"prod\",\n \"FASHION_DB_INITIALIZER_ENABLED\": \"false\",\n \"APP_CORS_ALLOWED_ORIGIN_PATTERNS\": \"https://*.tcloudbase.com,https://*.service.tcloudbase.com,https://*.ap-shanghai.service.tcloudbase.com\",\n \"COS_SECRET_ID\": \"{{COS_SECRET_ID}}\",\n \"COS_SECRET_KEY\": \"{{COS_SECRET_KEY}}\",\n \"COS_REGION\": \"{{COS_REGION}}\",\n \"COS_BUCKET\": \"{{COS_BUCKET}}\",\n \"SERPAPI_KEY\": \"{{SERPAPI_KEY}}\",\n \"SERPAPI_ENABLED\": \"true\"\n },\n \"customLogs\": \"stdout\",\n \"healthPath\": \"/actuator/health/liveness\"\n }\n },\n \"frontend\": {\n \"use\": \"@cloudbase/framework-plugin-container\",\n \"inputs\": {\n \"serviceName\": \"frontend\",\n \"servicePath\": \"/\",\n \"localPath\": \"./frontend\",\n \"containerPort\": 80,\n \"cpu\": 0.5,\n \"mem\": 1,\n \"minNum\": 1,\n \"maxNum\": 2,\n \"envVariables\": {\n \"BACKEND_URL\": \"http://backend:8088\"\n }\n }\n }\n }\n }\n}\n"}, "b5a72b5c7060b6722ec3683f93a29968358e4a4b": {"t": 1785749982.104251, "n": 2294, "h": "c4158c5a28591e68", "c": "{\n \"envId\": \"{{ENV_ID}}\",\n \"framework\": {\n \"name\": \"fashion-supplychain\",\n \"plugins\": {\n \"backend\": {\n \"use\": \"@cloudbase/framework-plugin-container\",\n \"inputs\": {\n \"serviceName\": \"backend\",\n \"servicePath\": \"/backend\",\n \"localPath\": \"./backend\",\n \"containerPort\": 8088,\n \"cpu\": 2,\n \"mem\": 4,\n \"minNum\": 1,\n \"maxNum\": 4,\n \"policyType\": \"cpu\",\n \"policyThreshold\": 60,\n \"envVariables\": {\n \"PORT\": \"8088\",\n \"SPRING_DATASOURCE_URL\": \"jdbc:mysql://{{MYSQL_HOST}}:3306/fashion_supplychain?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&connectTimeout=5000&socketTimeout=10000&tcpKeepAlive=true\",\n \"SPRING_DATASOURCE_USERNAME\": \"{{MYSQL_USER}}\",\n \"SPRING_DATASOURCE_PASSWORD\": \"{{MYSQL_PASSWORD}}\",\n \"SPRING_REDIS_HOST\": \"dgfbkffb.my-redis.j6jq9qgw.o7t59w4f.com\",\n \"SPRING_REDIS_PORT\": \"6379\",\n \"APP_AUTH_JWT_SECRET\": \"{{JWT_SECRET}}\",\n \"WECHAT_MP_APPID\": \"{{WECHAT_APPID}}\",\n \"WECHAT_MP_SECRET\": \"{{WECHAT_SECRET}}\",\n \"WECHAT_MINI_PROGRAM_MOCK_ENABLED\": \"false\",\n \"FLYWAY_ENABLED\": \"true\",\n \"SPRING_PROFILES_ACTIVE\": \"prod\",\n \"FASHION_DB_INITIALIZER_ENABLED\": \"false\",\n \"APP_CORS_ALLOWED_ORIGIN_PATTERNS\": \"https://*.tcloudbase.com,https://*.service.tcloudbase.com,https://*.ap-shanghai.service.tcloudbase.com\",\n \"COS_SECRET_ID\": \"{{COS_SECRET_ID}}\",\n \"COS_SECRET_KEY\": \"{{COS_SECRET_KEY}}\",\n \"COS_REGION\": \"{{COS_REGION}}\",\n \"COS_BUCKET\": \"{{COS_BUCKET}}\"\n },\n \"customLogs\": \"stdout\",\n \"healthPath\": \"/actuator/health/liveness\"\n }\n },\n \"frontend\": {\n \"use\": \"@cloudbase/framework-plugin-container\",\n \"inputs\": {\n \"serviceName\": \"frontend\",\n \"servicePath\": \"/\",\n \"localPath\": \"./frontend\",\n \"containerPort\": 80,\n \"cpu\": 0.5,\n \"mem\": 1,\n \"minNum\": 1,\n \"maxNum\": 2,\n \"envVariables\": {\n \"BACKEND_URL\": \"http://backend:8088\"\n }\n }\n }\n }\n }\n}\n"}, "7e3b905db3795692be63c5757355ad74956e44be": {"t": 1785749983.2247484, "n": 2353, "h": "c71db57996def376", "c": "{\n \"envId\": \"{{ENV_ID}}\",\n \"framework\": {\n \"name\": \"fashion-supplychain\",\n \"plugins\": {\n \"backend\": {\n \"use\": \"@cloudbase/framework-plugin-container\",\n \"inputs\": {\n \"serviceName\": \"backend\",\n \"servicePath\": \"/backend\",\n \"localPath\": \"./backend\",\n \"containerPort\": 8088,\n \"cpu\": 2,\n \"mem\": 4,\n \"minNum\": 1,\n \"maxNum\": 4,\n \"policyType\": \"cpu\",\n \"policyThreshold\": 60,\n \"envVariables\": {\n \"PORT\": \"8088\",\n \"SPRING_DATASOURCE_URL\": \"jdbc:mysql://{{MYSQL_HOST}}:3306/fashion_supplychain?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&connectTimeout=5000&socketTimeout=10000&tcpKeepAlive=true\",\n \"SPRING_DATASOURCE_USERNAME\": \"{{MYSQL_USER}}\",\n \"SPRING_DATASOURCE_PASSWORD\": \"{{MYSQL_PASSWORD}}\",\n \"SPRING_REDIS_HOST\": \"dgfbkffb.my-redis.j6jq9qgw.o7t59w4f.com\",\n \"SPRING_REDIS_PORT\": \"6379\",\n \"SPRING_REDIS_PASSWORD\": \"{{REDIS_PASSWORD}}\",\n \"APP_AUTH_JWT_SECRET\": \"{{JWT_SECRET}}\",\n \"WECHAT_MP_APPID\": \"{{WECHAT_APPID}}\",\n \"WECHAT_MP_SECRET\": \"{{WECHAT_SECRET}}\",\n \"WECHAT_MINI_PROGRAM_MOCK_ENABLED\": \"false\",\n \"FLYWAY_ENABLED\": \"true\",\n \"SPRING_PROFILES_ACTIVE\": \"prod\",\n \"FASHION_DB_INITIALIZER_ENABLED\": \"false\",\n \"APP_CORS_ALLOWED_ORIGIN_PATTERNS\": \"https://*.tcloudbase.com,https://*.service.tcloudbase.com,https://*.ap-shanghai.service.tcloudbase.com\",\n \"COS_SECRET_ID\": \"{{COS_SECRET_ID}}\",\n \"COS_SECRET_KEY\": \"{{COS_SECRET_KEY}}\",\n \"COS_REGION\": \"{{COS_REGION}}\",\n \"COS_BUCKET\": \"{{COS_BUCKET}}\"\n },\n \"customLogs\": \"stdout\",\n \"healthPath\": \"/actuator/health/liveness\"\n }\n },\n \"frontend\": {\n \"use\": \"@cloudbase/framework-plugin-container\",\n \"inputs\": {\n \"serviceName\": \"frontend\",\n \"servicePath\": \"/\",\n \"localPath\": \"./frontend\",\n \"containerPort\": 80,\n \"cpu\": 0.5,\n \"mem\": 1,\n \"minNum\": 1,\n \"maxNum\": 2,\n \"envVariables\": {\n \"BACKEND_URL\": \"http://backend:8088\"\n }\n }\n }\n }\n }\n}\n"}, "2eb2c092df42834f13ba9748da605aacf77d750a": {"t": 1785749984.2849438, "n": 2269, "h": "bd39884af4741658", "c": "{\n \"envId\": \"{{ENV_ID}}\",\n \"framework\": {\n \"name\": \"fashion-supplychain\",\n \"plugins\": {\n \"backend\": {\n \"use\": \"@cloudbase/framework-plugin-container\",\n \"inputs\": {\n \"serviceName\": \"backend\",\n \"servicePath\": \"/backend\",\n \"localPath\": \"./backend\",\n \"containerPort\": 8088,\n \"cpu\": 2,\n \"mem\": 4,\n \"minNum\": 1,\n \"maxNum\": 4,\n \"policyType\": \"cpu\",\n \"policyThreshold\": 60,\n \"envVariables\": {\n \"PORT\": \"8088\",\n \"SPRING_DATASOURCE_URL\": \"jdbc:mysql://{{MYSQL_HOST}}:3306/fashion_supplychain?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&connectTimeout=5000&socketTimeout=10000&tcpKeepAlive=true\",\n \"SPRING_DATASOURCE_USERNAME\": \"{{MYSQL_USER}}\",\n \"SPRING_DATASOURCE_PASSWORD\": \"{{MYSQL_PASSWORD}}\",\n \"SPRING_REDIS_HOST\": \"{{REDIS_HOST}}\",\n \"SPRING_REDIS_PORT\": \"6379\",\n \"APP_AUTH_JWT_SECRET\": \"{{JWT_SECRET}}\",\n \"WECHAT_MP_APPID\": \"{{WECHAT_APPID}}\",\n \"WECHAT_MP_SECRET\": \"{{WECHAT_SECRET}}\",\n \"WECHAT_MINI_PROGRAM_MOCK_ENABLED\": \"false\",\n \"FLYWAY_ENABLED\": \"true\",\n \"SPRING_PROFILES_ACTIVE\": \"prod\",\n \"FASHION_DB_INITIALIZER_ENABLED\": \"false\",\n \"APP_CORS_ALLOWED_ORIGIN_PATTERNS\": \"https://*.tcloudbase.com,https://*.service.tcloudbase.com,https://*.ap-shanghai.service.tcloudbase.com\",\n \"COS_SECRET_ID\": \"{{COS_SECRET_ID}}\",\n \"COS_SECRET_KEY\": \"{{COS_SECRET_KEY}}\",\n \"COS_REGION\": \"{{COS_REGION}}\",\n \"COS_BUCKET\": \"{{COS_BUCKET}}\"\n },\n \"customLogs\": \"stdout\",\n \"healthPath\": \"/actuator/health/liveness\"\n }\n },\n \"frontend\": {\n \"use\": \"@cloudbase/framework-plugin-container\",\n \"inputs\": {\n \"serviceName\": \"frontend\",\n \"servicePath\": \"/\",\n \"localPath\": \"./frontend\",\n \"containerPort\": 80,\n \"cpu\": 0.5,\n \"mem\": 1,\n \"minNum\": 1,\n \"maxNum\": 2,\n \"envVariables\": {\n \"BACKEND_URL\": \"http://backend:8088\"\n }\n }\n }\n }\n }\n}\n"}, "ad51ee6c6a3f767ca4208e39dffd66fb54532203": {"t": 1785749985.343798, "n": 2211, "h": "67574c62768cce8f", "c": "{\n \"envId\": \"{{ENV_ID}}\",\n \"framework\": {\n \"name\": \"fashion-supplychain\",\n \"plugins\": {\n \"backend\": {\n \"use\": \"@cloudbase/framework-plugin-container\",\n \"inputs\": {\n \"serviceName\": \"backend\",\n \"servicePath\": \"/backend\",\n \"localPath\": \"./backend\",\n \"containerPort\": 8088,\n \"cpu\": 2,\n \"mem\": 4,\n \"minNum\": 1,\n \"maxNum\": 4,\n \"policyType\": \"cpu\",\n \"policyThreshold\": 60,\n \"envVariables\": {\n \"PORT\": \"8088\",\n \"SPRING_DATASOURCE_URL\": \"jdbc:mysql://{{MYSQL_HOST}}:3306/fashion_supplychain?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true\",\n \"SPRING_DATASOURCE_USERNAME\": \"{{MYSQL_USER}}\",\n \"SPRING_DATASOURCE_PASSWORD\": \"{{MYSQL_PASSWORD}}\",\n \"SPRING_REDIS_HOST\": \"{{REDIS_HOST}}\",\n \"SPRING_REDIS_PORT\": \"6379\",\n \"APP_AUTH_JWT_SECRET\": \"{{JWT_SECRET}}\",\n \"WECHAT_MP_APPID\": \"{{WECHAT_APPID}}\",\n \"WECHAT_MP_SECRET\": \"{{WECHAT_SECRET}}\",\n \"WECHAT_MINI_PROGRAM_MOCK_ENABLED\": \"false\",\n \"FLYWAY_ENABLED\": \"true\",\n \"SPRING_PROFILES_ACTIVE\": \"prod\",\n \"FASHION_DB_INITIALIZER_ENABLED\": \"false\",\n \"APP_CORS_ALLOWED_ORIGIN_PATTERNS\": \"https://*.tcloudbase.com,https://*.service.tcloudbase.com,https://*.ap-shanghai.service.tcloudbase.com\",\n \"COS_SECRET_ID\": \"{{COS_SECRET_ID}}\",\n \"COS_SECRET_KEY\": \"{{COS_SECRET_KEY}}\",\n \"COS_REGION\": \"{{COS_REGION}}\",\n \"COS_BUCKET\": \"{{COS_BUCKET}}\"\n },\n \"customLogs\": \"stdout\",\n \"healthPath\": \"/actuator/health/liveness\"\n }\n },\n \"frontend\": {\n \"use\": \"@cloudbase/framework-plugin-container\",\n \"inputs\": {\n \"serviceName\": \"frontend\",\n \"servicePath\": \"/\",\n \"localPath\": \"./frontend\",\n \"containerPort\": 80,\n \"cpu\": 0.5,\n \"mem\": 1,\n \"minNum\": 1,\n \"maxNum\": 2,\n \"envVariables\": {\n \"BACKEND_URL\": \"http://backend:8088\"\n }\n }\n }\n }\n }\n}\n"}, "601715e5cbc4c33900ddcd2a3df14884eaf406b0": {"t": 1785749986.407472, "n": 2165, "h": "8cf96518164031d4", "c": "{\n \"envId\": \"{{ENV_ID}}\",\n \"framework\": {\n \"name\": \"fashion-supplychain\",\n \"plugins\": {\n \"backend\": {\n \"use\": \"@cloudbase/framework-plugin-container\",\n \"inputs\": {\n \"serviceName\": \"backend\",\n \"servicePath\": \"/backend\",\n \"localPath\": \"./backend\",\n \"containerPort\": 8088,\n \"cpu\": 2,\n \"mem\": 4,\n \"minNum\": 1,\n \"maxNum\": 4,\n \"policyType\": \"cpu\",\n \"policyThreshold\": 60,\n \"envVariables\": {\n \"PORT\": \"8088\",\n \"SPRING_DATASOURCE_URL\": \"jdbc:mysql://{{MYSQL_HOST}}:3306/fashion_supplychain?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true\",\n \"SPRING_DATASOURCE_USERNAME\": \"{{MYSQL_USER}}\",\n \"SPRING_DATASOURCE_PASSWORD\": \"{{MYSQL_PASSWORD}}\",\n \"SPRING_REDIS_HOST\": \"{{REDIS_HOST}}\",\n \"SPRING_REDIS_PORT\": \"6379\",\n \"APP_AUTH_JWT_SECRET\": \"{{JWT_SECRET}}\",\n \"WECHAT_MP_APPID\": \"{{WECHAT_APPID}}\",\n \"WECHAT_MP_SECRET\": \"{{WECHAT_SECRET}}\",\n \"WECHAT_MINI_PROGRAM_MOCK_ENABLED\": \"false\",\n \"FLYWAY_ENABLED\": \"true\",\n \"FASHION_DB_INITIALIZER_ENABLED\": \"false\",\n \"APP_CORS_ALLOWED_ORIGIN_PATTERNS\": \"https://*.tcloudbase.com,https://*.service.tcloudbase.com,https://*.ap-shanghai.service.tcloudbase.com\",\n \"COS_SECRET_ID\": \"{{COS_SECRET_ID}}\",\n \"COS_SECRET_KEY\": \"{{COS_SECRET_KEY}}\",\n \"COS_REGION\": \"{{COS_REGION}}\",\n \"COS_BUCKET\": \"{{COS_BUCKET}}\"\n },\n \"customLogs\": \"stdout\",\n \"healthPath\": \"/actuator/health/liveness\"\n }\n },\n \"frontend\": {\n \"use\": \"@cloudbase/framework-plugin-container\",\n \"inputs\": {\n \"serviceName\": \"frontend\",\n \"servicePath\": \"/\",\n \"localPath\": \"./frontend\",\n \"containerPort\": 80,\n \"cpu\": 0.5,\n \"mem\": 1,\n \"minNum\": 1,\n \"maxNum\": 2,\n \"envVariables\": {\n \"BACKEND_URL\": \"http://backend:8088\"\n }\n }\n }\n }\n }\n}\n"}, "40ac69d8bb188db9ff3a7e76bcbef748accb06a1": {"t": 1785749987.5192716, "n": 2156, "h": "2bfc268bdbb7fd0f", "c": "{\n \"envId\": \"{{ENV_ID}}\",\n \"framework\": {\n \"name\": \"fashion-supplychain\",\n \"plugins\": {\n \"backend\": {\n \"use\": \"@cloudbase/framework-plugin-container\",\n \"inputs\": {\n \"serviceName\": \"backend\",\n \"servicePath\": \"/backend\",\n \"localPath\": \"./backend\",\n \"containerPort\": 8088,\n \"cpu\": 2,\n \"mem\": 4,\n \"minNum\": 1,\n \"maxNum\": 4,\n \"policyType\": \"cpu\",\n \"policyThreshold\": 60,\n \"envVariables\": {\n \"PORT\": \"8088\",\n \"SPRING_DATASOURCE_URL\": \"jdbc:mysql://{{MYSQL_HOST}}:3306/fashion_supplychain?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true\",\n \"SPRING_DATASOURCE_USERNAME\": \"{{MYSQL_USER}}\",\n \"SPRING_DATASOURCE_PASSWORD\": \"{{MYSQL_PASSWORD}}\",\n \"SPRING_REDIS_HOST\": \"{{REDIS_HOST}}\",\n \"SPRING_REDIS_PORT\": \"6379\",\n \"APP_AUTH_JWT_SECRET\": \"{{JWT_SECRET}}\",\n \"WECHAT_MP_APPID\": \"{{WECHAT_APPID}}\",\n \"WECHAT_MP_SECRET\": \"{{WECHAT_SECRET}}\",\n \"WECHAT_MINI_PROGRAM_MOCK_ENABLED\": \"false\",\n \"FLYWAY_ENABLED\": \"true\",\n \"FASHION_DB_INITIALIZER_ENABLED\": \"false\",\n \"APP_CORS_ALLOWED_ORIGIN_PATTERNS\": \"https://*.tcloudbase.com,https://*.service.tcloudbase.com,https://*.ap-shanghai.service.tcloudbase.com\",\n \"COS_SECRET_ID\": \"{{COS_SECRET_ID}}\",\n \"COS_SECRET_KEY\": \"{{COS_SECRET_KEY}}\",\n \"COS_REGION\": \"{{COS_REGION}}\",\n \"COS_BUCKET\": \"{{COS_BUCKET}}\"\n },\n \"customLogs\": \"stdout\",\n \"healthPath\": \"/actuator/health\"\n }\n },\n \"frontend\": {\n \"use\": \"@cloudbase/framework-plugin-container\",\n \"inputs\": {\n \"serviceName\": \"frontend\",\n \"servicePath\": \"/\",\n \"localPath\": \"./frontend\",\n \"containerPort\": 80,\n \"cpu\": 0.5,\n \"mem\": 1,\n \"minNum\": 1,\n \"maxNum\": 2,\n \"envVariables\": {\n \"BACKEND_URL\": \"http://backend:8088\"\n }\n }\n }\n }\n }\n}\n"}, "270bc97bb3d0f62e5eded3e08752b3677c7c64af": {"t": 1785749988.746784, "n": 1966, "h": "c22de3d22a3029a3", "c": "{\n \"envId\": \"{{ENV_ID}}\",\n \"framework\": {\n \"name\": \"fashion-supplychain\",\n \"plugins\": {\n \"backend\": {\n \"use\": \"@cloudbase/framework-plugin-container\",\n \"inputs\": {\n \"serviceName\": \"backend\",\n \"servicePath\": \"/backend\",\n \"localPath\": \"./backend\",\n \"containerPort\": 8088,\n \"cpu\": 2,\n \"mem\": 4,\n \"minNum\": 1,\n \"maxNum\": 4,\n \"policyType\": \"cpu\",\n \"policyThreshold\": 60,\n \"envVariables\": {\n \"PORT\": \"8088\",\n \"SPRING_DATASOURCE_URL\": \"jdbc:mysql://{{MYSQL_HOST}}:3306/fashion_supplychain?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true\",\n \"SPRING_DATASOURCE_USERNAME\": \"{{MYSQL_USER}}\",\n \"SPRING_DATASOURCE_PASSWORD\": \"{{MYSQL_PASSWORD}}\",\n \"SPRING_REDIS_HOST\": \"{{REDIS_HOST}}\",\n \"SPRING_REDIS_PORT\": \"6379\",\n \"APP_AUTH_JWT_SECRET\": \"{{JWT_SECRET}}\",\n \"WECHAT_MP_APPID\": \"{{WECHAT_APPID}}\",\n \"WECHAT_MP_SECRET\": \"{{WECHAT_SECRET}}\",\n \"WECHAT_MINI_PROGRAM_MOCK_ENABLED\": \"false\",\n \"FLYWAY_ENABLED\": \"true\",\n \"FASHION_DB_INITIALIZER_ENABLED\": \"false\",\n \"APP_CORS_ALLOWED_ORIGIN_PATTERNS\": \"https://*.tcloudbase.com,https://*.service.tcloudbase.com,https://*.ap-shanghai.service.tcloudbase.com\"\n },\n \"customLogs\": \"stdout\",\n \"healthPath\": \"/actuator/health\"\n }\n },\n \"frontend\": {\n \"use\": \"@cloudbase/framework-plugin-container\",\n \"inputs\": {\n \"serviceName\": \"frontend\",\n \"servicePath\": \"/\",\n \"localPath\": \"./frontend\",\n \"containerPort\": 80,\n \"cpu\": 0.5,\n \"mem\": 1,\n \"minNum\": 1,\n \"maxNum\": 2,\n \"envVariables\": {\n \"BACKEND_URL\": \"http://backend:8088\"\n }\n }\n }\n }\n }\n}\n"}}, "backend/src/main/java/com/fashion/supplychain/intelligence/gateway/AiInferenceRouter.java": {"92136382831779e22f23b72a08f4e137bfa17dd3": {"t": 1785745687.3106582, "n": 20750, "h": "31e72f3762438f68", "c": "package com.fashion.supplychain.intelligence.gateway;\n\nimport com.fashion.supplychain.intelligence.agent.AiMessage;\nimport com.fashion.supplychain.intelligence.agent.AiTool;\nimport com.fashion.supplychain.intelligence.dto.IntelligenceInferenceResult;\nimport com.fashion.supplychain.intelligence.orchestration.AiCostTrackingOrchestrator;\nimport com.fashion.supplychain.intelligence.orchestration.AiOperationAuditOrchestrator;\nimport com.fashion.supplychain.intelligence.service.CostExplosionGuard;\nimport com.fashion.supplychain.intelligence.service.ModelSelectionRouter;\nimport com.fashion.supplychain.common.UserContext;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.atomic.AtomicInteger;\nimport java.util.concurrent.atomic.AtomicLong;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.context.annotation.Primary;\nimport org.springframework.stereotype.Service;\nimport org.springframework.context.annotation.Lazy;\n\n@Slf4j\n@Service\n@Lazy\n@Primary\npublic class AiInferenceRouter implements AiInferenceGateway {\n\n private static final int CIRCUIT_OPEN_THRESHOLD = 3;\n private static final long CIRCUIT_RESET_MS = 30_000L;\n\n @Value(\"${ai.gateway.routing-strategy:failover}\")\n private String routingStrategy;\n\n /**\n * 【P1-10修复】启动时校验 DEEPSEEK_API_KEY 非空。\n *\n *
原行为:key 为空时启动正常,但所有 AI 功能(对话/视觉/工具调用)都会失败,\n * 用户排查困难(错误散落在各 Service 中)。\n *\n *
现行为:\n *
受影响功能:DeepSeek 对话、Spring AI 工具调用、AiAdvisor 日常建议、\n * SelfCritic 自评分、EvolutionPipeline 自进化、Qdrant 向量检索(embedding 降级)。\n */\n @Value(\"${ai.api-key.fail-fast-on-empty:false}\")\n private boolean failFastOnEmptyApiKey;\n\n @Value(\"${ai.deepseek.api-key:${DEEPSEEK_API_KEY:}}\")\n private String deepseekApiKey;\n\n @Autowired\n private LegacyInferenceAdapter legacyAdapter;\n\n @Autowired(required = false)\n private SpringAiInferenceAdapter springAiAdapter;\n\n @Autowired(required = false)\n private AiCostTrackingOrchestrator aiCostTrackingOrchestrator;\n\n @Autowired(required = false)\n private AiOperationAuditOrchestrator aiOperationAuditOrchestrator;\n\n @Autowired(required = false)\n private ModelConsortiumRouter modelConsortiumRouter;\n\n @Autowired(required = false)\n private ModelSelectionRouter modelSelectionRouter;\n\n @Autowired(required = false)\n private CostExplosionGuard costExplosionGuard;\n\n private final AtomicInteger springAiConsecutiveFailures = new AtomicInteger(0);\n private final AtomicLong springAiCircuitOpenSince = new AtomicLong(0);\n\n /**\n * 【P1-10修复】启动时校验 DEEPSEEK_API_KEY。\n * 默认仅告警不阻止启动,开启 fail-fast-on-empty=true 可强制阻止启动。\n */\n @jakarta.annotation.PostConstruct\n public void validateApiKey() {\n boolean keyMissing = deepseekApiKey == null || deepseekApiKey.isBlank();\n if (!keyMissing) {\n log.info(\"[AiInferenceRouter] DEEPSEEK_API_KEY 已配置(长度={}),AI 功能正常可用\",\n deepseekApiKey.length());\n return;\n }\n\n String affectedFeatures = \"DeepSeek 对话 / Spring AI 工具调用 / AiAdvisor 日常建议 / \"\n + \"SelfCritic 自评分 / EvolutionPipeline 自进化 / Qdrant 向量检索(embedding 降级)\";\n\n if (failFastOnEmptyApiKey) {\n String msg = \"[AiInferenceRouter] DEEPSEEK_API_KEY 为空且 ai.api-key.fail-fast-on-empty=true,\"\n + \"应用启动被阻止。受影响功能:\" + affectedFeatures\n + \"。解决:设置环境变量 DEEPSEEK_API_KEY 或在 yml 配置 ai.deepseek.api-key\";\n log.error(msg);\n throw new IllegalStateException(msg);\n }\n\n log.error(\"[AiInferenceRouter] ⚠️ DEEPSEEK_API_KEY 为空!AI 功能将全部失败。受影响功能:{}\", affectedFeatures);\n log.error(\"[AiInferenceRouter] 解决:设置环境变量 DEEPSEEK_API_KEY 或在 yml 配置 ai.deepseek.api-key\");\n log.error(\"[AiInferenceRouter] 如需启动失败而非降级,设置 ai.api-key.fail-fast-on-empty=true\");\n }\n\n @Override\n public IntelligenceInferenceResult chat(String scene, String systemPrompt, String userMessage) {\n AiInferenceGateway gateway = resolveGateway(scene);\n IntelligenceInferenceResult result = gateway.chat(scene, systemPrompt, userMessage);\n trackSpringAiHealth(result);\n if (isSpringAiCircuitOpen() && !\"legacy\".equals(gateway.getProviderName())) {\n log.info(\"[AiInferenceRouter] Spring AI 熔断触发,降级到 legacy 重试 scene={}\", scene);\n result = legacyAdapter.chat(scene, systemPrompt, userMessage);\n result.setFallbackUsed(true);\n }\n recordCostAndAudit(scene, result);\n return result;\n }\n\n @Override\n public IntelligenceInferenceResult chat(String scene, List 路由到具体的底层 gateway(SpringAiInferenceAdapter / LegacyInferenceAdapter),\n * 由底层 adapter 真正实现 per-call model 覆盖。\n * 同时记录 LLM 调用成本到 CostExplosionGuard(用于成本爆炸防御)。\n */\n @Override\n public String chatWithModel(String prompt, Long tenantId, Long userId, String modelId) {\n long start = System.currentTimeMillis();\n AiInferenceGateway gateway = resolveGateway(\"model-selection\");\n String content;\n try {\n content = gateway.chatWithModel(prompt, tenantId, userId, modelId);\n } catch (Exception e) {\n log.warn(\"[AiInferenceRouter] chatWithModel failed, fallback to legacy: {}\", e.getMessage());\n content = legacyAdapter.chatWithModel(prompt, tenantId, userId, modelId);\n }\n long elapsed = System.currentTimeMillis() - start;\n\n // 记录 LLM 调用成本到 CostExplosionGuard(用于成本爆炸防御 + 重复检测)\n if (costExplosionGuard != null && tenantId != null) {\n try {\n // 用 \"llm_call\" 作为 toolName,prompt 哈希作为 paramsHash\n String paramsHash = costExplosionGuard.hashParams(prompt);\n costExplosionGuard.recordToolCall(tenantId, \"llm_call\", paramsHash,\n content != null ? content : \"\");\n log.debug(\"[AiInferenceRouter] LLM 调用成本已记录 tenantId={} modelId={} elapsed={}ms\",\n tenantId, modelId, elapsed);\n } catch (Exception e) {\n log.debug(\"[AiInferenceRouter] 记录 LLM 调用成本失败(不影响主流程): {}\", e.getMessage());\n }\n }\n\n log.info(\"[AiInferenceRouter] chatWithModel: gateway={} modelId={} elapsed={}ms contentLen={}\",\n gateway.getProviderName(), modelId != null ? modelId : \"default\",\n elapsed, content != null ? content.length() : 0);\n return content;\n }\n\n /**\n * Per-call 模型选择调用(借鉴 Claude Agent SDK per-call model selection)。\n *\n * 根据用户消息复杂度、预估工具调用数、多域标识自动选择模型分级:\n * ★ 接入点3:真正调用 gateway.chatWithModel 传递 modelId,而非仅用 scene 路由。\n *\n * @param prompt 完整提示词\n * @param userMessage 用户原始消息(用于复杂度评估)\n * @param estimatedToolCalls 预估工具调用数\n * @param isMultiDomain 是否多域任务\n * @return LLM 回答文本\n */\n public String chatWithModelSelection(String prompt, String userMessage,\n int estimatedToolCalls, boolean isMultiDomain) {\n if (modelSelectionRouter == null || !modelSelectionRouter.isEnabled()) {\n return chatSimple(prompt);\n }\n ModelSelectionRouter.ModelTier tier = modelSelectionRouter.selectModel(\n userMessage, estimatedToolCalls, isMultiDomain);\n String modelId = modelSelectionRouter.resolveModelId(tier);\n log.info(\"[AiInferenceRouter] per-call model selection: tier={} modelId={}\", tier, modelId);\n\n // 从 UserContext 获取 tenantId/userId(多租户隔离 + 成本追踪)\n Long tenantId = UserContext.tenantId();\n String userIdStr = UserContext.userId();\n\n // ★ 真正调用 gateway.chatWithModel 传递 modelId(接入点3 核心)\n try {\n return chatWithModel(prompt, tenantId, userIdStr != null ? userIdStr.hashCode() : 0L, modelId);\n } catch (Exception e) {\n log.warn(\"[AiInferenceRouter] chatWithModelSelection failed, fallback to chatSimple: {}\", e.getMessage());\n return chatSimple(prompt);\n }\n }\n\n /**\n * 强制使用 PREMIUM 模型调用(高风险场景,如复杂排产优化)。\n *\n * @param prompt 完整提示词\n * @return LLM 回答文本\n */\n public String chatPremium(String prompt) {\n if (modelSelectionRouter == null || !modelSelectionRouter.isEnabled()) {\n return chatSimple(prompt);\n }\n String modelId = modelSelectionRouter.resolveModelId(ModelSelectionRouter.ModelTier.PREMIUM);\n log.info(\"[AiInferenceRouter] forced premium model: modelId={}\", modelId);\n IntelligenceInferenceResult result = chat(\"premium-reasoning\", null, prompt);\n return result != null ? result.getContent() : \"\";\n }\n\n /** 根据模型分级映射到 scene(用于网关路由 hint) */\n private String resolveSceneByTier(ModelSelectionRouter.ModelTier tier) {\n return switch (tier) {\n case ECONOMY -> \"economy-query\";\n case STANDARD -> \"reflection\";\n case PREMIUM -> \"premium-reasoning\";\n };\n }\n\n /**\n * 获取模型推荐参数(temperature、maxTokens、timeout)。\n */\n public ModelConsortiumRouter.ModelParams recommendModelParams(String userMessage, boolean hasImage, int toolCount) {\n if (modelConsortiumRouter != null) {\n ModelConsortiumRouter.Complexity complexity = modelConsortiumRouter.classifyComplexity(\n userMessage, hasImage, toolCount);\n return modelConsortiumRouter.getModelParams(complexity);\n }\n return new ModelConsortiumRouter.ModelParams(0.5, 1024, 30);\n }\n\n private boolean hasText(String value) {\n return value != null && !value.trim().isEmpty();\n }\n}\n"}, "92b7fd9578d448a1537b900a5ee7fb0da73abc84": {"t": 1785749954.3668191, "n": 20750, "h": "31e72f3762438f68", "c": "package com.fashion.supplychain.intelligence.gateway;\n\nimport com.fashion.supplychain.intelligence.agent.AiMessage;\nimport com.fashion.supplychain.intelligence.agent.AiTool;\nimport com.fashion.supplychain.intelligence.dto.IntelligenceInferenceResult;\nimport com.fashion.supplychain.intelligence.orchestration.AiCostTrackingOrchestrator;\nimport com.fashion.supplychain.intelligence.orchestration.AiOperationAuditOrchestrator;\nimport com.fashion.supplychain.intelligence.service.CostExplosionGuard;\nimport com.fashion.supplychain.intelligence.service.ModelSelectionRouter;\nimport com.fashion.supplychain.common.UserContext;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.atomic.AtomicInteger;\nimport java.util.concurrent.atomic.AtomicLong;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.context.annotation.Primary;\nimport org.springframework.stereotype.Service;\nimport org.springframework.context.annotation.Lazy;\n\n@Slf4j\n@Service\n@Lazy\n@Primary\npublic class AiInferenceRouter implements AiInferenceGateway {\n\n private static final int CIRCUIT_OPEN_THRESHOLD = 3;\n private static final long CIRCUIT_RESET_MS = 30_000L;\n\n @Value(\"${ai.gateway.routing-strategy:failover}\")\n private String routingStrategy;\n\n /**\n * 【P1-10修复】启动时校验 DEEPSEEK_API_KEY 非空。\n *\n * 原行为:key 为空时启动正常,但所有 AI 功能(对话/视觉/工具调用)都会失败,\n * 用户排查困难(错误散落在各 Service 中)。\n *\n * 现行为:\n * 受影响功能:DeepSeek 对话、Spring AI 工具调用、AiAdvisor 日常建议、\n * SelfCritic 自评分、EvolutionPipeline 自进化、Qdrant 向量检索(embedding 降级)。\n */\n @Value(\"${ai.api-key.fail-fast-on-empty:false}\")\n private boolean failFastOnEmptyApiKey;\n\n @Value(\"${ai.deepseek.api-key:${DEEPSEEK_API_KEY:}}\")\n private String deepseekApiKey;\n\n @Autowired\n private LegacyInferenceAdapter legacyAdapter;\n\n @Autowired(required = false)\n private SpringAiInferenceAdapter springAiAdapter;\n\n @Autowired(required = false)\n private AiCostTrackingOrchestrator aiCostTrackingOrchestrator;\n\n @Autowired(required = false)\n private AiOperationAuditOrchestrator aiOperationAuditOrchestrator;\n\n @Autowired(required = false)\n private ModelConsortiumRouter modelConsortiumRouter;\n\n @Autowired(required = false)\n private ModelSelectionRouter modelSelectionRouter;\n\n @Autowired(required = false)\n private CostExplosionGuard costExplosionGuard;\n\n private final AtomicInteger springAiConsecutiveFailures = new AtomicInteger(0);\n private final AtomicLong springAiCircuitOpenSince = new AtomicLong(0);\n\n /**\n * 【P1-10修复】启动时校验 DEEPSEEK_API_KEY。\n * 默认仅告警不阻止启动,开启 fail-fast-on-empty=true 可强制阻止启动。\n */\n @jakarta.annotation.PostConstruct\n public void validateApiKey() {\n boolean keyMissing = deepseekApiKey == null || deepseekApiKey.isBlank();\n if (!keyMissing) {\n log.info(\"[AiInferenceRouter] DEEPSEEK_API_KEY 已配置(长度={}),AI 功能正常可用\",\n deepseekApiKey.length());\n return;\n }\n\n String affectedFeatures = \"DeepSeek 对话 / Spring AI 工具调用 / AiAdvisor 日常建议 / \"\n + \"SelfCritic 自评分 / EvolutionPipeline 自进化 / Qdrant 向量检索(embedding 降级)\";\n\n if (failFastOnEmptyApiKey) {\n String msg = \"[AiInferenceRouter] DEEPSEEK_API_KEY 为空且 ai.api-key.fail-fast-on-empty=true,\"\n + \"应用启动被阻止。受影响功能:\" + affectedFeatures\n + \"。解决:设置环境变量 DEEPSEEK_API_KEY 或在 yml 配置 ai.deepseek.api-key\";\n log.error(msg);\n throw new IllegalStateException(msg);\n }\n\n log.error(\"[AiInferenceRouter] ⚠️ DEEPSEEK_API_KEY 为空!AI 功能将全部失败。受影响功能:{}\", affectedFeatures);\n log.error(\"[AiInferenceRouter] 解决:设置环境变量 DEEPSEEK_API_KEY 或在 yml 配置 ai.deepseek.api-key\");\n log.error(\"[AiInferenceRouter] 如需启动失败而非降级,设置 ai.api-key.fail-fast-on-empty=true\");\n }\n\n @Override\n public IntelligenceInferenceResult chat(String scene, String systemPrompt, String userMessage) {\n AiInferenceGateway gateway = resolveGateway(scene);\n IntelligenceInferenceResult result = gateway.chat(scene, systemPrompt, userMessage);\n trackSpringAiHealth(result);\n if (isSpringAiCircuitOpen() && !\"legacy\".equals(gateway.getProviderName())) {\n log.info(\"[AiInferenceRouter] Spring AI 熔断触发,降级到 legacy 重试 scene={}\", scene);\n result = legacyAdapter.chat(scene, systemPrompt, userMessage);\n result.setFallbackUsed(true);\n }\n recordCostAndAudit(scene, result);\n return result;\n }\n\n @Override\n public IntelligenceInferenceResult chat(String scene, List 路由到具体的底层 gateway(SpringAiInferenceAdapter / LegacyInferenceAdapter),\n * 由底层 adapter 真正实现 per-call model 覆盖。\n * 同时记录 LLM 调用成本到 CostExplosionGuard(用于成本爆炸防御)。\n */\n @Override\n public String chatWithModel(String prompt, Long tenantId, Long userId, String modelId) {\n long start = System.currentTimeMillis();\n AiInferenceGateway gateway = resolveGateway(\"model-selection\");\n String content;\n try {\n content = gateway.chatWithModel(prompt, tenantId, userId, modelId);\n } catch (Exception e) {\n log.warn(\"[AiInferenceRouter] chatWithModel failed, fallback to legacy: {}\", e.getMessage());\n content = legacyAdapter.chatWithModel(prompt, tenantId, userId, modelId);\n }\n long elapsed = System.currentTimeMillis() - start;\n\n // 记录 LLM 调用成本到 CostExplosionGuard(用于成本爆炸防御 + 重复检测)\n if (costExplosionGuard != null && tenantId != null) {\n try {\n // 用 \"llm_call\" 作为 toolName,prompt 哈希作为 paramsHash\n String paramsHash = costExplosionGuard.hashParams(prompt);\n costExplosionGuard.recordToolCall(tenantId, \"llm_call\", paramsHash,\n content != null ? content : \"\");\n log.debug(\"[AiInferenceRouter] LLM 调用成本已记录 tenantId={} modelId={} elapsed={}ms\",\n tenantId, modelId, elapsed);\n } catch (Exception e) {\n log.debug(\"[AiInferenceRouter] 记录 LLM 调用成本失败(不影响主流程): {}\", e.getMessage());\n }\n }\n\n log.info(\"[AiInferenceRouter] chatWithModel: gateway={} modelId={} elapsed={}ms contentLen={}\",\n gateway.getProviderName(), modelId != null ? modelId : \"default\",\n elapsed, content != null ? content.length() : 0);\n return content;\n }\n\n /**\n * Per-call 模型选择调用(借鉴 Claude Agent SDK per-call model selection)。\n *\n * 根据用户消息复杂度、预估工具调用数、多域标识自动选择模型分级:\n * ★ 接入点3:真正调用 gateway.chatWithModel 传递 modelId,而非仅用 scene 路由。\n *\n * @param prompt 完整提示词\n * @param userMessage 用户原始消息(用于复杂度评估)\n * @param estimatedToolCalls 预估工具调用数\n * @param isMultiDomain 是否多域任务\n * @return LLM 回答文本\n */\n public String chatWithModelSelection(String prompt, String userMessage,\n int estimatedToolCalls, boolean isMultiDomain) {\n if (modelSelectionRouter == null || !modelSelectionRouter.isEnabled()) {\n return chatSimple(prompt);\n }\n ModelSelectionRouter.ModelTier tier = modelSelectionRouter.selectModel(\n userMessage, estimatedToolCalls, isMultiDomain);\n String modelId = modelSelectionRouter.resolveModelId(tier);\n log.info(\"[AiInferenceRouter] per-call model selection: tier={} modelId={}\", tier, modelId);\n\n // 从 UserContext 获取 tenantId/userId(多租户隔离 + 成本追踪)\n Long tenantId = UserContext.tenantId();\n String userIdStr = UserContext.userId();\n\n // ★ 真正调用 gateway.chatWithModel 传递 modelId(接入点3 核心)\n try {\n return chatWithModel(prompt, tenantId, userIdStr != null ? userIdStr.hashCode() : 0L, modelId);\n } catch (Exception e) {\n log.warn(\"[AiInferenceRouter] chatWithModelSelection failed, fallback to chatSimple: {}\", e.getMessage());\n return chatSimple(prompt);\n }\n }\n\n /**\n * 强制使用 PREMIUM 模型调用(高风险场景,如复杂排产优化)。\n *\n * @param prompt 完整提示词\n * @return LLM 回答文本\n */\n public String chatPremium(String prompt) {\n if (modelSelectionRouter == null || !modelSelectionRouter.isEnabled()) {\n return chatSimple(prompt);\n }\n String modelId = modelSelectionRouter.resolveModelId(ModelSelectionRouter.ModelTier.PREMIUM);\n log.info(\"[AiInferenceRouter] forced premium model: modelId={}\", modelId);\n IntelligenceInferenceResult result = chat(\"premium-reasoning\", null, prompt);\n return result != null ? result.getContent() : \"\";\n }\n\n /** 根据模型分级映射到 scene(用于网关路由 hint) */\n private String resolveSceneByTier(ModelSelectionRouter.ModelTier tier) {\n return switch (tier) {\n case ECONOMY -> \"economy-query\";\n case STANDARD -> \"reflection\";\n case PREMIUM -> \"premium-reasoning\";\n };\n }\n\n /**\n * 获取模型推荐参数(temperature、maxTokens、timeout)。\n */\n public ModelConsortiumRouter.ModelParams recommendModelParams(String userMessage, boolean hasImage, int toolCount) {\n if (modelConsortiumRouter != null) {\n ModelConsortiumRouter.Complexity complexity = modelConsortiumRouter.classifyComplexity(\n userMessage, hasImage, toolCount);\n return modelConsortiumRouter.getModelParams(complexity);\n }\n return new ModelConsortiumRouter.ModelParams(0.5, 1024, 30);\n }\n\n private boolean hasText(String value) {\n return value != null && !value.trim().isEmpty();\n }\n}\n"}, "6fe4a586649c80cfffa2ec60afd3daca815705ed": {"t": 1785749955.5275457, "n": 18783, "h": "d1c9ce85273937de", "c": "package com.fashion.supplychain.intelligence.gateway;\n\nimport com.fashion.supplychain.intelligence.agent.AiMessage;\nimport com.fashion.supplychain.intelligence.agent.AiTool;\nimport com.fashion.supplychain.intelligence.dto.IntelligenceInferenceResult;\nimport com.fashion.supplychain.intelligence.orchestration.AiCostTrackingOrchestrator;\nimport com.fashion.supplychain.intelligence.orchestration.AiOperationAuditOrchestrator;\nimport com.fashion.supplychain.intelligence.service.CostExplosionGuard;\nimport com.fashion.supplychain.intelligence.service.ModelSelectionRouter;\nimport com.fashion.supplychain.common.UserContext;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.atomic.AtomicInteger;\nimport java.util.concurrent.atomic.AtomicLong;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.context.annotation.Primary;\nimport org.springframework.stereotype.Service;\nimport org.springframework.context.annotation.Lazy;\n\n@Slf4j\n@Service\n@Lazy\n@Primary\npublic class AiInferenceRouter implements AiInferenceGateway {\n\n private static final int CIRCUIT_OPEN_THRESHOLD = 3;\n private static final long CIRCUIT_RESET_MS = 30_000L;\n\n @Value(\"${ai.gateway.routing-strategy:failover}\")\n private String routingStrategy;\n\n @Autowired\n private LegacyInferenceAdapter legacyAdapter;\n\n @Autowired(required = false)\n private SpringAiInferenceAdapter springAiAdapter;\n\n @Autowired(required = false)\n private AiCostTrackingOrchestrator aiCostTrackingOrchestrator;\n\n @Autowired(required = false)\n private AiOperationAuditOrchestrator aiOperationAuditOrchestrator;\n\n @Autowired(required = false)\n private ModelConsortiumRouter modelConsortiumRouter;\n\n @Autowired(required = false)\n private ModelSelectionRouter modelSelectionRouter;\n\n @Autowired(required = false)\n private CostExplosionGuard costExplosionGuard;\n\n private final AtomicInteger springAiConsecutiveFailures = new AtomicInteger(0);\n private final AtomicLong springAiCircuitOpenSince = new AtomicLong(0);\n\n @Override\n public IntelligenceInferenceResult chat(String scene, String systemPrompt, String userMessage) {\n AiInferenceGateway gateway = resolveGateway(scene);\n IntelligenceInferenceResult result = gateway.chat(scene, systemPrompt, userMessage);\n trackSpringAiHealth(result);\n if (isSpringAiCircuitOpen() && !\"legacy\".equals(gateway.getProviderName())) {\n log.info(\"[AiInferenceRouter] Spring AI 熔断触发,降级到 legacy 重试 scene={}\", scene);\n result = legacyAdapter.chat(scene, systemPrompt, userMessage);\n result.setFallbackUsed(true);\n }\n recordCostAndAudit(scene, result);\n return result;\n }\n\n @Override\n public IntelligenceInferenceResult chat(String scene, List 路由到具体的底层 gateway(SpringAiInferenceAdapter / LegacyInferenceAdapter),\n * 由底层 adapter 真正实现 per-call model 覆盖。\n * 同时记录 LLM 调用成本到 CostExplosionGuard(用于成本爆炸防御)。\n */\n @Override\n public String chatWithModel(String prompt, Long tenantId, Long userId, String modelId) {\n long start = System.currentTimeMillis();\n AiInferenceGateway gateway = resolveGateway(\"model-selection\");\n String content;\n try {\n content = gateway.chatWithModel(prompt, tenantId, userId, modelId);\n } catch (Exception e) {\n log.warn(\"[AiInferenceRouter] chatWithModel failed, fallback to legacy: {}\", e.getMessage());\n content = legacyAdapter.chatWithModel(prompt, tenantId, userId, modelId);\n }\n long elapsed = System.currentTimeMillis() - start;\n\n // 记录 LLM 调用成本到 CostExplosionGuard(用于成本爆炸防御 + 重复检测)\n if (costExplosionGuard != null && tenantId != null) {\n try {\n // 用 \"llm_call\" 作为 toolName,prompt 哈希作为 paramsHash\n String paramsHash = costExplosionGuard.hashParams(prompt);\n costExplosionGuard.recordToolCall(tenantId, \"llm_call\", paramsHash,\n content != null ? content : \"\");\n log.debug(\"[AiInferenceRouter] LLM 调用成本已记录 tenantId={} modelId={} elapsed={}ms\",\n tenantId, modelId, elapsed);\n } catch (Exception e) {\n log.debug(\"[AiInferenceRouter] 记录 LLM 调用成本失败(不影响主流程): {}\", e.getMessage());\n }\n }\n\n log.info(\"[AiInferenceRouter] chatWithModel: gateway={} modelId={} elapsed={}ms contentLen={}\",\n gateway.getProviderName(), modelId != null ? modelId : \"default\",\n elapsed, content != null ? content.length() : 0);\n return content;\n }\n\n /**\n * Per-call 模型选择调用(借鉴 Claude Agent SDK per-call model selection)。\n *\n * 根据用户消息复杂度、预估工具调用数、多域标识自动选择模型分级:\n * ★ 接入点3:真正调用 gateway.chatWithModel 传递 modelId,而非仅用 scene 路由。\n *\n * @param prompt 完整提示词\n * @param userMessage 用户原始消息(用于复杂度评估)\n * @param estimatedToolCalls 预估工具调用数\n * @param isMultiDomain 是否多域任务\n * @return LLM 回答文本\n */\n public String chatWithModelSelection(String prompt, String userMessage,\n int estimatedToolCalls, boolean isMultiDomain) {\n if (modelSelectionRouter == null || !modelSelectionRouter.isEnabled()) {\n return chatSimple(prompt);\n }\n ModelSelectionRouter.ModelTier tier = modelSelectionRouter.selectModel(\n userMessage, estimatedToolCalls, isMultiDomain);\n String modelId = modelSelectionRouter.resolveModelId(tier);\n log.info(\"[AiInferenceRouter] per-call model selection: tier={} modelId={}\", tier, modelId);\n\n // 从 UserContext 获取 tenantId/userId(多租户隔离 + 成本追踪)\n Long tenantId = UserContext.tenantId();\n String userIdStr = UserContext.userId();\n\n // ★ 真正调用 gateway.chatWithModel 传递 modelId(接入点3 核心)\n try {\n return chatWithModel(prompt, tenantId, userIdStr != null ? userIdStr.hashCode() : 0L, modelId);\n } catch (Exception e) {\n log.warn(\"[AiInferenceRouter] chatWithModelSelection failed, fallback to chatSimple: {}\", e.getMessage());\n return chatSimple(prompt);\n }\n }\n\n /**\n * 强制使用 PREMIUM 模型调用(高风险场景,如复杂排产优化)。\n *\n * @param prompt 完整提示词\n * @return LLM 回答文本\n */\n public String chatPremium(String prompt) {\n if (modelSelectionRouter == null || !modelSelectionRouter.isEnabled()) {\n return chatSimple(prompt);\n }\n String modelId = modelSelectionRouter.resolveModelId(ModelSelectionRouter.ModelTier.PREMIUM);\n log.info(\"[AiInferenceRouter] forced premium model: modelId={}\", modelId);\n IntelligenceInferenceResult result = chat(\"premium-reasoning\", null, prompt);\n return result != null ? result.getContent() : \"\";\n }\n\n /** 根据模型分级映射到 scene(用于网关路由 hint) */\n private String resolveSceneByTier(ModelSelectionRouter.ModelTier tier) {\n return switch (tier) {\n case ECONOMY -> \"economy-query\";\n case STANDARD -> \"reflection\";\n case PREMIUM -> \"premium-reasoning\";\n };\n }\n\n /**\n * 获取模型推荐参数(temperature、maxTokens、timeout)。\n */\n public ModelConsortiumRouter.ModelParams recommendModelParams(String userMessage, boolean hasImage, int toolCount) {\n if (modelConsortiumRouter != null) {\n ModelConsortiumRouter.Complexity complexity = modelConsortiumRouter.classifyComplexity(\n userMessage, hasImage, toolCount);\n return modelConsortiumRouter.getModelParams(complexity);\n }\n return new ModelConsortiumRouter.ModelParams(0.5, 1024, 30);\n }\n\n private boolean hasText(String value) {\n return value != null && !value.trim().isEmpty();\n }\n}\n"}, "0fc1de77cd3cb0bcbf5f816d748f22c7ffbf316f": {"t": 1785749956.6754286, "n": 18449, "h": "15ced3ba90c9e71c", "c": "package com.fashion.supplychain.intelligence.gateway;\n\nimport com.fashion.supplychain.intelligence.agent.AiMessage;\nimport com.fashion.supplychain.intelligence.agent.AiTool;\nimport com.fashion.supplychain.intelligence.dto.IntelligenceInferenceResult;\nimport com.fashion.supplychain.intelligence.orchestration.AiCostTrackingOrchestrator;\nimport com.fashion.supplychain.intelligence.orchestration.AiOperationAuditOrchestrator;\nimport com.fashion.supplychain.intelligence.service.CostExplosionGuard;\nimport com.fashion.supplychain.intelligence.service.ModelSelectionRouter;\nimport com.fashion.supplychain.common.UserContext;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.atomic.AtomicInteger;\nimport java.util.concurrent.atomic.AtomicLong;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.context.annotation.Primary;\nimport org.springframework.stereotype.Service;\nimport org.springframework.context.annotation.Lazy;\n\n@Slf4j\n@Service\n@Lazy\n@Primary\npublic class AiInferenceRouter implements AiInferenceGateway {\n\n private static final int CIRCUIT_OPEN_THRESHOLD = 3;\n private static final long CIRCUIT_RESET_MS = 30_000L;\n\n @Value(\"${ai.gateway.routing-strategy:failover}\")\n private String routingStrategy;\n\n @Autowired\n private LegacyInferenceAdapter legacyAdapter;\n\n @Autowired(required = false)\n private SpringAiInferenceAdapter springAiAdapter;\n\n @Autowired(required = false)\n private AiCostTrackingOrchestrator aiCostTrackingOrchestrator;\n\n @Autowired(required = false)\n private AiOperationAuditOrchestrator aiOperationAuditOrchestrator;\n\n @Autowired(required = false)\n private ModelConsortiumRouter modelConsortiumRouter;\n\n @Autowired(required = false)\n private ModelSelectionRouter modelSelectionRouter;\n\n @Autowired(required = false)\n private CostExplosionGuard costExplosionGuard;\n\n private final AtomicInteger springAiConsecutiveFailures = new AtomicInteger(0);\n private final AtomicLong springAiCircuitOpenSince = new AtomicLong(0);\n\n @Override\n public IntelligenceInferenceResult chat(String scene, String systemPrompt, String userMessage) {\n AiInferenceGateway gateway = resolveGateway(scene);\n IntelligenceInferenceResult result = gateway.chat(scene, systemPrompt, userMessage);\n trackSpringAiHealth(result);\n if (isSpringAiCircuitOpen() && !\"legacy\".equals(gateway.getProviderName())) {\n log.info(\"[AiInferenceRouter] Spring AI 熔断触发,降级到 legacy 重试 scene={}\", scene);\n result = legacyAdapter.chat(scene, systemPrompt, userMessage);\n result.setFallbackUsed(true);\n }\n recordCostAndAudit(scene, result);\n return result;\n }\n\n @Override\n public IntelligenceInferenceResult chat(String scene, List 路由到具体的底层 gateway(SpringAiInferenceAdapter / LegacyInferenceAdapter),\n * 由底层 adapter 真正实现 per-call model 覆盖。\n * 同时记录 LLM 调用成本到 CostExplosionGuard(用于成本爆炸防御)。\n */\n @Override\n public String chatWithModel(String prompt, Long tenantId, Long userId, String modelId) {\n long start = System.currentTimeMillis();\n AiInferenceGateway gateway = resolveGateway(\"model-selection\");\n String content;\n try {\n content = gateway.chatWithModel(prompt, tenantId, userId, modelId);\n } catch (Exception e) {\n log.warn(\"[AiInferenceRouter] chatWithModel failed, fallback to legacy: {}\", e.getMessage());\n content = legacyAdapter.chatWithModel(prompt, tenantId, userId, modelId);\n }\n long elapsed = System.currentTimeMillis() - start;\n\n // 记录 LLM 调用成本到 CostExplosionGuard(用于成本爆炸防御 + 重复检测)\n if (costExplosionGuard != null && tenantId != null) {\n try {\n // 用 \"llm_call\" 作为 toolName,prompt 哈希作为 paramsHash\n String paramsHash = costExplosionGuard.hashParams(prompt);\n costExplosionGuard.recordToolCall(tenantId, \"llm_call\", paramsHash,\n content != null ? content : \"\");\n log.debug(\"[AiInferenceRouter] LLM 调用成本已记录 tenantId={} modelId={} elapsed={}ms\",\n tenantId, modelId, elapsed);\n } catch (Exception e) {\n log.debug(\"[AiInferenceRouter] 记录 LLM 调用成本失败(不影响主流程): {}\", e.getMessage());\n }\n }\n\n log.info(\"[AiInferenceRouter] chatWithModel: gateway={} modelId={} elapsed={}ms contentLen={}\",\n gateway.getProviderName(), modelId != null ? modelId : \"default\",\n elapsed, content != null ? content.length() : 0);\n return content;\n }\n\n /**\n * Per-call 模型选择调用(借鉴 Claude Agent SDK per-call model selection)。\n *\n * 根据用户消息复杂度、预估工具调用数、多域标识自动选择模型分级:\n * ★ 接入点3:真正调用 gateway.chatWithModel 传递 modelId,而非仅用 scene 路由。\n *\n * @param prompt 完整提示词\n * @param userMessage 用户原始消息(用于复杂度评估)\n * @param estimatedToolCalls 预估工具调用数\n * @param isMultiDomain 是否多域任务\n * @return LLM 回答文本\n */\n public String chatWithModelSelection(String prompt, String userMessage,\n int estimatedToolCalls, boolean isMultiDomain) {\n if (modelSelectionRouter == null || !modelSelectionRouter.isEnabled()) {\n return chatSimple(prompt);\n }\n ModelSelectionRouter.ModelTier tier = modelSelectionRouter.selectModel(\n userMessage, estimatedToolCalls, isMultiDomain);\n String modelId = modelSelectionRouter.resolveModelId(tier);\n log.info(\"[AiInferenceRouter] per-call model selection: tier={} modelId={}\", tier, modelId);\n\n // 从 UserContext 获取 tenantId/userId(多租户隔离 + 成本追踪)\n Long tenantId = UserContext.tenantId();\n String userIdStr = UserContext.userId();\n\n // ★ 真正调用 gateway.chatWithModel 传递 modelId(接入点3 核心)\n try {\n return chatWithModel(prompt, tenantId, userIdStr != null ? userIdStr.hashCode() : 0L, modelId);\n } catch (Exception e) {\n log.warn(\"[AiInferenceRouter] chatWithModelSelection failed, fallback to chatSimple: {}\", e.getMessage());\n return chatSimple(prompt);\n }\n }\n\n /**\n * 强制使用 PREMIUM 模型调用(高风险场景,如复杂排产优化)。\n *\n * @param prompt 完整提示词\n * @return LLM 回答文本\n */\n public String chatPremium(String prompt) {\n if (modelSelectionRouter == null || !modelSelectionRouter.isEnabled()) {\n return chatSimple(prompt);\n }\n String modelId = modelSelectionRouter.resolveModelId(ModelSelectionRouter.ModelTier.PREMIUM);\n log.info(\"[AiInferenceRouter] forced premium model: modelId={}\", modelId);\n IntelligenceInferenceResult result = chat(\"premium-reasoning\", null, prompt);\n return result != null ? result.getContent() : \"\";\n }\n\n /** 根据模型分级映射到 scene(用于网关路由 hint) */\n private String resolveSceneByTier(ModelSelectionRouter.ModelTier tier) {\n return switch (tier) {\n case ECONOMY -> \"economy-query\";\n case STANDARD -> \"reflection\";\n case PREMIUM -> \"premium-reasoning\";\n };\n }\n\n /**\n * 获取模型推荐参数(temperature、maxTokens、timeout)。\n */\n public ModelConsortiumRouter.ModelParams recommendModelParams(String userMessage, boolean hasImage, int toolCount) {\n if (modelConsortiumRouter != null) {\n ModelConsortiumRouter.Complexity complexity = modelConsortiumRouter.classifyComplexity(\n userMessage, hasImage, toolCount);\n return modelConsortiumRouter.getModelParams(complexity);\n }\n return new ModelConsortiumRouter.ModelParams(0.5, 1024, 30);\n }\n\n private boolean hasText(String value) {\n return value != null && !value.trim().isEmpty();\n }\n}\n"}, "dd96c9df2639c5b031479024e45d7cf36378fc96": {"t": 1785749957.8133483, "n": 18289, "h": "f37704f23d2abafb", "c": "package com.fashion.supplychain.intelligence.gateway;\n\nimport com.fashion.supplychain.intelligence.agent.AiMessage;\nimport com.fashion.supplychain.intelligence.agent.AiTool;\nimport com.fashion.supplychain.intelligence.dto.IntelligenceInferenceResult;\nimport com.fashion.supplychain.intelligence.orchestration.AiCostTrackingOrchestrator;\nimport com.fashion.supplychain.intelligence.orchestration.AiOperationAuditOrchestrator;\nimport com.fashion.supplychain.intelligence.service.CostExplosionGuard;\nimport com.fashion.supplychain.intelligence.service.ModelSelectionRouter;\nimport com.fashion.supplychain.common.UserContext;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.atomic.AtomicInteger;\nimport java.util.concurrent.atomic.AtomicLong;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.context.annotation.Primary;\nimport org.springframework.stereotype.Service;\nimport org.springframework.context.annotation.Lazy;\n\n@Slf4j\n@Service\n@Lazy\n@Primary\npublic class AiInferenceRouter implements AiInferenceGateway {\n\n private static final int CIRCUIT_OPEN_THRESHOLD = 3;\n private static final long CIRCUIT_RESET_MS = 30_000L;\n\n @Value(\"${ai.gateway.routing-strategy:failover}\")\n private String routingStrategy;\n\n @Autowired\n private LegacyInferenceAdapter legacyAdapter;\n\n @Autowired(required = false)\n private SpringAiInferenceAdapter springAiAdapter;\n\n @Autowired(required = false)\n private AiCostTrackingOrchestrator aiCostTrackingOrchestrator;\n\n @Autowired(required = false)\n private AiOperationAuditOrchestrator aiOperationAuditOrchestrator;\n\n @Autowired(required = false)\n private ModelConsortiumRouter modelConsortiumRouter;\n\n @Autowired(required = false)\n private ModelSelectionRouter modelSelectionRouter;\n\n @Autowired(required = false)\n private CostExplosionGuard costExplosionGuard;\n\n private final AtomicInteger springAiConsecutiveFailures = new AtomicInteger(0);\n private final AtomicLong springAiCircuitOpenSince = new AtomicLong(0);\n\n @Override\n public IntelligenceInferenceResult chat(String scene, String systemPrompt, String userMessage) {\n AiInferenceGateway gateway = resolveGateway(scene);\n IntelligenceInferenceResult result = gateway.chat(scene, systemPrompt, userMessage);\n trackSpringAiHealth(result);\n if (isSpringAiCircuitOpen() && !\"legacy\".equals(gateway.getProviderName())) {\n log.info(\"[AiInferenceRouter] Spring AI 熔断触发,降级到 legacy 重试 scene={}\", scene);\n result = legacyAdapter.chat(scene, systemPrompt, userMessage);\n result.setFallbackUsed(true);\n }\n recordCostAndAudit(scene, result);\n return result;\n }\n\n @Override\n public IntelligenceInferenceResult chat(String scene, List 路由到具体的底层 gateway(SpringAiInferenceAdapter / LegacyInferenceAdapter),\n * 由底层 adapter 真正实现 per-call model 覆盖。\n * 同时记录 LLM 调用成本到 CostExplosionGuard(用于成本爆炸防御)。\n */\n @Override\n public String chatWithModel(String prompt, Long tenantId, Long userId, String modelId) {\n long start = System.currentTimeMillis();\n AiInferenceGateway gateway = resolveGateway(\"model-selection\");\n String content;\n try {\n content = gateway.chatWithModel(prompt, tenantId, userId, modelId);\n } catch (Exception e) {\n log.warn(\"[AiInferenceRouter] chatWithModel failed, fallback to legacy: {}\", e.getMessage());\n content = legacyAdapter.chatWithModel(prompt, tenantId, userId, modelId);\n }\n long elapsed = System.currentTimeMillis() - start;\n\n // 记录 LLM 调用成本到 CostExplosionGuard(用于成本爆炸防御 + 重复检测)\n if (costExplosionGuard != null && tenantId != null) {\n try {\n // 用 \"llm_call\" 作为 toolName,prompt 哈希作为 paramsHash\n String paramsHash = costExplosionGuard.hashParams(prompt);\n costExplosionGuard.recordToolCall(tenantId, \"llm_call\", paramsHash,\n content != null ? content : \"\");\n log.debug(\"[AiInferenceRouter] LLM 调用成本已记录 tenantId={} modelId={} elapsed={}ms\",\n tenantId, modelId, elapsed);\n } catch (Exception e) {\n log.debug(\"[AiInferenceRouter] 记录 LLM 调用成本失败(不影响主流程): {}\", e.getMessage());\n }\n }\n\n log.info(\"[AiInferenceRouter] chatWithModel: gateway={} modelId={} elapsed={}ms contentLen={}\",\n gateway.getProviderName(), modelId != null ? modelId : \"default\",\n elapsed, content != null ? content.length() : 0);\n return content;\n }\n\n /**\n * Per-call 模型选择调用(借鉴 Claude Agent SDK per-call model selection)。\n *\n * 根据用户消息复杂度、预估工具调用数、多域标识自动选择模型分级:\n * ★ 接入点3:真正调用 gateway.chatWithModel 传递 modelId,而非仅用 scene 路由。\n *\n * @param prompt 完整提示词\n * @param userMessage 用户原始消息(用于复杂度评估)\n * @param estimatedToolCalls 预估工具调用数\n * @param isMultiDomain 是否多域任务\n * @return LLM 回答文本\n */\n public String chatWithModelSelection(String prompt, String userMessage,\n int estimatedToolCalls, boolean isMultiDomain) {\n if (modelSelectionRouter == null || !modelSelectionRouter.isEnabled()) {\n return chatSimple(prompt);\n }\n ModelSelectionRouter.ModelTier tier = modelSelectionRouter.selectModel(\n userMessage, estimatedToolCalls, isMultiDomain);\n String modelId = modelSelectionRouter.resolveModelId(tier);\n log.info(\"[AiInferenceRouter] per-call model selection: tier={} modelId={}\", tier, modelId);\n\n // 从 UserContext 获取 tenantId/userId(多租户隔离 + 成本追踪)\n Long tenantId = UserContext.tenantId();\n String userIdStr = UserContext.userId();\n\n // ★ 真正调用 gateway.chatWithModel 传递 modelId(接入点3 核心)\n try {\n return chatWithModel(prompt, tenantId, userIdStr != null ? userIdStr.hashCode() : 0L, modelId);\n } catch (Exception e) {\n log.warn(\"[AiInferenceRouter] chatWithModelSelection failed, fallback to chatSimple: {}\", e.getMessage());\n return chatSimple(prompt);\n }\n }\n\n /**\n * 强制使用 PREMIUM 模型调用(高风险场景,如复杂排产优化)。\n *\n * @param prompt 完整提示词\n * @return LLM 回答文本\n */\n public String chatPremium(String prompt) {\n if (modelSelectionRouter == null || !modelSelectionRouter.isEnabled()) {\n return chatSimple(prompt);\n }\n String modelId = modelSelectionRouter.resolveModelId(ModelSelectionRouter.ModelTier.PREMIUM);\n log.info(\"[AiInferenceRouter] forced premium model: modelId={}\", modelId);\n IntelligenceInferenceResult result = chat(\"premium-reasoning\", null, prompt);\n return result != null ? result.getContent() : \"\";\n }\n\n /** 根据模型分级映射到 scene(用于网关路由 hint) */\n private String resolveSceneByTier(ModelSelectionRouter.ModelTier tier) {\n return switch (tier) {\n case ECONOMY -> \"economy-query\";\n case STANDARD -> \"reflection\";\n case PREMIUM -> \"premium-reasoning\";\n };\n }\n\n /**\n * 获取模型推荐参数(temperature、maxTokens、timeout)。\n */\n public ModelConsortiumRouter.ModelParams recommendModelParams(String userMessage, boolean hasImage, int toolCount) {\n if (modelConsortiumRouter != null) {\n ModelConsortiumRouter.Complexity complexity = modelConsortiumRouter.classifyComplexity(\n userMessage, hasImage, toolCount);\n return modelConsortiumRouter.getModelParams(complexity);\n }\n return new ModelConsortiumRouter.ModelParams(0.5, 1024, 30);\n }\n\n private boolean hasText(String value) {\n return value != null && !value.trim().isEmpty();\n }\n}\n"}, "5096c8449e8798402e701944701dd0cc7033e1b7": {"t": 1785749959.105322, "n": 13376, "h": "ea0aae905f5cd560", "c": "package com.fashion.supplychain.intelligence.gateway;\n\nimport com.fashion.supplychain.intelligence.agent.AiMessage;\nimport com.fashion.supplychain.intelligence.agent.AiTool;\nimport com.fashion.supplychain.intelligence.dto.IntelligenceInferenceResult;\nimport com.fashion.supplychain.intelligence.orchestration.AiCostTrackingOrchestrator;\nimport com.fashion.supplychain.intelligence.orchestration.AiOperationAuditOrchestrator;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.atomic.AtomicInteger;\nimport java.util.concurrent.atomic.AtomicLong;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.context.annotation.Primary;\nimport org.springframework.stereotype.Service;\nimport org.springframework.context.annotation.Lazy;\n\n@Slf4j\n@Service\n@Lazy\n@Primary\npublic class AiInferenceRouter implements AiInferenceGateway {\n\n private static final int CIRCUIT_OPEN_THRESHOLD = 3;\n private static final long CIRCUIT_RESET_MS = 30_000L;\n\n @Value(\"${ai.gateway.routing-strategy:failover}\")\n private String routingStrategy;\n\n @Autowired\n private LegacyInferenceAdapter legacyAdapter;\n\n @Autowired(required = false)\n private SpringAiInferenceAdapter springAiAdapter;\n\n @Autowired(required = false)\n private AiCostTrackingOrchestrator aiCostTrackingOrchestrator;\n\n @Autowired(required = false)\n private AiOperationAuditOrchestrator aiOperationAuditOrchestrator;\n\n @Autowired(required = false)\n private ModelConsortiumRouter modelConsortiumRouter;\n\n private final AtomicInteger springAiConsecutiveFailures = new AtomicInteger(0);\n private final AtomicLong springAiCircuitOpenSince = new AtomicLong(0);\n\n @Override\n public IntelligenceInferenceResult chat(String scene, String systemPrompt, String userMessage) {\n AiInferenceGateway gateway = resolveGateway(scene);\n IntelligenceInferenceResult result = gateway.chat(scene, systemPrompt, userMessage);\n trackSpringAiHealth(result);\n if (isSpringAiCircuitOpen() && !\"legacy\".equals(gateway.getProviderName())) {\n log.info(\"[AiInferenceRouter] Spring AI 熔断触发,降级到 legacy 重试 scene={}\", scene);\n result = legacyAdapter.chat(scene, systemPrompt, userMessage);\n result.setFallbackUsed(true);\n }\n recordCostAndAudit(scene, result);\n return result;\n }\n\n @Override\n public IntelligenceInferenceResult chat(String scene, List 原行为:key 为空时启动正常,但所有 AI 功能(对话/视觉/工具调用)都会失败,\n * 用户排查困难(错误散落在各 Service 中)。\n *\n * 现行为:\n * 受影响功能:DeepSeek 对话、Spring AI 工具调用、AiAdvisor 日常建议、\n * SelfCritic 自评分、EvolutionPipeline 自进化、Qdrant 向量检索(embedding 降级)。\n */\n @Value(\"${ai.api-key.fail-fast-on-empty:false}\")\n private boolean failFastOnEmptyApiKey;\n\n @Value(\"${ai.deepseek.api-key:${DEEPSEEK_API_KEY:}}\")\n private String deepseekApiKey;\n\n @Autowired\n private LegacyInferenceAdapter legacyAdapter;\n\n @Autowired(required = false)\n private SpringAiInferenceAdapter springAiAdapter;\n\n @Autowired(required = false)\n private AiCostTrackingOrchestrator aiCostTrackingOrchestrator;\n\n @Autowired(required = false)\n private AiOperationAuditOrchestrator aiOperationAuditOrchestrator;\n\n @Autowired(required = false)\n private ModelConsortiumRouter modelConsortiumRouter;\n\n @Autowired(required = false)\n private ModelSelectionRouter modelSelectionRouter;\n\n @Autowired(required = false)\n private CostExplosionGuard costExplosionGuard;\n\n private final AtomicInteger springAiConsecutiveFailures = new AtomicInteger(0);\n private final AtomicLong springAiCircuitOpenSince = new AtomicLong(0);\n\n /**\n * 【P1-10修复】启动时校验 DEEPSEEK_API_KEY。\n * 默认仅告警不阻止启动,开启 fail-fast-on-empty=true 可强制阻止启动。\n */\n @jakarta.annotation.PostConstruct\n public void validateApiKey() {\n boolean keyMissing = deepseekApiKey == null || deepseekApiKey.isBlank();\n if (!keyMissing) {\n log.info(\"[AiInferenceRouter] DEEPSEEK_API_KEY 已配置(长度={}),AI 功能正常可用\",\n deepseekApiKey.length());\n return;\n }\n\n String affectedFeatures = \"DeepSeek 对话 / Spring AI 工具调用 / AiAdvisor 日常建议 / \"\n + \"SelfCritic 自评分 / EvolutionPipeline 自进化 / Qdrant 向量检索(embedding 降级)\";\n\n if (failFastOnEmptyApiKey) {\n String msg = \"[AiInferenceRouter] DEEPSEEK_API_KEY 为空且 ai.api-key.fail-fast-on-empty=true,\"\n + \"应用启动被阻止。受影响功能:\" + affectedFeatures\n + \"。解决:设置环境变量 DEEPSEEK_API_KEY 或在 yml 配置 ai.deepseek.api-key\";\n log.error(msg);\n throw new IllegalStateException(msg);\n }\n\n log.error(\"[AiInferenceRouter] ⚠️ DEEPSEEK_API_KEY 为空!AI 功能将全部失败。受影响功能:{}\", affectedFeatures);\n log.error(\"[AiInferenceRouter] 解决:设置环境变量 DEEPSEEK_API_KEY 或在 yml 配置 ai.deepseek.api-key\");\n log.error(\"[AiInferenceRouter] 如需启动失败而非降级,设置 ai.api-key.fail-fast-on-empty=true\");\n }\n\n @Override\n public IntelligenceInferenceResult chat(String scene, String systemPrompt, String userMessage) {\n AiInferenceGateway gateway = resolveGateway(scene);\n IntelligenceInferenceResult result = gateway.chat(scene, systemPrompt, userMessage);\n trackSpringAiHealth(result);\n if (isSpringAiCircuitOpen() && !\"legacy\".equals(gateway.getProviderName())) {\n log.info(\"[AiInferenceRouter] Spring AI 熔断触发,降级到 legacy 重试 scene={}\", scene);\n result = legacyAdapter.chat(scene, systemPrompt, userMessage);\n result.setFallbackUsed(true);\n }\n recordCostAndAudit(scene, result);\n return result;\n }\n\n @Override\n public IntelligenceInferenceResult chat(String scene, List 路由到具体的底层 gateway(SpringAiInferenceAdapter / LegacyInferenceAdapter),\n * 由底层 adapter 真正实现 per-call model 覆盖。\n * 同时记录 LLM 调用成本到 CostExplosionGuard(用于成本爆炸防御)。\n */\n @Override\n public String chatWithModel(String prompt, Long tenantId, Long userId, String modelId) {\n long start = System.currentTimeMillis();\n AiInferenceGateway gateway = resolveGateway(\"model-selection\");\n String content;\n try {\n content = gateway.chatWithModel(prompt, tenantId, userId, modelId);\n } catch (Exception e) {\n log.warn(\"[AiInferenceRouter] chatWithModel failed, fallback to legacy: {}\", e.getMessage());\n content = legacyAdapter.chatWithModel(prompt, tenantId, userId, modelId);\n }\n long elapsed = System.currentTimeMillis() - start;\n\n // 记录 LLM 调用成本到 CostExplosionGuard(用于成本爆炸防御 + 重复检测)\n if (costExplosionGuard != null && tenantId != null) {\n try {\n // 用 \"llm_call\" 作为 toolName,prompt 哈希作为 paramsHash\n String paramsHash = costExplosionGuard.hashParams(prompt);\n costExplosionGuard.recordToolCall(tenantId, \"llm_call\", paramsHash,\n content != null ? content : \"\");\n log.debug(\"[AiInferenceRouter] LLM 调用成本已记录 tenantId={} modelId={} elapsed={}ms\",\n tenantId, modelId, elapsed);\n } catch (Exception e) {\n log.debug(\"[AiInferenceRouter] 记录 LLM 调用成本失败(不影响主流程): {}\", e.getMessage());\n }\n }\n\n log.info(\"[AiInferenceRouter] chatWithModel: gateway={} modelId={} elapsed={}ms contentLen={}\",\n gateway.getProviderName(), modelId != null ? modelId : \"default\",\n elapsed, content != null ? content.length() : 0);\n return content;\n }\n\n /**\n * Per-call 模型选择调用(借鉴 Claude Agent SDK per-call model selection)。\n *\n * 根据用户消息复杂度、预估工具调用数、多域标识自动选择模型分级:\n * ★ 接入点3:真正调用 gateway.chatWithModel 传递 modelId,而非仅用 scene 路由。\n *\n * @param prompt 完整提示词\n * @param userMessage 用户原始消息(用于复杂度评估)\n * @param estimatedToolCalls 预估工具调用数\n * @param isMultiDomain 是否多域任务\n * @return LLM 回答文本\n */\n public String chatWithModelSelection(String prompt, String userMessage,\n int estimatedToolCalls, boolean isMultiDomain) {\n if (modelSelectionRouter == null || !modelSelectionRouter.isEnabled()) {\n return chatSimple(prompt);\n }\n ModelSelectionRouter.ModelTier tier = modelSelectionRouter.selectModel(\n userMessage, estimatedToolCalls, isMultiDomain);\n String modelId = modelSelectionRouter.resolveModelId(tier);\n log.info(\"[AiInferenceRouter] per-call model selection: tier={} modelId={}\", tier, modelId);\n\n // 从 UserContext 获取 tenantId/userId(多租户隔离 + 成本追踪)\n Long tenantId = UserContext.tenantId();\n String userIdStr = UserContext.userId();\n\n // ★ 真正调用 gateway.chatWithModel 传递 modelId(接入点3 核心)\n try {\n return chatWithModel(prompt, tenantId, userIdStr != null ? userIdStr.hashCode() : 0L, modelId);\n } catch (Exception e) {\n log.warn(\"[AiInferenceRouter] chatWithModelSelection failed, fallback to chatSimple: {}\", e.getMessage());\n return chatSimple(prompt);\n }\n }\n\n /**\n * 强制使用 PREMIUM 模型调用(高风险场景,如复杂排产优化)。\n *\n * @param prompt 完整提示词\n * @return LLM 回答文本\n */\n public String chatPremium(String prompt) {\n if (modelSelectionRouter == null || !modelSelectionRouter.isEnabled()) {\n return chatSimple(prompt);\n }\n String modelId = modelSelectionRouter.resolveModelId(ModelSelectionRouter.ModelTier.PREMIUM);\n log.info(\"[AiInferenceRouter] forced premium model: modelId={}\", modelId);\n IntelligenceInferenceResult result = chat(\"premium-reasoning\", null, prompt);\n return result != null ? result.getContent() : \"\";\n }\n\n /** 根据模型分级映射到 scene(用于网关路由 hint) */\n private String resolveSceneByTier(ModelSelectionRouter.ModelTier tier) {\n return switch (tier) {\n case ECONOMY -> \"economy-query\";\n case STANDARD -> \"reflection\";\n case PREMIUM -> \"premium-reasoning\";\n };\n }\n\n /**\n * 获取模型推荐参数(temperature、maxTokens、timeout)。\n */\n public ModelConsortiumRouter.ModelParams recommendModelParams(String userMessage, boolean hasImage, int toolCount) {\n if (modelConsortiumRouter != null) {\n ModelConsortiumRouter.Complexity complexity = modelConsortiumRouter.classifyComplexity(\n userMessage, hasImage, toolCount);\n return modelConsortiumRouter.getModelParams(complexity);\n }\n return new ModelConsortiumRouter.ModelParams(0.5, 1024, 30);\n }\n\n private boolean hasText(String value) {\n return value != null && !value.trim().isEmpty();\n }\n}\n"}, "b9ae58d3147bb0308a7a5ead91018682cac43d9c": {"t": 1786068301.6568787, "n": 20750, "h": "31e72f3762438f68", "c": "package com.fashion.supplychain.intelligence.gateway;\n\nimport com.fashion.supplychain.intelligence.agent.AiMessage;\nimport com.fashion.supplychain.intelligence.agent.AiTool;\nimport com.fashion.supplychain.intelligence.dto.IntelligenceInferenceResult;\nimport com.fashion.supplychain.intelligence.orchestration.AiCostTrackingOrchestrator;\nimport com.fashion.supplychain.intelligence.orchestration.AiOperationAuditOrchestrator;\nimport com.fashion.supplychain.intelligence.service.CostExplosionGuard;\nimport com.fashion.supplychain.intelligence.service.ModelSelectionRouter;\nimport com.fashion.supplychain.common.UserContext;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.atomic.AtomicInteger;\nimport java.util.concurrent.atomic.AtomicLong;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.context.annotation.Primary;\nimport org.springframework.stereotype.Service;\nimport org.springframework.context.annotation.Lazy;\n\n@Slf4j\n@Service\n@Lazy\n@Primary\npublic class AiInferenceRouter implements AiInferenceGateway {\n\n private static final int CIRCUIT_OPEN_THRESHOLD = 3;\n private static final long CIRCUIT_RESET_MS = 30_000L;\n\n @Value(\"${ai.gateway.routing-strategy:failover}\")\n private String routingStrategy;\n\n /**\n * 【P1-10修复】启动时校验 DEEPSEEK_API_KEY 非空。\n *\n * 原行为:key 为空时启动正常,但所有 AI 功能(对话/视觉/工具调用)都会失败,\n * 用户排查困难(错误散落在各 Service 中)。\n *\n * 现行为:\n * 受影响功能:DeepSeek 对话、Spring AI 工具调用、AiAdvisor 日常建议、\n * SelfCritic 自评分、EvolutionPipeline 自进化、Qdrant 向量检索(embedding 降级)。\n */\n @Value(\"${ai.api-key.fail-fast-on-empty:false}\")\n private boolean failFastOnEmptyApiKey;\n\n @Value(\"${ai.deepseek.api-key:${DEEPSEEK_API_KEY:}}\")\n private String deepseekApiKey;\n\n @Autowired\n private LegacyInferenceAdapter legacyAdapter;\n\n @Autowired(required = false)\n private SpringAiInferenceAdapter springAiAdapter;\n\n @Autowired(required = false)\n private AiCostTrackingOrchestrator aiCostTrackingOrchestrator;\n\n @Autowired(required = false)\n private AiOperationAuditOrchestrator aiOperationAuditOrchestrator;\n\n @Autowired(required = false)\n private ModelConsortiumRouter modelConsortiumRouter;\n\n @Autowired(required = false)\n private ModelSelectionRouter modelSelectionRouter;\n\n @Autowired(required = false)\n private CostExplosionGuard costExplosionGuard;\n\n private final AtomicInteger springAiConsecutiveFailures = new AtomicInteger(0);\n private final AtomicLong springAiCircuitOpenSince = new AtomicLong(0);\n\n /**\n * 【P1-10修复】启动时校验 DEEPSEEK_API_KEY。\n * 默认仅告警不阻止启动,开启 fail-fast-on-empty=true 可强制阻止启动。\n */\n @jakarta.annotation.PostConstruct\n public void validateApiKey() {\n boolean keyMissing = deepseekApiKey == null || deepseekApiKey.isBlank();\n if (!keyMissing) {\n log.info(\"[AiInferenceRouter] DEEPSEEK_API_KEY 已配置(长度={}),AI 功能正常可用\",\n deepseekApiKey.length());\n return;\n }\n\n String affectedFeatures = \"DeepSeek 对话 / Spring AI 工具调用 / AiAdvisor 日常建议 / \"\n + \"SelfCritic 自评分 / EvolutionPipeline 自进化 / Qdrant 向量检索(embedding 降级)\";\n\n if (failFastOnEmptyApiKey) {\n String msg = \"[AiInferenceRouter] DEEPSEEK_API_KEY 为空且 ai.api-key.fail-fast-on-empty=true,\"\n + \"应用启动被阻止。受影响功能:\" + affectedFeatures\n + \"。解决:设置环境变量 DEEPSEEK_API_KEY 或在 yml 配置 ai.deepseek.api-key\";\n log.error(msg);\n throw new IllegalStateException(msg);\n }\n\n log.error(\"[AiInferenceRouter] ⚠️ DEEPSEEK_API_KEY 为空!AI 功能将全部失败。受影响功能:{}\", affectedFeatures);\n log.error(\"[AiInferenceRouter] 解决:设置环境变量 DEEPSEEK_API_KEY 或在 yml 配置 ai.deepseek.api-key\");\n log.error(\"[AiInferenceRouter] 如需启动失败而非降级,设置 ai.api-key.fail-fast-on-empty=true\");\n }\n\n @Override\n public IntelligenceInferenceResult chat(String scene, String systemPrompt, String userMessage) {\n AiInferenceGateway gateway = resolveGateway(scene);\n IntelligenceInferenceResult result = gateway.chat(scene, systemPrompt, userMessage);\n trackSpringAiHealth(result);\n if (isSpringAiCircuitOpen() && !\"legacy\".equals(gateway.getProviderName())) {\n log.info(\"[AiInferenceRouter] Spring AI 熔断触发,降级到 legacy 重试 scene={}\", scene);\n result = legacyAdapter.chat(scene, systemPrompt, userMessage);\n result.setFallbackUsed(true);\n }\n recordCostAndAudit(scene, result);\n return result;\n }\n\n @Override\n public IntelligenceInferenceResult chat(String scene, List 路由到具体的底层 gateway(SpringAiInferenceAdapter / LegacyInferenceAdapter),\n * 由底层 adapter 真正实现 per-call model 覆盖。\n * 同时记录 LLM 调用成本到 CostExplosionGuard(用于成本爆炸防御)。\n */\n @Override\n public String chatWithModel(String prompt, Long tenantId, Long userId, String modelId) {\n long start = System.currentTimeMillis();\n AiInferenceGateway gateway = resolveGateway(\"model-selection\");\n String content;\n try {\n content = gateway.chatWithModel(prompt, tenantId, userId, modelId);\n } catch (Exception e) {\n log.warn(\"[AiInferenceRouter] chatWithModel failed, fallback to legacy: {}\", e.getMessage());\n content = legacyAdapter.chatWithModel(prompt, tenantId, userId, modelId);\n }\n long elapsed = System.currentTimeMillis() - start;\n\n // 记录 LLM 调用成本到 CostExplosionGuard(用于成本爆炸防御 + 重复检测)\n if (costExplosionGuard != null && tenantId != null) {\n try {\n // 用 \"llm_call\" 作为 toolName,prompt 哈希作为 paramsHash\n String paramsHash = costExplosionGuard.hashParams(prompt);\n costExplosionGuard.recordToolCall(tenantId, \"llm_call\", paramsHash,\n content != null ? content : \"\");\n log.debug(\"[AiInferenceRouter] LLM 调用成本已记录 tenantId={} modelId={} elapsed={}ms\",\n tenantId, modelId, elapsed);\n } catch (Exception e) {\n log.debug(\"[AiInferenceRouter] 记录 LLM 调用成本失败(不影响主流程): {}\", e.getMessage());\n }\n }\n\n log.info(\"[AiInferenceRouter] chatWithModel: gateway={} modelId={} elapsed={}ms contentLen={}\",\n gateway.getProviderName(), modelId != null ? modelId : \"default\",\n elapsed, content != null ? content.length() : 0);\n return content;\n }\n\n /**\n * Per-call 模型选择调用(借鉴 Claude Agent SDK per-call model selection)。\n *\n * 根据用户消息复杂度、预估工具调用数、多域标识自动选择模型分级:\n * ★ 接入点3:真正调用 gateway.chatWithModel 传递 modelId,而非仅用 scene 路由。\n *\n * @param prompt 完整提示词\n * @param userMessage 用户原始消息(用于复杂度评估)\n * @param estimatedToolCalls 预估工具调用数\n * @param isMultiDomain 是否多域任务\n * @return LLM 回答文本\n */\n public String chatWithModelSelection(String prompt, String userMessage,\n int estimatedToolCalls, boolean isMultiDomain) {\n if (modelSelectionRouter == null || !modelSelectionRouter.isEnabled()) {\n return chatSimple(prompt);\n }\n ModelSelectionRouter.ModelTier tier = modelSelectionRouter.selectModel(\n userMessage, estimatedToolCalls, isMultiDomain);\n String modelId = modelSelectionRouter.resolveModelId(tier);\n log.info(\"[AiInferenceRouter] per-call model selection: tier={} modelId={}\", tier, modelId);\n\n // 从 UserContext 获取 tenantId/userId(多租户隔离 + 成本追踪)\n Long tenantId = UserContext.tenantId();\n String userIdStr = UserContext.userId();\n\n // ★ 真正调用 gateway.chatWithModel 传递 modelId(接入点3 核心)\n try {\n return chatWithModel(prompt, tenantId, userIdStr != null ? userIdStr.hashCode() : 0L, modelId);\n } catch (Exception e) {\n log.warn(\"[AiInferenceRouter] chatWithModelSelection failed, fallback to chatSimple: {}\", e.getMessage());\n return chatSimple(prompt);\n }\n }\n\n /**\n * 强制使用 PREMIUM 模型调用(高风险场景,如复杂排产优化)。\n *\n * @param prompt 完整提示词\n * @return LLM 回答文本\n */\n public String chatPremium(String prompt) {\n if (modelSelectionRouter == null || !modelSelectionRouter.isEnabled()) {\n return chatSimple(prompt);\n }\n String modelId = modelSelectionRouter.resolveModelId(ModelSelectionRouter.ModelTier.PREMIUM);\n log.info(\"[AiInferenceRouter] forced premium model: modelId={}\", modelId);\n IntelligenceInferenceResult result = chat(\"premium-reasoning\", null, prompt);\n return result != null ? result.getContent() : \"\";\n }\n\n /** 根据模型分级映射到 scene(用于网关路由 hint) */\n private String resolveSceneByTier(ModelSelectionRouter.ModelTier tier) {\n return switch (tier) {\n case ECONOMY -> \"economy-query\";\n case STANDARD -> \"reflection\";\n case PREMIUM -> \"premium-reasoning\";\n };\n }\n\n /**\n * 获取模型推荐参数(temperature、maxTokens、timeout)。\n */\n public ModelConsortiumRouter.ModelParams recommendModelParams(String userMessage, boolean hasImage, int toolCount) {\n if (modelConsortiumRouter != null) {\n ModelConsortiumRouter.Complexity complexity = modelConsortiumRouter.classifyComplexity(\n userMessage, hasImage, toolCount);\n return modelConsortiumRouter.getModelParams(complexity);\n }\n return new ModelConsortiumRouter.ModelParams(0.5, 1024, 30);\n }\n\n private boolean hasText(String value) {\n return value != null && !value.trim().isEmpty();\n }\n}\n"}}, "backend/src/main/java/com/fashion/supplychain/intelligence/service/AiAdvisorService.java": {"92136382831779e22f23b72a08f4e137bfa17dd3": {"t": 1785745701.6783586, "n": 5838, "h": "0c462b9a466a0964", "c": "package com.fashion.supplychain.intelligence.service;\n\nimport com.fashion.supplychain.intelligence.dto.IntelligenceInferenceResult;\nimport com.fashion.supplychain.intelligence.orchestration.IntelligenceInferenceOrchestrator;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.cache.annotation.Cacheable;\nimport org.springframework.scheduling.annotation.Scheduled;\nimport org.springframework.stereotype.Service;\nimport org.springframework.context.annotation.Lazy;\n\nimport java.time.LocalDate;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.atomic.AtomicInteger;\n\n/**\n * AI 顾问服务。\n *\n * 职责:保留配额与启用判断,对外提供 AI 顾问语义能力。\n * 真实推理由独立的 {@link IntelligenceInferenceOrchestrator} 执行。 默认直连 DeepSeek,也支持通过 LiteLLM 网关接入,配置方式(环境变量):\n * 若未配置 AI_API_KEY,所有调用会立即返回 null,系统继续走本地规则引擎,\n * 不影响任何现有功能。\n */\n@Service\n@Lazy\n@Slf4j\npublic class AiAdvisorService {\n\n @Value(\"${ai.deepseek.api-key:}\")\n private String apiKey;\n\n /** 每个租户每天最多允许调用 DeepSeek 的次数(0 = 不限制) */\n @Value(\"${ai.deepseek.daily-quota-per-tenant:50}\")\n private int dailyQuotaPerTenant;\n\n @Autowired\n private IntelligenceInferenceOrchestrator intelligenceInferenceOrchestrator;\n\n /**\n * 每租户每日调用计数器\n * key = \"tenantId_yyyyMMdd\",value = 当日已调用次数\n * 不同日期的 key 自动过期(不再被读写),内存占用可忽略不计\n */\n private final ConcurrentHashMap 结果按 contextSummary 内容缓存 5 分钟,相同摘要(同一租户当日数据未变化)\n * 直接返回缓存值,避免重复调用 DeepSeek API(每次约 2~3 秒延迟)。\n * Redis 不可用时自动降级为直接调用 AI(见 RedisConfig.errorHandler)。 职责:保留配额与启用判断,对外提供 AI 顾问语义能力。\n * 真实推理由独立的 {@link IntelligenceInferenceOrchestrator} 执行。 默认直连 DeepSeek,也支持通过 LiteLLM 网关接入,配置方式(环境变量):\n * 若未配置 AI_API_KEY,所有调用会立即返回 null,系统继续走本地规则引擎,\n * 不影响任何现有功能。\n */\n@Service\n@Lazy\n@Slf4j\npublic class AiAdvisorService {\n\n @Value(\"${ai.deepseek.api-key:}\")\n private String apiKey;\n\n /** 每个租户每天最多允许调用 DeepSeek 的次数(0 = 不限制) */\n @Value(\"${ai.deepseek.daily-quota-per-tenant:50}\")\n private int dailyQuotaPerTenant;\n\n @Autowired\n private IntelligenceInferenceOrchestrator intelligenceInferenceOrchestrator;\n\n /**\n * 每租户每日调用计数器\n * key = \"tenantId_yyyyMMdd\",value = 当日已调用次数\n * 不同日期的 key 自动过期(不再被读写),内存占用可忽略不计\n */\n private final ConcurrentHashMap 结果按 contextSummary 内容缓存 5 分钟,相同摘要(同一租户当日数据未变化)\n * 直接返回缓存值,避免重复调用 DeepSeek API(每次约 2~3 秒延迟)。\n * Redis 不可用时自动降级为直接调用 AI(见 RedisConfig.errorHandler)。 职责:保留配额与启用判断,对外提供 AI 顾问语义能力。\n * 真实推理由独立的 {@link IntelligenceInferenceOrchestrator} 执行。 默认直连 DeepSeek,也支持通过 LiteLLM 网关接入,配置方式(环境变量):\n * 若未配置 AI_API_KEY,所有调用会立即返回 null,系统继续走本地规则引擎,\n * 不影响任何现有功能。\n */\n@Service\n@Slf4j\npublic class AiAdvisorService {\n\n @Value(\"${ai.deepseek.api-key:}\")\n private String apiKey;\n\n /** 每个租户每天最多允许调用 DeepSeek 的次数(0 = 不限制) */\n @Value(\"${ai.deepseek.daily-quota-per-tenant:50}\")\n private int dailyQuotaPerTenant;\n\n @Autowired\n private IntelligenceInferenceOrchestrator intelligenceInferenceOrchestrator;\n\n /**\n * 每租户每日调用计数器\n * key = \"tenantId_yyyyMMdd\",value = 当日已调用次数\n * 不同日期的 key 自动过期(不再被读写),内存占用可忽略不计\n */\n private final ConcurrentHashMap 结果按 contextSummary 内容缓存 5 分钟,相同摘要(同一租户当日数据未变化)\n * 直接返回缓存值,避免重复调用 DeepSeek API(每次约 2~3 秒延迟)。\n * Redis 不可用时自动降级为直接调用 AI(见 RedisConfig.errorHandler)。 职责:保留配额与启用判断,对外提供 AI 顾问语义能力。\n * 真实推理由独立的 {@link IntelligenceInferenceOrchestrator} 执行。 默认直连 DeepSeek,也支持通过 LiteLLM 网关接入,配置方式(环境变量):\n * 若未配置 AI_API_KEY,所有调用会立即返回 null,系统继续走本地规则引擎,\n * 不影响任何现有功能。\n */\n@Service\n@Slf4j\npublic class AiAdvisorService {\n\n @Value(\"${ai.deepseek.api-key:}\")\n private String apiKey;\n\n /** 每个租户每天最多允许调用 DeepSeek 的次数(0 = 不限制) */\n @Value(\"${ai.deepseek.daily-quota-per-tenant:50}\")\n private int dailyQuotaPerTenant;\n\n @Autowired\n private IntelligenceInferenceOrchestrator intelligenceInferenceOrchestrator;\n\n /**\n * 每租户每日调用计数器\n * key = \"tenantId_yyyyMMdd\",value = 当日已调用次数\n * 不同日期的 key 自动过期(不再被读写),内存占用可忽略不计\n */\n private final ConcurrentHashMap 结果按 contextSummary 内容缓存 5 分钟,相同摘要(同一租户当日数据未变化)\n * 直接返回缓存值,避免重复调用 DeepSeek API(每次约 2~3 秒延迟)。\n * Redis 不可用时自动降级为直接调用 AI(见 RedisConfig.errorHandler)。 职责:保留配额与启用判断,对外提供 AI 顾问语义能力。\n * 真实推理由独立的 {@link IntelligenceInferenceOrchestrator} 执行。 默认直连 DeepSeek,也支持通过 LiteLLM 网关接入,配置方式(环境变量):\n * 若未配置 AI_API_KEY,所有调用会立即返回 null,系统继续走本地规则引擎,\n * 不影响任何现有功能。\n */\n@Service\n@Slf4j\npublic class AiAdvisorService {\n\n @Value(\"${ai.deepseek.api-key:}\")\n private String apiKey;\n\n /** 每个租户每天最多允许调用 DeepSeek 的次数(0 = 不限制) */\n @Value(\"${ai.deepseek.daily-quota-per-tenant:50}\")\n private int dailyQuotaPerTenant;\n\n @Autowired\n private IntelligenceInferenceOrchestrator intelligenceInferenceOrchestrator;\n\n /**\n * 每租户每日调用计数器\n * key = \"tenantId_yyyyMMdd\",value = 当日已调用次数\n * 不同日期的 key 自动过期(不再被读写),内存占用可忽略不计\n */\n private final ConcurrentHashMap 结果按 contextSummary 内容缓存 5 分钟,相同摘要(同一租户当日数据未变化)\n * 直接返回缓存值,避免重复调用 DeepSeek API(每次约 2~3 秒延迟)。\n * Redis 不可用时自动降级为直接调用 AI(见 RedisConfig.errorHandler)。 职责:保留配额与启用判断,对外提供 AI 顾问语义能力。\n * 真实推理由独立的 {@link IntelligenceInferenceOrchestrator} 执行。 默认直连 DeepSeek,也支持通过 LiteLLM 网关接入,配置方式(环境变量):\n * 若未配置 AI_API_KEY,所有调用会立即返回 null,系统继续走本地规则引擎,\n * 不影响任何现有功能。\n */\n@Service\n@Slf4j\npublic class AiAdvisorService {\n\n @Value(\"${ai.deepseek.api-key:}\")\n private String apiKey;\n\n /** 每个租户每天最多允许调用 DeepSeek 的次数(0 = 不限制) */\n @Value(\"${ai.deepseek.daily-quota-per-tenant:50}\")\n private int dailyQuotaPerTenant;\n\n @Autowired\n private IntelligenceInferenceOrchestrator intelligenceInferenceOrchestrator;\n\n /**\n * 每租户每日调用计数器\n * key = \"tenantId_yyyyMMdd\",value = 当日已调用次数\n * 不同日期的 key 自动过期(不再被读写),内存占用可忽略不计\n */\n private final ConcurrentHashMap 结果按 contextSummary 内容缓存 5 分钟,相同摘要(同一租户当日数据未变化)\n * 直接返回缓存值,避免重复调用 DeepSeek API(每次约 2~3 秒延迟)。\n * Redis 不可用时自动降级为直接调用 AI(见 RedisConfig.errorHandler)。 职责:保留配额与启用判断,对外提供 AI 顾问语义能力。\n * 真实推理由独立的 {@link IntelligenceInferenceOrchestrator} 执行。 默认直连 DeepSeek,也支持通过 LiteLLM 网关接入,配置方式(环境变量):\n * 若未配置 AI_API_KEY,所有调用会立即返回 null,系统继续走本地规则引擎,\n * 不影响任何现有功能。\n */\n@Service\n@Slf4j\npublic class AiAdvisorService {\n\n @Value(\"${ai.deepseek.api-key:}\")\n private String apiKey;\n\n /** 每个租户每天最多允许调用 DeepSeek 的次数(0 = 不限制) */\n @Value(\"${ai.deepseek.daily-quota-per-tenant:50}\")\n private int dailyQuotaPerTenant;\n\n @Autowired\n private IntelligenceInferenceOrchestrator intelligenceInferenceOrchestrator;\n\n /**\n * 每租户每日调用计数器\n * key = \"tenantId_yyyyMMdd\",value = 当日已调用次数\n * 不同日期的 key 自动过期(不再被读写),内存占用可忽略不计\n */\n private final ConcurrentHashMap 默认接 DeepSeek,配置方式(环境变量):\n * 若未配置 AI_API_KEY,所有调用会立即返回 null,系统继续走本地规则引擎,\n * 不影响任何现有功能。\n *\n * 切换 OpenAI / 其他兼容 API 只需改 AI_API_URL 和 AI_MODEL。\n */\n@Service\n@Slf4j\npublic class AiAdvisorService {\n\n @Value(\"${ai.deepseek.api-key:}\")\n private String apiKey;\n\n @Value(\"${ai.deepseek.api-url:https://api.deepseek.com/v1/chat/completions}\")\n private String apiUrl;\n\n @Value(\"${ai.deepseek.model:deepseek-chat}\")\n private String model;\n\n /** 每个租户每天最多允许调用 DeepSeek 的次数(0 = 不限制) */\n @Value(\"${ai.deepseek.daily-quota-per-tenant:50}\")\n private int dailyQuotaPerTenant;\n\n private static final int TIMEOUT_SECONDS = 30;\n private static final ObjectMapper MAPPER = new ObjectMapper();\n\n /**\n * 每租户每日调用计数器\n * key = \"tenantId_yyyyMMdd\",value = 当日已调用次数\n * 不同日期的 key 自动过期(不再被读写),内存占用可忽略不计\n */\n private final ConcurrentHashMap 默认接 DeepSeek,配置方式(环境变量):\n * 若未配置 AI_API_KEY,所有调用会立即返回 null,系统继续走本地规则引擎,\n * 不影响任何现有功能。\n *\n * 切换 OpenAI / 其他兼容 API 只需改 AI_API_URL 和 AI_MODEL。\n */\n@Service\n@Slf4j\npublic class AiAdvisorService {\n\n @Value(\"${ai.deepseek.api-key:}\")\n private String apiKey;\n\n @Value(\"${ai.deepseek.api-url:https://api.deepseek.com/v1/chat/completions}\")\n private String apiUrl;\n\n @Value(\"${ai.deepseek.model:deepseek-chat}\")\n private String model;\n\n private static final int TIMEOUT_SECONDS = 30;\n private static final ObjectMapper MAPPER = new ObjectMapper();\n\n /**\n * 是否已启用 AI(有 API Key)\n */\n public boolean isEnabled() {\n return apiKey != null && !apiKey.isBlank();\n }\n\n /**\n * 发送单轮对话请求\n *\n * @param systemPrompt 系统角色描述(如:你是服装供应链分析师)\n * @param userMessage 用户消息\n * @return AI 回复文本,未配置 Key 或调用失败时返回 null\n */\n public String chat(String systemPrompt, String userMessage) {\n if (!isEnabled()) {\n log.debug(\"[AiAdvisor] AI_API_KEY 未配置,跳过 AI 调用\");\n return null;\n }\n\n try {\n String body = buildRequestBody(systemPrompt, userMessage);\n HttpClient client = HttpClient.newBuilder()\n .connectTimeout(Duration.ofSeconds(10))\n .build();\n HttpRequest request = HttpRequest.newBuilder()\n .uri(URI.create(apiUrl))\n .header(\"Content-Type\", \"application/json\")\n .header(\"Authorization\", \"Bearer \" + apiKey)\n .POST(HttpRequest.BodyPublishers.ofString(body))\n .timeout(Duration.ofSeconds(TIMEOUT_SECONDS))\n .build();\n\n HttpResponse 职责:保留配额与启用判断,对外提供 AI 顾问语义能力。\n * 真实推理由独立的 {@link IntelligenceInferenceOrchestrator} 执行。 默认直连 DeepSeek,也支持通过 LiteLLM 网关接入,配置方式(环境变量):\n * 若未配置 AI_API_KEY,所有调用会立即返回 null,系统继续走本地规则引擎,\n * 不影响任何现有功能。\n */\n@Service\n@Lazy\n@Slf4j\npublic class AiAdvisorService {\n\n @Value(\"${ai.deepseek.api-key:}\")\n private String apiKey;\n\n /** 每个租户每天最多允许调用 DeepSeek 的次数(0 = 不限制) */\n @Value(\"${ai.deepseek.daily-quota-per-tenant:50}\")\n private int dailyQuotaPerTenant;\n\n @Autowired\n private IntelligenceInferenceOrchestrator intelligenceInferenceOrchestrator;\n\n /**\n * 每租户每日调用计数器\n * key = \"tenantId_yyyyMMdd\",value = 当日已调用次数\n * 不同日期的 key 自动过期(不再被读写),内存占用可忽略不计\n */\n private final ConcurrentHashMap 结果按 contextSummary 内容缓存 5 分钟,相同摘要(同一租户当日数据未变化)\n * 直接返回缓存值,避免重复调用 DeepSeek API(每次约 2~3 秒延迟)。\n * Redis 不可用时自动降级为直接调用 AI(见 RedisConfig.errorHandler)。 职责:保留配额与启用判断,对外提供 AI 顾问语义能力。\n * 真实推理由独立的 {@link IntelligenceInferenceOrchestrator} 执行。 默认直连 DeepSeek,也支持通过 LiteLLM 网关接入,配置方式(环境变量):\n * 若未配置 AI_API_KEY,所有调用会立即返回 null,系统继续走本地规则引擎,\n * 不影响任何现有功能。\n */\n@Service\n@Lazy\n@Slf4j\npublic class AiAdvisorService {\n\n @Value(\"${ai.deepseek.api-key:}\")\n private String apiKey;\n\n /** 每个租户每天最多允许调用 DeepSeek 的次数(0 = 不限制) */\n @Value(\"${ai.deepseek.daily-quota-per-tenant:50}\")\n private int dailyQuotaPerTenant;\n\n @Autowired\n private IntelligenceInferenceOrchestrator intelligenceInferenceOrchestrator;\n\n /**\n * 每租户每日调用计数器\n * key = \"tenantId_yyyyMMdd\",value = 当日已调用次数\n * 不同日期的 key 自动过期(不再被读写),内存占用可忽略不计\n */\n private final ConcurrentHashMap 结果按 contextSummary 内容缓存 5 分钟,相同摘要(同一租户当日数据未变化)\n * 直接返回缓存值,避免重复调用 DeepSeek API(每次约 2~3 秒延迟)。\n * Redis 不可用时自动降级为直接调用 AI(见 RedisConfig.errorHandler)。 通过 Qdrant REST API v1.x 实现向量存储与相似检索。\n * 每个租户使用同一个 collection,通过 tenant_id payload 过滤隔离。\n *\n * 向量生成优先级:① Agnes 视觉分析 + Agnes/DeepSeek Embedding(图片→文字描述→向量,推荐)\n * ② DeepSeek Embedding API(text-embedding-v2,用图片URL文本生成向量)\n * ③ 关键词哈希伪向量(pseudoEmbedding,128维,无需 API Key)\n *\n * 配置项(application.yml):\n * 使用 Qdrant 的 /points/query 端点,支持稀疏+稠密混合检索。\n * 如果 Qdrant 不支持混合检索(旧版本),自动降级到纯稠密检索。\n *\n * @param tenantId 租户ID(必须非null,否则拒绝搜索)\n * @param queryText 查询文本\n * @param topK 返回条数\n * @return 匹配点列表(按综合分数降序)\n */\n public List 分词策略:中文按双字符滑动窗口 + 单字符,英文按空格分词。\n * token-id 通过字符串哈希映射到正整数空间。\n * tf(词频)归一化到 [0, 1] 区间。\n */\n private SparseVector computeSparseVector(String text) {\n if (text == null || text.isBlank()) return null;\n try {\n // 分词:中文双字符窗口 + 单字符 + 英文单词\n Map 向量生成优先级(尽量只用 AGNES_API_KEY 一个 Key):\n * 1. Agnes 视觉分析 + Agnes Embedding(图片→描述→向量,只需 AGNES_API_KEY)\n * 2. Agnes 视觉分析 + DeepSeek Embedding(图片→描述→向量,需两个 Key)\n * 3. DeepSeek 纯文本 Embedding(用图片 URL 文本生成向量,质量一般)\n * 4. 伪向量(哈希)— 最低质量,仅兜底\n */\n public float[] computeMultimodalEmbedding(String imageUrl) {\n if (!qdrantEnabled) return null;\n if (imageUrl == null || imageUrl.isBlank()) {\n throw new IllegalArgumentException(\"imageUrl 不能为空\");\n }\n\n // ========== 运行时诊断:打印当前配置状态 ==========\n boolean hasAgnesKey = agnesApiKey != null && !agnesApiKey.isEmpty();\n boolean hasDeepSeekKey = deepseekApiKey != null && !deepseekApiKey.isEmpty();\n boolean hasInferenceOrch = inferenceOrchestrator != null;\n log.info(\"[Qdrant] 向量生成启动 imageUrlLen={} hasAgnesKey={} hasDeepSeekKey={} hasInferenceOrch={}\",\n imageUrl.length(), hasAgnesKey, hasDeepSeekKey, hasInferenceOrch);\n\n // ========== 第 1 级:Agnes 视觉分析 → 文字描述 → Embedding 向量 ==========\n if (hasAgnesKey && hasInferenceOrch) {\n try {\n log.info(\"[Qdrant] 尝试方案1: Agnes视觉描述 + Agnes Embedding (模型={})\", agnesModel);\n String visualDescription = describeImageWithAgnes(imageUrl);\n if (visualDescription != null && !visualDescription.isBlank()) {\n log.info(\"[Qdrant] Agnes视觉描述成功 descLen={}\", visualDescription.length());\n // 方案1a: 尝试用 Agnes Embedding\n float[] vec = tryAgnesEmbedding(visualDescription);\n if (vec != null) {\n log.info(\"[Qdrant] ✓ 方案1成功 Agnes视觉+Agnes Embedding 维度={}\", vec.length);\n return vec;\n }\n // 方案1b: Agnes Embedding 不支持,降级到 DeepSeek Embedding\n if (hasDeepSeekKey) {\n vec = callEmbeddingApi(visualDescription);\n log.info(\"[Qdrant] ✓ 方案2成功 Agnes视觉+DeepSeek Embedding 维度={}\", vec.length);\n return vec;\n }\n // 方案1c: 只有 Agnes Key,用 Agnes Embedding 重试一次(兜底特殊路径)\n log.warn(\"[Qdrant] 已获取视觉描述但无可用 Embedding endpoint,继续降级\");\n } else {\n log.warn(\"[Qdrant] Agnes视觉描述返回空\");\n }\n } catch (Exception e) {\n log.warn(\"[Qdrant] Agnes视觉+Embedding 失败: {}\", e.getMessage());\n }\n } else {\n log.warn(\"[Qdrant] 跳过 Agnes 链路: hasAgnesKey={} hasInferenceOrch={}\", hasAgnesKey, hasInferenceOrch);\n }\n\n // ========== 第 2 级:DeepSeek 纯文本 Embedding(用图片 URL 文本生成向量) ==========\n if (hasDeepSeekKey) {\n log.info(\"[Qdrant] 尝试方案3: DeepSeek 文本 Embedding(用 imageUrl 文本)\");\n try {\n float[] vec = callEmbeddingApi(imageUrl);\n log.info(\"[Qdrant] ✓ 方案3成功 DeepSeek 文本 Embedding 维度={}\", vec.length);\n return vec;\n } catch (Exception e) {\n log.warn(\"[Qdrant] DeepSeek Embedding 失败: {}\", e.getMessage());\n }\n }\n\n // ========== 第 3 级:伪向量兜底 ==========\n log.error(\"[Qdrant] ⚠ 所有 Embedding 方案不可用,降级为伪向量(搜索质量极低)!\" +\n \"建议:1)在微信云【环境变量】配置 AGNES_API_KEY 以启用视觉分析;\" +\n \"2)或配置 DEEPSEEK_API_KEY 启用文本 Embedding\");\n return pseudoEmbedding(imageUrl);\n }\n\n /**\n * 尝试调用 Agnes 的 /v1/embeddings 端点。\n * Agnes 声称 OpenAI 兼容,如果支持 embeddings 端点,则只需一个 API Key。\n * 如果不支持,返回 null,由调用方降级到 DeepSeek。\n */\n private float[] tryAgnesEmbedding(String text) {\n try {\n String agnesBaseUrl = agnesApiUrl.replace(\"/chat/completions\", \"\");\n String url = agnesBaseUrl + \"/embeddings\";\n ObjectNode body = objectMapper.createObjectNode();\n body.put(\"model\", agnesModel);\n body.put(\"input\", text);\n body.put(\"encoding_format\", \"float\");\n\n HttpHeaders headers = new HttpHeaders();\n headers.setContentType(MediaType.APPLICATION_JSON);\n headers.setBearerAuth(agnesApiKey);\n HttpEntity 根据 originalCreateTime 自动计算分级(HOT/WARM/COLD),写入 payload.tier 字段。\n * 召回时 {@link #searchArchivalTiered} 可按 tier 过滤,优先返回 HOT 数据。\n *\n * @param tier 分级(null 时根据 createTime 自动计算;createTime 也为空时默认 HOT)\n * @return true=成功;false=失败或 Qdrant 不可用\n */\n public boolean upsertArchivalTiered(Long tenantId, String originalId, String memoryType,\n String summary, String keyEntities, String createTime,\n com.fashion.supplychain.intelligence.entity.ArchivalTier tier) {\n if (!qdrantEnabled) return false;\n if (tenantId == null || originalId == null) return false;\n if (summary == null || summary.isBlank()) return false;\n\n ensureArchivalCollection(tenantId);\n\n // P3-3:分级计算(优先使用传入 tier,否则根据 createTime 推导)\n com.fashion.supplychain.intelligence.entity.ArchivalTier finalTier = tier;\n if (finalTier == null) {\n java.time.LocalDateTime originalTime = parseCreateTime(createTime);\n finalTier = com.fashion.supplychain.intelligence.entity.ArchivalTier.of(\n originalTime, java.time.LocalDateTime.now());\n }\n\n try {\n float[] vector = computeEmbedding(summary);\n if (vector == null) {\n log.debug(\"[Archival] 向量生成失败,跳过归档 tenantId={} originalId={}\", tenantId, originalId);\n return false;\n }\n\n String pointId = tenantId + \":\" + originalId;\n ObjectNode body = objectMapper.createObjectNode();\n ArrayNode points = body.putArray(\"points\");\n ObjectNode point = points.addObject();\n point.put(\"id\", pointId);\n point.put(\"vector\", toJsonArray(vector));\n\n ObjectNode payload = point.putObject(\"payload\");\n payload.put(\"tenant_id\", tenantId); // P0 铁律 4:payload 必含 tenant_id\n payload.put(\"original_id\", originalId);\n payload.put(\"memory_type\", memoryType != null ? memoryType : \"unknown\");\n payload.put(\"summary\", summary.length() > 1000 ? summary.substring(0, 1000) : summary);\n payload.put(\"key_entities\", keyEntities != null ? keyEntities : \"\");\n payload.put(\"create_time\", createTime != null ? createTime : \"\");\n payload.put(\"archived_at\", System.currentTimeMillis());\n payload.put(\"tier\", finalTier.name()); // P3-3:分级字段\n\n String url = qdrantUrl + \"/collections/\" + archivalCollectionName(tenantId) + \"/points?wait=true\";\n restTemplate.postForEntity(url, jsonEntity(body.toString()), String.class);\n return true;\n } catch (Exception e) {\n log.warn(\"[Archival] 写入归档失败 tenantId={} originalId={}: {}\", tenantId, originalId, e.getMessage());\n return false;\n }\n }\n\n /**\n * P3-3:解析 create_time 字符串为 LocalDateTime(容错处理)。\n */\n private java.time.LocalDateTime parseCreateTime(String createTime) {\n if (createTime == null || createTime.isBlank()) return null;\n try {\n return java.time.LocalDateTime.parse(createTime);\n } catch (Exception e) {\n // 兼容 ISO 末尾带 Z 的格式\n try {\n return java.time.LocalDateTime.parse(createTime.replace(\"Z\", \"\"));\n } catch (Exception ex) {\n return null;\n }\n }\n }\n\n /**\n * 检索归档记忆(L5 召回)。\n * @param tenantId 租户ID(必填,跨租户隔离)\n * @param queryText 查询文本\n * @param topK 返回条数(默认 5)\n * @param startTimeIso 可选起始时间(ISO 格式,如 \"2026-01-01T00:00:00\"),null 不过滤\n * @param endTimeIso 可选结束时间,null 不过滤\n * @return 命中结果列表(按相似度降序);空列表表示无结果或 Qdrant 不可用\n */\n public List 策略说明:\n * 调用流程:\n * \n *
\n *\n * \n *
\n *\n * \n *
\n *\n * \n *
\n *\n * \n *
\n *\n * \n *
\n *\n * \n *
\n *\n * \n *
\n *\n * \n *
\n *\n * \n *
\n *\n * \n * DEEPSEEK_API_KEY = sk-xxxxxxxxxxxxxxxx\n * AI_LITELLM_ENABLED = true/false\n * AI_LITELLM_BASE_URL = http://localhost:4000\n * AI_LITELLM_API_KEY = sk-virtual-key\n *
\n *\n * \n * DEEPSEEK_API_KEY = sk-xxxxxxxxxxxxxxxx\n * AI_LITELLM_ENABLED = true/false\n * AI_LITELLM_BASE_URL = http://localhost:4000\n * AI_LITELLM_API_KEY = sk-virtual-key\n *
\n *\n * \n * DEEPSEEK_API_KEY = sk-xxxxxxxxxxxxxxxx\n * AI_LITELLM_ENABLED = true/false\n * AI_LITELLM_BASE_URL = http://localhost:4000\n * AI_LITELLM_API_KEY = sk-virtual-key\n *
\n *\n * \n * DEEPSEEK_API_KEY = sk-xxxxxxxxxxxxxxxx\n * AI_LITELLM_ENABLED = true/false\n * AI_LITELLM_BASE_URL = http://localhost:4000\n * AI_LITELLM_API_KEY = sk-virtual-key\n *
\n *\n * \n * DEEPSEEK_API_KEY = sk-xxxxxxxxxxxxxxxx\n * AI_LITELLM_ENABLED = true/false\n * AI_LITELLM_BASE_URL = http://localhost:4000\n * AI_LITELLM_API_KEY = sk-virtual-key\n *
\n *\n * \n * DEEPSEEK_API_KEY = sk-xxxxxxxxxxxxxxxx\n * AI_LITELLM_ENABLED = true/false\n * AI_LITELLM_BASE_URL = http://localhost:4000\n * AI_LITELLM_API_KEY = sk-virtual-key\n *
\n *\n * \n * DEEPSEEK_API_KEY = sk-xxxxxxxxxxxxxxxx\n * AI_LITELLM_ENABLED = true/false\n * AI_LITELLM_BASE_URL = http://localhost:4000\n * AI_LITELLM_API_KEY = sk-virtual-key\n *
\n *\n * \n * AI_API_KEY = sk-xxxxxxxxxxxxxxxx (必填,有此值才会真实调用 AI)\n * AI_API_URL = https://api.deepseek.com/v1/chat/completions (可选,默认 DeepSeek)\n * AI_MODEL = deepseek-chat (可选,默认 deepseek-chat)\n *
\n *\n * \n * AI_API_KEY = sk-xxxxxxxxxxxxxxxx (必填,有此值才会真实调用 AI)\n * AI_API_URL = https://api.deepseek.com/v1/chat/completions (可选,默认 DeepSeek)\n * AI_MODEL = deepseek-chat (可选,默认 deepseek-chat)\n *
\n *\n * \n * DEEPSEEK_API_KEY = sk-xxxxxxxxxxxxxxxx\n * AI_LITELLM_ENABLED = true/false\n * AI_LITELLM_BASE_URL = http://localhost:4000\n * AI_LITELLM_API_KEY = sk-virtual-key\n *
\n *\n * \n * DEEPSEEK_API_KEY = sk-xxxxxxxxxxxxxxxx\n * AI_LITELLM_ENABLED = true/false\n * AI_LITELLM_BASE_URL = http://localhost:4000\n * AI_LITELLM_API_KEY = sk-virtual-key\n *
\n *\n * \n * intelligence:\n * qdrant:\n * url: http://localhost:6333\n * collection: fashion_memory\n * vector-size: 1024\n * ai:\n * agnes:\n * api-key: ${AGNES_API_KEY:} # Agnes AI 视觉+Embedding\n * model: agnes-2.5-flash\n * deepseek:\n * api-key: sk-xxx\n * embedding-model: text-embedding-v2\n * \n */\n@Service\n@Lazy\n@Slf4j\npublic class QdrantService {\n\n private static final String COLLECTION_DEFAULT = \"fashion_memory\";\n private static final int VECTOR_DIM_REAL = 1024;\n private static final int VECTOR_DIM_PSEUDO = 128;\n /** 款式图片单独集合,与文字记忆集合分离 */\n private static final String STYLE_IMAGE_COLLECTION = \"style_images\";\n /** 稀疏向量名称,与 Qdrant 集合的 sparse_vectors 配置对应 */\n private static final String SPARSE_VECTOR_NAME = \"text-sparse\";\n\n @Value(\"${intelligence.qdrant.url:http://localhost:6333}\")\n private String qdrantUrl;\n\n @Value(\"${intelligence.qdrant.collection:\" + COLLECTION_DEFAULT + \"}\")\n private String collectionName;\n\n @Value(\"${ai.deepseek.api-key:}\")\n private String deepseekApiKey;\n\n @Value(\"${ai.deepseek.embedding-model:text-embedding-v2}\")\n private String embeddingModel;\n\n @Value(\"${ai.deepseek.base-url:https://api.deepseek.com}\")\n private String deepseekBaseUrl;\n\n @Value(\"${intelligence.qdrant.enabled:false}\")\n private boolean qdrantEnabled;\n\n @Value(\"${intelligence.qdrant.timeout-seconds:10}\")\n private int qdrantTimeoutSeconds;\n\n @Autowired\n private ObjectMapper objectMapper;\n\n @Value(\"${ai.agnes.api-key:}\")\n private String agnesApiKey;\n\n @Value(\"${ai.agnes.api-url:https://apihub.agnes-ai.com/v1/chat/completions}\")\n private String agnesApiUrl;\n\n @Value(\"${ai.agnes.model:agnes-2.5-flash}\")\n private String agnesModel;\n\n @Autowired\n private com.fashion.supplychain.intelligence.orchestration.IntelligenceInferenceOrchestrator inferenceOrchestrator;\n\n private RestTemplate restTemplate;\n\n private final ConcurrentHashMap\n *
\n *\n * @param tierFilter 分级过滤(null 表示全量搜索)\n */\n public List\n *
\n *\n * @param includeCold 是否最终兜底到 COLD 层\n */\n public List