Files
one-pipe-system/scripts/tests/orderSilenceBrowserFixtures.mjs
luo f2614d8efb
All checks were successful
构建并部署前端到测试环境 / deploy (push) Successful in 54s
fix: some
2026-10-28 15:18:44 +08:00

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)
}
}