feat: AI routing DB-driven, payment gateway full integration, WeChat mini-program CI/CD
- AI routing rules now stored in system_configs DB table instead of hardcoded config - Multi-model support via name|model composite key for same-provider routing - UnifiedPayService with HMAC-SHA256 gateway integration (alipay/wechat) - Admin payment panel: list, stats, search, filter, refund - WeChat mini-program CI/CD via miniprogram-ci (v1.0.9) - Translation quota extended to LLM provider tier - SearchService with DB-driven provider config (bing/google_cse/searxng) - Footer cleanup across admin/workspace/uni-app - Private key excluded from git tracking
This commit is contained in:
Generated
+11601
-69
File diff suppressed because it is too large
Load Diff
@@ -6,13 +6,16 @@
|
||||
"dev:mp-weixin": "uni -p mp-weixin",
|
||||
"build:mp-weixin": "uni build -p mp-weixin",
|
||||
"dev:h5": "uni",
|
||||
"build:h5": "uni build"
|
||||
"build:h5": "uni build",
|
||||
"upload": "node scripts/upload.js",
|
||||
"preview": "node scripts/upload.js preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@dcloudio/uni-app": "3.0.0-4010520240507001",
|
||||
"@dcloudio/uni-components": "3.0.0-4010520240507001",
|
||||
"@dcloudio/uni-h5": "3.0.0-4010520240507001",
|
||||
"@dcloudio/uni-mp-weixin": "3.0.0-4010520240507001",
|
||||
"miniprogram-ci": "^2.1.31",
|
||||
"vue": "3.4.21"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
const ci = require('miniprogram-ci')
|
||||
const path = require('path')
|
||||
|
||||
const APPID = process.env.WX_APPID || 'wxdad62baf4ccd09e3'
|
||||
const KEY_PATH = process.env.WX_PRIVATE_KEY_PATH || path.resolve(__dirname, '../private.key')
|
||||
const VERSION = process.env.WX_VERSION || '1.0.6'
|
||||
|
||||
// miniprogram-ci 2.x 已知 bug:上传完成后进程残留 setTimeout 无法自动退出
|
||||
// 设置强制退出计时器,防止进程卡死
|
||||
const FORCE_EXIT_MS = 600_000
|
||||
let forceExitTimer = setTimeout(() => {
|
||||
console.error('Upload timed out, forcing exit')
|
||||
process.exit(1)
|
||||
}, FORCE_EXIT_MS)
|
||||
|
||||
async function main() {
|
||||
const project = new ci.Project({
|
||||
appid: APPID,
|
||||
type: 'miniProgram',
|
||||
projectPath: path.resolve(__dirname, '../dist/build/mp-weixin/'),
|
||||
privateKeyPath: KEY_PATH,
|
||||
ignores: ['node_modules/**/*'],
|
||||
})
|
||||
|
||||
const action = process.argv[2] || 'upload'
|
||||
|
||||
if (action === 'preview') {
|
||||
const qrcodeDest = path.resolve(__dirname, '../dist/qrcode.jpg')
|
||||
await ci.preview({
|
||||
project,
|
||||
version: VERSION,
|
||||
desc: process.env.WX_DESC || '自动预览',
|
||||
setting: { minify: true, es6: true, autoPrefixWXSS: true },
|
||||
qrcodeFormat: 'image',
|
||||
qrcodeOutputDest: qrcodeDest,
|
||||
onProgressUpdate: console.log,
|
||||
})
|
||||
console.log('Preview QR:', qrcodeDest)
|
||||
} else {
|
||||
console.log(`Uploading v${VERSION} ...`)
|
||||
const result = await ci.upload({
|
||||
project,
|
||||
version: VERSION,
|
||||
desc: process.env.WX_DESC || '自动构建上传',
|
||||
setting: { minify: true, es6: true, autoPrefixWXSS: true },
|
||||
onProgressUpdate: console.log,
|
||||
})
|
||||
console.log('Upload done:', JSON.stringify(result))
|
||||
}
|
||||
}
|
||||
|
||||
main().then(() => {
|
||||
clearTimeout(forceExitTimer)
|
||||
process.exit(0)
|
||||
}).catch(e => {
|
||||
console.error('Upload failed:', e.message)
|
||||
clearTimeout(forceExitTimer)
|
||||
process.exit(1)
|
||||
})
|
||||
@@ -6,8 +6,12 @@
|
||||
</script>
|
||||
|
||||
<style>
|
||||
/* #ifdef H5 */
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
/* #endif */
|
||||
html, body, #app { height: 100%; width: 100%; }
|
||||
/* #ifdef H5 */
|
||||
uni-page { overflow-y: auto !important; }
|
||||
uni-page-body { overflow-y: auto !important; min-height: 100% !important; }
|
||||
/* #endif */
|
||||
</style>
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
},
|
||||
"quickapp": {},
|
||||
"mp-weixin": {
|
||||
"appid": "",
|
||||
"appid": "wxdad62baf4ccd09e3",
|
||||
"setting": {
|
||||
"urlCheck": false,
|
||||
"es6": true,
|
||||
|
||||
@@ -277,63 +277,22 @@
|
||||
</view>
|
||||
|
||||
<view class="footer">
|
||||
<view class="footer-qrcode">
|
||||
<view class="qrcode-item">
|
||||
<image src="/static/images/yzr/yuzhiran.jpg" class="qrcode-img" mode="aspectFill" />
|
||||
<text class="qrcode-label">公众号</text>
|
||||
</view>
|
||||
<view class="qrcode-item">
|
||||
<image src="/static/images/yzr/yuzhiran-tech.jpg" class="qrcode-img" mode="aspectFill" />
|
||||
<text class="qrcode-label">服务号</text>
|
||||
</view>
|
||||
<view class="qrcode-item">
|
||||
<image src="/static/images/yzr/yuzhiran-yhl.jpg" class="qrcode-img" mode="aspectFill" />
|
||||
<text class="qrcode-label">小程序</text>
|
||||
</view>
|
||||
<view class="qrcode-item">
|
||||
<image src="/static/images/yzr/kefu.png" class="qrcode-img" mode="aspectFill" />
|
||||
<text class="qrcode-label">客服</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="footer-links">
|
||||
<text class="footer-link" @click="goToPage(PAGES.AGREEMENT_PRIVACY)">隐私政策</text>
|
||||
<text class="footer-divider">|</text>
|
||||
<text class="footer-link" @click="goToPage(PAGES.AGREEMENT_TERMS)">用户协议</text>
|
||||
</view>
|
||||
<view class="footer-beian">
|
||||
<a class="footer-beian-link" :href="beianUrl" target="_blank">{{ beianIcp }}</a>
|
||||
<text class="footer-divider">|</text>
|
||||
<a class="footer-beian-link" :href="beianPsbUrl" target="_blank">{{ beianPsb }}</a>
|
||||
</view>
|
||||
<text class="footer-copyright">© {{ copyrightYear }} 北京宇之然科技中心. 保留所有权利.</text>
|
||||
</view>
|
||||
<AiAssistant />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, onUnmounted } from 'vue'
|
||||
import { ref, onUnmounted } from 'vue'
|
||||
import { onShow } from '@dcloudio/uni-app'
|
||||
import { authApi, customerApi, analyticsApi, onboardingApi, notificationApi, followupApi, translateApi, BASE_URL } from '@/utils/api.js'
|
||||
import AiAssistant from '@/components/ai-assistant.vue'
|
||||
import { STORAGE_KEYS, PAGES, EXTERNAL_URLS, APP_INFO, EXTRACT_FIELD_LABELS } from '@/config.js'
|
||||
|
||||
const beianInfo = computed(() => {
|
||||
let hostname = ''
|
||||
try { hostname = window.location.hostname } catch {}
|
||||
if (hostname === 'yuzhiran.com' || hostname === 'www.yuzhiran.com') {
|
||||
return { icp: '京ICP备2026007249号-1', psb: '京公网安备11011502039545号', psbUrl: 'https://beian.mps.gov.cn/#/query/webSearch?code=11011502039545' }
|
||||
}
|
||||
if (hostname === 'yuzhiran.com.cn' || hostname === 'www.yuzhiran.com.cn') {
|
||||
return { icp: '京ICP备2026007249号-2', psb: '京公网安备11011502039622号', psbUrl: 'https://beian.mps.gov.cn/#/query/webSearch?code=11011502039622' }
|
||||
}
|
||||
return { icp: APP_INFO.ICP, psb: APP_INFO.PSB, psbUrl: EXTERNAL_URLS.BEIAN_PSB }
|
||||
})
|
||||
const beianUrl = computed(() => EXTERNAL_URLS.BEIAN)
|
||||
const beianIcp = computed(() => beianInfo.value.icp)
|
||||
const beianPsb = computed(() => beianInfo.value.psb)
|
||||
const beianPsbUrl = computed(() => beianInfo.value.psbUrl)
|
||||
const copyrightYear = computed(() => new Date().getFullYear())
|
||||
import { STORAGE_KEYS, PAGES, EXTRACT_FIELD_LABELS } from '@/config.js'
|
||||
|
||||
const showAnnouncement = ref(false)
|
||||
const currentAnnouncement = ref(0)
|
||||
|
||||
@@ -49,21 +49,31 @@
|
||||
<text class="menu-text">意见反馈</text>
|
||||
<text class="menu-arrow">›</text>
|
||||
</view>
|
||||
<view class="menu-item" @click="goAgreement('privacy')">
|
||||
<text class="menu-icon">📄</text>
|
||||
<text class="menu-text">隐私政策</text>
|
||||
<text class="menu-arrow">›</text>
|
||||
</view>
|
||||
|
||||
<view class="section">
|
||||
<view class="section-title">关于我们</view>
|
||||
<view class="about-item">
|
||||
<text class="about-label">版本</text>
|
||||
<text class="about-value">1.0.0</text>
|
||||
</view>
|
||||
<view class="menu-item" @click="goAgreement('terms')">
|
||||
<text class="menu-icon">📋</text>
|
||||
<text class="menu-text">用户协议</text>
|
||||
<text class="menu-arrow">›</text>
|
||||
<view class="about-item" @click="goAgreement('privacy')">
|
||||
<text class="about-label">隐私政策</text>
|
||||
<text class="about-arrow">›</text>
|
||||
</view>
|
||||
<view class="menu-item">
|
||||
<text class="menu-icon">ℹ️</text>
|
||||
<text class="menu-text">版本</text>
|
||||
<text class="menu-value">1.0.0</text>
|
||||
<view class="about-item" @click="goAgreement('terms')">
|
||||
<text class="about-label">用户协议</text>
|
||||
<text class="about-arrow">›</text>
|
||||
</view>
|
||||
<view class="about-item">
|
||||
<text class="about-label">ICP 备案</text>
|
||||
<text class="about-value">京ICP备2026007249号-1</text>
|
||||
</view>
|
||||
<view class="about-item">
|
||||
<text class="about-label">公安备案</text>
|
||||
<text class="about-value">京公网安备11011502039545号</text>
|
||||
</view>
|
||||
<view class="about-copyright">© 2026 北京宇之然科技中心. 保留所有权利.</view>
|
||||
</view>
|
||||
|
||||
<view class="logout-btn" v-if="user.tier !== 'guest'" @click="logout">退出登录</view>
|
||||
@@ -412,4 +422,36 @@ onShow(loadUser)
|
||||
background: #1890ff;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.about-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 24rpx 30rpx;
|
||||
border-bottom: 2rpx solid #f0f0f0;
|
||||
}
|
||||
|
||||
.about-item:last-child { border-bottom: none; }
|
||||
|
||||
.about-label {
|
||||
flex: 1;
|
||||
font-size: 26rpx;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.about-value {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.about-arrow {
|
||||
font-size: 32rpx;
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
.about-copyright {
|
||||
text-align: center;
|
||||
padding: 24rpx 30rpx;
|
||||
font-size: 22rpx;
|
||||
color: #bbb;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
import { STORAGE_KEYS, PAGES } from '@/config.js'
|
||||
|
||||
export const BASE_URL = '/api/v1'
|
||||
// #ifdef MP-WEIXIN
|
||||
const API_HOST = 'https://trade.yuzhiran.com'
|
||||
// #endif
|
||||
// #ifndef MP-WEIXIN
|
||||
const API_HOST = ''
|
||||
// #endif
|
||||
export const BASE_URL = `${API_HOST}/api/v1`
|
||||
|
||||
const getAuthHeader = () => {
|
||||
const token = uni.getStorageSync(STORAGE_KEYS.TOKEN)
|
||||
|
||||
Reference in New Issue
Block a user