This commit is contained in:
gr
2026-09-24 09:45:44 +08:00
parent dbf474ddd7
commit bd22e5f476
38 changed files with 19152 additions and 0 deletions
File diff suppressed because it is too large Load Diff
+10
View File
@@ -0,0 +1,10 @@
<?php
/**
* AI 数据目录的人工审核结论:覆盖 generated.php 的自动判断。
* 按后台目录拆分在 review/*.php,每个文件返回 [资源标识 => 条目],字段说明见 review/README.md。
*/
$entries = [];
foreach (glob(__DIR__ . DIRECTORY_SEPARATOR . 'review' . DIRECTORY_SEPARATOR . '*.php') ?: [] as $file) {
$entries = array_merge($entries, (array) require $file);
}
return $entries;
+32
View File
@@ -0,0 +1,32 @@
# AI 数据目录人工审核
`generated.php` 由 `php app/mcp/cli/catalog.php --write` 扫描后台全部接口生成,只是盘点。
本目录下每个 `*.php` 文件返回 `[资源标识 => 条目]`,覆盖自动判断,决定 AI 能否查询、怎么查询。
资源标识就是后台权限点写法,如 `tcm.diagnosis/lists`。
## 条目字段
| 字段 | 说明 |
|---|---|
| `status` | `open` 开放 / `pending` 待整改(必须写 `reason`)/ `excluded` 不开放(必须写 `reason`) |
| `reason` | 未开放的原因,会展示给使用者和模型 |
| `name` | 中文名称(菜单名称不清楚时填写) |
| `note` | 口径说明,如“按预约日期统计,不含已取消” |
| `kind` | 覆盖自动识别:`list` 列表 / `detail` 单条详情 / `report` 统计或其他查询 |
| `params_allow` | 允许的查询参数及中文说明 `['patient_name' => '患者姓名(模糊)']`;不填则用扫描到的参数减去禁用参数 |
| `forbid` | 额外禁用的参数(会扩大数据范围的开关等),全局禁用见 `Catalog::GLOBAL_FORBID` |
| `force` | 固定参数,如 `['apply_data_scope' => 1]`、`['only_archived' => 1]` |
| `guard` | 详情类必填:`'builtin'`(接口自身已做逐条权限校验,需在注释写明函数)、`['callable' => [类::class, '方法'], 'args' => ['id', 'admin_id', 'admin_info']]`(调用已有校验函数,返回 true 放行)、`['via' => '列表资源标识', 'filter' => '参数名', 'match' => 'id']`(用列表的数据范围判断) |
| `handler` | 控制器里夹带写操作时改为直接调 Logic:`['logic' => [类::class, '方法'], 'args' => ['params', 'admin_id', 'admin_info'], 'validate' => [验证器::class, '场景'], 'error' => [类::class, 'getError']]` |
| `http` | 只读但必须 POST 的接口填 `'POST'` |
## 开放门槛(全部满足才可 `open`)
1. 只读:调用链不写业务表(运行时在只读事务里执行,写库会直接报错并回滚);
2. 不调用外部接口(企微、腾讯 IM、物流、短信等),或可用固定参数避开;
3. 数据范围与后台页面一致;后台本身不做数据范围的,在 `note` 里写明“对有权限的账号返回全量”;
4. 详情类有逐条权限校验(`guard`);
5. 不返回凭据(各类密钥、令牌、证书),配置类接口一律 `excluded`;
6. 去掉会扩大范围的参数(`forbid`),分页由 MCP 统一控制。
运行时还会检查权限点是否已在菜单登记;未登记的资源即使写了 `open` 也按“待整改”处理。
+300
View File
@@ -0,0 +1,300 @@
<?php
/**
* AI 数据目录人工审核:医生/挂号、收款订单、财务、药房、用户、粉丝、充值、消息、工作台、资源分发(asset)。
* 字段说明见 README.md。每条结论都读过控制器动作及其调用的 Lists/Logic 代码(行号以 2026-09-24 代码为准)。
* 注意:本文件被 require 进 Catalog::all() 的作用域,不要在这里定义变量或常量。
*/
use app\adminapi\logic\tcm\DiagnosisLogic;
return [
// ================= 医生 / 挂号 =================
// AppointmentLists::lists() 190-297:医生角色(1) 只看 a.doctor_id=本人,医助角色(2) 只看 u.assistant_id=本人,
// 再按数据范围 (a.doctor_id OR u.assistant_id) IN 可见账号;progress_board / diag_scope_relax 会同时去掉角色收窄和数据范围(271-286、33-49),必须禁用。
// include_status_counts 只在 extend 里按同样条件 GROUP BY 状态,不扩大范围。
'doctor.appointment/lists' => [
'status' => 'open', 'name' => '接诊台挂号列表',
'note' => '医生账号只看挂自己号的记录,医助只看自己诊单的挂号,另按数据范围过滤;按预约日期 appointment_date 筛选。每行含诊单 diagnosis(病历字段,个人信息按权限脱敏)。',
'forbid' => ['progress_board', 'diag_scope_relax'],
'params_allow' => [
'start_date' => '预约日期起 YYYY-MM-DD', 'end_date' => '预约日期止 YYYY-MM-DD',
'status' => '状态:1 已预约、2 已取消、3 已完成、4 已过号', 'exclude_cancelled' => '1=排除已取消',
'patient_name' => '患者姓名(模糊)', 'patient_id' => '诊单ID(挂号表 patient_id 存的是诊单ID)',
'doctor_id' => '接诊医生(后台账号)ID', 'doctor_name' => '医生姓名(模糊)',
'assistant_id' => '医助ID(诊单医助或挂号医助任一命中)', 'assistant_dept_id' => '部门ID(医生/医助所属部门,含下级部门)',
'appointment_type' => '问诊方式:video 视频、text 图文', 'channel_source' => '渠道(字典 channels 的值)',
'diagnosis_confirmed' => '诊单是否已确认:1 已确认、0 未确认',
'prescription_today_only' => '1=开方标记只看今天开的处方', 'include_status_counts' => '1=在 extend.status_count 返回各状态数量',
],
],
// guard builtin:AppointmentController::reception() 148-156 → AppointmentLogic::reception() 635-660 先取挂号行与诊单医助,
// 再调 AppointmentLogic::appointmentRowManageableByAdmin() 916-971(与 AppointmentLists 相同:医生=本人、医助=本人诊单、数据范围命中医生或医助;不含看板放宽),
// 不通过返回空 → 控制器报“预约记录不存在或无权访问”。后续只读:detail()、DiagnosisLogic::detail()、DoctorNoteLogic/TrackingNoteLogic::getByDiagnosis()。
'doctor.appointment/reception' => [
'status' => 'open', 'name' => '接诊台详情(挂号+病历+备注)', 'kind' => 'detail', 'guard' => 'builtin', 'params_allow' => [],
'note' => '按挂号(预约)ID 返回挂号信息、完整诊单病历、医生备注和跟踪备注;接口逐条校验该挂号在当前账号接诊台可见范围内。',
],
// AppointmentController::detail() 106-111 → AppointmentLogic::detail() 482-516 按 ID 直接查,无任何行级校验。
'doctor.appointment/detail' => [
'status' => 'pending', 'name' => '挂号详情', 'kind' => 'detail',
'reason' => '按挂号ID直接返回(含患者姓名、手机号),接口没有逐条权限校验(AppointmentLogic::detail);需补与接诊台列表一致的行级校验后开放。可改用 doctor.appointment/reception(已校验)。',
],
// 控制器 190-201 先调 DiagnosisLogic::canViewReadonlyDiagnosis()(4301-4335),这里再用同一函数做一次 guard;DoctorNoteLogic::getByDiagnosis() 只读。
'doctor.appointment/doctorNotes' => [
'status' => 'open', 'name' => '诊单医生备注', 'kind' => 'detail', 'params_allow' => [],
'guard' => ['callable' => [DiagnosisLogic::class, 'canViewReadonlyDiagnosis'], 'args' => ['id', 'admin_id', 'admin_info'], 'param' => 'diagnosis_id'],
'note' => 'id 填诊单ID;返回该诊单最近 30 条医生备注(每天一条,含舌象/报告附件)。先校验诊单在当前账号只读可见范围内(医助仅本人诊单,另按数据范围)。',
],
// AppointmentLogic::getAvailableSlots() 110-273:只读排班与当天挂号的时间点,不含患者信息。
'doctor.appointment/availableSlots' => [
'status' => 'open', 'name' => '医生某日可约时段', 'kind' => 'report',
'note' => '后台本身不按数据范围过滤:拥有该权限的账号可看到全部。只返回时段及是否已被约,不含患者信息。',
'params_allow' => ['doctor_id' => '医生(后台账号)ID,必填', 'appointment_date' => '日期 YYYY-MM-DD,必填', 'period' => '时段:morning、afternoon、all'],
],
// AppointmentLogic::getDoctorAvailability() 524-569:只返回剩余号源数量。
'doctor.appointment/doctorAvailability' => [
'status' => 'open', 'name' => '医生某日剩余号源数', 'kind' => 'report',
'note' => '后台本身不按数据范围过滤:拥有该权限的账号可看到全部。只返回 available_count。',
'params_allow' => ['doctor_id' => '医生(后台账号)ID,必填', 'date' => '日期 YYYY-MM-DD,必填'],
],
// MedicineLists:药品目录,无数据范围,不含个人信息。
'doctor.medicine/lists' => [
'status' => 'open', 'name' => '药品库列表',
'note' => '后台本身不按数据范围过滤:拥有该权限的账号可看到全部(药品目录,不含个人信息)。',
'params_allow' => ['name' => '药品名称(模糊,纯字母按拼音首字母)', 'supplier' => '供应商(模糊)', 'status' => '状态'],
],
'doctor.medicine/detail' => [
'status' => 'pending', 'name' => '药品详情', 'kind' => 'detail',
'reason' => '按ID直接返回、无逐条校验(MedicineLogic::detail),列表也不支持按ID过滤无法做 via 校验;药品库列表已含全部字段,请用 doctor.medicine/lists。',
],
// RosterLists 34-53:无数据范围,且 lists() 不加 limit(不分页,返回条件内全部排班)。
'doctor.roster/lists' => [
'status' => 'open', 'name' => '医生排班',
'note' => '后台本身不按数据范围过滤:拥有该权限的账号可看到全部(排班不含患者信息)。后台列表不分页,会返回条件内全部排班,请同时传 start_date 与 end_date。',
'params_allow' => [
'start_date' => '排班日期起 YYYY-MM-DD(需与 end_date 同时传)', 'end_date' => '排班日期止 YYYY-MM-DD',
'doctor_id' => '医生(后台账号)ID', 'period' => '时段:morning、afternoon、night、segment',
'status' => '出诊状态:1 出诊、2 停诊、3 休息、4 请假',
],
],
'doctor.roster/detail' => [
'status' => 'pending', 'name' => '排班详情', 'kind' => 'detail',
'reason' => '按ID直接返回、无逐条校验(RosterLogic::detail),排班列表不支持按ID过滤;列表已含全部字段,请用 doctor.roster/lists 按医生和日期查询。',
],
// StatisticsLists 40-393:按医生聚合挂号数/诊单数/成交数,无数据范围。
'doctor.statistics/lists' => [
'status' => 'open', 'name' => '医生挂号统计',
'note' => '后台本身不按数据范围过滤:拥有该权限的账号可看到全部医生的统计。按预约日期统计;成交数=统计期内挂号诊单中有未作废处方的诊单数。',
'params_allow' => [
'time_type' => '时间范围:today、week(近7天)、month(近30天)、custom(用 start_date/end_date)',
'start_date' => '开始日期 YYYY-MM-DD(time_type=custom)', 'end_date' => '结束日期 YYYY-MM-DD(time_type=custom)',
'doctor_id' => '只看某位医生',
],
],
// StatisticsLists::getDeptStatistics() 399-455:按医助所在部门聚合挂号数,无数据范围。
'doctor.statistics/deptLists' => [
'status' => 'open', 'name' => '部门挂号统计',
'note' => '后台本身不按数据范围过滤:拥有该权限的账号可看到全部部门的统计。按挂号医助所属部门、预约日期统计。',
'params_allow' => [
'time_type' => '时间范围:today、week(近7天)、month(近30天)、custom(用 start_date/end_date)',
'start_date' => '开始日期 YYYY-MM-DD(time_type=custom)', 'end_date' => '结束日期 YYYY-MM-DD(time_type=custom)',
'dept_id' => '只看某个部门',
],
],
// ================= 收款订单 =================
// OrderLists 162-185:非主管角色(project.order_list_view_all_roles)只看 creator_id=本人;219-220 再按数据范围 creator_id 过滤。无放宽参数。
'order.order/lists' => [
'status' => 'open', 'name' => '收款订单(支付单)列表',
'note' => '非主管角色只看本人创建的支付单,另按数据范围(创建人)过滤。每行含关联诊单 patient 与创建人 creator(密码等字段已去除)。',
'params_allow' => [
'order_no' => '订单号(模糊)', 'patient_keyword' => '患者姓名/手机号(模糊)或诊单ID',
'order_type' => '费用类型:1 挂号费、2 问诊费、3 药品费用、4 首付、5 尾款、6 其他、7 全部费用、8 驼奶费用',
'status' => '状态:1 待支付、2 已支付、3 已取消、4 已退款、5 待审核',
'create_time_start' => '创建时间起 YYYY-MM-DD 或 YYYY-MM-DD HH:mm:ss', 'create_time_end' => '创建时间止',
'assistant_id' => '创建人(医助)ID', 'patient_association' => '患者关联:pending 待关联、associated 已关联',
],
],
'order.order/export' => [
'status' => 'excluded', 'name' => '收款订单导出',
'reason' => '导出权限接口(与收款订单列表同一数据,用于批量导出),AI 请用 order.order/lists 分页查询。',
],
// OrderLogic::orderStats() 1020-1243:只读聚合;按 DataScopeService 可见账号过滤 creator_id(1031-1065)。
'order.order/orderStats' => [
'status' => 'open', 'name' => '收款订单统计(按员工/部门)',
'note' => '按订单创建时间统计已支付(status=2)订单的笔数与金额,order_type=0 统计已退款(status=4),-1 为全部费用类型;按数据范围(创建人)过滤,但不像订单列表那样把非主管限制为本人:同一数据范围内同事的排名也可见。',
'params_allow' => [
'order_type' => '-1 全部已支付、0 退款、1 挂号费、2 问诊费、3 药品费用、4 首付、5 尾款、6 其他、7 全部费用、8 驼奶费用(默认 1)',
'days' => '最近多少天(1–90,0=今天,默认 7)', 'end_time' => '截止时间 YYYY-MM-DD HH:mm:ss(默认现在)',
],
],
// OrderLogic::todayRevenue() 744-763:主管角色(project.order_edit_all_roles)看全部,其他账号 creator_id=本人;不做数据范围。
'order.order/todayRevenue' => [
'status' => 'open', 'name' => '今日收款', 'kind' => 'report', 'params_allow' => [],
'note' => '今天(按支付时间)已支付订单的金额与笔数。主管角色看全公司、其他账号只看本人创建;后台本身不按数据范围过滤:主管角色可看到全部。',
],
// OrderController::actionLogs() 162-173 → OrderActionLogLogic::listByOrderId():只按 order_id 查,不校验该订单是否对当前账号可见。
'order.order/actionLogs' => [
'status' => 'pending', 'name' => '支付单操作日志', 'kind' => 'report',
'reason' => '按订单ID返回操作日志,不校验该订单是否在当前账号可见范围(非主管本应只能看本人创建的订单);订单列表也不支持按ID过滤,无法用 via 校验。需先补行级校验。',
],
// OrderActionLogLogic::statsByAdmin():按员工聚合操作次数,无数据范围。
'order.order/actionLogStats' => [
'status' => 'open', 'name' => '支付单操作次数统计(按员工)', 'kind' => 'report',
'note' => '后台本身不按数据范围过滤:拥有该权限的账号可看到全部员工的操作次数(含查看详情)。不传日期默认最近 7 天。',
'params_allow' => ['start_time' => '开始日期 YYYY-MM-DD(只写日期)', 'end_time' => '结束日期 YYYY-MM-DD(只写日期)', 'limit' => '最多返回多少人(1–200,默认 50)'],
],
// OrderLogic::listPaidOrdersForDiagnosis() 1303-1368:主管或该诊单医助看该诊单全部已支付单,否则只看本人创建;本身不校验诊单可见性 → 加诊单只读 guard。
'order.order/paidOrdersForDiagnosis' => [
'status' => 'open', 'name' => '诊单下可关联的已支付支付单', 'kind' => 'report',
'guard' => ['callable' => [DiagnosisLogic::class, 'canViewReadonlyDiagnosis'], 'args' => ['id', 'admin_id', 'admin_info'], 'param' => 'diagnosis_id'],
'params_allow' => ['diagnosis_id' => '诊单ID,必填'],
'note' => '先校验诊单在当前账号只读可见范围内;只列已支付、未被业务订单占用、2026-04-20 之后创建的支付单。主管或该诊单医助看全部,其他人只看本人创建。',
],
// ================= 财务 =================
// AccountCostLists:投放账户消耗(按日期/渠道/部门),无数据范围,不含个人信息;extend 只读(MediaChannelService 仅读库和缓存)。
'finance.accountCost/lists' => [
'status' => 'open', 'name' => '账户消耗(投放花费)列表',
'note' => '后台本身不按数据范围过滤:拥有该权限的账号可看到全部。extend.total_amount 为条件内合计金额,days_count 为天数。',
'params_allow' => [
'start_date' => '消耗日期起 YYYY-MM-DD', 'end_date' => '消耗日期止 YYYY-MM-DD', 'media_channel_code' => '渠道编码',
'dept_id' => '部门ID', 'dept_name' => '部门名称(模糊)', 'remark' => '备注(模糊)',
'creator_name' => '录入人(模糊)', 'updater_name' => '最后修改人(模糊)',
],
],
'finance.accountCost/detail' => [
'status' => 'pending', 'name' => '账户消耗详情', 'kind' => 'detail',
'reason' => '按ID直接返回、无逐条校验(AccountCostLogic::detail),列表不支持按ID过滤;列表已含全部字段,请用 finance.accountCost/lists。',
],
// AccountLogLists:likeadmin 用户余额流水,无数据范围;返回用户昵称/账号/手机号(手机号按权限脱敏)。
'finance.accountLog/lists' => [
'status' => 'open', 'name' => '用户余额明细',
'note' => '后台本身不按数据范围过滤:拥有该权限的账号可看到全部(小程序/H5 用户的余额变动)。',
'params_allow' => [
'type' => 'um=只看余额类变动', 'change_type' => '变动类型(见 finance.accountLog/getUmChangeType)',
'user_info' => '用户编号/昵称/手机号/账号(模糊)',
'start_time' => '开始时间 YYYY-MM-DD HH:mm:ss', 'end_time' => '结束时间 YYYY-MM-DD HH:mm:ss',
],
],
'finance.accountLog/getUmChangeType' => [
'status' => 'open', 'name' => '余额变动类型', 'params_allow' => [],
'note' => '固定枚举(AccountLogEnum),不读业务数据。',
],
// DeptPerformanceTargetLogic::monthMatrix() 21-47:部门树按 DataScopeService::getAllowedDeptIdSet() 收窄,只读。
'finance.deptPerformanceTarget/monthMatrix' => [
'status' => 'open', 'name' => '部门月度业绩目标',
'note' => '按数据范围只显示可见部门;target_amount 单位为元,total_target 为可见部门合计。',
'params_allow' => ['year_month' => '月份 YYYY-MM,必填'],
],
// RefundRecordLists / RefundLogic:likeadmin 充值退款,无数据范围;RefundLog 隐藏了 refund_msg(支付网关原始返回)。
'finance.refund/record' => [
'status' => 'open', 'name' => '退款记录',
'note' => '后台本身不按数据范围过滤:拥有该权限的账号可看到全部。extend 为各退款状态笔数。',
'params_allow' => [
'sn' => '退款单号', 'order_sn' => '来源订单号', 'refund_type' => '退款类型:1 后台退款',
'refund_status' => '退款状态:0 退款中、1 成功、2 失败', 'user_info' => '用户编号/昵称/手机号/账号(模糊)',
'start_time' => '开始时间 YYYY-MM-DD HH:mm:ss', 'end_time' => '结束时间 YYYY-MM-DD HH:mm:ss',
],
],
'finance.refund/log' => [
'status' => 'open', 'name' => '退款日志', 'params_allow' => ['record_id' => '退款记录ID(来自 finance.refund/record)'],
'note' => '后台本身不按数据范围过滤:拥有该权限的账号可看到全部(退款记录本身也不分范围)。',
],
'finance.refund/stat' => [
'status' => 'open', 'name' => '退款金额统计', 'params_allow' => [],
'note' => '后台本身不按数据范围过滤:拥有该权限的账号可看到全部。全部退款记录按状态汇总的订单金额(元)。',
],
// ================= 药房 =================
// MedicineMappingLists / MedicineMappingLogic:本地药品与恩济药房目录的映射、目录搜索、同步状态,均只读、不调外部接口(sync 才调,已是写接口)。
'pharmacy.medicineMapping/lists' => [
'status' => 'open', 'name' => '药材映射(本地药品库↔恩济药房目录)',
'note' => '后台本身不按数据范围过滤:拥有该权限的账号可看到全部。mapping_status:0 未映射、1 已映射、2 映射失效。',
'params_allow' => ['local_name' => '本地药品名(模糊)', 'remote_keyword' => '药房目录名称或编码(模糊)', 'mapping_status' => 'mapped 已映射、unmapped 未映射、invalid 失效'],
],
'pharmacy.medicineMapping/status' => [
'status' => 'open', 'name' => '药房目录同步状态', 'params_allow' => [],
'note' => '目录总数、有效数、未映射的本地药品数和最近一次同步结果;不含接口凭据。',
],
'pharmacy.medicineMapping/catalogOptions' => [
'status' => 'open', 'name' => '恩济药房药材目录搜索',
'note' => '后台本身不按数据范围过滤:拥有该权限的账号可看到全部(药材目录,不含个人信息)。',
'params_allow' => ['keyword' => '药材名称或编码(模糊)', 'limit' => '返回条数(1–50,默认 30)'],
],
// ================= 充值 / 用户 / 粉丝 =================
'recharge.recharge/getConfig' => [
'status' => 'excluded', 'name' => '充值设置',
'reason' => '充值功能配置(开关、最低金额),配置类接口不对 AI 开放。',
],
'recharge.recharge/lists' => [
'status' => 'open', 'name' => '用户充值记录',
'note' => '后台本身不按数据范围过滤:拥有该权限的账号可看到全部(小程序/H5 用户的余额充值单)。',
'params_allow' => [
'sn' => '充值单号', 'pay_way' => '支付方式:1 余额、2 微信、3 支付宝', 'pay_status' => '支付状态:0 未支付、1 已支付',
'user_info' => '用户编号/昵称/手机号/账号(模糊)',
'start_time' => '下单时间起 YYYY-MM-DD HH:mm:ss(需与 end_time 同时传)', 'end_time' => '下单时间止',
],
],
// UserLists:小程序/H5 注册用户(不是患者诊单),无数据范围。
'user.user/lists' => [
'status' => 'open', 'name' => '用户(小程序/H5 注册用户)列表',
'note' => '后台本身不按数据范围过滤:拥有该权限的账号可看到全部。这里是前台注册用户,不是诊单患者。',
'params_allow' => [
'keyword' => '用户编号/昵称/手机号/账号(模糊)', 'channel' => '注册来源:1 小程序、2 公众号、3 H5、4 PC、5 iOS、6 安卓',
'create_time_start' => '注册时间起 YYYY-MM-DD HH:mm:ss', 'create_time_end' => '注册时间止 YYYY-MM-DD HH:mm:ss',
],
],
'user.user/detail' => [
'status' => 'pending', 'name' => '用户详情', 'kind' => 'detail',
'reason' => '按ID直接返回(含真实姓名、余额),无逐条校验(UserLogic::detail),用户列表不支持按ID过滤无法做 via 校验;主要字段可用 user.user/lists 查询。',
],
'user.user/search' => [
'status' => 'open', 'name' => '用户搜索', 'params_allow' => ['keyword' => '昵称/手机号/账号(模糊),必填'],
'note' => '后台本身不按数据范围过滤:拥有该权限的账号可看到全部。只返回前 10 条匹配的前台注册用户。',
],
// FanLists:粉丝(线索)表,无数据范围,含手机号和身份证号(按权限脱敏)。
'fan/lists' => [
'status' => 'open', 'name' => '粉丝列表',
'note' => '后台本身不按数据范围过滤:拥有该权限的账号可看到全部。visit_count 为回访次数。',
'params_allow' => ['name' => '姓名(模糊)', 'phone' => '手机号(模糊)', 'gender' => '性别:0 未知、1 男、2 女', 'status' => '状态:0 禁用、1 启用'],
],
'fan/detail' => [
'status' => 'pending', 'name' => '粉丝详情', 'kind' => 'detail',
'reason' => '按ID直接返回(含手机号、身份证号),无逐条校验(FanLogic::detail),粉丝列表不支持按ID过滤无法做 via 校验;列表已含全部字段,请用 fan/lists。',
],
// FanLogic::visitRecordLists() 214-243:可按 fan_id 过滤,不传则返回全部回访记录(分页)。
'fan/visitRecordLists' => [
'status' => 'open', 'name' => '粉丝回访记录', 'kind' => 'list',
'note' => '后台本身不按数据范围过滤:拥有该权限的账号可看到全部。不传 fan_id 时返回所有粉丝的回访记录;visit_type:1 电话、2 微信、3 短信、4 上门、5 其他。',
'params_allow' => ['fan_id' => '粉丝ID'],
],
// ================= 消息 / 工作台 / 资源分发 =================
'chat/notifications' => [
'status' => 'excluded', 'name' => '聊天消息推送轮询',
'reason' => '轮询接口“读取即消费”:ChatNotifyLogic::getNotifies(adminId, true) 读取后删除缓存里的待推送消息(缓存不受只读事务保护),会让该账号的后台页面收不到提醒;且只是临时通知。',
],
'workbench/index' => [
'status' => 'excluded', 'name' => '工作台(likeadmin 演示面板)',
'reason' => 'likeadmin 自带演示工作台:今日数据是写死的示例值,访客/销量是随机数(WorkbenchLogic::today/visitor/sale),另含系统版本信息,不是真实业务数据。',
],
// AssetUserController::lists() 12-35 直接返回 AssetUser 模型,模型只隐藏 password(AssetUser.php:12),token/token_expire_time 原样返回;
// 该 token 就是资源分发端的登录凭据(api/controller/asset/AssetAppController.php:23-31 按 token 查用户)。
'asset.assetUser/lists' => [
'status' => 'pending', 'name' => '资源分发账号列表',
'reason' => '接口原样返回分发账号的登录令牌 token 及过期时间(AssetUser 模型只隐藏了 password),属于凭据;需先在模型或接口中隐藏 token、token_expire_time 后再评估开放。',
],
// AssetResourceController::lists() 38 用 with('users') 带出绑定账号,同样包含 token。
'asset.assetResource/lists' => [
'status' => 'pending', 'name' => '资源素材下发列表',
'reason' => '列表通过 with(users) 带出绑定的分发账号,其中含登录令牌 token(AssetUser 模型未隐藏);需先隐藏 token 后再评估开放。',
],
];
+562
View File
@@ -0,0 +1,562 @@
<?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 等前缀),给出准确结论。
*/
$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' => [
'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' => [
'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' => [
'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' => [
'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' => [
'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' => [
'status' => 'open', 'kind' => 'report', 'name' => '业绩看板 · 渠道选项',
'note' => '启用中的投放渠道(企微标签渠道),按来源分组,附带打了该标签的客户数;供 channel_code 参数取值。',
'params_allow' => [],
],
'stats.yejiStats/deptOptions' => [
'status' => 'open', 'kind' => 'report', 'name' => '业绩看板 · 部门选项',
'note' => '按当前账号数据范围收窄的部门列表(id、name、pid、完整路径),供 dept_ids 参数取值。',
'params_allow' => [],
],
'stats.yejiStats/leadLines' => [
'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' => [
'status' => 'open', 'kind' => 'report', 'name' => '业绩看板 · 医助排行榜',
'note' => '按展示部门分表的医助排行:诊金=订单创建人为该医助的业务订单金额(剔除取消/拒收/退款),另有进线、被指派、接诊、成交单、预约诊单、接诊率(元/进线);二中心医助附复诊分项。结果 range_note 有完整口径。' . $yejiScopeNote,
'params_allow' => $dateRange + $yejiFilter,
],
'stats.yejiStats/multi' => [
'status' => 'open', 'kind' => 'report', 'name' => '业绩看板 · 多区间',
'note' => '一次返回本月(1 日至今天)、本周(周一至今天)、今日、昨日四个区间的业绩看板,每个区间与 stats.yejiStats/overview 相同;不支持自定义区间(请用 overview)。' . $yejiScopeNote,
'params_allow' => $yejiFilter,
],
'stats.yejiStats/overview' => [
'status' => 'open', 'kind' => 'report', 'name' => '业绩看板',
'note' => '部门×日期区间:进线=企微添加客户事件(按接待员工部门);被指派数=区间内非继承指派(医助×诊单去重);已完成挂号按预约日期;接诊诊单/成交单数=计入业绩的业务订单条数;合计业绩=业务订单金额,按订单创建时间、剔除已取消(4)/拒收(9)/退款(10),按订单创建人部门归属;投放成本按进线占比分摊,ROI=业绩÷投放成本;复诊只统计二中心。受数据范围限制的账号不显示“未归属中心”行,底栏合计=表内各行之和。结果 channel_filter_note 有完整口径。',
'params_allow' => $dateRange + $yejiFilter,
],
'stats.yejiStats/revisitBreakdown' => [
'status' => 'open', 'kind' => 'report', 'name' => '业绩看板 · 二中心复诊拆解',
'note' => '二中心部门行的复诊业务订单按医助拆解(订单创建人归属);与看板“复诊”列同口径。' . $yejiScopeNote,
'params_allow' => $dateRange + $yejiFilter + [
'dept_id' => '部门行ID(必填,须为二中心子树内的展示行)',
'revisit_slot' => '复诊分项:0=复诊合计(默认),2=复诊2,3=复诊3……',
],
],
'stats.yejiStats/unassignedBreakdown' => [
'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' => '上传文件到企业微信临时素材(外部接口),写操作',
],
];
+162
View File
@@ -0,0 +1,162 @@
<?php
/**
* AI 数据目录人工审核:系统设置、员工与权限、组织架构、文章、渠道、消息通知、装修、定时任务、开发工具,
* 以及根级控制器(config/、file/、login/、desktop/、iam/)。字段说明见 README.md。
*
* 审核结论概要:
* - 开放:员工账号列表(强制数据范围)、角色列表、部门列表/部门树(数据范围版)、岗位列表、文章与栏目列表、数据字典。
* - 待整改:员工详情、文章详情(无逐条校验,且对应列表不支持按 id 过滤,无法用 via 校验)。
* - 不开放:各类配置(含 AppSecret/存储密钥/短信密钥/支付配置)、开发工具、定时任务、系统日志与环境、
* 登录/IAM/桌面端会话、素材中心、装修;与列表字段相同的下拉/详情接口按“重复”不开放。
* 本文件没有使用 'builtin' 校验,也没有 handler。
*/
return [
// ---------------- 员工与权限 ----------------
// AdminLists::queryWhere 只有 apply_data_scope=1 时才按数据范围过滤(后台医生/医助列表页都传 1),这里固定为 1;
// progress_board 会改为“面诊进度”口径(按挂号反查医生),不开放。
'auth.admin/lists' => [
'status' => 'open', 'kind' => 'list', 'name' => '员工账号列表(含医生、医助)',
'params_allow' => [
'name' => '姓名(模糊)', 'account' => '登录账号(模糊)',
'role_id' => '角色ID(1 医生、2 医助,其他见 auth.role/lists)', 'exclude_disabled' => '传 1 排除已停用(禁止登录)的账号',
],
'forbid' => ['progress_board'], 'force' => ['apply_data_scope' => 1],
'note' => '按调用账号的数据范围(本人/本部门/本部门及下级/全部)过滤,与后台医生、医助列表一致;含职称、科室、擅长、学历、从业经历、荣誉、角色/部门/岗位名称。role_id 对应角色没有成员时后台不按角色过滤。手机号按权限脱敏',
],
// AdminLogic::detail 只有 AdminValidate::checkAdmin(账号存在)校验,不按数据范围;AdminLists 不支持按 id 过滤,via 只能核对前 50 条
'auth.admin/detail' => ['status' => 'pending', 'kind' => 'detail', 'name' => '员工账号详情',
'reason' => '详情接口只校验账号存在,不按数据范围校验(任何有权限的账号可看任意员工,含执业证号、资质图片、企业微信 userid);员工列表不支持按 id 过滤,无法用列表做逐条校验。医生职称、科室、擅长、简介等请用 auth.admin/lists'],
'auth.admin/mySelf' => ['status' => 'excluded', 'reason' => '登录会话接口:返回当前账号的菜单树和按钮权限;当前账号信息请用 zyt_whoami'],
'auth.menu/route' => ['status' => 'excluded', 'reason' => '登录会话接口:当前账号的后台路由菜单'],
'auth.menu/lists' => ['status' => 'excluded', 'reason' => '后台菜单与权限点配置,属系统配置'],
'auth.menu/all' => ['status' => 'excluded', 'reason' => '后台菜单树(权限配置下拉),属系统配置'],
'auth.menu/detail' => ['status' => 'excluded', 'reason' => '后台菜单与权限点配置,属系统配置'],
'auth.role/lists' => [
'status' => 'open', 'kind' => 'list', 'name' => '角色列表', 'params_allow' => [],
'note' => '后台本身不按数据范围过滤:拥有该权限的账号可看到全部。data_scope:1 全部、2 本部门及下级、3 本部门、4 仅本人;num 为成员数;menu_id 为授权的菜单/权限ID(可用 fields 省略)',
],
'auth.role/all' => ['status' => 'excluded', 'reason' => '角色下拉选项接口,内容与角色列表(auth.role/lists)相同'],
'auth.role/detail' => ['status' => 'excluded', 'reason' => '单个角色的权限配置,字段与角色列表(auth.role/lists)相同'],
// ---------------- 组织架构 ----------------
'dept.dept/lists' => [
'status' => 'open', 'kind' => 'report', 'name' => '部门列表(树)',
'params_allow' => ['name' => '部门名称(模糊)', 'status' => '状态:1 正常、0 停用'],
'note' => '后台本身不按数据范围过滤:拥有该权限的账号可看到全部。返回部门树(children 为下级),admin_count 为含下级部门的人数;负责人电话按权限脱敏',
],
// DeptController::all:apply_data_scope=1 时走 DeptLogic::getAllDataScoped(与业绩看板部门下拉同一套可见范围)
'dept.dept/all' => [
'status' => 'open', 'kind' => 'report', 'name' => '部门树(按数据范围)', 'params_allow' => [], 'force' => ['apply_data_scope' => 1],
'note' => '按调用账号的数据范围收窄的部门树(保留必要的上级节点),含停用部门;用于查部门ID(如业绩统计的 dept_ids)',
],
'dept.dept/detail' => ['status' => 'excluded', 'reason' => '单个部门字段与部门列表(dept.dept/lists)相同'],
'dept.dept/leaderDept' => ['status' => 'excluded', 'reason' => '表单“上级部门”下拉接口,内容已包含在部门列表中'],
'dept.jobs/lists' => [
'status' => 'open', 'kind' => 'list', 'name' => '岗位列表',
'params_allow' => ['name' => '岗位名称(模糊)', 'code' => '岗位编码', 'status' => '状态:1 正常、0 停用'],
'note' => '后台本身不按数据范围过滤:拥有该权限的账号可看到全部',
],
'dept.jobs/all' => ['status' => 'excluded', 'reason' => '岗位下拉选项接口,内容与岗位列表(dept.jobs/lists)相同'],
'dept.jobs/detail' => ['status' => 'excluded', 'reason' => '单个岗位字段与岗位列表(dept.jobs/lists)相同'],
// ---------------- 文章资讯 ----------------
'article.article/lists' => [
'status' => 'open', 'kind' => 'list', 'name' => '文章资讯列表',
'params_allow' => ['title' => '标题(模糊)', 'cid' => '栏目ID(见 article.articleCate/lists)', 'is_show' => '是否显示:1 显示、0 隐藏'],
'note' => '后台本身不按数据范围过滤:拥有该权限的账号可看到全部。content 为正文 HTML,列表中过长会截断',
],
// ArticleLogic::detail 直接 Article::findOrEmpty($id);ArticleLists 只支持 title/cid/is_show 过滤
'article.article/detail' => ['status' => 'pending', 'kind' => 'detail', 'name' => '文章详情',
'reason' => '详情接口按 id 直接读取、没有逐条校验;文章为公开资讯不涉及数据范围,但文章列表不支持按 id 过滤,无法配置列表校验。正文可先用 article.article/lists 查看(过长截断)'],
'article.articleCate/lists' => [
'status' => 'open', 'kind' => 'list', 'name' => '文章栏目列表', 'params_allow' => [],
'note' => '后台本身不按数据范围过滤:拥有该权限的账号可看到全部。article_count 为栏目下文章数',
],
'article.articleCate/all' => ['status' => 'excluded', 'reason' => '栏目下拉选项接口,内容与文章栏目列表(article.articleCate/lists)相同'],
'article.articleCate/detail' => ['status' => 'excluded', 'reason' => '单个栏目字段与文章栏目列表(article.articleCate/lists)相同'],
// ---------------- 数据字典 ----------------
// ConfigController::dict 在后台是免登录接口(notNeedLogin),只读 DictData(代码→名称对照),无凭据;
// 未在菜单登记,这里以 AI 助手使用权限 ai.mcp/access 作为权限点(比后台免登录更严)。
'config/dict' => [
'status' => 'open', 'kind' => 'report', 'perm' => 'ai.mcp/access', 'domain' => '系统设置', 'name' => '数据字典(代码→名称对照)',
'params_allow' => ['type' => '字典类型值,多个用英文逗号分隔,如 diagnosis_type,syndrome_type,past_history'],
'note' => '返回 {类型值: [{name 名称, value 代码, status 1 正常/0 停用}]},用于解读诊单、处方里的代码。常用类型:diagnosis_type 诊断类型、syndrome_type 证型、past_history 既往史、diabetes_type 糖尿病类型、appetite 口腔感觉、water_intake 每日饮水量、diet_condition 饮食情况、weight_change 体重变化、body_feeling 肢体感觉、sleep_condition 睡眠、eye_condition 眼睛、head_feeling 头部感觉、sweat_condition 出汗、skin_condition 皮肤、urine_condition 小便、stool_condition 大便、kidney_condition 腰肾、fatty_liver_degree 脂肪肝程度、sex 性别',
],
'setting.dict.dictType/lists' => [
'status' => 'open', 'kind' => 'list', 'name' => '字典类型列表',
'params_allow' => ['name' => '字典名称(模糊)', 'type' => '字典类型值(模糊),如 diagnosis_type', 'status' => '状态:1 正常、0 停用'],
'note' => '后台本身不按数据范围过滤:拥有该权限的账号可看到全部。type 即 config/dict 的类型值',
],
'setting.dict.dictData/lists' => [
'status' => 'open', 'kind' => 'list', 'name' => '字典数据列表',
'params_allow' => ['name' => '选项名称(模糊)', 'type_value' => '字典类型值(模糊),如 syndrome_type', 'type_id' => '字典类型ID', 'status' => '状态:1 正常、0 停用'],
'note' => '后台本身不按数据范围过滤:拥有该权限的账号可看到全部。value 为代码、name 为名称',
],
'setting.dict.dictType/all' => ['status' => 'excluded', 'reason' => '字典类型下拉接口,内容与字典类型列表(setting.dict.dictType/lists)相同'],
'setting.dict.dictType/detail' => ['status' => 'excluded', 'reason' => '单个字典类型字段与字典类型列表相同'],
'setting.dict.dictData/detail' => ['status' => 'excluded', 'reason' => '单个字典数据字段与字典数据列表相同'],
// ---------------- 系统设置(配置类一律不开放) ----------------
'config/getConfig' => ['status' => 'excluded', 'reason' => '后台站点基础配置(免登录接口:名称、logo、文件域名、版本号),不属于业务数据'],
'setting.storage/lists' => ['status' => 'excluded', 'reason' => '存储引擎配置,属系统配置'],
'setting.storage/detail' => ['status' => 'excluded', 'reason' => '返回对象存储 access_key/secret_key 等凭据'],
'setting.pay.payConfig/getConfig' => ['status' => 'excluded', 'reason' => '返回支付配置(商户号、密钥、证书等凭据)'],
'setting.pay.payConfig/lists' => ['status' => 'excluded', 'reason' => '支付配置列表,属支付系统配置'],
'setting.pay.payWay/getPayWay' => ['status' => 'excluded', 'reason' => '各端支付方式配置,属支付系统配置'],
'setting.transactionSettings/getConfig' => ['status' => 'excluded', 'reason' => '交易设置(未支付订单自动取消时长等),属系统配置'],
'setting.customerService/getConfig' => ['status' => 'excluded', 'reason' => '客服配置(二维码、微信、电话),属系统配置'],
'setting.hotSearch/getConfig' => ['status' => 'excluded', 'reason' => '用户端热门搜索配置,属系统配置'],
'setting.user.user/getConfig' => ['status' => 'excluded', 'reason' => '用户端默认头像等配置,属系统配置'],
'setting.user.user/getRegisterConfig' => ['status' => 'excluded', 'reason' => '用户端登录注册方式配置,属系统配置'],
'setting.web.webSetting/getWebsite' => ['status' => 'excluded', 'reason' => '网站信息配置,属系统配置'],
'setting.web.webSetting/getCopyright' => ['status' => 'excluded', 'reason' => '网站备案配置,属系统配置'],
'setting.web.webSetting/getAgreement' => ['status' => 'excluded', 'reason' => '服务协议/隐私政策配置,属系统配置'],
'setting.web.webSetting/getSiteStatistics' => ['status' => 'excluded', 'reason' => '站点统计代码配置,属系统配置'],
'setting.desktopWorkstation/getConfig' => ['status' => 'excluded', 'reason' => '医生工作站桌面端升级配置(安装包地址等),属系统配置'],
'setting.desktopWorkstation/check' => ['status' => 'excluded', 'reason' => '桌面端免登录升级检测接口,不属于后台账号数据'],
'setting.system.system/info' => ['status' => 'excluded', 'reason' => '服务器环境信息(操作系统、Web 服务器、PHP 版本、目录权限)'],
'setting.system.log/lists' => ['status' => 'excluded', 'reason' => '系统操作日志:含各账号的请求参数原文和来源 IP,可能夹带密码、密钥和患者信息'],
// ---------------- 渠道设置(凭据与第三方平台配置) ----------------
'channel.mnpSettings/getConfig' => ['status' => 'excluded', 'reason' => '返回微信小程序 AppID/AppSecret 等凭据'],
'channel.officialAccountSetting/getConfig' => ['status' => 'excluded', 'reason' => '返回公众号 AppSecret、Token、EncodingAESKey 等凭据'],
'channel.openSetting/getConfig' => ['status' => 'excluded', 'reason' => '返回微信开放平台 AppSecret 等凭据'],
'channel.appSetting/getConfig' => ['status' => 'excluded', 'reason' => 'APP 下载地址配置,属渠道配置'],
'channel.webPageSetting/getConfig' => ['status' => 'excluded', 'reason' => 'H5 渠道开关配置,属渠道配置'],
'channel.officialAccountMenu/detail' => ['status' => 'excluded', 'reason' => '公众号自定义菜单配置,属渠道配置'],
'channel.officialAccountReply/lists' => ['status' => 'excluded', 'reason' => '公众号自动回复规则配置,属渠道配置'],
'channel.officialAccountReply/detail' => ['status' => 'excluded', 'reason' => '公众号自动回复规则配置,属渠道配置'],
'channel.officialAccountReply/index' => ['status' => 'excluded', 'reason' => '公众号服务器消息回调(免登录,调用微信 SDK 应答),不是查询接口'],
// ---------------- 消息通知 ----------------
'notice.smsConfig/getConfig' => ['status' => 'excluded', 'reason' => '返回短信服务商配置(含 app_key/secret_key 等凭据)'],
'notice.smsConfig/detail' => ['status' => 'excluded', 'reason' => '返回短信服务商 app_key/secret_key 等凭据'],
'notice.notice/settingLists' => ['status' => 'excluded', 'reason' => '通知场景与模板配置,属系统配置'],
'notice.notice/detail' => ['status' => 'excluded', 'reason' => '通知模板配置(短信/公众号/小程序模板ID与内容),属系统配置'],
// ---------------- 装修、素材 ----------------
'decorate.page/detail' => ['status' => 'excluded', 'reason' => '用户端页面装修配置,不属于业务数据'],
'decorate.tabbar/detail' => ['status' => 'excluded', 'reason' => '用户端底部导航装修配置,不属于业务数据'],
'decorate.data/article' => ['status' => 'excluded', 'reason' => '装修组件取数接口(最新文章),文章请用 article.article/lists'],
'decorate.data/pc' => ['status' => 'excluded', 'reason' => 'PC 端装修信息(更新时间、访问地址),不属于业务数据'],
'file/lists' => ['status' => 'excluded', 'reason' => '素材中心:当前账号上传的文件及地址,属上传/文件管理'],
'file/listCate' => ['status' => 'excluded', 'reason' => '素材中心分组,属上传/文件管理'],
// ---------------- 定时任务、开发工具 ----------------
'crontab.crontab/lists' => ['status' => 'excluded', 'reason' => '定时任务配置(命令、参数、执行状态),属系统运维'],
'crontab.crontab/detail' => ['status' => 'excluded', 'reason' => '定时任务配置,属系统运维'],
'crontab.crontab/expression' => ['status' => 'excluded', 'reason' => 'cron 表达式解析工具,不属于业务数据'],
'tools.generator/dataTable' => ['status' => 'excluded', 'reason' => '开发工具:列出数据库全部数据表'],
'tools.generator/generateTable' => ['status' => 'excluded', 'reason' => '开发工具:代码生成器已导入的数据表'],
'tools.generator/detail' => ['status' => 'excluded', 'reason' => '开发工具:数据表字段结构与代码生成配置'],
'tools.generator/getModels' => ['status' => 'excluded', 'reason' => '开发工具:列出程序模型类'],
// ---------------- 登录、IAM、桌面端会话 ----------------
'login/logout' => ['status' => 'excluded', 'reason' => '退出登录(让登录令牌失效),会改变会话状态'],
'login/workWechatConfig' => ['status' => 'excluded', 'reason' => '登录页企业微信扫码配置(免登录接口)'],
'login/checkDbColumn' => ['status' => 'excluded', 'reason' => '免登录调试接口,返回数据库名、表名和字段结构'],
'iam/config' => ['status' => 'excluded', 'reason' => '统一账号(IAM)登录配置(免登录接口)'],
'desktop/session' => ['status' => 'excluded', 'reason' => '企业微信客服桌面端会话接口(返回登录身份与权限)'],
];
+703
View File
@@ -0,0 +1,703 @@
<?php
// 后台没有页面的数据表(php app/mcp/cli/coverage.php --write-tables 生成,可手工调整 scope/columns)。
// 生成时间:2026-09-24 08:51:55
return array (
'table.av_permission_log/lists' =>
array (
'status' => 'open',
'kind' => 'table',
'perm' => 'ai.mcp/tables',
'name' => '数据表 av_permission_log',
'domain' => '其他数据表',
'note' => '后台没有页面的数据表:目前仅超级管理员可查;如需给其他角色开放,把 scope 改为属主列(如 [\'owner\' => [\'doctor_id\']])并审核',
'handler' =>
array (
'table' => 'av_permission_log',
'columns' =>
array (
0 => 'id',
1 => 'patient_id',
2 => 'doctor_id',
3 => 'denied_scope',
4 => 'scene',
5 => 'action',
6 => 'wx_version',
7 => 'create_time',
),
'filters' =>
array (
'id' => '=',
'patient_id' => '=',
'doctor_id' => '=',
),
'date' => 'create_time',
'date_type' => 'int',
'order' => 'id desc',
'scope' => 'root',
),
),
'table.express_state_log/lists' =>
array (
'status' => 'open',
'kind' => 'table',
'perm' => 'ai.mcp/tables',
'name' => '数据表 express_state_log',
'domain' => '其他数据表',
'note' => '后台没有页面的数据表:目前仅超级管理员可查;如需给其他角色开放,把 scope 改为属主列(如 [\'owner\' => [\'doctor_id\']])并审核',
'handler' =>
array (
'table' => 'express_state_log',
'columns' =>
array (
0 => 'id',
1 => 'tracking_id',
2 => 'tracking_number',
3 => 'old_state',
4 => 'old_state_text',
5 => 'new_state',
6 => 'new_state_text',
7 => 'change_time',
8 => 'change_reason',
9 => 'is_notified',
10 => 'notify_time',
11 => 'notify_result',
12 => 'create_time',
),
'filters' =>
array (
'id' => '=',
'tracking_id' => '=',
),
'date' => 'create_time',
'date_type' => 'int',
'order' => 'id desc',
'scope' => 'root',
),
),
'table.express_trace/lists' =>
array (
'status' => 'open',
'kind' => 'table',
'perm' => 'ai.mcp/tables',
'name' => '数据表 express_trace',
'domain' => '其他数据表',
'note' => '后台没有页面的数据表:目前仅超级管理员可查;如需给其他角色开放,把 scope 改为属主列(如 [\'owner\' => [\'doctor_id\']])并审核',
'handler' =>
array (
'table' => 'express_trace',
'columns' =>
array (
0 => 'id',
1 => 'tracking_id',
2 => 'tracking_number',
3 => 'trace_time',
4 => 'trace_time_stamp',
5 => 'trace_context',
6 => 'status',
7 => 'status_code',
8 => 'location',
9 => 'area_code',
10 => 'area_name',
11 => 'area_center',
12 => 'extra_data',
13 => 'create_time',
),
'filters' =>
array (
'id' => '=',
'tracking_id' => '=',
'status' => '=',
),
'date' => 'create_time',
'date_type' => 'int',
'order' => 'id desc',
'scope' => 'root',
),
),
'table.notice_record/lists' =>
array (
'status' => 'open',
'kind' => 'table',
'perm' => 'ai.mcp/tables',
'name' => '数据表 notice_record',
'domain' => '其他数据表',
'note' => '后台没有页面的数据表:目前仅超级管理员可查;如需给其他角色开放,把 scope 改为属主列(如 [\'owner\' => [\'doctor_id\']])并审核',
'handler' =>
array (
'table' => 'notice_record',
'columns' =>
array (
0 => 'id',
1 => 'user_id',
2 => 'title',
3 => 'content',
4 => 'scene_id',
5 => 'read',
6 => 'recipient',
7 => 'send_type',
8 => 'notice_type',
9 => 'extra',
10 => 'create_time',
11 => 'update_time',
12 => 'delete_time',
),
'filters' =>
array (
'id' => '=',
'user_id' => '=',
'scene_id' => '=',
),
'date' => 'create_time',
'date_type' => 'int',
'soft_delete' => 'delete_time',
'order' => 'id desc',
'scope' => 'root',
),
),
'table.order_detail/lists' =>
array (
'status' => 'open',
'kind' => 'table',
'perm' => 'ai.mcp/tables',
'name' => '数据表 order_detail',
'domain' => '其他数据表',
'note' => '后台没有页面的数据表:目前仅超级管理员可查;如需给其他角色开放,把 scope 改为属主列(如 [\'owner\' => [\'doctor_id\']])并审核',
'handler' =>
array (
'table' => 'order_detail',
'columns' =>
array (
0 => 'id',
1 => 'order_id',
2 => 'related_type',
3 => 'related_id',
4 => 'name',
5 => 'price',
6 => 'quantity',
7 => 'amount',
8 => 'create_time',
9 => 'update_time',
),
'filters' =>
array (
'id' => '=',
'order_id' => '=',
'related_id' => '=',
),
'date' => 'create_time',
'date_type' => 'datetime',
'order' => 'id desc',
'scope' => 'root',
),
),
'table.pharmacy_submission_claim_audit/lists' =>
array (
'status' => 'open',
'kind' => 'table',
'perm' => 'ai.mcp/tables',
'name' => '数据表 pharmacy_submission_claim_audit',
'domain' => '其他数据表',
'note' => '后台没有页面的数据表:目前仅超级管理员可查;如需给其他角色开放,把 scope 改为属主列(如 [\'owner\' => [\'doctor_id\']])并审核',
'handler' =>
array (
'table' => 'pharmacy_submission_claim_audit',
'columns' =>
array (
0 => 'id',
1 => 'claim_id',
2 => 'prescription_order_id',
3 => 'source_revision',
4 => 'target',
5 => 'action',
6 => 'from_status',
7 => 'to_status',
8 => 'remote_order_no',
9 => 'note',
10 => 'operator_id',
11 => 'operator_name',
12 => 'create_time',
),
'filters' =>
array (
'id' => '=',
'claim_id' => '=',
'prescription_order_id' => '=',
'operator_id' => '=',
),
'date' => 'create_time',
'date_type' => 'int',
'order' => 'id desc',
'scope' => 'root',
),
),
'table.qywx_customer_acquisition_event/lists' =>
array (
'status' => 'open',
'kind' => 'table',
'perm' => 'ai.mcp/tables',
'name' => '数据表 qywx_customer_acquisition_event',
'domain' => '其他数据表',
'note' => '后台没有页面的数据表:目前仅超级管理员可查;如需给其他角色开放,把 scope 改为属主列(如 [\'owner\' => [\'doctor_id\']])并审核',
'handler' =>
array (
'table' => 'qywx_customer_acquisition_event',
'columns' =>
array (
0 => 'id',
1 => 'event_key',
2 => 'change_type',
3 => 'chat_key',
4 => 'link_id',
5 => 'external_userid',
6 => 'userid',
7 => 'status',
8 => 'attempts',
9 => 'event_time',
10 => 'expire_time',
11 => 'next_retry',
12 => 'error_message',
13 => 'raw_json',
14 => 'create_time',
15 => 'update_time',
),
'filters' =>
array (
'id' => '=',
'link_id' => '=',
'status' => '=',
),
'date' => 'create_time',
'date_type' => 'int',
'order' => 'id desc',
'scope' => 'root',
),
),
'table.qywx_external_contact_event_tag/lists' =>
array (
'status' => 'open',
'kind' => 'table',
'perm' => 'ai.mcp/tables',
'name' => '数据表 qywx_external_contact_event_tag',
'domain' => '其他数据表',
'note' => '后台没有页面的数据表:目前仅超级管理员可查;如需给其他角色开放,把 scope 改为属主列(如 [\'owner\' => [\'doctor_id\']])并审核',
'handler' =>
array (
'table' => 'qywx_external_contact_event_tag',
'columns' =>
array (
0 => 'id',
1 => 'event_id',
2 => 'follow_user_id',
3 => 'tag_id',
4 => 'tag_name',
5 => 'group_name',
6 => 'snapshot_source',
7 => 'create_time',
),
'filters' =>
array (
'id' => '=',
'event_id' => '=',
'follow_user_id' => '=',
'tag_id' => '=',
),
'date' => 'create_time',
'date_type' => 'int',
'order' => 'id desc',
'scope' => 'root',
),
),
'table.qywx_promotion_account/lists' =>
array (
'status' => 'open',
'kind' => 'table',
'perm' => 'ai.mcp/tables',
'name' => '数据表 qywx_promotion_account',
'domain' => '其他数据表',
'note' => '后台没有页面的数据表:目前仅超级管理员可查;如需给其他角色开放,把 scope 改为属主列(如 [\'owner\' => [\'doctor_id\']])并审核',
'handler' =>
array (
'table' => 'qywx_promotion_account',
'columns' =>
array (
0 => 'id',
1 => 'corp_id',
2 => 'corp_name',
3 => 'agent_id',
4 => 'auth_info_json',
5 => 'auth_status',
6 => 'owner_admin_id',
7 => 'dept_id',
8 => 'authorized_at',
9 => 'last_refresh_at',
10 => 'create_time',
11 => 'update_time',
12 => 'delete_time',
),
'filters' =>
array (
'id' => '=',
'corp_id' => '=',
'agent_id' => '=',
'owner_admin_id' => '=',
'dept_id' => '=',
),
'date' => 'create_time',
'date_type' => 'int',
'soft_delete' => 'delete_time',
'order' => 'id desc',
'scope' => 'root',
),
),
'table.qywx_promotion_automation_action_log/lists' =>
array (
'status' => 'open',
'kind' => 'table',
'perm' => 'ai.mcp/tables',
'name' => '数据表 qywx_promotion_automation_action_log',
'domain' => '其他数据表',
'note' => '后台没有页面的数据表:目前仅超级管理员可查;如需给其他角色开放,把 scope 改为属主列(如 [\'owner\' => [\'doctor_id\']])并审核',
'handler' =>
array (
'table' => 'qywx_promotion_automation_action_log',
'columns' =>
array (
0 => 'id',
1 => 'task_id',
2 => 'action',
3 => 'status',
4 => 'attempt',
5 => 'reason',
6 => 'error_code',
7 => 'create_time',
),
'filters' =>
array (
'id' => '=',
'task_id' => '=',
'status' => '=',
),
'date' => 'create_time',
'date_type' => 'int',
'order' => 'id desc',
'scope' => 'root',
),
),
'table.qywx_promotion_automation_task/lists' =>
array (
'status' => 'open',
'kind' => 'table',
'perm' => 'ai.mcp/tables',
'name' => '数据表 qywx_promotion_automation_task',
'domain' => '其他数据表',
'note' => '后台没有页面的数据表:目前仅超级管理员可查;如需给其他角色开放,把 scope 改为属主列(如 [\'owner\' => [\'doctor_id\']])并审核',
'handler' =>
array (
'table' => 'qywx_promotion_automation_task',
'columns' =>
array (
0 => 'id',
1 => 'event_key',
2 => 'pool_id',
3 => 'member_admin_id',
4 => 'change_type',
5 => 'userid',
6 => 'external_userid',
7 => 'event_time',
8 => 'received_at',
9 => 'config_json',
10 => 'actions_json',
11 => 'welcome_code_hash',
12 => 'welcome_expires_at',
13 => 'welcome_status',
14 => 'welcome_next_retry',
15 => 'status',
16 => 'next_retry',
17 => 'lock_until',
18 => 'create_time',
19 => 'update_time',
),
'filters' =>
array (
'id' => '=',
'pool_id' => '=',
'member_admin_id' => '=',
'status' => '=',
),
'date' => 'create_time',
'date_type' => 'int',
'order' => 'id desc',
'scope' => 'root',
),
),
'table.qywx_promotion_media/lists' =>
array (
'status' => 'open',
'kind' => 'table',
'perm' => 'ai.mcp/tables',
'name' => '数据表 qywx_promotion_media',
'domain' => '其他数据表',
'note' => '后台没有页面的数据表:目前仅超级管理员可查;如需给其他角色开放,把 scope 改为属主列(如 [\'owner\' => [\'doctor_id\']])并审核',
'handler' =>
array (
'table' => 'qywx_promotion_media',
'columns' =>
array (
0 => 'asset_id',
1 => 'admin_id',
2 => 'name',
3 => 'type',
4 => 'mime',
5 => 'size',
6 => 'sha256',
7 => 'storage_name',
8 => 'media_id',
9 => 'media_expires_at',
10 => 'last_error',
11 => 'create_time',
12 => 'update_time',
),
'filters' =>
array (
'asset_id' => '=',
'admin_id' => '=',
'type' => '=',
'media_id' => '=',
),
'date' => 'create_time',
'date_type' => 'int',
'scope' => 'root',
),
),
'table.tcm_daily_family_like/lists' =>
array (
'status' => 'open',
'kind' => 'table',
'perm' => 'ai.mcp/tables',
'name' => '数据表 tcm_daily_family_like',
'domain' => '其他数据表',
'note' => '后台没有页面的数据表:目前仅超级管理员可查;如需给其他角色开放,把 scope 改为属主列(如 [\'owner\' => [\'doctor_id\']])并审核',
'handler' =>
array (
'table' => 'tcm_daily_family_like',
'columns' =>
array (
0 => 'id',
1 => 'diagnosis_id',
2 => 'like_date',
3 => 'invite_code',
4 => 'viewer_key',
5 => 'nickname',
6 => 'create_time',
),
'filters' =>
array (
'id' => '=',
'diagnosis_id' => '=',
),
'date' => 'create_time',
'date_type' => 'int',
'order' => 'id desc',
'scope' => 'root',
),
),
'table.tcm_daily_gamify/lists' =>
array (
'status' => 'open',
'kind' => 'table',
'perm' => 'ai.mcp/tables',
'name' => '数据表 tcm_daily_gamify',
'domain' => '其他数据表',
'note' => '后台没有页面的数据表:目前仅超级管理员可查;如需给其他角色开放,把 scope 改为属主列(如 [\'owner\' => [\'doctor_id\']])并审核',
'handler' =>
array (
'table' => 'tcm_daily_gamify',
'columns' =>
array (
0 => 'id',
1 => 'diagnosis_id',
2 => 'user_id',
3 => 'points',
4 => 'badges',
5 => 'task_awards',
6 => 'create_time',
7 => 'update_time',
),
'filters' =>
array (
'id' => '=',
'diagnosis_id' => '=',
'user_id' => '=',
),
'date' => 'create_time',
'date_type' => 'int',
'order' => 'id desc',
'scope' => 'root',
),
),
'table.tcm_daily_share_invite/lists' =>
array (
'status' => 'open',
'kind' => 'table',
'perm' => 'ai.mcp/tables',
'name' => '数据表 tcm_daily_share_invite',
'domain' => '其他数据表',
'note' => '后台没有页面的数据表:目前仅超级管理员可查;如需给其他角色开放,把 scope 改为属主列(如 [\'owner\' => [\'doctor_id\']])并审核',
'handler' =>
array (
'table' => 'tcm_daily_share_invite',
'columns' =>
array (
0 => 'id',
1 => 'invite_code',
2 => 'diagnosis_id',
3 => 'user_id',
4 => 'invite_date',
5 => 'create_time',
),
'filters' =>
array (
'id' => '=',
'diagnosis_id' => '=',
'user_id' => '=',
),
'date' => 'create_time',
'date_type' => 'int',
'order' => 'id desc',
'scope' => 'root',
),
),
'table.tcm_game_share_invite/lists' =>
array (
'status' => 'open',
'kind' => 'table',
'perm' => 'ai.mcp/tables',
'name' => '数据表 tcm_game_share_invite',
'domain' => '其他数据表',
'note' => '后台没有页面的数据表:目前仅超级管理员可查;如需给其他角色开放,把 scope 改为属主列(如 [\'owner\' => [\'doctor_id\']])并审核',
'handler' =>
array (
'table' => 'tcm_game_share_invite',
'columns' =>
array (
0 => 'id',
1 => 'invite_code',
2 => 'user_id',
3 => 'week_start',
4 => 'open_count',
5 => 'create_time',
6 => 'update_time',
),
'filters' =>
array (
'id' => '=',
'user_id' => '=',
),
'date' => 'create_time',
'date_type' => 'int',
'order' => 'id desc',
'scope' => 'root',
),
),
'table.tcm_game_share_visit/lists' =>
array (
'status' => 'open',
'kind' => 'table',
'perm' => 'ai.mcp/tables',
'name' => '数据表 tcm_game_share_visit',
'domain' => '其他数据表',
'note' => '后台没有页面的数据表:目前仅超级管理员可查;如需给其他角色开放,把 scope 改为属主列(如 [\'owner\' => [\'doctor_id\']])并审核',
'handler' =>
array (
'table' => 'tcm_game_share_visit',
'columns' =>
array (
0 => 'id',
1 => 'invite_code',
2 => 'inviter_user_id',
3 => 'visitor_user_id',
4 => 'create_time',
),
'filters' =>
array (
'id' => '=',
'inviter_user_id' => '=',
'visitor_user_id' => '=',
),
'date' => 'create_time',
'date_type' => 'int',
'order' => 'id desc',
'scope' => 'root',
),
),
'table.tcm_game_weekly_group/lists' =>
array (
'status' => 'open',
'kind' => 'table',
'perm' => 'ai.mcp/tables',
'name' => '数据表 tcm_game_weekly_group',
'domain' => '其他数据表',
'note' => '后台没有页面的数据表:目前仅超级管理员可查;如需给其他角色开放,把 scope 改为属主列(如 [\'owner\' => [\'doctor_id\']])并审核',
'handler' =>
array (
'table' => 'tcm_game_weekly_group',
'columns' =>
array (
0 => 'id',
1 => 'week_start',
2 => 'sex',
3 => 'group_no',
4 => 'member_count',
5 => 'create_time',
6 => 'update_time',
),
'filters' =>
array (
'id' => '=',
),
'date' => 'create_time',
'date_type' => 'int',
'order' => 'id desc',
'scope' => 'root',
),
),
'table.tcm_game_weekly_score/lists' =>
array (
'status' => 'open',
'kind' => 'table',
'perm' => 'ai.mcp/tables',
'name' => '数据表 tcm_game_weekly_score',
'domain' => '其他数据表',
'note' => '后台没有页面的数据表:目前仅超级管理员可查;如需给其他角色开放,把 scope 改为属主列(如 [\'owner\' => [\'doctor_id\']])并审核',
'handler' =>
array (
'table' => 'tcm_game_weekly_score',
'columns' =>
array (
0 => 'id',
1 => 'group_id',
2 => 'week_start',
3 => 'user_id',
4 => 'learned_count',
5 => 'best_score',
6 => 'games_played',
7 => 'share_count',
8 => 'nickname',
9 => 'avatar',
10 => 'sex',
11 => 'create_time',
12 => 'update_time',
),
'filters' =>
array (
'id' => '=',
'group_id' => '=',
'user_id' => '=',
),
'date' => 'create_time',
'date_type' => 'int',
'order' => 'id desc',
'scope' => 'root',
),
),
);
+216
View File
@@ -0,0 +1,216 @@
<?php
/**
* AI 数据目录人工审核:中医诊单与处方(tcm.*)的全部非写、非 POST 接口(51 个)。
*
* 约定:
* - 按诊单/患者/记录ID取数的接口一律按「详情」调用(kind=detail):zyt_get 会把 ID 固定为单个值,
* 避免数组参数与逐条校验不一致;
* - 诊单级逐条校验复用 DiagnosisLogic::canViewReadonlyDiagnosis(logic/tcm/DiagnosisLogic.php:4301),
* 与诊单列表同口径:医助角色仅本人诊单,其余按数据范围(诊单医助 ∈ 可见账号);
* - 'builtin' 表示接口自身已有逐条校验,函数写在各条目上方注释里。
*/
use app\adminapi\logic\tcm\DiagnosisLogic;
use app\adminapi\validate\tcm\DiagnosisValidate;
return [
// ───────────── 血糖血压 / 饮食 / 运动记录 ─────────────
'tcm.bloodRecord/detail' => ['status' => 'pending', 'name' => '血糖血压记录详情',
'reason' => '按记录ID读取任意患者的血糖血压记录,无逐条权限校验(BloodRecordLogic::detail),现有校验函数只接受诊单ID;可改用 tcm.diagnosis/trackingWindow 按诊单查询'],
'tcm.bloodRecord/getBloodSugarTrend' => ['status' => 'open', 'kind' => 'detail', 'name' => '血糖趋势(按诊单)',
'guard' => ['callable' => [DiagnosisLogic::class, 'canViewReadonlyDiagnosis'], 'param' => 'diagnosis_id', 'args' => ['id', 'admin_id', 'admin_info']],
'params_allow' => ['days' => '最近天数,默认 7'],
'note' => 'id 为诊单ID;按天返回空腹/餐后2小时/其他血糖(每天取第一条有效值)'],
'tcm.bloodRecord/getRecordsByPatient' => ['status' => 'open', 'kind' => 'detail', 'name' => '血糖血压记录(按诊单)',
'guard' => ['callable' => [DiagnosisLogic::class, 'canViewReadonlyDiagnosis'], 'param' => 'diagnosis_id', 'args' => ['id', 'admin_id', 'admin_info']],
'params_allow' => [], 'note' => 'id 为诊单ID;返回该诊单全部血糖血压记录(按日期倒序)'],
'tcm.dietRecord/detail' => ['status' => 'pending', 'name' => '饮食记录详情',
'reason' => '按记录ID读取任意患者的饮食记录,无逐条权限校验(DietRecordLogic::detail);可改用 tcm.diagnosis/trackingWindow 按诊单查询'],
'tcm.dietRecord/getRecordsByPatient' => ['status' => 'open', 'kind' => 'detail', 'name' => '饮食记录(按诊单)',
'guard' => ['callable' => [DiagnosisLogic::class, 'canViewReadonlyDiagnosis'], 'param' => 'diagnosis_id', 'args' => ['id', 'admin_id', 'admin_info']],
'params_allow' => [], 'note' => 'id 为诊单ID'],
'tcm.exerciseRecord/detail' => ['status' => 'pending', 'name' => '运动记录详情',
'reason' => '按记录ID读取任意患者的运动记录,无逐条权限校验(ExerciseRecordLogic::detail);可改用 tcm.diagnosis/trackingWindow 按诊单查询'],
'tcm.exerciseRecord/getExerciseTrend' => ['status' => 'open', 'kind' => 'detail', 'name' => '运动时长趋势(按诊单)',
'guard' => ['callable' => [DiagnosisLogic::class, 'canViewReadonlyDiagnosis'], 'param' => 'diagnosis_id', 'args' => ['id', 'admin_id', 'admin_info']],
'params_allow' => ['start_date' => '开始日期 YYYY-MM-DD(与 end_date 同时传)', 'end_date' => '结束日期 YYYY-MM-DD', 'days' => '不传日期时取最近天数,默认 7'],
'note' => 'id 为诊单ID'],
'tcm.exerciseRecord/getRecordsByPatient' => ['status' => 'open', 'kind' => 'detail', 'name' => '运动记录(按诊单)',
'guard' => ['callable' => [DiagnosisLogic::class, 'canViewReadonlyDiagnosis'], 'param' => 'diagnosis_id', 'args' => ['id', 'admin_id', 'admin_info']],
'params_allow' => [], 'note' => 'id 为诊单ID'],
// ───────────── 诊单 ─────────────
'tcm.diagnosis/lists' => ['status' => 'open', 'kind' => 'list',
'params_allow' => [
'keyword' => '患者姓名或手机号(模糊)', 'patient_name' => '患者姓名(模糊)', 'patient_id' => '患者ID(诊单 patient_id)',
'gender' => '性别 1男 0女', 'diagnosis_type' => '诊断类型(字典值)', 'syndrome_type' => '证型(字典值)',
'status' => '诊单状态 1启用 0禁用', 'assistant_id' => '医助(后台账号)ID', 'assistant_dept_id' => '医助所属部门ID(含下级部门)',
'start_time' => '诊断日期起(须与 end_time 同时传)', 'end_time' => '诊断日期止',
'diagnosis_confirmed' => '是否已确认诊单 1是 0否', 'appointment_date' => '挂号日期 YYYY-MM-DD',
'has_appointment' => '是否有有效挂号 1是 0否', 'completed_appointment' => '传 1 只看有已完成挂号的诊单',
'only_has_prescription' => '传 1 只看已开方的诊单',
'latest_appointment_start_date' => '最近一次挂号日期起 YYYY-MM-DD', 'latest_appointment_end_date' => '最近一次挂号日期止 YYYY-MM-DD',
'latest_appointment_channel_source' => '最近一次挂号的渠道来源(字典值)',
'latest_assign_start_date' => '最近一次指派医助日期起 YYYY-MM-DD', 'latest_assign_end_date' => '最近一次指派医助日期止 YYYY-MM-DD',
'sort_unserved_days' => '按未服务天数排序 asc/desc',
],
// pending_assign=1(全局禁用)会跳过医助本人过滤和数据范围,配合关键词可按姓名/手机/身份证全库检索(DiagnosisLists:76-98、929-1014)
'forbid' => ['pending_assign', 'pending_assign_keyword', 'pending_assign_order_month'],
'note' => '医助角色只看本人诊单,其余按数据范围(诊单医助 ∈ 可见账号);不含「待分配医助」视图'],
// 编辑页详情:控制器会顺手 markAssignRead 写库,改为直接调 DiagnosisLogic::detail(只读),并用列表同口径校验逐条可见
'tcm.diagnosis/detail' => ['status' => 'open', 'kind' => 'detail', 'name' => '诊单详情',
'guard' => ['callable' => [DiagnosisLogic::class, 'canViewReadonlyDiagnosis'], 'args' => ['id', 'admin_id', 'admin_info']],
'handler' => ['logic' => [DiagnosisLogic::class, 'detail'], 'args' => ['params', 'admin_info'],
'validate' => [DiagnosisValidate::class, 'id'], 'error' => [DiagnosisLogic::class, 'getError']],
'params_allow' => [], 'note' => '后台原接口无逐条校验,这里补上与诊单列表一致的可见性校验'],
// builtin:DiagnosisLogic::readonlyDetail(DiagnosisLogic.php:4241)先调 canViewReadonlyDiagnosis(:4251/:4301);
// 控制器会顺手 markAssignRead 写库,故改为直接调 Logic
'tcm.diagnosis/readonlyDetail' => ['status' => 'open', 'kind' => 'detail', 'guard' => 'builtin',
'handler' => ['logic' => [DiagnosisLogic::class, 'readonlyDetail'], 'args' => ['params', 'admin_id', 'admin_info'],
'validate' => [DiagnosisValidate::class, 'readonlyDetail'], 'error' => [DiagnosisLogic::class, 'getError']],
'params_allow' => [], 'note' => '返回最近挂号、诊单病例、医生备注、跟踪备注、未服务天数'],
// builtin:DiagnosisController::trackingWindow(controller/tcm/DiagnosisController.php:175)先调 canViewReadonlyDiagnosis;
// 权限点沿用控制器注释写明的 tcm.diagnosis/readonlyDetail(trackingWindow 本身未在菜单登记)
'tcm.diagnosis/trackingWindow' => ['status' => 'open', 'kind' => 'detail', 'guard' => 'builtin',
'perm' => 'tcm.diagnosis/readonlyDetail', 'name' => '诊单跟踪记录(血糖血压/饮食/运动)',
'params_allow' => ['start_date' => '开始日期 YYYY-MM-DD', 'end_date' => '结束日期 YYYY-MM-DD'],
'note' => 'id 为诊单ID;不传日期返回全部记录,建议按日期区间查询'],
'tcm.diagnosis/trackingNotes' => ['status' => 'open', 'kind' => 'detail',
'guard' => ['callable' => [DiagnosisLogic::class, 'canViewReadonlyDiagnosis'], 'param' => 'diagnosis_id', 'args' => ['id', 'admin_id', 'admin_info']],
'params_allow' => [], 'note' => 'id 为诊单ID;最近 60 条跟踪备注(按天合并,每天一条)'],
'tcm.diagnosis/guahaoLogList' => ['status' => 'open', 'kind' => 'detail',
'guard' => ['callable' => [DiagnosisLogic::class, 'canViewReadonlyDiagnosis'], 'args' => ['id', 'admin_id', 'admin_info']],
'params_allow' => [], 'note' => 'id 为诊单ID;挂号/取消挂号操作日志(最多 200 条)'],
'tcm.diagnosis/getCallRecords' => ['status' => 'open', 'kind' => 'detail', 'name' => '诊单通话记录(含录音与转写)',
'guard' => ['callable' => [DiagnosisLogic::class, 'canViewReadonlyDiagnosis'], 'param' => 'diagnosis_id', 'args' => ['id', 'admin_id', 'admin_info']],
'params_allow' => [], 'note' => 'id 为诊单ID;录音/录像地址按附件处理,transcript_text 为通话转写全文'],
// builtin:DiagnosisController::getImChatMessages(DiagnosisController.php:453)先调 canViewReadonlyDiagnosis,诊单ID已转 int;
// only_archived=1 只读本地归档,不调用腾讯 IM、不写库。不设为 detail:zyt_file 走详情分支时不带 force
'tcm.diagnosis/getImChatMessages' => ['status' => 'open', 'kind' => 'report', 'guard' => 'builtin', 'name' => '诊单 IM 聊天记录(已归档)',
'params_allow' => ['diagnosis_id' => '诊单ID(必填)'], 'force' => ['only_archived' => 1],
'note' => '只返回已归档到本地的患者与医生/医助 IM 消息(同一患者的历次诊单合并)'],
'tcm.diagnosis/getWechatChatRecords' => ['status' => 'open', 'kind' => 'detail', 'name' => '企业微信聊天记录(按诊单)',
'guard' => ['callable' => [DiagnosisLogic::class, 'canViewReadonlyDiagnosis'], 'param' => 'diagnosis_id', 'args' => ['id', 'admin_id', 'admin_info']],
'params_allow' => ['page_no' => '页码,每页 20 条'], 'forbid' => ['patient_id', 'page_size'],
'note' => 'id 为诊单ID;按聊天时间倒序'],
'tcm.diagnosis/aiPatientOptions' => ['status' => 'open', 'kind' => 'list',
// builtin:DiagnosisAiLogic::patientOptions(logic/tcm/DiagnosisAiLogic.php:150)校验 tcm.diagnosis/aiAssistant 权限并按 MyPatientLogic::applyScope 收窄
'params_allow' => ['keyword' => '患者姓名、手机号或诊单ID/患者ID'],
'note' => '「我的患者」范围内的启用诊单;还需要 tcm.diagnosis/aiAssistant 权限;手机号已脱敏,每页最多 50 条'],
// builtin:DiagnosisAiLogic::getSavedReports(DiagnosisAiLogic.php:275)→ loadAuthorizedDiagnosis(:1151)校验 tcm.diagnosis/aiReports 权限 + MyPatientLogic::canAccessDiagnosis
'tcm.diagnosis/aiReports' => ['status' => 'open', 'kind' => 'detail', 'guard' => 'builtin', 'name' => '诊单 AI 报告(已保存)',
'params_allow' => [], 'note' => 'id 为诊单ID;只读已保存的报告,不触发模型调用;case_summary 为患者纵向资料摘要,内容较长'],
// builtin:PatientAiReportLogic::reports(logic/tcm/PatientAiReportLogic.php:130)→ loadAuthorizedDiagnoses(:349)校验权限 + MyPatientLogic::applyScope
'tcm.diagnosis/patientAiReports' => ['status' => 'open', 'kind' => 'detail', 'guard' => 'builtin', 'id_param' => 'patient_id',
'params_allow' => [], 'note' => 'id 为患者ID(诊单 patient_id);只读历史报告快照,不触发模型调用'],
'tcm.diagnosis/assistantDiagnosisStats' => ['status' => 'open', 'kind' => 'report', 'name' => '医助诊单统计(按部门/按人)',
'params_allow' => ['days' => '最近天数(1-90,默认 7;0 表示今天)', 'start_time' => '开始时间 YYYY-MM-DD HH:mm:ss', 'end_time' => '结束时间 YYYY-MM-DD HH:mm:ss'],
'note' => '按诊单创建时间统计各医助新建诊单数,只含当前账号数据范围内的医助'],
'tcm.diagnosis/getAssistants' => ['status' => 'open', 'kind' => 'report', 'name' => '医助名单', 'params_allow' => [],
'note' => '按当前账号数据范围返回在职医助(ID、姓名、登录账号、部门),可用于把姓名换成 assistant_id'],
'tcm.diagnosis/getDoctors' => ['status' => 'open', 'kind' => 'report', 'name' => '医生名单', 'params_allow' => [],
'note' => '后台本身不按数据范围过滤:拥有该权限的账号可看到全部(在职医生 ID、姓名、登录账号)'],
'tcm.diagnosis/searchPatient' => ['status' => 'pending', 'name' => '全库搜索患者',
'reason' => '按姓名/手机号/身份证号在全部诊单中模糊搜索并返回手机号、身份证号,不按数据范围过滤,且未登记权限点(DiagnosisController::searchPatient);需改为按数据范围检索,可用 tcm.diagnosis/lists 的 keyword 替代'],
'tcm.diagnosis/getWechatExternalContact' => ['status' => 'pending', 'name' => '患者企微外部联系人',
'reason' => '调用企业微信会话存档接口(外部调用),且按 patient_id 可取任意患者姓名、手机号、external_userid,无逐条权限校验(DiagnosisLogic::getWechatExternalContact)'],
'tcm.diagnosis/getMsgAuditPermitUsers' => ['status' => 'excluded', 'name' => '企微会话存档成员',
'reason' => '企业微信会话存档配置信息(开启存档的成员),需调用企业微信接口,不属于业务数据'],
'tcm.diagnosis/diagnosisDetail' => ['status' => 'excluded', 'name' => '诊单详情(患者端)',
'reason' => '患者端接口:只比对请求里的 user_id 与诊单 patient_id,不是后台账号的数据权限校验;后台请用 tcm.diagnosis/readonlyDetail'],
'tcm.diagnosis/getDoctorSignature' => ['status' => 'excluded', 'name' => '医助通话签名',
'reason' => '为任意 doctor_{ID} 生成 TRTC/IM UserSig(凭据)并调用腾讯 IM,不对 AI 开放'],
'tcm.diagnosis/getPatientSignature' => ['status' => 'excluded', 'name' => '患者通话签名',
'reason' => '为任意患者生成 TRTC/IM UserSig(凭据)并调用腾讯 IM,不对 AI 开放'],
'tcm.diagnosis/watchCall' => ['status' => 'excluded',
'reason' => '返回旁观视频通话的 TRTC 进房参数与 UserSig(凭据),并调用腾讯 IM,不对 AI 开放'],
'tcm.diagnosis/test' => ['status' => 'excluded', 'name' => '诊单测试接口', 'reason' => '测试接口'],
// ───────────── 诊单待办 ─────────────
'tcm.diagnosisTodo/lists' => ['status' => 'open', 'kind' => 'list',
'guard' => ['callable' => [DiagnosisLogic::class, 'canViewReadonlyDiagnosis'], 'param' => 'diagnosis_id', 'args' => ['id', 'admin_id', 'admin_info']],
'params_allow' => ['diagnosis_id' => '诊单ID(必填)', 'status' => '状态 0待执行 1已发送 2已取消 3发送失败', 'creator_id' => '创建人ID'],
'note' => '后台列表只按诊单ID过滤、不校验诊单归属,这里补上诊单可见性校验'],
'tcm.diagnosisTodo/detail' => ['status' => 'pending', 'name' => '诊单待办详情',
'reason' => '按待办ID读取任意诊单的待办,无逐条权限校验(DiagnosisTodoLogic::detail);可改用 tcm.diagnosisTodo/lists(按诊单ID,已校验诊单可见)'],
// ───────────── 处方 ─────────────
'tcm.prescription/lists' => ['status' => 'open', 'kind' => 'list',
'params_allow' => ['patient_name' => '患者姓名(模糊)', 'sn' => '处方编号(模糊)',
'start_time' => '创建时间起(须与 end_time 同时传)', 'end_time' => '创建时间止',
'creator_ids' => '开方医生ID,多个用逗号分隔', 'audit_filter' => '审核:passed 已通过 / not_passed 未通过 / pending 待审 / rejected 驳回',
'source_filter' => '来源:system 系统代开 / manual 手工开方'],
'note' => '非全量角色只看共享、本人开具、本人为医助或指定给本人角色的处方,并叠加数据范围(开方人/医助)'],
'tcm.prescription/detail' => ['status' => 'pending',
'reason' => 'PrescriptionLogic::canViewPrescription(:83-139)对 order_edit_all_roles 角色及任何拥有 tcm.prescriptionOrder/detail 权限的账号放行全部处方,不受数据范围限制,比处方列表宽;可改用 tcm.prescription/listByDiagnosis'],
'tcm.prescription/getByAppointment' => ['status' => 'pending', 'name' => '按挂号取处方',
'reason' => '按挂号ID取处方只做 canViewPrescription 校验(同 tcm.prescription/detail,全量角色和业务订单详情权限可看任意处方);可改用 tcm.prescription/listByDiagnosis'],
// builtin:PrescriptionLogic::listByDiagnosis(logic/tcm/PrescriptionLogic.php:1046)先调 canViewReadonlyDiagnosis(:1049),再逐条 canViewPrescription 过滤(:1061)
'tcm.prescription/listByDiagnosis' => ['status' => 'open', 'kind' => 'detail', 'guard' => 'builtin', 'id_param' => 'diagnosis_id',
'name' => '诊单处方列表', 'params_allow' => [], 'note' => 'id 为诊单ID;返回该诊单下当前账号可见的全部处方(含作废)'],
// ───────────── 处方 AI 分析(控制器拒绝未知参数,并按 PrescriptionAiAccess 重新校验账号与数据范围) ─────────────
// builtin:PrescriptionAiLogic::detail(logic/tcm/PrescriptionAiLogic.php:105)→ loadBatch/visibleBatch(:345/:354):处方可见 + 诊单“我的患者”范围 + 来源快照授权
'tcm.prescriptionAi/detail' => ['status' => 'open', 'kind' => 'detail', 'guard' => 'builtin', 'id_param' => 'batch_id',
'params_allow' => [], 'note' => 'id 为分析批次 batch_id(来自处方AI历史/状态);含各模型报告正文与复核意见'],
// builtin:PrescriptionAiLogic::reports(:60)按 Access::prescription / Access::diagnosis 校验,并逐条 visibleBatch 过滤后再分页
'tcm.prescriptionAi/reports' => ['status' => 'open', 'kind' => 'report',
'params_allow' => ['prescription_id' => '处方ID(与 diagnosis_id 二选一)', 'diagnosis_id' => '诊单ID(与 prescription_id 二选一)',
'page_no' => '页码', 'page_size' => '每页条数,最多 50'],
'note' => '历次处方 AI 分析批次(摘要,不含报告正文;正文用 tcm.prescriptionAi/detail)'],
// builtin:PrescriptionAiLogic::statuses(:20)逐个 Access::prescription + visibleBatch
'tcm.prescriptionAi/statuses' => ['status' => 'open', 'kind' => 'report',
'params_allow' => ['ids' => '处方ID数组(或逗号分隔),最多 100 个'], 'note' => '无权查看的处方不会出现在结果中'],
// builtin:PrescriptionAiLogic::statistics(:204)逐批 visibleBatch 过滤
'tcm.prescriptionAi/statistics' => ['status' => 'open', 'kind' => 'report',
'params_allow' => ['date_from' => '开始日期 YYYY-MM-DD(默认 30 天前)', 'date_to' => '结束日期 YYYY-MM-DD(跨度不超过一年)', 'doctor_id' => '医生ID'],
'note' => '按医生统计处方 AI 药味与剂量一致度(不代表临床准确率),只计当前账号可见的批次'],
// ───────────── 处方库(协定方模板,非患者数据) ─────────────
'tcm.prescriptionLibrary/lists' => ['status' => 'open', 'kind' => 'list', 'name' => '处方库列表',
'params_allow' => ['prescription_name' => '处方名称(模糊)', 'is_public' => '是否公开 1是 0否', 'creator_id' => '创建人ID', 'formula_type' => '主方 / 辅方'],
// prescribing_creator_id:开方页导入专用,可读取指定医生的非公开处方(PrescriptionLibraryLists:37-44)
'forbid' => ['prescribing_creator_id'],
'note' => '管理角色看全部;其余账号看本人创建和公开的处方'],
// builtin:PrescriptionLibraryLogic::detail(logic/tcm/PrescriptionLibraryLogic.php:192,校验在 :200):本人创建、公开或管理角色
'tcm.prescriptionLibrary/detail' => ['status' => 'open', 'kind' => 'detail', 'guard' => 'builtin', 'name' => '处方库详情', 'params_allow' => []],
// builtin:PrescriptionLibraryAiLogic::getSavedReports(logic/tcm/PrescriptionLibraryAiLogic.php:60)→ loadAuthorizedPrescription(:371)校验权限 + PrescriptionLibraryLogic::detail
'tcm.prescriptionLibrary/aiReports' => ['status' => 'open', 'kind' => 'detail', 'guard' => 'builtin', 'name' => '处方库 AI 解释',
'params_allow' => [], 'note' => 'id 为处方库ID;只读已保存的解释,不触发模型调用'],
// builtin:PrescriptionLibraryAiLogic::getMissingReports(:83)校验权限,并按本人/公开/管理角色收窄(:103-108)
'tcm.prescriptionLibrary/missingAiReports' => ['status' => 'open', 'kind' => 'report', 'name' => '处方库待生成 AI 解释清单',
'params_allow' => ['limit' => '返回条数 1-500,默认 500']],
// ───────────── 处方业务订单 ─────────────
'tcm.prescriptionOrder/lists' => ['status' => 'open', 'kind' => 'list',
'params_allow' => [
'order_no' => '业务订单号(模糊)', 'prescription_id' => '处方ID', 'diagnosis_id' => '诊单ID', 'patient_id' => '患者ID(诊单 patient_id,跨诊单)',
'patient_keyword' => '患者姓名或手机号(模糊)',
'fulfillment_status' => '履约状态 1待双审通过 2待发货 3已完成 4已取消 5已发货 6已签收 7进行中 8暂不制药 9拒收 10退款 11保留药方 12制药缓发',
'prescription_audit_status' => '处方审核 0待审核 1通过 2驳回', 'payment_slip_audit_status' => '支付单审核 0待审核 1通过 2驳回',
'start_time' => '创建时间起 YYYY-MM-DD HH:mm:ss(也是 extend 金额统计区间,不传为今天)', 'end_time' => '创建时间止 YYYY-MM-DD HH:mm:ss',
'doctor_id' => '开方医生ID', 'assistant_id' => '订单创建人(医助)ID,仅数据范围内有效', 'assistant_dept_id' => '订单创建人所属部门ID(含下级部门)',
'audit_admin_id' => '审核人(下单角色)ID', 'audit_admin_keyword' => '审核人姓名(模糊)',
'express_company' => '快递公司 sf 顺丰 / jd 京东', 'express_keyword' => '快递单号或快递公司(模糊)',
'service_channel' => '服务渠道(0 表示未指派)', 'supply_mode' => '供货方式 gancao 甘草 / direct 洛阳直发 / self 自营',
'has_aux_formula' => '是否含辅方 1是 0否', 'exclude_fulfillment_cancelled' => '传 1 剔除已取消/拒收/退款订单',
],
// scene=diagnosis_edit(全局禁用)+ patient_id + context_diagnosis_id 会跳过创建人可见性和数据范围(PrescriptionOrderLists:121-127、464-482);
// yeji_* 业绩看板侧栏参数会跳过「仅本人订单」(:487-526、1326-1359)
'forbid' => ['scene', 'context_diagnosis_id', 'yeji_order_drawer', 'yeji_drawer_match_table_performance', 'yeji_er_center_revisit_only',
'yeji_er_center_revisit_slot', 'yeji_table_row_dept_ids', 'dept_ids', 'channel_code', 'create_time'],
'note' => '非全量角色默认只看本人创建的订单(有「查看本人开方订单」权限时含本人开方的订单),再叠加数据范围;extend.stats_* 为列表顶部金额统计(口径见 stats_scope);内部成本仅财务角色可见'],
'tcm.prescriptionOrder/detail' => ['status' => 'pending',
'reason' => 'PrescriptionOrderLogic::canAccessOrder(:377-398)对拥有任一 tcm.prescriptionOrder/* 权限的账号直接放行(hasPrescriptionOrderMenuAccess),全量角色也不受数据范围限制,可读取列表范围外的任意订单;需补充与列表一致的逐条校验'],
'tcm.prescriptionOrder/logs' => ['status' => 'pending',
'reason' => '逐条校验同 canAccessOrder:拥有任一业务订单权限即可读取任意订单的操作日志;需补充与列表一致的逐条校验'],
'tcm.prescriptionOrder/logisticsTrace' => ['status' => 'pending',
'reason' => '本地无轨迹时调用快递100接口(外部调用,无参数可限定只读本地缓存),且逐条校验同 canAccessOrder(任一业务订单权限即放行)'],
'tcm.prescriptionOrder/export' => ['status' => 'excluded', 'reason' => '导出文件接口;查询请用 tcm.prescriptionOrder/lists'],
// builtin 归属规则:OrderLogic::listPaidOrdersForDiagnosis(logic/order/OrderLogic.php:1303,:1317-1322)非全量角色且非该诊单医助时只返回本人创建的收款单;
// 后台原接口不校验诊单归属,这里补上诊单可见性校验
'tcm.prescriptionOrder/paidPayOrders' => ['status' => 'open', 'kind' => 'detail',
'guard' => ['callable' => [DiagnosisLogic::class, 'canViewReadonlyDiagnosis'], 'param' => 'diagnosis_id', 'args' => ['id', 'admin_id', 'admin_info']],
'params_allow' => ['prescription_order_id' => '编辑中的业务订单ID(其已关联的收款单也列出)'],
'note' => 'id 为诊单ID;该诊单下已支付、尚未被其他业务订单占用的收款单(2026-04-20 之后创建)'],
];