257 lines
8.6 KiB
JavaScript
257 lines
8.6 KiB
JavaScript
/**
|
||
* 商家子客服 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`,
|
||
orderStats: `${STAFF_PREFIX}/order/stats`,
|
||
memberUpdateRemark: `${STAFF_PREFIX}/member/update-remark`,
|
||
};
|
||
|
||
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();
|
||
const staffFlag = Number(wx.getStorageSync('staffstatus')) === 1;
|
||
page.setData({
|
||
isShangjia: portal || staffFlag || !!(page.data && page.data.isShangjia),
|
||
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;
|
||
}
|