/** * 商家子客服 API 与身份上下文 * * 身份说明(互斥,不冲突): * - 商家老板:shangjiastatus=1,走原商家接口,扣商家余额 * - 子客服(含总管/财务/派单员):staff_context.active,走 merchant-staff 接口,扣个人额度 * - 同账号不能既是老板又是子客服;换角色只改权限,额度/统计仍归属同一 member 记录 */ import { isApiSuccess } from './api-helper.js'; const STAFF_PREFIX = '/yonghu/merchant-staff'; export const STAFF_API = { me: `${STAFF_PREFIX}/me`, bind: `${STAFF_PREFIX}/bind`, inviteCreate: `${STAFF_PREFIX}/invite/create`, inviteList: `${STAFF_PREFIX}/invite/list`, inviteRevoke: `${STAFF_PREFIX}/invite/revoke`, memberList: `${STAFF_PREFIX}/member/list`, memberUpdateRole: `${STAFF_PREFIX}/member/update-role`, memberRemove: `${STAFF_PREFIX}/member/remove`, memberDisable: `${STAFF_PREFIX}/member/disable`, roleList: `${STAFF_PREFIX}/role/list`, roleSetPerms: `${STAFF_PREFIX}/role/set-perms`, walletAllocate: `${STAFF_PREFIX}/wallet/allocate`, walletRevoke: `${STAFF_PREFIX}/wallet/revoke`, walletLedger: `${STAFF_PREFIX}/wallet/ledger`, auditList: `${STAFF_PREFIX}/audit/list`, rank: `${STAFF_PREFIX}/rank`, roleCreate: `${STAFF_PREFIX}/role/create`, roleSetPerms: `${STAFF_PREFIX}/role/set-perms`, memberUpdatePerms: `${STAFF_PREFIX}/member/update-perms`, templateList: `${STAFF_PREFIX}/template/list`, templateCreate: `${STAFF_PREFIX}/template/create`, templateUpdate: `${STAFF_PREFIX}/template/update`, templateDelete: `${STAFF_PREFIX}/template/delete`, linkGenerate: `${STAFF_PREFIX}/link/generate`, linkList: `${STAFF_PREFIX}/link/list`, orderList: `${STAFF_PREFIX}/order/list`, orderDetail: `${STAFF_PREFIX}/order/detail`, orderDispatch: `${STAFF_PREFIX}/order/dispatch`, productTypes: `${STAFF_PREFIX}/order/product-types`, orderSettle: `${STAFF_PREFIX}/order/settle`, orderCancel: `${STAFF_PREFIX}/order/cancel`, orderRefund: `${STAFF_PREFIX}/order/refund`, penaltyApply: `${STAFF_PREFIX}/order/penalty-apply`, penaltyManage: `${STAFF_PREFIX}/order/penalty-manage`, changePlayer: `${STAFF_PREFIX}/order/change-player`, }; export function getStaffContext() { return wx.getStorageSync('staff_context') || null; } export function setStaffContext(ctx) { if (ctx && ctx.active) { wx.setStorageSync('staff_context', ctx); wx.setStorageSync('staffstatus', 1); } else { wx.removeStorageSync('staff_context'); wx.setStorageSync('staffstatus', 0); } } export function isStaffMode() { // 商家老板与子客服互斥:已认证商家时一律按老板身份 if (Number(wx.getStorageSync('shangjiastatus')) === 1) { return false; } const ctx = getStaffContext(); if (ctx && ctx.active) { return true; } // 绑定/登录已写入 staffstatus,context 异步刷新期间仍视为客服 return Number(wx.getStorageSync('staffstatus')) === 1; } export function clearStaffSession() { wx.removeStorageSync('staff_context'); wx.setStorageSync('staffstatus', 0); } /** 是否仅为商家老板(非子客服) */ export function isMerchantOwnerOnly() { return Number(wx.getStorageSync('shangjiastatus')) === 1 && !isStaffMode(); } /** 子客服/非老板禁止进入商家充值提现等页面 */ export function guardMerchantOwnerAction(msg) { if (isMerchantOwnerOnly()) return true; wx.showToast({ title: msg || '仅商家老板可充值或提现,子客服请联系老板', icon: 'none', }); return false; } export function staffHasPerm(code) { const ctx = getStaffContext(); if (!ctx || !ctx.permissions) return false; return ctx.permissions.indexOf(code) >= 0; } function loadRequest() { // 延迟加载,避免 App 未注册时 request 模块被提前初始化 return require('./request.js').default; } export async function refreshStaffContext(req) { const cached = getStaffContext(); const token = wx.getStorageSync('token'); if (!token) return null; const doRequest = req || loadRequest(); try { const res = await doRequest({ url: STAFF_API.me, method: 'POST' }); const body = res.data || {}; if (res.statusCode === 401) { return cached; } if (isApiSuccess(body) && body.data && body.data.active) { setStaffContext(body.data); return body.data; } if (body.code === 404 || body.code === 403) { // 已有本地客服身份时勿清空,避免误入「商家入驻」 if (cached && cached.active) { return cached; } if (Number(wx.getStorageSync('staffstatus')) === 1) { return cached; } clearStaffSession(); return null; } return cached; } catch (e) { return cached; } } /** 登录/注册响应写入子客服身份 */ export function applyStaffFromLoginData(data) { if (!data || typeof data !== 'object') return; if (data.staff_context && data.staff_context.active) { setStaffContext(data.staff_context); return; } if (Number(data.staffstatus) === 1) { wx.setStorageSync('staffstatus', 1); return; } if (Object.prototype.hasOwnProperty.call(data, 'staffstatus') && Number(data.staffstatus) !== 1) { clearStaffSession(); } } /** 登录成功后从 /me 拉取最新子客服身份 */ export async function restoreStaffContextAfterAuth() { const token = wx.getStorageSync('token'); if (!token) return null; return refreshStaffContext(loadRequest()); } /** 客服模式下返回客服专用 URL,否则返回商家原 URL */ export function merchantApi(staffPath, ownerPath) { return isStaffMode() ? staffPath : ownerPath; } export function staffCan(action) { if (!isStaffMode()) return true; const map = { dispatch: 'order_dispatch', detail: 'order_detail', settle: 'order_settle', cancel: 'order_cancel', refund: 'order_refund_apply', penalty: 'penalty_apply', penaltyManage: 'penalty_manage', changePlayer: 'order_change_player', staffManage: 'staff_manage', audit: 'audit_view', finance: 'finance_view', walletAllocate: 'wallet_allocate', walletRevoke: 'wallet_revoke', template: 'template_manage', imChat: 'im_chat', }; return staffHasPerm(map[action] || action); } /** 商家老板或有效子客服,均可进入商家端工作台 */ export function isMerchantPortalUser() { return Number(wx.getStorageSync('shangjiastatus')) === 1 || isStaffMode(); } /** 将 /me 返回的 stats 写入页面 */ export function applyStaffStatsToPage(page, ctx) { const st = (ctx && ctx.stats) || {}; if (!st || typeof page.setData !== 'function') return; const patch = {}; if (page.data.stats) { patch.stats = { ...page.data.stats, todayDispatch: st.today_dispatch_count || 0, todayRefund: st.today_refund_count || 0, }; } if ('jinridingdan' in page.data) { patch.jinridingdan = st.today_dispatch_count || 0; patch.jinrituikuan = st.today_refund_count || 0; patch.fabu = st.dispatch_count || 0; patch.tuikuan = st.refund_count || 0; } if (Object.keys(patch).length) { page.setData(patch); } } /** 同步子客服 UI 权限到页面 data(并确保 isShangjia 对客服为 true) */ export function syncStaffUi(page) { const staff = isStaffMode(); const ctx = staff ? (getStaffContext() || {}) : null; const wallet = (ctx && ctx.wallet) || {}; const portal = isMerchantPortalUser(); page.setData({ isShangjia: portal, isStaffMode: staff, isMerchantOwner: !staff && Number(wx.getStorageSync('shangjiastatus')) === 1, staffRoleName: staff ? (ctx.role_name || '客服') : '', staffMerchantId: staff ? (ctx.merchant_id || '') : '', quotaAvailable: staff ? (wallet.quota_available || '0.00') : '', canDispatch: staffCan('dispatch'), canSettle: staffCan('settle'), canCancel: staffCan('cancel'), canRefund: staffCan('refund'), canPenalty: staffCan('penalty'), canPenaltyManage: staffCan('penaltyManage'), canChangePlayer: staffCan('changePlayer'), canStaffManage: staffCan('staffManage'), canWalletAllocate: staffCan('walletAllocate'), canWalletRevoke: staffCan('walletRevoke'), canFinance: staffCan('finance'), canTemplate: staffCan('template'), canAudit: !staff || staffCan('audit'), canImChat: !staff || staffCan('imChat'), showRechargeWithdraw: isMerchantOwnerOnly(), }); } /** 客服模式走 staff 专用 URL,老板走原商家 URL */ export function staffOrOwner(staffUrl, ownerUrl) { return isStaffMode() ? staffUrl : ownerUrl; }