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

301 lines
24 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 数据目录人工审核:医生/挂号、收款订单、财务、药房、用户、粉丝、充值、消息、工作台、资源分发(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 后再评估开放。',
],
];