492 lines
16 KiB
JavaScript
492 lines
16 KiB
JavaScript
// 全 API 本地拦截;沉默期响应依据 docs/api/package-silence.openapi.json。
|
|
import { mockApiPlugin as fallback } from './pollingSchedulerBrowserFixtures.mjs'
|
|
const time = '2026-10-28T10:00:00+08:00'
|
|
const log = []
|
|
let localPaymentStatus = 'pending_payment'
|
|
const silence = {
|
|
silence_status: 'expired',
|
|
silence_status_name: '沉默期已过期',
|
|
silence_deadline_at: '2026-10-27T23:59:59+08:00',
|
|
silence_period_days: 30,
|
|
silence_extended_days: 0,
|
|
silence_paid_extended_days: 0,
|
|
silence_extension_count: 0,
|
|
silence_free_extension_used: 0,
|
|
silence_adjust_pending: false,
|
|
extendable: true
|
|
}
|
|
const silenceOrder = () => ({
|
|
id: 7,
|
|
order_no: 'LOCAL-SILENCE-7',
|
|
status: localPaymentStatus,
|
|
status_name: localPaymentStatus === 'completed' ? '已完成' : '待支付',
|
|
package_usage_id: 4,
|
|
package_id: 8,
|
|
package_name: '本地实名套餐',
|
|
asset_identifier: '89860426012345678901',
|
|
asset_type: 'iot_card',
|
|
asset_id: 2,
|
|
source: 'admin',
|
|
source_name: '后台',
|
|
amount: 100,
|
|
paid_amount: null,
|
|
payment_method: 'wechat',
|
|
input_days: 31,
|
|
extension_days: 60,
|
|
qr_content: localPaymentStatus === 'pending_payment' ? 'https://local.invalid/payment/7' : '',
|
|
expire_at: '2026-10-28T23:59:59+08:00',
|
|
before_deadline_at: '2026-10-27T23:59:59+08:00',
|
|
expected_deadline_at: '2026-12-26T23:59:59+08:00',
|
|
created_at: time,
|
|
approval_required: false
|
|
})
|
|
const silencePolicy = {
|
|
id: 1,
|
|
name: '本地全部套餐政策',
|
|
scope_type: 'all',
|
|
scope_type_name: '全部套餐',
|
|
scope_ids: [],
|
|
unit_price: 100,
|
|
free_extension_limit: 1,
|
|
paid_max_months: 12,
|
|
single_max_months: null,
|
|
effective_from: time,
|
|
effective_to: null,
|
|
approval_exempt_types: [],
|
|
status: 1,
|
|
created_at: time,
|
|
updated_at: time
|
|
}
|
|
const silenceBatch = {
|
|
id: 3,
|
|
batch_no: 'LOCAL-BATCH-3',
|
|
path: 'special',
|
|
extension_type: 'free',
|
|
status: 'pending_approval',
|
|
approval_required: true,
|
|
approval_status: 'pending',
|
|
requested_count: 1,
|
|
succeeded_count: 0,
|
|
failed_count: 0,
|
|
reason: '本地原因',
|
|
basis: '本地依据',
|
|
amount: 0,
|
|
requested_at: time,
|
|
evidence_refs: [],
|
|
items: [
|
|
{
|
|
id: 9,
|
|
row_no: 1,
|
|
package_usage_id: 4,
|
|
asset_identifier: '89860426012345678901',
|
|
package_name: '本地实名套餐',
|
|
extension_days: 30,
|
|
status: 'exception',
|
|
failure_code: 'local_exception',
|
|
failure_summary: '仅本地模拟执行异常',
|
|
amount: 0,
|
|
before_deadline_at: time
|
|
}
|
|
]
|
|
}
|
|
const pkg = {
|
|
id: 8,
|
|
package_name: '本地实名套餐',
|
|
package_code: 'LOCAL',
|
|
package_type: 'formal',
|
|
series_id: 1,
|
|
series_name: '本地系列',
|
|
expiry_base: 'from_activation',
|
|
silence_period_days: 30,
|
|
silence_extension_enabled: true,
|
|
silence_config_required: true,
|
|
duration_months: 1,
|
|
calendar_type: 'natural_month',
|
|
status: 1,
|
|
shelf_status: 1,
|
|
cost_price: 100,
|
|
suggested_retail_price: 200,
|
|
real_data_mb: 1024,
|
|
is_gift: false,
|
|
created_at: time,
|
|
updated_at: time
|
|
}
|
|
const entries = [
|
|
['平台订单', '/order-management/platform-orders', 'orders:platform_page'],
|
|
['代理商订单', '/order-management/agent-orders', 'orders:agent_page'],
|
|
['在线支付订单', '/order-management/online-orders', 'orders:online_page'],
|
|
['订单列表', '/order-management/order-list', ''],
|
|
['订单详情', '/order-management/order-list/detail/:id', '']
|
|
]
|
|
const page = (items, url) => ({
|
|
items,
|
|
total: items.length,
|
|
page: Number(url.searchParams.get('page') || 1),
|
|
page_size: Number(url.searchParams.get('page_size') || 20),
|
|
size: Number(url.searchParams.get('page_size') || 20),
|
|
total_pages: 1
|
|
})
|
|
const ok = (res, data, code = 0, msg = 'local mock only') => {
|
|
res.setHeader('Content-Type', 'application/json; charset=utf-8')
|
|
res.end(JSON.stringify({ code, msg, data }))
|
|
}
|
|
export const mockApiPlugin = {
|
|
name: 'order-silence-known-contract-mock',
|
|
configureServer(server) {
|
|
server.middlewares.use(async (req, res, next) => {
|
|
const url = new URL(req.url, 'http://127.0.0.1'),
|
|
path = url.pathname
|
|
if (path === '/__order-silence-log') return ok(res, log)
|
|
const handled =
|
|
['/api/auth/me', '/api/auth/login', '/api/auth/logout'].includes(path) ||
|
|
/^\/api\/admin\/(orders|packages|package-series|package-silence|employee-collection-payment-methods|assets|system-configs|iot-cards|devices|shops|accounts)(\/|$)/.test(
|
|
path
|
|
)
|
|
if (!handled) return next()
|
|
let raw = ''
|
|
for await (const part of req) raw += part
|
|
const input = raw ? JSON.parse(raw) : {},
|
|
token = String(req.headers.authorization || ''),
|
|
type = token.includes('enterprise')
|
|
? 4
|
|
: token.includes('agent')
|
|
? 3
|
|
: token.includes('super')
|
|
? 1
|
|
: 2
|
|
log.push({
|
|
path,
|
|
method: req.method,
|
|
body: input,
|
|
query: Object.fromEntries(url.searchParams)
|
|
})
|
|
const matches = (items, field, keyword) =>
|
|
items.filter((item) => !keyword || String(item[field]).includes(keyword))
|
|
if (path === '/api/admin/iot-cards/standalone')
|
|
return ok(
|
|
res,
|
|
page(
|
|
matches(
|
|
[
|
|
{
|
|
id: 2,
|
|
iccid: '89860426012345678901',
|
|
virtual_no: 'LOCAL-CARD',
|
|
shop_name: '本地店铺'
|
|
}
|
|
],
|
|
'iccid',
|
|
url.searchParams.get('keyword')
|
|
),
|
|
url
|
|
)
|
|
)
|
|
if (path === '/api/admin/devices')
|
|
return ok(
|
|
res,
|
|
page(
|
|
matches(
|
|
[
|
|
{
|
|
id: 3,
|
|
virtual_no: 'LOCAL-DEVICE',
|
|
device_name: '本地设备',
|
|
imei: '860000000000001',
|
|
shop_name: '本地店铺'
|
|
}
|
|
],
|
|
'virtual_no',
|
|
url.searchParams.get('keyword')
|
|
),
|
|
url
|
|
)
|
|
)
|
|
if (path === '/api/admin/shops')
|
|
return ok(
|
|
res,
|
|
page(
|
|
matches(
|
|
[{ id: 6, shop_name: '本地店铺' }],
|
|
'shop_name',
|
|
url.searchParams.get('shop_name')
|
|
),
|
|
url
|
|
)
|
|
)
|
|
if (path === '/api/admin/accounts')
|
|
return ok(
|
|
res,
|
|
page(
|
|
matches(
|
|
[{ id: 2, username: '本地操作员', shop_name: '本地店铺' }],
|
|
'username',
|
|
url.searchParams.get('username')
|
|
),
|
|
url
|
|
)
|
|
)
|
|
if (path.startsWith('/api/auth/'))
|
|
return ok(res, {
|
|
access_token: `local-silence-${type === 1 ? 'super' : type === 3 ? 'agent' : type === 4 ? 'enterprise' : 'platform'}`,
|
|
refresh_token: '',
|
|
expires_in: 3600,
|
|
user: {
|
|
id: type,
|
|
user_type: type,
|
|
username: '本地订单沉默期验收',
|
|
roles: [type === 1 ? 'R_SUPER' : 'R_ADMIN']
|
|
},
|
|
permissions: [
|
|
'orders:platform_page',
|
|
'orders:agent_page',
|
|
'orders:online_page',
|
|
'package_silence:orders_page',
|
|
'package_silence:order_detail_page',
|
|
'package_silence:batches_page',
|
|
'package_silence:batch_detail_page',
|
|
'package_silence:usages_page',
|
|
'package_silence:policies_page'
|
|
],
|
|
buttons: [
|
|
'orders:add',
|
|
'orders:export',
|
|
'orders:view_detail',
|
|
'package:add',
|
|
'package:edit',
|
|
'package:detail',
|
|
'package_silence:override',
|
|
'package_silence:extend',
|
|
'package_silence:extend_batch',
|
|
'package_silence:offline',
|
|
'package_silence:exception',
|
|
'package_silence:policy',
|
|
'package_silence:order_detail',
|
|
'package_silence:batch_detail',
|
|
'package_silence:order_export',
|
|
'package_silence:usage_export'
|
|
],
|
|
menus: [
|
|
{
|
|
name: '订单管理',
|
|
url: '/order-management',
|
|
children: entries.map(([name, url, perm_code]) => ({ name, url, perm_code }))
|
|
},
|
|
{
|
|
name: '套餐管理',
|
|
url: '/package-management',
|
|
children: [
|
|
{ name: '套餐列表', url: '/package-management/package-list' },
|
|
{ name: '套餐详情', url: '/package-management/package-list/detail/:id' },
|
|
...['orders', 'batches', 'usages', 'policies'].map((kind, index) => ({
|
|
name: ['延期订单', '延期批次与异常', '临期/过期套餐', '延期政策'][index],
|
|
url: `/package-management/silence-${kind}`,
|
|
perm_code: `package_silence:${kind}_page`
|
|
})),
|
|
{
|
|
name: '延期订单详情',
|
|
url: '/package-management/silence-orders/detail/:id',
|
|
perm_code: 'package_silence:order_detail_page',
|
|
is_hidden: true
|
|
},
|
|
{
|
|
name: '延期批次详情',
|
|
url: '/package-management/silence-batches/detail/:id',
|
|
perm_code: 'package_silence:batch_detail_page',
|
|
is_hidden: true
|
|
}
|
|
]
|
|
},
|
|
{
|
|
name: '设置',
|
|
url: '/settings',
|
|
children: [{ name: '沉默期总开关', url: '/settings/package-silence-switch' }]
|
|
}
|
|
]
|
|
})
|
|
if (path.startsWith('/api/admin/orders')) {
|
|
if (type === 4) return ok(res, null, 403, '企业账号无权访问订单')
|
|
if (path.endsWith('/online') && type === 3) return ok(res, page([], url))
|
|
const order = {
|
|
id: 1,
|
|
order_no: `LOCAL-${path.split('/').at(-1)}`,
|
|
order_type: 'single_card',
|
|
buyer_type: 'personal',
|
|
buyer_id: 1,
|
|
payment_status: 2,
|
|
payment_status_text: '已支付',
|
|
payment_method: 'offline',
|
|
total_amount: 200,
|
|
actual_paid_amount: 200,
|
|
paid_at: time,
|
|
items: [
|
|
{
|
|
id: 1,
|
|
package_id: 8,
|
|
package_name: pkg.package_name,
|
|
quantity: 1,
|
|
unit_price: 200,
|
|
amount: 200
|
|
}
|
|
],
|
|
asset_identifier: '89860426012345678901',
|
|
asset_type: 'card',
|
|
created_at: time,
|
|
updated_at: time,
|
|
commission_status: 2,
|
|
commission_config_version: 1,
|
|
payments: []
|
|
}
|
|
return ok(
|
|
res,
|
|
/\/orders\/\d+$/.test(path) || req.method === 'POST' ? order : page([order], url)
|
|
)
|
|
}
|
|
if (path === '/api/admin/package-silence/switch') {
|
|
if (type !== 1) return ok(res, null, 403, '仅超级管理员可操作')
|
|
const precheck = [
|
|
{ code: 'package', name: '套餐沉默期配置', passed: false, count: 2 },
|
|
{ code: 'policy', name: '延期政策', passed: true, count: 1 },
|
|
{ code: 'template', name: '企微审批模板', passed: true, count: 1 },
|
|
{ code: 'member', name: '默认审批人', passed: true, count: 1 }
|
|
]
|
|
return req.method === 'PUT'
|
|
? ok(res, { failed_items: [precheck[0]] }, 1275, '检查未通过,存在未配置沉默期套餐')
|
|
: ok(res, { enabled: false, precheck })
|
|
}
|
|
if (path === '/api/admin/employee-collection-payment-methods')
|
|
return ok(res, page([{ id: 2, name: '本地银行收款', enabled: true }], url))
|
|
if (path.startsWith('/api/admin/assets/') && path.endsWith('/first-purchase-records'))
|
|
return ok(res, [
|
|
{
|
|
id: 1,
|
|
generation: 1,
|
|
asset_identifier: 'LOCAL-ASSET',
|
|
first_purchase_at: time,
|
|
source: 'order',
|
|
source_name: '首次购买套餐',
|
|
order_no: 'LOCAL-ORDER',
|
|
end_reason: 'exchange_return',
|
|
ended_at: time,
|
|
created_at: time
|
|
}
|
|
])
|
|
if (path.startsWith('/api/admin/package-silence/')) {
|
|
if (path.endsWith('/eligibility'))
|
|
return ok(res, {
|
|
package_usage_id: 4,
|
|
can_extend: true,
|
|
special_extension_available: true,
|
|
free_extension_available: true,
|
|
min_days: 1,
|
|
max_days: 360,
|
|
min_months: 1,
|
|
max_months: 12,
|
|
unit_price: 100,
|
|
options: [{ amount: 100, billed_days: 30, months: 1, new_deadline: '2026-11-26' }],
|
|
original_deadline: '2026-10-27',
|
|
reason: '',
|
|
policy_id: 1,
|
|
policy_name: '本地全部套餐政策'
|
|
})
|
|
if (path.endsWith('/quote'))
|
|
return ok(res, {
|
|
amount: 200,
|
|
billed_days: 60,
|
|
input_days: Number(url.searchParams.get('days')),
|
|
months: 2,
|
|
new_deadline: '2026-12-26',
|
|
valid: true
|
|
})
|
|
if (path.endsWith('/orders/qrcode')) {
|
|
localPaymentStatus = 'pending_payment'
|
|
return ok(res, silenceOrder())
|
|
}
|
|
if (path.endsWith('/orders/offline'))
|
|
return ok(res, {
|
|
...silenceOrder(),
|
|
status: 'pending_approval',
|
|
status_name: '待审批',
|
|
approval_required: true,
|
|
qr_content: '',
|
|
payment_method: 'offline'
|
|
})
|
|
if (path.endsWith('/orders')) return ok(res, page([silenceOrder()], url))
|
|
if (/\/orders\/\d+$/.test(path)) {
|
|
const order = silenceOrder()
|
|
localPaymentStatus = 'completed'
|
|
return ok(res, order)
|
|
}
|
|
if (path.endsWith('/special')) return ok(res, silenceBatch)
|
|
if (path.endsWith('/batches')) return ok(res, page([silenceBatch], url))
|
|
if (/\/batches\/\d+$/.test(path)) return ok(res, silenceBatch)
|
|
if (path.endsWith('/handle'))
|
|
return ok(res, {
|
|
...silenceBatch.items[0],
|
|
status: input.action === 'close' ? 'closed' : 'completed',
|
|
failure_summary: ''
|
|
})
|
|
if (path.endsWith('/usages'))
|
|
return ok(
|
|
res,
|
|
page(
|
|
[
|
|
{
|
|
package_usage_id: 4,
|
|
package_id: 8,
|
|
package_name: pkg.package_name,
|
|
asset_id: 2,
|
|
asset_identifier: '89860426012345678901',
|
|
asset_type: 'iot_card',
|
|
shop_id: 1,
|
|
shop_name: '本地店铺',
|
|
created_at: time,
|
|
order_no: 'LOCAL-ORDER',
|
|
silence
|
|
}
|
|
],
|
|
url
|
|
)
|
|
)
|
|
if (path.endsWith('/records'))
|
|
return ok(res, [
|
|
{
|
|
id: 1,
|
|
event_type: 'set',
|
|
event_name: '设置沉默期',
|
|
before_status: null,
|
|
after_status: 'waiting',
|
|
after_deadline_at: time,
|
|
change_days: 30,
|
|
operator_id: 1,
|
|
operator_type: 'admin',
|
|
reason: '本地记录',
|
|
created_at: time
|
|
}
|
|
])
|
|
if (path.endsWith('/policies/match'))
|
|
return ok(res, { matched: true, package_id: 8, policy: silencePolicy })
|
|
if (path.endsWith('/policies'))
|
|
return ok(res, req.method === 'GET' ? [silencePolicy] : { ...silencePolicy, ...input })
|
|
if (/\/policies\/\d+(\/status)?$/.test(path)) return ok(res, { ...silencePolicy, ...input })
|
|
return ok(res, null, 400, '本地未定义的沉默期接口已阻止')
|
|
}
|
|
if (path.startsWith('/api/admin/packages'))
|
|
return ok(
|
|
res,
|
|
path === '/api/admin/packages' && req.method === 'GET'
|
|
? page([pkg], url)
|
|
: { ...pkg, ...input }
|
|
)
|
|
if (path.startsWith('/api/admin/package-series'))
|
|
return ok(
|
|
res,
|
|
page([{ id: 1, series_name: '本地系列', series_code: 'LOCAL', status: 1 }], url)
|
|
)
|
|
if (path.startsWith('/api/admin/system-configs'))
|
|
return ok(res, { list: [], total: 0, page: 1, page_size: 20 })
|
|
return ok(res, null, 400, '本地未定义,已阻止请求')
|
|
})
|
|
fallback.configureServer(server)
|
|
}
|
|
}
|