const crypto = require('crypto') const AppError = require('../utils/appError') const logger = require('../utils/logger') const wechatService = require('./wechatService') const jwtService = require('./jwtService') const pocketbaseService = require('./pocketbaseService') const userMutationLocks = new Map() const GUEST_USER_TYPE = '游客' const REGISTERED_USER_TYPE = '注册用户' function buildUserId() { const now = new Date() const date = `${now.getFullYear()}${String(now.getMonth() + 1).padStart(2, '0')}${String(now.getDate()).padStart(2, '0')}` const suffix = crypto.randomInt(1000, 9999) return `U${date}${suffix}` } async function enrichUser(user) { const company = await pocketbaseService.getCompanyByCompanyId(user.company_id) return { pb_id: user.id, users_id: user.users_id, users_type: user.users_type || GUEST_USER_TYPE, users_name: user.users_name, users_phone: user.users_phone, users_phone_masked: maskPhone(user.users_phone), users_picture: user.users_picture, users_wx_openid: user.users_wx_openid, company_id: user.company_id || '', company, created: user.created, updated: user.updated, raw: user, } } async function findUserByOpenid(usersWxOpenid) { const users = await pocketbaseService.listUsersByFilter(`users_wx_openid = "${usersWxOpenid}"`) return users[0] || null } function maskPhone(phone = '') { if (!phone || phone.length < 7) return '' return `${phone.slice(0, 3)}****${phone.slice(-4)}` } function isInfoComplete(user = {}) { return Boolean(user.users_name && user.users_phone && user.users_picture) } function isAllProfileFieldsEmpty(user = {}) { return !user.users_name && !user.users_phone && !user.users_picture } async function withUserLock(lockKey, handler) { const previous = userMutationLocks.get(lockKey) || Promise.resolve() let release const current = new Promise((resolve) => { release = resolve }) userMutationLocks.set(lockKey, previous.then(() => current)) await previous try { return await handler() } finally { release() if (userMutationLocks.get(lockKey) === current) { userMutationLocks.delete(lockKey) } } } async function authenticateWechatUser(payload) { const openid = await wechatService.getWxOpenId(payload.users_wx_code) return withUserLock(`auth:${openid}`, async () => { const existing = await findUserByOpenid(openid) if (existing) { logger.warn('微信注册命中已存在账号', { users_wx_openid: openid, users_type: existing.users_type || GUEST_USER_TYPE, }) const user = await enrichUser(existing) const token = jwtService.signAccessToken({ users_id: user.users_id, users_wx_openid: user.users_wx_openid, }) return { status: 'login_success', is_info_complete: isInfoComplete(existing), token, user, } } const created = await pocketbaseService.createUser({ users_id: buildUserId(), users_wx_openid: openid, users_type: GUEST_USER_TYPE, }) const user = await enrichUser(created) const token = jwtService.signAccessToken({ users_id: user.users_id, users_wx_openid: user.users_wx_openid, }) logger.info('微信用户注册成功', { users_id: user.users_id, users_phone: user.users_phone, users_type: user.users_type, }) return { status: 'register_success', is_info_complete: false, token, user, } }) } async function updateWechatUserProfile(usersWxOpenid, payload) { return withUserLock(`profile:${usersWxOpenid}`, async () => { const currentUser = await findUserByOpenid(usersWxOpenid) if (!currentUser) { throw new AppError('未找到待编辑的用户', 404) } const usersPhone = await wechatService.getWxPhoneNumber(payload.users_phone_code) if (usersPhone && usersPhone !== currentUser.users_phone) { const samePhoneUsers = await pocketbaseService.listUsersByFilter(`users_phone = "${usersPhone}"`) const phoneUsedByOther = samePhoneUsers.some((item) => item.id !== currentUser.id) if (phoneUsedByOther) { throw new AppError('手机号已被注册', 400) } } const shouldPromoteUserType = isAllProfileFieldsEmpty(currentUser) && payload.users_name && usersPhone && payload.users_picture && (currentUser.users_type === GUEST_USER_TYPE || !currentUser.users_type) const updatePayload = { users_name: payload.users_name, users_phone: usersPhone, users_picture: payload.users_picture, } if (shouldPromoteUserType) { updatePayload.users_type = REGISTERED_USER_TYPE } const updated = await pocketbaseService.updateUser(currentUser.id, updatePayload) const user = await enrichUser(updated) logger.info('微信用户资料更新成功', { users_id: user.users_id, users_phone: user.users_phone, users_type_before: currentUser.users_type || GUEST_USER_TYPE, users_type_after: user.users_type, users_type_promoted: shouldPromoteUserType, }) return { status: 'update_success', user, } }) } async function refreshWechatToken(usersWxOpenid) { const userRecord = await findUserByOpenid(usersWxOpenid) if (!userRecord) { throw new AppError('未注册用户', 404) } const token = jwtService.signAccessToken({ users_id: userRecord.users_id, users_wx_openid: userRecord.users_wx_openid, }) logger.info('微信用户刷新令牌成功', { users_id: userRecord.users_id, users_wx_openid: userRecord.users_wx_openid, }) return { token, } } module.exports = { authenticateWechatUser, updateWechatUserProfile, refreshWechatToken, }