Files
platforms/frontend/platform_admin/src/api/resources.ts

661 lines
16 KiB
TypeScript

export type ResourceMode = 'writable' | 'readonly' | 'append_only';
export type ResourcePageKind = 'list' | 'tree';
export type ResourceFieldType =
| 'text'
| 'password'
| 'identity'
| 'role-code'
| 'menu-identities'
| 'number'
| 'boolean'
| 'date'
| 'datetime'
| 'json'
| 'textarea';
export type ResourceField = {
key: string;
label: string;
type: ResourceFieldType;
required?: boolean;
};
export type DetailAction = {
name: string;
resource: string;
fields: ResourceField[];
payload?: Record<string, unknown>;
};
export type ResourceUiDefinition = {
key: string;
name: string;
resource: string;
title: string;
mode: ResourceMode;
pageKind: ResourcePageKind;
fields: ResourceField[];
requiredIdentities: string[];
detailActions?: DetailAction[];
};
const labels: Record<string, string> = {
code: '编码',
name: '名称',
credit_code: '统一信用代码',
principal: '负责人',
address: '地址',
longitude: '经度',
latitude: '纬度',
username: '用户名',
password: '密码',
display_name: '显示名称',
role_code: '角色编码',
delivery_code: '配送编码',
phone: '联系电话',
avatar: '头像',
work_status: '工作状态',
credential_type: '资质类型',
credential_no: '资质编号',
expired_at: '到期时间',
real_name: '实名姓名',
is_default: '默认地址',
device_no: '设备编号',
model: '设备型号',
online_status: '在线状态',
effective_at: '生效时间',
reported_at: '上报时间',
payload: '遥测数据',
rule_code: '规则编码',
version_no: '版本号',
threshold: '阈值',
action: '处置动作',
gray_scope: '灰度范围',
event_code: '事件编码',
level: '事件等级',
title: '标题',
sla_at: '处置时限',
result: '检查结果',
evidence_uri: '凭证地址',
reason: '处置原因',
sort_no: '排序号',
product_code: '商品编码',
price_amount: '售价',
stock_quantity: '库存',
value: '属性值',
image_uri: '图片地址',
is_cover: '封面图',
quantity: '数量',
selected: '是否选中',
order_no: '订单号',
total_amount: '订单金额',
product_snapshot: '商品快照',
sale_amount: '成交金额',
score: '评分',
channel: '渠道',
amount: '金额',
paid_at: '支付时间',
settlement_no: '结算单号',
subject_type: '结算对象类型',
period_start: '结算开始时间',
period_end: '结算结束时间',
bill_date: '账单日期',
difference_amount: '差异金额',
content_type: '内容类型',
body: '正文',
publish_status: '发布状态',
template_code: '模板编码',
content: '内容',
ticket_no: '工单号',
category: '分类',
priority: '优先级',
platform_role_code: '平台角色',
menu_identities: '菜单权限',
data_scope: '数据范围',
menu_code: '菜单编码',
icon: '图标',
path: '路径',
balance_amount: '余额',
frozen_amount: '冻结金额',
balance_after: '变动后余额',
report_code: '报表编码',
report_type: '报表类型',
stat_period: '统计周期',
generated_at: '生成时间',
dimension: '维度',
metric_code: '指标编码',
metric_value: '指标值',
scope_type: '范围类型',
stat_at: '统计时间',
applicant_identity: '申请人标识',
business_type: '业务类型',
business_identity: '业务标识',
handler_identity: '处理人标识',
opinion: '审批意见',
purpose: '用途',
field_scope: '字段范围',
approved_at: '批准时间',
file_uri: '文件地址',
operator_identity: '操作人标识',
object_identity: '对象标识',
resource_type: '资源类型',
handled_at: '处理时间',
created_at: '创建时间',
status: '状态',
};
const numberFields = new Set([
'version_no',
'level',
'sort_no',
'price_amount',
'stock_quantity',
'quantity',
'total_amount',
'sale_amount',
'score',
'amount',
'difference_amount',
'balance_amount',
'frozen_amount',
'balance_after',
]);
const booleanFields = new Set(['is_default', 'is_cover', 'selected']);
const dateFields = new Set(['bill_date']);
const datetimeFields = new Set([
'expired_at',
'effective_at',
'reported_at',
'sla_at',
'occurred_at',
'started_at',
'completed_at',
'paid_at',
'period_start',
'period_end',
'generated_at',
'stat_at',
'approved_at',
'handled_at',
'created_at',
]);
const jsonFields = new Set([
'payload',
'threshold',
'gray_scope',
'product_snapshot',
'field_scope',
]);
const textareaFields = new Set(['body', 'content', 'reason', 'opinion']);
const fieldType = (key: string): ResourceFieldType => {
if (key === 'platform_role_code') return 'role-code';
if (key === 'menu_identities') return 'menu-identities';
if (key.endsWith('_identity')) return 'identity';
if (key === 'password') return 'password';
if (numberFields.has(key)) return 'number';
if (booleanFields.has(key)) return 'boolean';
if (dateFields.has(key)) return 'date';
if (datetimeFields.has(key)) return 'datetime';
if (jsonFields.has(key)) return 'json';
if (textareaFields.has(key)) return 'textarea';
return 'text';
};
const field = (value: string): ResourceField => {
const key = value.replace(/!$/, '');
return {
key,
label:
labels[key] ?? (key.endsWith('_identity') ? '关联业务标识' : '业务字段'),
type: fieldType(key),
required: value.endsWith('!') || undefined,
};
};
const titles: Record<string, string> = {
gas_basic: '气站管理',
gas_account: '气站账户',
delivery_basic: '配送点管理',
delivery_account: '配送账户',
delivery_task: '配送任务',
delivery_track: '配送轨迹',
delivery_track_point: '轨迹点',
staff_account: '服务人员',
staff_credential: '人员资质',
user_account: '用户账户',
user_address: '用户地址',
user_service_relation: '用户服务关系',
dev_smart_cylinder_valve: '智能钢瓶阀',
dev_device_binding: '设备绑定',
dev_telemetry: '设备遥测',
saf_rule: '安全规则',
saf_event: '安全事件',
saf_inspection: '安全检查',
saf_event_disposal: '事件处置',
ec_category: '商品分类',
ec_product: '商品管理',
ec_product_attribute: '商品属性',
ec_product_image: '商品图片',
ec_cart: '购物车',
ec_order: '订单管理',
ec_order_item: '订单明细',
ec_review: '商品评价',
fin_payment: '支付记录',
fin_settlement: '财务结算',
fin_reconciliation: '财务对账',
cnt_content: '内容管理',
ntf_template: '通知模板',
cs_ticket: '客服工单',
platfrom_account: '平台账户',
platform_role: '平台角色',
platform_menu: '平台菜单',
wallet: '钱包',
wallet_ledger: '钱包流水',
wallet_recharge: '钱包充值',
wallet_withdrawal: '钱包提现',
report: '报表',
report_item: '报表项目',
report_metric_snapshot: '指标快照',
aud_operation_log: '操作审计',
aud_export_log: '导出审计',
aud_approval: '审批审计',
};
const define = (
name: string,
resource: string,
mode: ResourceMode,
pageKind: ResourcePageKind,
keys: string[],
detailActions?: DetailAction[],
): ResourceUiDefinition => {
const fields = keys.map(field);
return {
key: name.replace(/_/g, '-'),
name,
resource,
title: titles[name] ?? '业务资源',
mode,
pageKind,
fields,
requiredIdentities: fields
.filter((item) => item.required && item.key.endsWith('_identity'))
.map((item) => item.key),
...(detailActions ? { detailActions } : {}),
};
};
const action = (
name: string,
resource: string,
keys: string[],
payload?: Record<string, unknown>,
): DetailAction => ({
name,
resource,
fields: keys.map(field),
...(payload ? { payload } : {}),
});
/** Exact UI contract for every backend ExpectedResources entry. */
export const resources: ResourceUiDefinition[] = [
define('gas_basic', '/gas/gas_basic', 'writable', 'list', [
'code!',
'name!',
'credit_code',
'principal',
'address',
'longitude',
'latitude',
]),
define('gas_account', '/gas/gas_account', 'writable', 'list', [
'username!',
'password!',
'display_name',
'role_code',
'gas_basic_identity!',
]),
define('delivery_basic', '/delivery/delivery_basic', 'writable', 'list', [
'delivery_code!',
'name!',
'gas_basic_identity',
'principal',
'address',
]),
define('delivery_account', '/delivery/delivery_account', 'writable', 'list', [
'username!',
'password!',
'display_name',
'role_code',
'delivery_basic_identity!',
]),
define('delivery_task', '/delivery/delivery_task', 'writable', 'list', [
'ec_order_identity!',
'delivery_basic_identity!',
'staff_account_identity',
]),
define('delivery_track', '/delivery/delivery_track', 'writable', 'list', [
'delivery_task_identity!',
'started_at',
'completed_at',
]),
define(
'delivery_track_point',
'/delivery/delivery_track_point',
'readonly',
'list',
[
'delivery_track_identity',
'point_type',
'occurred_at',
'longitude',
'latitude',
],
),
define('staff_account', '/staff/account', 'writable', 'list', [
'username!',
'password!',
'name!',
'phone',
'avatar',
'role_code',
'gas_basic_identity',
'delivery_basic_identity',
'work_status',
]),
define('staff_credential', '/staff/credential', 'writable', 'list', [
'staff_account_identity!',
'credential_type!',
'credential_no',
'expired_at',
]),
define('user_account', '/user/account', 'writable', 'list', [
'username!',
'password!',
'name!',
'phone',
'avatar',
'real_name',
]),
define('user_address', '/user/address', 'writable', 'list', [
'user_account_identity!',
'address!',
'longitude',
'latitude',
'is_default',
]),
define(
'user_service_relation',
'/user/service_relation',
'writable',
'list',
[
'user_account_identity!',
'gas_basic_identity',
'delivery_basic_identity',
'staff_account_identity',
],
),
define(
'dev_smart_cylinder_valve',
'/device/dev_smart_cylinder_valve',
'writable',
'list',
['device_no!', 'model', 'online_status', 'owner_identity'],
),
define(
'dev_device_binding',
'/device/dev_device_binding',
'writable',
'list',
[
'smart_cylinder_valve_identity!',
'user_account_identity!',
'effective_at',
'expired_at',
],
),
define('dev_telemetry', '/device/dev_telemetry', 'readonly', 'list', [
'smart_cylinder_valve_identity',
'reported_at',
'payload',
]),
define('saf_rule', '/safety/saf_rule', 'writable', 'list', [
'rule_code!',
'version_no',
'threshold',
'action',
'gray_scope',
]),
define(
'saf_event',
'/safety/saf_event',
'writable',
'list',
[
'event_code!',
'level!',
'title!',
'smart_cylinder_valve_identity!',
'sla_at',
],
[
action('saf_event_disposal', '/safety/saf_event/:identity/disposals', [
'action!',
'reason!',
]),
],
),
define('saf_inspection', '/safety/saf_inspection', 'writable', 'list', [
'user_account_identity!',
'staff_account_identity!',
'result!',
'evidence_uri',
]),
define(
'saf_event_disposal',
'/safety/saf_event/:identity/disposals',
'append_only',
'list',
['action!', 'reason!'],
),
define('ec_category', '/ec/ec_category', 'writable', 'tree', [
'parent_identity',
'name!',
'sort_no',
]),
define('ec_product', '/ec/ec_product', 'writable', 'list', [
'ec_category_identity!',
'product_code!',
'name!',
'price_amount!',
'stock_quantity',
]),
define(
'ec_product_attribute',
'/ec/ec_product_attribute',
'writable',
'list',
['ec_product_identity!', 'name!', 'value!', 'sort_no'],
),
define('ec_product_image', '/ec/ec_product_image', 'writable', 'list', [
'ec_product_identity!',
'image_uri!',
'sort_no',
'is_cover',
]),
define('ec_cart', '/ec/ec_cart', 'writable', 'list', [
'user_account_identity!',
'ec_product_identity!',
'quantity!',
'selected',
]),
define('ec_order', '/ec/ec_order', 'writable', 'list', [
'user_account_identity!',
'gas_basic_identity',
'delivery_basic_identity',
'order_no!',
'total_amount!',
]),
define('ec_order_item', '/ec/ec_order_item', 'writable', 'list', [
'ec_order_identity!',
'ec_product_identity!',
'product_snapshot!',
'quantity!',
'sale_amount!',
]),
define('ec_review', '/ec/ec_review', 'writable', 'list', [
'ec_order_identity!',
'ec_product_identity!',
'user_account_identity!',
'score!',
'content!',
]),
define('fin_payment', '/finance/fin_payment', 'writable', 'list', [
'ec_order_identity!',
'channel!',
'amount!',
'paid_at',
]),
define('fin_settlement', '/finance/fin_settlement', 'writable', 'list', [
'settlement_no!',
'subject_type!',
'subject_identity!',
'period_start!',
'period_end!',
]),
define(
'fin_reconciliation',
'/finance/fin_reconciliation',
'writable',
'list',
['channel!', 'bill_date!', 'difference_amount!'],
),
define('cnt_content', '/content/cnt_content', 'writable', 'list', [
'content_type!',
'title!',
'body!',
'version_no',
'publish_status',
]),
define('ntf_template', '/notification/ntf_template', 'writable', 'list', [
'template_code!',
'channel!',
'content!',
]),
define('cs_ticket', '/customer_service/cs_ticket', 'writable', 'list', [
'user_account_identity!',
'ticket_no!',
'category!',
'priority!',
]),
define('platfrom_account', '/platform/platfrom_account', 'writable', 'list', [
'username!',
'password!',
'display_name',
'avatar',
'platform_role_code!',
'phone',
]),
define(
'platform_role',
'/platform/platform_role',
'writable',
'list',
['role_code!', 'name!', 'data_scope!'],
[
action(
'分配菜单',
'/platform/platform_role/:identity/menu',
['menu_identities!'],
),
],
),
define('platform_menu', '/platform/platform_menu', 'writable', 'tree', [
'parent_identity',
'menu_code!',
'name!',
'icon',
'path',
'sort_no',
]),
define('wallet', '/wallet/wallet', 'readonly', 'list', [
'owner_identity',
'balance_amount',
'frozen_amount',
'status',
]),
define('wallet_ledger', '/wallet/wallet_ledger', 'readonly', 'list', [
'wallet_identity',
'amount',
'balance_after',
]),
define('wallet_recharge', '/wallet/wallet_recharge', 'readonly', 'list', [
'wallet_identity',
'amount',
'status',
]),
define('wallet_withdrawal', '/wallet/wallet_withdrawal', 'readonly', 'list', [
'wallet_identity',
'amount',
'status',
]),
define('report', '/report/report', 'readonly', 'list', [
'report_code',
'report_type',
'stat_period',
'generated_at',
'status',
]),
define('report_item', '/report/report_item', 'readonly', 'list', [
'report_identity',
'dimension',
'metric_code',
'metric_value',
]),
define(
'report_metric_snapshot',
'/report/report_metric_snapshot',
'readonly',
'list',
['metric_code', 'scope_type', 'stat_at', 'metric_value'],
),
define('aud_operation_log', '/audit/aud_operation_log', 'readonly', 'list', [
'operator_identity',
'action',
'object_identity',
'created_at',
]),
define('aud_export_log', '/audit/aud_export_log', 'readonly', 'list', [
'applicant_identity',
'purpose',
'field_scope',
'approved_at',
'file_uri',
]),
define(
'aud_approval',
'/audit/aud_approval',
'readonly',
'list',
[
'business_type',
'business_identity',
'applicant_identity',
'opinion',
'handler_identity',
'status',
'handled_at',
],
[
action('同意', '/audit/aud_approval/:identity/approve', ['opinion'], {
status: 'approved',
}),
action('驳回', '/audit/aud_approval/:identity/approve', ['opinion!'], {
status: 'rejected',
}),
],
),
];
export const resourceByName = Object.fromEntries(
resources.map((definition) => [definition.resource, definition]),
) as Record<string, ResourceUiDefinition>;
export function getResource(resourcePath: string): ResourceUiDefinition {
const definition = resourceByName[resourcePath];
if (!definition) throw new Error('Unknown resource: ' + resourcePath);
return definition;
}