feat: 员工门户UI优化 - PortalLayout/Logo组件/QR扫码登录/合同与政策页面重构
This commit is contained in:
@@ -3,8 +3,10 @@ import bcrypt from 'bcryptjs'
|
||||
import multer from 'multer'
|
||||
import path from 'path'
|
||||
import fs from 'fs'
|
||||
import jwt from 'jsonwebtoken'
|
||||
import prisma from '../lib/prisma'
|
||||
import { signAccessToken, verifyAccessToken } from '../lib/jwt'
|
||||
import { authMiddleware, AuthRequest } from '../middleware/auth'
|
||||
import { portalLoginSchema, portalSendCodeSchema, portalVerifyCodeSchema, onboardingSchema, contractConfirmSchema, contractSendCodeSchema } from '../schemas/portal.schema'
|
||||
import { setCode, getCode, deleteCode, updateCode, checkRateLimit } from '../lib/codeStore'
|
||||
import { createEvidence } from '../services/evidence.service'
|
||||
@@ -544,4 +546,69 @@ router.post('/policies/:id/read', portalAuth, async (req: Request, res: Response
|
||||
}
|
||||
})
|
||||
|
||||
// ========== 一次性自动登录 ==========
|
||||
|
||||
const AUTO_LOGIN_SECRET = process.env.JWT_SECRET || 'dev-secret'
|
||||
|
||||
/**
|
||||
* 管理端生成员工一次性自动登录 token(10 分钟有效)
|
||||
* POST /portal/auto-login-token body: { employeeId }
|
||||
*/
|
||||
router.post('/auto-login-token', authMiddleware, async (req: AuthRequest, res, next) => {
|
||||
try {
|
||||
const { employeeId } = req.body
|
||||
if (!employeeId) {
|
||||
return res.status(400).json({ success: false, error: { code: 'BAD_REQUEST', message: '缺少 employeeId' } })
|
||||
}
|
||||
const employee = await prisma.employee.findFirst({
|
||||
where: { id: employeeId, orgId: req.user!.orgId, status: 'ACTIVE' },
|
||||
select: { id: true, name: true, phone: true, orgId: true },
|
||||
})
|
||||
if (!employee) {
|
||||
return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '员工不存在或已离职' } })
|
||||
}
|
||||
const token = jwt.sign(
|
||||
{ id: employee.id, orgId: employee.orgId, role: 'EMPLOYEE_AUTO', name: employee.name },
|
||||
AUTO_LOGIN_SECRET,
|
||||
{ expiresIn: '10m' }
|
||||
)
|
||||
res.json({ success: true, data: { token, employeeName: employee.name, phone: employee.phone } })
|
||||
} catch (err) {
|
||||
next(err)
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
* 员工端自动登录(消费一次性 token)
|
||||
* GET /portal/auto-login?token=xxx
|
||||
*/
|
||||
router.get('/auto-login', async (req, res, next) => {
|
||||
try {
|
||||
const { token } = req.query
|
||||
if (!token || typeof token !== 'string') {
|
||||
return res.status(400).json({ success: false, error: { code: 'BAD_REQUEST', message: '缺少 token' } })
|
||||
}
|
||||
let payload: any
|
||||
try {
|
||||
payload = jwt.verify(token, AUTO_LOGIN_SECRET)
|
||||
} catch {
|
||||
return res.status(401).json({ success: false, error: { code: 'TOKEN_EXPIRED', message: '链接已过期,请重新扫码' } })
|
||||
}
|
||||
if (payload.role !== 'EMPLOYEE_AUTO') {
|
||||
return res.status(401).json({ success: false, error: { code: 'TOKEN_INVALID', message: '无效的登录链接' } })
|
||||
}
|
||||
const employee = await prisma.employee.findFirst({
|
||||
where: { id: payload.id, orgId: payload.orgId, status: 'ACTIVE' },
|
||||
select: { id: true, name: true, department: true, orgId: true },
|
||||
})
|
||||
if (!employee) {
|
||||
return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '员工不存在或已离职' } })
|
||||
}
|
||||
const accessToken = signAccessToken({ id: employee.id, orgId: employee.orgId, role: 'EMPLOYEE' })
|
||||
res.json({ success: true, data: { token: accessToken, employee: { id: employee.id, name: employee.name, department: employee.department } } })
|
||||
} catch (err) {
|
||||
next(err)
|
||||
}
|
||||
})
|
||||
|
||||
export default router
|
||||
|
||||
Reference in New Issue
Block a user