Files
zyt/server/app/mcp/catalog/review/stats.php
T
2026-09-24 11:03:44 +08:00

580 lines
42 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
/**
* AI 数据目录人工审核:数据统计(stats.*)、一诊(firstvisit.*)、企业微信(qywx.*)。
* 字段说明见同目录 README.md。
*
* 审核要点:
* - 统计类接口大多由 Logic 自带 ($params, $adminId, $adminInfo) 并在内部按 DataScopeService 收窄,
* MCP 以同一账号身份调用原控制器,范围与后台页面一致;所有资源都写了 params_allow(白名单),
* 未列出的参数(如 admin_id 别名、ranges、include_filters、_t 等)一律拒绝。
* - perm:以下子接口在原代码里就绑定到页面权限(控制器 hasPagePermission() 或 AuthMiddleware 别名),
* 菜单里没有单独的权限点,这里把 MCP 权限点指向同一个页面权限,避免“永远未登记”:
* firstvisit.conversion/fansDetail → ConversionController::hasPagePermission()(firstvisit.conversion/overview)
* firstvisit.myPatient/orders|progress|assistants|orderDetail → MyPatientController::hasPagePermission()(firstvisit.myPatient/lists)
* firstvisit.wecomPromotion/customerStatistics → AuthMiddleware 获客助手整组绑定 + QywxPromotionOperatorAccess::PAGE_PERMISSION
* stats.selfInput/mediaSourceOptions → AuthMiddleware::matchPermissionAlias(复用自录转化统计/账户消耗列表权限)
* - 末尾几条是扫描误判为 write 的 GET 接口(confirm/sync/upload 等前缀),给出准确结论。
* - perm_fallback:业绩看板(fans/yeji)的子接口在部分环境没有单独登记权限点(线上曾出现 deptOptions “未在菜单登记”)。
* 子接口自身已登记时仍按自身判断(与后台一致);未登记时依次改用 Tab 权限(tabLeaderboard/tabDoctor/tabZyyt…)
* 或页面权限 fans/yeji ——后台对未登记接口不校验、只靠这些权限控制页面可见,这样不会比后台页面更宽。
* - timeout:业绩看板、医生统计、提成结算、综合转化是多张大表的聚合(后台控制器自己放宽到 120 秒),单条 SQL 超时放宽到 30 秒。
*/
$dateRange = [
'start_date' => '开始日期 YYYY-MM-DD(不传默认今天)',
'end_date' => '结束日期 YYYY-MM-DD(不传同开始日期)',
];
$yejiFilter = [
'dept_ids' => '展示部门ID,多个用逗号分隔;选父部门会展开为其下级部门行(取值见 stats.yejiStats/deptOptions);不传=默认全部“中心”',
'channel_code' => '渠道编码(取值见 stats.yejiStats/channelOptions,如 tag_xxx);不传=不限渠道',
];
$yejiScopeNote = '按当前账号数据范围收窄(受限账号只统计可见员工,部门/医助超出范围时返回空并在 note 说明)。';
return [
// ───────────────────────────── 数据统计 stats.* ─────────────────────────────
'stats.assistantPerformance/overview' => [
'status' => 'open', 'kind' => 'report', 'name' => '医助个人业绩',
'note' => '只统计当前账号本人创建、履约已完成(fulfillment_status=3)且关联诊单的处方业务订单,按订单创建时间;week=最近7天、month=最近30天(均含今天)。',
'params_allow' => [
'time_type' => '时间范围:today 今日 / yesterday 昨日 / week 最近7天 / month 最近30天(默认)/ custom 自定义',
'start_date' => '自定义开始日期 YYYY-MM-DD(time_type=custom 时必填)',
'end_date' => '自定义结束日期 YYYY-MM-DD(time_type=custom 时必填)',
],
],
'stats.autoAssignLog/lists' => [
'status' => 'open', 'kind' => 'list', 'name' => '待分配诊单自动指派日志',
'note' => '后台本身不按数据范围过滤:拥有该权限的账号可看到全部自动指派日志(含患者姓名、手机号快照)。数据由定时任务 tcm:auto-assign-pending 写入,每条待指派诊单一行;action 1=已分配、0=未分配,tier 为医助上月二诊复诊接诊率档位(gt70/60_70/50_60),reason 为分配或不分配原因。',
'params_allow' => [
'run_date' => '执行日期 YYYY-MM-DD(精确匹配)',
'start_date' => '执行日期起 YYYY-MM-DD',
'end_date' => '执行日期止 YYYY-MM-DD',
'start_time' => '记录时间起 YYYY-MM-DD 或 YYYY-MM-DD HH:mm:ss',
'end_time' => '记录时间止 YYYY-MM-DD 或 YYYY-MM-DD HH:mm:ss',
'action' => '结果:1 已分配、0 未分配',
'assistant_id' => '分得的医助(后台账号)ID',
'batch_no' => '执行批次号',
'stat_month' => '接诊率统计月份 YYYY-MM',
'keyword' => '患者姓名/手机号/医助姓名模糊匹配;纯数字时同时匹配诊单ID',
'is_rollback' => '是否已回退:1 已回退、0 未回退',
],
],
'stats.commissionSettlement/channelOptions' => [
'status' => 'open', 'kind' => 'report', 'name' => '提成结算 · 渠道选项',
'note' => '启用中的投放渠道(企微标签渠道),按来源分组,供 channel_code 参数取值;不含客户数。',
'params_allow' => [],
],
'stats.commissionSettlement/deptOptions' => [
'status' => 'open', 'kind' => 'report', 'name' => '提成结算 · 部门选项',
'note' => '按当前账号数据范围收窄的部门列表(id、name、pid、完整路径),供 dept_ids 参数取值。',
'params_allow' => [],
],
'stats.commissionSettlement/orderLines' => [
'status' => 'pending', 'kind' => 'report', 'name' => '提成结算 · 核对明细',
'reason' => '明细会对库内缺少签收时间的订单实时调用快递100查询物流并回写轨迹(ExpressTrackingService::syncSignUnixFromLogisticsForPrescriptionOrder,外部接口 + 写库),只读事务下会失败;需提供不回查快递的只读模式后再开放',
],
'stats.commissionSettlement/overview' => [
'timeout' => 30,
'status' => 'open', 'kind' => 'report', 'name' => '提成结算业绩汇总',
'note' => 'settlement_month 必填。默认订单池=结算月的上一个自然月创建、履约已完成(默认 fulfillment_status=3)、默认仅系统代开处方的业务订单;签收时间与尾款支付时间均不晚于结算月 7 日 23:59:59 计入“本期提成”,否则“顺延下期”;上期确定业绩时顺延的订单并入本期。传 start_time+end_time 时订单池改为与处方订单列表一致的创建时间段(默认含手动开方)。业绩归属订单创建人,只统计当前账号数据范围内可见医助;签收时间仅用库内物流数据推导,不实时查快递。返回部门/医助/医生三个维度及确认状态 confirm(confirm 按“结算月+渠道+部门筛选”共享,其中 totals_json 是确定人确定时的合计快照,不随查看人的数据范围变化,与后台一致)。',
'params_allow' => [
'settlement_month' => '结算月 YYYY-MM(必填),如 2026-09 表示结算 8 月创建的订单',
'start_time' => '订单创建时间起 YYYY-MM-DD HH:mm:ss(与 end_time 同时传才生效)',
'end_time' => '订单创建时间止 YYYY-MM-DD HH:mm:ss',
'fulfillment_status' => '履约状态,默认 3(已完成)',
'require_system_auto_prescription' => '仅在传 start_time/end_time 时有效:1=只统计系统代开处方',
'dept_ids' => '展示部门ID,多个用逗号分隔(取值见 stats.commissionSettlement/deptOptions)',
'channel_code' => '渠道编码(取值见 stats.commissionSettlement/channelOptions)',
],
],
'stats.conversion/overview' => [
'timeout' => 30,
'status' => 'open', 'kind' => 'report', 'name' => '综合转化统计',
'note' => '按当前账号数据范围(可见员工)统计加粉、预约、面诊、成交单数与金额、投放成本(按加粉占比分摊)及各转化率;dimension=dept 返回部门树(include_members=1 时含成员行),assistant/doctor 返回按人统计。time_type:week=最近7天、month=最近30天。结果 lists 为当前页,summary/charts 为汇总。',
'params_allow' => [
'time_type' => '时间范围:today(默认)/ yesterday / week 最近7天 / month 最近30天 / custom 自定义',
'start_date' => '自定义开始日期 YYYY-MM-DD(time_type=custom 时)',
'end_date' => '自定义结束日期 YYYY-MM-DD(time_type=custom 时)',
'dimension' => '统计维度:dept 部门(默认)/ assistant 医助 / doctor 医生',
'dept_id' => '只看某部门(含下级)',
'assistant_id' => '只看某医助(dimension=assistant 时)',
'doctor_id' => '只看某医生(dimension=doctor 时)',
'media_channel_code' => '媒体渠道编码(企微标签渠道)',
'include_members' => '部门维度是否附带成员行:1 是(默认)、0 否',
'page_no' => '页码,默认 1',
'page_size' => '每页条数,默认 15,最大 100',
],
],
'stats.doctorDailyStats/overview' => [
'perm_fallback' => ['stats.yejiStats/tabDoctor', 'fans/yeji'], 'timeout' => 30,
'status' => 'open', 'kind' => 'report', 'name' => '医生日统计',
'note' => '按医生汇总:系统/手动开方数(处方日期)、成交业务订单数与金额(订单创建时间,剔除已取消/拒收/退款)、挂号总数/已完成/过号/取消与挂号率(=成交单数÷总挂号,按预约日期)。医生列表按当前账号数据范围收窄;传 dept_ids 时只统计该部门医助经手的数据并隐藏全 0 医生。未传日期默认今天。',
'params_allow' => $dateRange + $yejiFilter + [
'doctor_id' => '只看某位医生(后台账号ID)',
],
],
'stats.performanceDashboard/overview' => [
'status' => 'open', 'kind' => 'report', 'name' => '数据驾驶舱',
'note' => '首页驾驶舱,服务端按角色收窄:医助=本人、组长=本小组、经理=本部门及下级、管理员=全部;业绩按业务订单创建时间与创建人统计(剔除已取消/拒收/退款),挂号=支付时间内已支付且 0<实收<10 元的订单,预约按预约日期;本月业绩与上月同期比较,趋势固定最近 7 天;排行榜按一中心/二中心规则。',
'params_allow' => [
'ranking_dept_id' => '排行榜部门ID(只能选返回的 filters.ranking_departments 中的部门,否则忽略)',
],
],
// 逐条校验:PersonalAccountCostController::detail() → PersonalAccountCostLogic::detail() → PersonalStatsScopeTrait::assertRecordVisible()(录入人不在可见范围时返回“记录不存在或无权查看”)
'stats.personalAccountCost/detail' => [
'status' => 'open', 'kind' => 'detail', 'guard' => 'builtin', 'name' => '账户消耗录入 · 详情',
'note' => '单条账户消耗录入记录;录入人须在当前账号可见范围内。',
'params_allow' => ['id' => '账户消耗记录ID'],
],
'stats.personalAccountCost/lists' => [
'status' => 'open', 'kind' => 'list', 'name' => '账户消耗录入',
'note' => '员工自录的投放账户消耗,按当前账号数据范围(录入人)过滤;extend.total_amount 为筛选结果金额合计,extend.days_count 为天数。',
'params_allow' => [
'start_date' => '消耗日期起 YYYY-MM-DD',
'end_date' => '消耗日期止 YYYY-MM-DD',
'media_source' => '自媒体来源(精确匹配,取值见 stats.selfInput/mediaSourceOptions)',
'creator_name' => '录入人姓名(模糊)',
'remark' => '备注(模糊)',
'dept_id' => '录入人所在部门ID(含下级)',
],
],
// 逐条校验:PersonalYejiController::detail() → PersonalYejiLogic::detail() → PersonalStatsScopeTrait::assertRecordVisible()
'stats.personalYeji/detail' => [
'status' => 'open', 'kind' => 'detail', 'guard' => 'builtin', 'name' => '员工自录业绩 · 详情',
'note' => '单条员工自录业绩记录;录入人须在当前账号可见范围内。',
'params_allow' => ['id' => '自录业绩记录ID'],
],
'stats.personalYeji/lists' => [
'status' => 'open', 'kind' => 'list', 'name' => '员工自录业绩',
'note' => '员工每日自录的加粉、开口、预约、面诊、成交等数据,按当前账号数据范围(录入人)过滤。',
'params_allow' => [
'start_date' => '业绩日期起 YYYY-MM-DD',
'end_date' => '业绩日期止 YYYY-MM-DD',
'media_source' => '自媒体来源(精确匹配,取值见 stats.selfInput/mediaSourceOptions)',
'creator_name' => '录入人姓名(模糊)',
'creator_id' => '录入人(后台账号)ID',
'remark' => '备注(模糊)',
'dept_id' => '录入人所在部门ID(含下级)',
],
],
'stats.revisitRate/assignLines' => [
'status' => 'open', 'kind' => 'report', 'name' => '复诊接诊率 · 被指派明细',
'note' => '后台本身不按数据范围过滤:拥有该权限的账号可看到二中心全部医助当月被指派的诊单(含患者姓名、手机号)。口径:按指派操作时间落月、非继承指派、医助×诊单去重,剔除名下有拒收/退款订单的诊单;仅统计二中心及其下级部门。不传 assistant_id/dept_id 时返回全部。',
'params_allow' => [
'month' => '统计月份 YYYY-MM(默认本月)',
'dept_ids' => '部门筛选(限二中心子树),多个逗号分隔',
'assistant_id' => '只看某医助',
'dept_id' => '只看某部门分组(0=未分配部门)',
],
],
'stats.revisitRate/deptOptions' => [
'status' => 'open', 'kind' => 'report', 'name' => '复诊接诊率 · 部门选项',
'note' => '二中心及其下级部门(id、pid、name),供 dept_ids 参数取值。',
'params_allow' => [],
],
'stats.revisitRate/overview' => [
'status' => 'open', 'kind' => 'report', 'name' => '复诊接诊率',
'note' => '后台本身不按数据范围过滤:拥有该权限的账号可看到二中心全部医助的数据。口径:当月被指派数=当月非继承指派的医助×诊单(剔除名下有拒收/退款订单的诊单);N 诊单数=当月下单且为该诊单全局第 N 笔计入业绩的业务订单(剔除取消/拒收/退款,诊次跨月累计),归属下单时的持有医助;N 诊接诊率=N 诊单数÷当月被指派数(往月指派当月成交会使比率超过 100%)。按部门→医助分组并有合计行。',
'params_allow' => [
'month' => '统计月份 YYYY-MM(默认本月)',
'dept_ids' => '部门筛选(限二中心子树,含下级),多个逗号分隔',
],
],
'stats.revisitRate/visitOrderLines' => [
'status' => 'open', 'kind' => 'report', 'name' => '复诊接诊率 · N 诊订单明细',
'note' => '后台本身不按数据范围过滤:拥有该权限的账号可看到二中心全部医助的 N 诊订单(含订单号、金额、患者姓名、手机号)。与复诊接诊率“N 诊单数”同口径,可对账。',
'params_allow' => [
'month' => '统计月份 YYYY-MM(默认本月)',
'slot' => '诊次 N(必填,2=二诊,最大 50)',
'dept_ids' => '部门筛选(限二中心子树),多个逗号分隔',
'assistant_id' => '只看某医助',
'dept_id' => '只看某部门分组(0=未分配部门)',
],
],
'stats.selfInput/mediaSourceOptions' => [
'status' => 'open', 'kind' => 'report', 'name' => '自媒体来源选项',
'perm' => 'stats.selfInput/overview',
'note' => '字典“推广渠道”(channels)中启用的来源名称,供 media_source 参数取值。',
'params_allow' => [],
],
'stats.selfInput/overview' => [
'status' => 'open', 'kind' => 'report', 'name' => '自录转化统计',
'note' => '基于员工自录业绩与账户消耗:按录入人数据范围过滤(配置 self_input_stats_view_all_roles 的角色可见全部);没有财务可见权限时不返回账户消耗、现金成本、ROI。time_type:week=最近7天、month=最近30天。lists 为当前页明细,summary 为筛选范围合计。',
'params_allow' => [
'time_type' => '时间范围:today(默认)/ yesterday / week 最近7天 / month 最近30天 / custom 自定义',
'start_date' => '自定义开始日期 YYYY-MM-DD(time_type=custom 时)',
'end_date' => '自定义结束日期 YYYY-MM-DD(time_type=custom 时)',
'media_source' => '自媒体来源(精确匹配,取值见 stats.selfInput/mediaSourceOptions)',
'dept_id' => '录入人所在部门ID(含下级)',
'page_no' => '页码,默认 1',
'page_size' => '每页条数,默认 15,最大 100',
],
],
'stats.yejiStats/appointmentLines' => [
'perm_fallback' => ['stats.yejiStats/overview', 'fans/yeji'],
'status' => 'open', 'kind' => 'report', 'name' => '业绩看板 · 预约挂号明细',
'note' => '与业绩看板/医助排行榜“预约诊单”同口径的逐条挂号:预约日期在区间内,状态为已预约/已完成/已过号(不含已取消)。传 assistant_id 看某医助,或传 dept_id 看某部门行(二选一)。' . $yejiScopeNote,
'params_allow' => $dateRange + $yejiFilter + [
'assistant_id' => '医助ID(排行榜行)',
'dept_id' => '部门行ID(看板部门行,0=未归属中心)',
'page' => '页码,默认 1',
'page_size' => '每页条数,默认 20,最大 100',
],
],
'stats.yejiStats/assignLines' => [
'perm_fallback' => ['stats.yejiStats/overview', 'fans/yeji'],
'status' => 'open', 'kind' => 'report', 'name' => '业绩看板 · 被指派明细',
'note' => '与业绩看板“被指派数”同口径:区间内非继承的成功指派,按指派操作时间落区间,医助×诊单去重,剔除已删诊单。传 assistant_id 或 dept_id(二选一)。' . $yejiScopeNote,
'params_allow' => $dateRange + $yejiFilter + [
'assistant_id' => '医助ID(排行榜行)',
'dept_id' => '部门行ID(看板部门行)',
'page' => '页码,默认 1',
'page_size' => '每页条数,默认 20,最大 100',
],
],
'stats.yejiStats/channelOptions' => [
'perm_fallback' => ['stats.yejiStats/overview', 'fans/yeji'],
'status' => 'open', 'kind' => 'report', 'name' => '业绩看板 · 渠道选项',
'note' => '启用中的投放渠道(企微标签渠道),按来源分组,附带打了该标签的客户数;供 channel_code 参数取值。',
'params_allow' => [],
],
'stats.yejiStats/deptOptions' => [
'perm_fallback' => ['stats.yejiStats/overview', 'fans/yeji'],
'status' => 'open', 'kind' => 'report', 'name' => '业绩看板 · 部门选项',
'note' => '按当前账号数据范围收窄的部门列表(id、name、pid、完整路径),供 dept_ids 参数取值。',
'params_allow' => [],
],
'stats.yejiStats/leadLines' => [
'perm_fallback' => ['stats.yejiStats/overview', 'fans/yeji'],
'status' => 'open', 'kind' => 'report', 'name' => '业绩看板 · 进线明细',
'note' => '与业绩看板“进线数据”同口径:企业微信添加客户事件(add_external_contact)逐条,按接待员工归属部门行;选渠道时只含带该标签的客户。dept_id 必填(看板部门行)。' . $yejiScopeNote,
'params_allow' => $dateRange + $yejiFilter + [
'dept_id' => '部门行ID(必填)',
'page' => '页码,默认 1',
'page_size' => '每页条数,默认 20,最大 100',
],
],
'stats.yejiStats/leaderboard' => [
'perm_fallback' => ['stats.yejiStats/tabLeaderboard', 'fans/yeji'], 'timeout' => 30,
'status' => 'open', 'kind' => 'report', 'name' => '业绩看板 · 医助排行榜',
'note' => '按展示部门分表的医助排行:诊金=订单创建人为该医助的业务订单金额(剔除取消/拒收/退款),另有进线、被指派、接诊、成交单、预约诊单、接诊率(元/进线);二中心医助附复诊分项。结果 range_note 有完整口径。' . $yejiScopeNote,
'params_allow' => $dateRange + $yejiFilter,
],
'stats.yejiStats/multi' => [
'perm_fallback' => ['stats.yejiStats/tabZyyt', 'stats.yejiStats/tabTargetMatrix', 'fans/yeji'], 'timeout' => 30,
'status' => 'open', 'kind' => 'report', 'name' => '业绩看板 · 多区间',
'note' => '一次返回本月(1 日至今天)、本周(周一至今天)、今日、昨日四个区间的业绩看板,每个区间与 stats.yejiStats/overview 相同;不支持自定义区间(请用 overview)。' . $yejiScopeNote,
'params_allow' => $yejiFilter,
],
'stats.yejiStats/overview' => [
'perm_fallback' => ['stats.yejiStats/tabZyyt', 'fans/yeji'], 'timeout' => 30,
'status' => 'open', 'kind' => 'report', 'name' => '业绩看板',
'note' => '部门×日期区间:进线=企微添加客户事件(按接待员工部门);被指派数=区间内非继承指派(医助×诊单去重);已完成挂号按预约日期;接诊诊单/成交单数=计入业绩的业务订单条数;合计业绩=业务订单金额,按订单创建时间、剔除已取消(4)/拒收(9)/退款(10),按订单创建人部门归属;投放成本按进线占比分摊,ROI=业绩÷投放成本;复诊只统计二中心。受数据范围限制的账号不显示“未归属中心”行,底栏合计=表内各行之和。结果 channel_filter_note 有完整口径。',
'params_allow' => $dateRange + $yejiFilter,
],
'stats.yejiStats/revisitBreakdown' => [
'perm_fallback' => ['stats.yejiStats/overview', 'fans/yeji'],
'status' => 'open', 'kind' => 'report', 'name' => '业绩看板 · 二中心复诊拆解',
'note' => '二中心部门行的复诊业务订单按医助拆解(订单创建人归属);与看板“复诊”列同口径。' . $yejiScopeNote,
'params_allow' => $dateRange + $yejiFilter + [
'dept_id' => '部门行ID(必填,须为二中心子树内的展示行)',
'revisit_slot' => '复诊分项:0=复诊合计(默认),2=复诊2,3=复诊3……',
],
],
'stats.yejiStats/unassignedBreakdown' => [
'perm_fallback' => ['stats.yejiStats/overview', 'fans/yeji'],
'status' => 'open', 'kind' => 'report', 'name' => '业绩看板 · 未归属中心拆解',
'note' => '业绩看板“未归属中心”补差按订单创建人拆解(创建人部门无法映射到任何展示中心);受限账号只列可见医助,但 admin_id=0 行(无创建人/无诊单的全站金额)与后台页面一致会显示。',
'params_allow' => $dateRange + [
'dept_ids' => '展示部门ID,多个用逗号分隔(与看板一致)',
],
],
// ───────────────────────────── 一诊 firstvisit.* ─────────────────────────────
'firstvisit.conversion/fansDetail' => [
'status' => 'open', 'kind' => 'list', 'name' => '综合数据转化 · 加粉明细',
'perm' => 'firstvisit.conversion/overview',
'note' => '先查 firstvisit.conversion/overview,再用其中一行作为实体:部门行 entity_type=dept、entity_id=部门ID;成员行 entity_type=member、entity_id=该行 id(形如 M{员工ID}_{部门ID})。实体须在当前账号数据范围内,否则返回空;时间与筛选参数应与总览一致。external_userid 为企微客户标识。',
'params_allow' => [
'entity_type' => '实体类型(必填):dept 部门行 / member 成员行',
'entity_id' => '实体ID(必填):部门ID,或成员行 id(M{员工ID}_{部门ID})',
'time_type' => '时间范围:today(默认)/ yesterday / week 本周 / month 本月 / quarter 本季度 / year 本年 / custom',
'start_date' => '自定义开始日期 YYYY-MM-DD(time_type=custom 时)',
'end_date' => '自定义结束日期 YYYY-MM-DD(time_type=custom 时)',
'dept_id' => '部门筛选(与总览一致)',
'assistant_id' => '员工筛选(与总览一致)',
'media_channel_code' => '企微标签渠道编码(与总览一致)',
],
],
'firstvisit.conversion/overview' => [
'status' => 'open', 'kind' => 'report', 'name' => '一诊综合数据转化',
'note' => '按当前账号数据范围与所选部门/员工取交集:加粉、预约(按预约日期,含已预约/已完成/已过号)、挂号(支付时间内已支付且 0<实收<10 元的订单)、面诊、成交与业绩(业务订单创建时间与创建人,剔除取消/拒收/退款及发生退款的订单),开口数来自个人业绩录入;没有“查看现金成本与ROI”权限时不返回账户消耗、现金成本、ROI。time_type:week=本周(周一起)、month=本月、quarter=本季度、year=本年。',
'params_allow' => [
'time_type' => '时间范围:today(默认)/ yesterday / week 本周 / month 本月 / quarter 本季度 / year 本年 / custom',
'start_date' => '自定义开始日期 YYYY-MM-DD(time_type=custom 时)',
'end_date' => '自定义结束日期 YYYY-MM-DD(time_type=custom 时)',
'dept_id' => '部门ID(只能收窄在数据范围内)',
'assistant_id' => '员工ID(只能收窄在数据范围内)',
'media_channel_code' => '企微标签渠道编码(取值见返回的 filters.media_channels)',
],
],
'firstvisit.doctorDashboard/overview' => [
'status' => 'open', 'kind' => 'report', 'name' => '一诊医生看板',
'note' => '以医生为展示维度:医生本人(仅本人数据范围)只看自己;医助/组长/经理只看数据范围内医助经手患者关联的医生数据;管理员看全部。预约含已预约/已取消/已完成/已过号,面诊=已完成预约;业绩按订单创建时间,排除取消/拒收/全额及部分退款,金额归属开方医生;挂号按支付时间统计 0<实收<10 元的已支付订单。time_type:week=本周、month=本月(默认)。',
'params_allow' => [
'time_type' => '时间范围:today / yesterday / week 本周 / month 本月(默认)/ custom',
'start_date' => '自定义开始日期 YYYY-MM-DD(time_type=custom 时)',
'end_date' => '自定义结束日期 YYYY-MM-DD(time_type=custom 时)',
'dept_id' => '部门ID(只能收窄在数据范围内)',
'doctor_id' => '只看某位医生',
'active_only' => '只含在职医生:1 是(默认)、0 否',
'alert_threshold' => '预警阈值(接诊转化率 %,1~100,默认 15)',
],
],
'firstvisit.myPatient/assistants' => [
'status' => 'open', 'kind' => 'report', 'name' => '我的患者 · 可指派医助',
'perm' => 'firstvisit.myPatient/lists',
'note' => '当前账号数据范围内的在职医助(ID、姓名、账号、部门)。后台还要求账号有诊单“指派”权限 tcm.diagnosis/assign,否则返回权限不足。',
'params_allow' => [],
],
'firstvisit.myPatient/lists' => [
'status' => 'open', 'kind' => 'list', 'name' => '我的患者',
'note' => '范围由 MyPatientLogic::applyScope 决定:医生=本人接诊过(有效挂号)的患者,医助=本人负责的患者,经理/诊室组长等按数据范围,root 看全部。后台已脱敏手机号(phone_masked),不返回身份证号(仅 has_id_card)。按下次预约时间排序;extend.summary 为今天/明天/后天的预约人数。',
'params_allow' => [
'keyword' => '患者姓名/手机号/医助姓名/接诊医生姓名(模糊)',
'status_filter' => '预约状态:unbooked 未预约 / pending_interview 待面诊 / completed 已完成 / missed 已过号',
'start_date' => '预约日期起 YYYY-MM-DD',
'end_date' => '预约日期止 YYYY-MM-DD',
],
],
// 逐条校验:MyPatientController::orderDetail() 先调 guardOrder()(页面权限 + tcm.prescriptionOrder/detail 权限 + MyPatientLogic::canAccessDiagnosis() 校验订单所属患者在“我的患者”范围内),
// 再由 PrescriptionOrderLogic::detail() → canAccessOrder() 二次校验;外部调用标记是 PharmacySubmissionClaimService 名称误匹配,实际只读本地表。
'firstvisit.myPatient/orderDetail' => [
'status' => 'open', 'kind' => 'detail', 'guard' => 'builtin', 'name' => '我的患者 · 订单详情',
'perm' => 'firstvisit.myPatient/lists',
'note' => '处方业务订单详情(含处方、关联支付单、挂号摘要);订单须属于当前账号“我的患者”范围,且账号需有处方订单详情权限 tcm.prescriptionOrder/detail;无药材明细权限时不返回药材。',
'params_allow' => ['id' => '处方业务订单ID'],
],
'firstvisit.myPatient/orders' => [
'status' => 'open', 'kind' => 'list', 'name' => '我的患者 · 订单',
'perm' => 'firstvisit.myPatient/lists',
'note' => '“我的患者”范围内患者的处方业务订单(按患者范围收窄,不按订单创建人);手机号已由后台脱敏。extend.summary:订单数、有效金额(剔除取消/拒收/退款)、待审核数、已完成数、拒收数与拒收率。',
'params_allow' => [
'keyword' => '订单号/患者姓名/手机号/收件人(模糊);纯数字时也匹配订单ID、处方ID、诊单ID',
'prescription_audit_status' => '处方审核:0 待审核、1 已通过、2 已驳回',
'payment_slip_audit_status' => '支付单审核:0 待审核、1 已通过、2 已驳回',
'fulfillment_status' => '履约状态:1 待双审通过、2 待发货、3 已完成、4 已取消、5 已发货、6 已签收、7 进行中、8 暂不制药、9 拒收、10 退款、11 保留药方、12 制药缓发',
'start_date' => '订单创建日期起 YYYY-MM-DD',
'end_date' => '订单创建日期止 YYYY-MM-DD',
],
],
'firstvisit.myPatient/progress' => [
'status' => 'open', 'kind' => 'list', 'name' => '我的患者 · 面诊进度',
'perm' => 'firstvisit.myPatient/lists',
'note' => '“我的患者”范围内的挂号面诊进度(确认、面诊、开方、候诊排队位次);日期默认今天,跨度最长 31 天;手机号已由后台脱敏。extend 含当日排班/号源概览与未来一周排班。',
'params_allow' => [
'keyword' => '患者姓名/手机号/医生/医助姓名(模糊);纯数字时也匹配挂号ID、诊单ID',
'status' => '挂号状态:1 已预约、3 已完成、4 已过号(不传=全部有效状态)',
'start_date' => '预约日期起 YYYY-MM-DD(默认今天)',
'end_date' => '预约日期止 YYYY-MM-DD(默认同开始日期)',
],
],
'firstvisit.registrationStats/overview' => [
'status' => 'open', 'kind' => 'report', 'name' => '一诊挂号统计',
'note' => '按员工(医助)统计:挂号=支付时间内已支付且 0<实收<10 元的订单(按订单创建人);预约=预约日期内已预约/已完成/已过号(优先挂号医助,再回退诊单医助);诊单=业务订单(按创建时间与创建人,排除取消/拒收/退款)。部门与员工筛选只能在当前账号数据范围内收窄;含与上一周期对比与年度目标进度。time_type:week=本周、month=本月。',
'params_allow' => [
'time_type' => '时间范围:today(默认)/ yesterday / week 本周 / month 本月',
'dept_id' => '部门ID(只能收窄在数据范围内)',
'assistant_id' => '员工ID(只能收窄在数据范围内)',
],
],
'firstvisit.wecomPromotion/checkApiPermission' => [
'status' => 'excluded', 'name' => '企业微信获客助手 · 接口权限自检',
'reason' => '获客助手应用配置与接口权限自检,会实时调用企业微信接口,属于系统配置检测',
],
'firstvisit.wecomPromotion/customerStatistics' => [
'status' => 'open', 'kind' => 'report', 'name' => '企业微信获客助手 · 获客客户统计',
'perm' => 'firstvisit.wecomPromotion/overview',
'note' => '获客链接带来的客户及会话统计,按当前账号数据范围(承接成员/链接归属人)及被共享的分流方案收窄;external_userid 已由后台脱敏。数据来自本地同步表,不实时调用企业微信(同步需在后台手动操作)。',
'params_allow' => [
'promotion_link_id' => '本地获客链接ID',
'userid' => '承接成员的企业微信 userid',
'chat_status' => '会话状态:1 已发消息、0 未发消息、2 未知',
'keyword' => '客户标识/成员 userid/成员姓名/链接名称(模糊)',
'page_no' => '页码,默认 1',
'page_size' => '每页条数,默认 20,最大 100',
],
],
'firstvisit.wecomPromotion/overview' => [
'status' => 'excluded', 'name' => '企业微信获客助手 · 配置总览',
'reason' => '获客助手配置页:返回企业微信应用配置状态(corp_id 掩码、agent_id、回调地址)、分流方案/链接/成员配置与网页安装代码,打开时还会回填分流成员(写库);属配置管理,不对 AI 开放',
],
'firstvisit.wecomPromotion/remoteLinkDetail' => [
'status' => 'excluded', 'name' => '企业微信获客助手 · 官方链接详情',
'reason' => '实时调用企业微信获客助手接口拉取链接详情并回写本地链接记录(外部接口 + 写库),属于同步操作',
],
'firstvisit.wecomPromotion/tagOptions' => [
'status' => 'pending', 'name' => '企业微信获客助手 · 企业标签选项',
'reason' => '每次都实时调用企业微信 externalcontact/get_corp_tag_list 取企业标签(外部接口),需改为读本地标签表后再开放;标签及客户数可先用 qywx.customer/tagStats 或 stats.yejiStats/channelOptions 查询',
],
// ───────────────────────────── 企业微信 qywx.* ─────────────────────────────
'qywx.customer/getSyncSettings' => [
'status' => 'excluded', 'name' => '企业微信客户 · 同步设置',
'reason' => '企业微信客户同步设置(自动同步开关、间隔、同步状态),配置类接口不对 AI 开放',
],
'qywx.customer/lists' => [
'status' => 'open', 'kind' => 'list', 'name' => '企业微信客户',
'note' => '后台本身不按数据范围过滤:拥有该权限的账号可看到全部企业微信外部联系人(含跟进人、跟进人备注与描述、标签、添加渠道),请谨慎授权。dedupe_mode=first 按客户首次添加时间筛选(默认),any 按添加事件流水筛选(含老客被其他员工重复添加)。',
'params_allow' => [
'name' => '客户名称(模糊)',
'tag_ids' => '企业标签ID,多个用逗号分隔(命中任一;取值见 qywx.customer/tagStats)',
'follow_user' => '跟进人姓名或企业微信 userid',
'add_time_start' => '添加日期起 YYYY-MM-DD',
'add_time_end' => '添加日期止 YYYY-MM-DD',
'dedupe_mode' => '添加时间口径:first 首次添加(默认)/ any 任意一次添加事件',
'add_way' => '添加方式编号(企业微信 add_way,如 1 扫码、2 搜索手机号、16 获客链接)',
],
],
'qywx.customer/stats' => [
'status' => 'open', 'kind' => 'report', 'name' => '企业微信客户 · 统计',
'note' => '后台本身不按数据范围过滤:全公司企业微信客户总数、今日添加事件数、今日新增客户的跟进人条数、最近同步时间与状态。',
'params_allow' => [],
],
'qywx.customer/tagStats' => [
'status' => 'open', 'kind' => 'report', 'name' => '企业微信客户 · 标签统计',
'note' => '后台本身不按数据范围过滤:全公司当前有效企业标签按分组列出客户数(按客户数倒序),供 tag_ids 参数取值。',
'params_allow' => [],
],
'qywx.customer/todayArrival' => [
'status' => 'open', 'kind' => 'report', 'name' => '企业微信客户 · 今日进入分布',
'note' => '后台本身不按数据范围过滤:今日全公司添加客户事件(add_external_contact)总数、最近一条时间、按小时分布与渠道 state Top5。',
'params_allow' => [],
],
'qywx.customer/todayArrivalList' => [
'status' => 'open', 'kind' => 'report', 'name' => '企业微信客户 · 今日进入明细',
'note' => '后台本身不按数据范围过滤:今日全公司添加客户事件逐条(时间、接待员工、客户名称、渠道 state),按时间倒序分页。',
'params_allow' => [
'page_no' => '页码,默认 1',
'page_size' => '每页条数,默认 20,最大 100',
],
],
'qywx.message/archive_list' => [
'status' => 'pending', 'name' => '企业微信会话存档 · 消息记录',
'reason' => '返回会话存档原文(解密落库的员工与客户聊天内容、原始报文和媒体,可能含患者病情);后台接口不按数据范围过滤,可按任意员工/客户/群查看全部会话,且未找到对应菜单权限点(未登记时后台对任意登录账号放行);需先按本人及数据范围内员工收窄后再开放',
],
'qywx.message/customer_of_staff' => [
'status' => 'open', 'kind' => 'report', 'name' => '企业微信消息 · 员工的客户',
'note' => '后台本身不按数据范围过滤:可查询任意员工(按企业微信 userid)已添加的客户(名称、类型、性别、企业名、unionid),最多 200 条;与企业微信客户列表同源。',
'params_allow' => [
'staff_userid' => '员工企业微信 userid(必填,取值见 qywx.message/staff_list)',
'keyword' => '客户名称(模糊)',
],
],
'qywx.message/pull_archive' => [
'status' => 'excluded', 'name' => '企业微信会话存档 · 手动拉取',
'reason' => '手动触发企业微信会话存档拉取(调用会话存档 SDK、写库、可下载媒体文件),属调试/定时任务类操作',
],
'qywx.message/send_task_list' => [
'status' => 'pending', 'name' => '企业微信群发任务',
'reason' => '后台接口不按数据范围过滤,返回全部员工的群发任务(含消息内容、附件与目标客户 external_userid 列表),且未找到对应菜单权限点;需先登记权限并按创建人/员工数据范围收窄后再开放',
],
'qywx.message/session_list' => [
'status' => 'pending', 'name' => '企业微信会话存档 · 会话列表',
'reason' => '会话列表含每个会话最后一条消息摘要(聊天内容)与客户信息;后台接口不按数据范围过滤,可查看全部员工与客户的会话,且未找到对应菜单权限点;需先按本人及数据范围内员工收窄后再开放',
],
'qywx.message/staff_list' => [
'status' => 'open', 'kind' => 'report', 'name' => '企业微信消息 · 可代发员工',
'note' => '后台本身不按数据范围过滤:已绑定企业微信的全部员工(ID、姓名、企业微信 userid、部门),最多 200 条。',
'params_allow' => [
'keyword' => '员工姓名或企业微信 userid(模糊)',
],
],
// ─────────── 扫描按名称误判为写操作的 GET 接口(不在候选清单内,给出准确结论) ───────────
'stats.commissionSettlement/confirmStatus' => [
'status' => 'open', 'kind' => 'report', 'name' => '提成结算 · 核对确认状态',
'note' => '只读:当前结算月 + 渠道 + 部门筛选组合的核对/确定状态(核对备注、确定人与时间、确定时的合计快照 totals_json),与 stats.commissionSettlement/overview 返回的 confirm 相同;该状态按筛选组合共享,不随查看人数据范围变化(与后台一致)。',
'params_allow' => [
'settlement_month' => '结算月 YYYY-MM(必填)',
'dept_ids' => '展示部门ID,多个用逗号分隔(须与汇总时一致)',
'channel_code' => '渠道编码(须与汇总时一致)',
],
],
'qywx.customer/sync' => [
'status' => 'excluded', 'name' => '企业微信客户 · 同步',
'reason' => '触发后台企业微信客户全量同步进程(调用企业微信接口并写库),写操作',
],
'qywx.message/archive_status' => [
'status' => 'excluded', 'name' => '企业微信会话存档 · 模块状态',
'reason' => '会话存档模块诊断信息(SDK 路径、公钥版本、私钥是否配置),属系统配置信息',
],
'qywx.message/send_task_detail' => [
'status' => 'excluded', 'name' => '企业微信群发 · 送达详情',
'reason' => '实时调用企业微信接口查询群发送达结果并回写任务状态(外部接口 + 写库)',
],
'qywx.message/upload_to_qywx' => [
'status' => 'excluded', 'name' => '企业微信 · 上传素材',
'reason' => '上传文件到企业微信临时素材(外部接口),写操作',
],
];