组员这次提交的两套新页面方向都对,但各有一处"接不上"的地方,这里补齐。 1) 访客客服浮窗(此前一问即失败) 客服 Agent 让访客走 query_knowledge(访客令牌的角色是 visitor、权限只有 agent:run + knowledge:query),但发布配置里三个知识意图只发了 search_knowledge, 于是 ToolExecutor 直接抛 ForbiddenAgentError,而客服代码对白名单失败是 「必须冒泡」的 —— 访客拿不到任何回答,登录客户侧却完全正常。 发布脚本的知识类意图改为同时发 search_knowledge 与 query_knowledge: 访客走前者、客户走后者,缺任一条对应人群就失败关闭。 (suitability_check 不发 query_knowledge:访客意图白名单不含它,访客到不了。) 2) 发布脚本会静默丢提示词 旧写法只查 platform_config_item 就当作"继承",而 config_release 是整版本替换 语义,新版本没带上的行等于被删除 —— 实际把 customer_service_chitchat 提示词 漏在了旧版本里(admin 端只在激活时打一句 stderr 警告)。 改为走 ConfigReleaseService.effective_snapshot() 读全三张受管表,补上提示词 搬运(version 重分配、带上 input_schema/output_schema),并在激活后硬校验 配置项与提示词条数,条数不符即失败退出。 (model_routing_rule 本环境为空;不为空则直接中止,不假装支持。) 丢失的那条提示词已按原文恢复,active 版本现为 9 条配置项 + 1 条提示词。 3) 投顾工作台永远为空 published() 取的是 customer_id == 自己 user_id,而投顾是员工账号、不可能是 客户;且只认 advisor_recommendation_plan + approved,而投顾交付的主产物是 investment_goal_book,发布后状态是 published。三重不匹配下页面永远显示空态。 改为按「本人 + sys_customer_assignment 里名下归属客户」过滤(不用 data_scope: 投顾因持有 all 级权限会把整个身份的 scope 抬到 all,那会放开到全部客户), 并覆盖两类 content_type 与两种已发布取值。 实测:投顾可见归属客户 9001 的方案书,客户仍只见自己的,风控仍 403。 4) 访客页把"演示数据"声明删了但假数据还在 mock-data.js 的 MOCK_SOURCE_NOTICE 与两个页面的 data-source-notice 区块被删除, 而 MOCK_PRODUCTS/MOCK_RANKING_CHANGE 仍在渲染(详情页含历史净值曲线)。 恢复声明常量、页面区块与样式,并给 products/product-detail 的 link 与 script 加上版本参数 —— 此前没有版本号,浏览器会命中旧缓存,改动看不见。 其他:投顾页显示交付物类型与客户编号(后端新返回的字段),README 补上投顾页 数据口径、访客/客户两条检索工具的差别,以及"渲染 mock 必须带来源声明"的约定。
66 lines
3.7 KiB
JavaScript
66 lines
3.7 KiB
JavaScript
const PRODUCT_BASE = {
|
|
exchange_code: 'SSE',
|
|
product_category: 'ETF',
|
|
risk_level: 'R3',
|
|
fund_manager: '南方基金管理股份有限公司',
|
|
currency: 'CNY',
|
|
lot_size: '100.0000',
|
|
price_tick: '0.001000',
|
|
min_amount: '100.00',
|
|
open_start_at: null,
|
|
open_end_at: null,
|
|
open_period_start: null,
|
|
open_period_end: null,
|
|
transaction_fee_rate: '0.000500',
|
|
single_investor_max_holding_ratio: '10.0000',
|
|
management_fee_rate: '0.5000',
|
|
custodian_fee_rate: '0.1000',
|
|
risk_disclosure_required: 1,
|
|
second_confirmation_required: 0,
|
|
recording_required: 0,
|
|
status: '正常',
|
|
created_at: '2026-01-02T09:00:00',
|
|
updated_at: '2026-09-11T15:00:00',
|
|
};
|
|
|
|
export const MOCK_PRODUCTS = Object.freeze([
|
|
{ ...PRODUCT_BASE, id: 1, product_code: '510300', product_name: '南方沪深300ETF', current_nav: '4.218000', current_nav_at: '2026-09-11T15:00:00', product_category: '宽基ETF', risk_level: 'R3' },
|
|
{ ...PRODUCT_BASE, id: 2, product_code: '510500', product_name: '南方中证500ETF', current_nav: '6.742000', current_nav_at: '2026-09-11T15:00:00', product_category: '宽基ETF', risk_level: 'R4' },
|
|
{ ...PRODUCT_BASE, id: 3, product_code: '159915', product_name: '南方创业板ETF', current_nav: '2.184000', current_nav_at: '2026-09-11T15:00:00', product_category: '成长ETF', risk_level: 'R4', exchange_code: 'SZSE' },
|
|
{ ...PRODUCT_BASE, id: 4, product_code: '512100', product_name: '南方中证1000ETF', current_nav: '2.367000', current_nav_at: '2026-09-11T15:00:00', product_category: '宽基ETF', risk_level: 'R4' },
|
|
{ ...PRODUCT_BASE, id: 5, product_code: '513100', product_name: '南方纳指ETF', current_nav: '1.728000', current_nav_at: '2026-09-11T15:00:00', product_category: '跨境ETF', risk_level: 'R5' },
|
|
{ ...PRODUCT_BASE, id: 6, product_code: '511360', product_name: '南方短融ETF', current_nav: '110.126000', current_nav_at: '2026-09-11T15:00:00', product_category: '债券ETF', risk_level: 'R2' },
|
|
{ ...PRODUCT_BASE, id: 7, product_code: '159645', product_name: '南方新能源ETF', current_nav: '0.864000', current_nav_at: '2026-09-11T15:00:00', product_category: '行业ETF', risk_level: 'R5', exchange_code: 'SZSE' },
|
|
{ ...PRODUCT_BASE, id: 8, product_code: '159925', product_name: '南方沪深300成长ETF', current_nav: '1.462000', current_nav_at: '2026-09-11T15:00:00', product_category: '策略ETF', risk_level: 'R4', exchange_code: 'SZSE' },
|
|
]);
|
|
|
|
const CLOSE_SERIES = [4.02, 4.08, 4.05, 4.12, 4.16, 4.11, 4.19, 4.23, 4.18, 4.26, 4.21, 4.218];
|
|
|
|
export function getMockNavHistory(productId) {
|
|
const product = MOCK_PRODUCTS.find((item) => item.id === Number(productId)) || MOCK_PRODUCTS[0];
|
|
const ratio = Number(product.current_nav) / CLOSE_SERIES.at(-1);
|
|
return CLOSE_SERIES.map((close, index) => ({
|
|
id: product.id * 100 + index,
|
|
product_id: product.id,
|
|
nav_date: `2026-${String(index + 1).padStart(2, '0')}-11`,
|
|
nav: (close * ratio).toFixed(6),
|
|
created_at: `2026-${String(index + 1).padStart(2, '0')}-11T15:00:00`,
|
|
}));
|
|
}
|
|
|
|
export const MOCK_RANKING_CHANGE = Object.freeze({
|
|
'510300': 1.26,
|
|
'510500': 2.18,
|
|
'159915': -0.42,
|
|
'512100': 1.88,
|
|
'513100': 0.76,
|
|
'511360': 0.05,
|
|
'159645': -1.12,
|
|
'159925': 1.54,
|
|
});
|
|
|
|
//: 公开产品 HTTP 接口尚未实现,访客页面的产品、净值与涨跌**来自本文件而不是数据库**。
|
|
//: 这是接口缺位时的显式降级,不是真实行情 —— 凡展示这些数据的页面都必须原样呈现这句话。
|
|
//: 删掉它并不会让数据变真,只会让客户以为看到的是真实净值。
|
|
export const MOCK_SOURCE_NOTICE = '公开产品接口尚未提供,本页使用与 fin_product、fin_nav_history 模型同字段的演示数据,非真实行情。';
|