feat(dev): 本地开发工具集 v0.5.6-dev-tooling

包含本地 dev 链路完整跑通的工具集(不进生产):

backend:
- dev_auth.py: /api/dev/login Mock 企微 OAuth(/dev/* 路由)
- messages.py: dev 模式短路企微推送,避免 invalid corpid 噪音
- main.py: dev 模式启动时建 5 条 demo conversation,让前端有数据可测

frontend:
- PortalSelect.vue: dev 模式 enterRole 跳完整 URL(5173/5174/5175 端口),生产仍走相对路径

infrastructure:
- docker-compose.dev.yml: dev compose(包含 backend/postgres/redis)

scripts(Windows PowerShell):
- dev-frontend-install.ps1: 一次性装 4 个前端依赖
- dev-frontend-start.ps1: 后台起 4 个前端 dev server
- dev-check-schema-drift.ps1: 对比 SQLAlchemy 模型 vs Postgres schema,漂移 exit 1

docs:
- CURRENT-FOCUS.md: 项目状态看板(每次 session 维护)
This commit is contained in:
Simon
2026-06-16 19:24:02 +08:00
parent cec5607c45
commit eee2bcc071
9 changed files with 963 additions and 22 deletions
+51 -7
View File
@@ -140,12 +140,16 @@ const selectedRole = ref<string | null>(null)
// ==================== 生命周期 ====================
onMounted(async () => {
/**
* 初始化门户会话(可重入)
* 流程:OAuth2 回调 → 缓存 token → 没登录就尝试 Mock(OAuth2 失败时)→ 加载用户信息
*/
async function initPortalSession(): Promise<boolean> {
const urlParams = new URLSearchParams(window.location.search)
const token = urlParams.get('token')
const code = urlParams.get('code')
// 1. 企微 OAuth2 回调URL 中有 code 参数
// 1. 企微 OAuth2 回调:URL 中有 code 参数
if (code && !token) {
loading.value = true
try {
@@ -197,6 +201,30 @@ onMounted(async () => {
}
}
// 无 corpId 或获取失败:显示错误(开发环境可 Mock 登录)
// DEV_MODE 兜底:本地 dev 环境自动调 /api/dev/login 拿 token
if (import.meta.env.DEV || import.meta.env.VITE_DEV_MODE === 'true') {
try {
loading.value = true
const devResp = await apiClient.get('/dev/login', {
params: {
userid: 'dev-multi-001', // 多角色用户,可以看到完整角色选择
name: 'Dev User',
dept: '信息技术部',
},
})
const devToken = devResp.data?.data?.token
if (devToken) {
portalStore.setToken(devToken)
// 直接刷新页面,onMounted 重跑,这时 token 已在 localStorage 里
window.location.reload()
return
}
} catch (devErr) {
console.error('DEV_MODE Mock 登录失败:', devErr)
} finally {
loading.value = false
}
}
error.value = '未登录,请通过企业微信工作台访问'
return
}
@@ -218,6 +246,13 @@ onMounted(async () => {
}
}
// 否则:显示角色选择页面(让用户选择)
}
/**
* 钩到 Vue 生命周期:挂载时执行一次
*/
onMounted(() => {
initPortalSession()
})
// ==================== 方法 ====================
@@ -237,11 +272,20 @@ function enterRole(role: string) {
localStorage.setItem('portal_selected_role', role)
// 跳转到对应的工作台
const roleUrls: Record<string, string> = {
user: '/itdesk/',
agent: '/itagent/',
admin: '/itadmin/',
}
// dev 模式:4 个前端在不同端口,要用完整 URL(从 import.meta.env 读)
// 生产模式:nnginx 同一域名不同子路径,直接用相对路径
const isDev = import.meta.env.DEV
const roleUrls: Record<string, string> = isDev
? {
user: import.meta.env.VITE_DESK_URL_DEV || 'http://localhost:5174/itdesk/',
agent: import.meta.env.VITE_AGENT_URL_DEV || 'http://localhost:5173/itagent/',
admin: import.meta.env.VITE_ADMIN_URL_DEV || 'http://localhost:5175/itadmin/',
}
: {
user: '/itdesk/',
agent: '/itagent/',
admin: '/itadmin/',
}
const url = roleUrls[role]
if (url) {