This commit is contained in:
Your Name
2026-04-30 09:27:42 +08:00
parent dc899e4af4
commit eb782305e6
460 changed files with 10785 additions and 928 deletions
@@ -9,6 +9,7 @@ use app\adminapi\logic\qywx\CustomerLogic;
use app\common\lists\ListsSearchInterface;
use app\common\model\auth\Admin;
use app\common\model\QywxExternalContact;
use think\facade\Db;
/**
* 企业微信客户列表
@@ -26,6 +27,32 @@ class CustomerLists extends BaseAdminDataLists implements ListsSearchInterface
];
}
/**
* @return int[] 入参 tag_ids 规范化后的非空字符串数组(实际为 string[])
*/
private function normalizeTagIds(): array
{
$raw = $this->params['tag_ids'] ?? null;
if ($raw === null || $raw === '') {
return [];
}
if (is_string($raw)) {
$raw = explode(',', $raw);
}
if (!is_array($raw)) {
return [];
}
$ids = [];
foreach ($raw as $v) {
$s = trim((string) $v);
if ($s !== '') {
$ids[] = $s;
}
}
return array_values(array_unique($ids));
}
private function baseQuery()
{
$query = QywxExternalContact::where($this->searchWhere);
@@ -34,6 +61,22 @@ class CustomerLists extends BaseAdminDataLists implements ListsSearchInterface
$query->whereLike('follow_users', '%' . $kw . '%');
}
// 标签筛选:JOIN 关系表按 tag_id 过滤;多个标签为 OR(命中任一即返回)。
// 走 zyt_qywx_external_contact_tag.idx_tag 索引,比 LIKE follow_users 快得多
$tagIds = $this->normalizeTagIds();
if ($tagIds !== []) {
$matchedExtIds = Db::name('qywx_external_contact_tag')
->whereIn('tag_id', $tagIds)
->group('external_userid')
->column('external_userid');
if ($matchedExtIds === []) {
// 没人命中:直接给一个不可能成立的条件,避免下面命中所有客户
$query->whereRaw('1=0');
} else {
$query->whereIn('external_userid', $matchedExtIds);
}
}
return $query;
}
@@ -91,6 +134,10 @@ class CustomerLists extends BaseAdminDataLists implements ListsSearchInterface
$followAdminIds = json_decode($item['follow_admin_ids'] ?? '[]', true);
$item['follow_admin_ids'] = is_array($followAdminIds) ? $followAdminIds : [];
// 解析标签 JSON 数组(值由 CustomerLogic::extractFollowUserTags 写入;按 tag_id 去重)
$tags = json_decode((string) ($item['tags'] ?? '[]'), true);
$item['tags'] = is_array($tags) ? $tags : [];
$fromDb = (int) ($item['external_first_add_time'] ?? 0);
$fromJson = CustomerLogic::minFollowCreatetime($followUsers);
$item['external_first_add_time'] = $fromDb > 0 ? $fromDb : $fromJson;
@@ -17,6 +17,100 @@ use app\common\model\tcm\PrescriptionOrder;
class PrescriptionLists extends BaseAdminDataLists implements ListsSearchInterface
{
use HasDataScopeFilter;
/**
* 识别「有业务订单且处方药材为空白/重复」的处方 ID(用于全局置顶排序)
*
* @param array<int,int|string> $candidateIds
* @return array<int,int>
*/
private function collectRiskPrescriptionIds(array $candidateIds): array
{
$candidateIds = array_values(array_unique(array_filter(array_map('intval', $candidateIds), static function (int $id): bool {
return $id > 0;
})));
if ($candidateIds === []) {
return [];
}
// 仅在「存在有效业务订单」的处方里做风险判定
$orderRxIds = PrescriptionOrder::whereIn('prescription_id', $candidateIds)
->whereNull('delete_time')
->where('fulfillment_status', '<>', 4)
->column('prescription_id');
$orderRxIds = array_values(array_unique(array_filter(array_map('intval', $orderRxIds), static function (int $id): bool {
return $id > 0;
})));
if ($orderRxIds === []) {
return [];
}
$rows = Prescription::whereIn('id', $orderRxIds)
->whereNull('delete_time')
->field(['id', 'herbs'])
->select()
->toArray();
$riskIds = [];
foreach ($rows as $row) {
$rid = (int) ($row['id'] ?? 0);
if ($rid <= 0) {
continue;
}
if ($this->isRiskHerbs($row['herbs'] ?? null)) {
$riskIds[] = $rid;
}
}
return array_values(array_unique($riskIds));
}
/**
* 药材风险判定:空白 or 重复(按名称,忽略空格与大小写)
*/
private function isRiskHerbs($herbsRaw): bool
{
$herbs = [];
if (is_array($herbsRaw)) {
$herbs = $herbsRaw;
} elseif (is_string($herbsRaw) && $herbsRaw !== '') {
$decoded = json_decode($herbsRaw, true);
if (is_array($decoded)) {
$herbs = $decoded;
}
}
$names = [];
foreach ($herbs as $h) {
if (!is_array($h)) {
continue;
}
$name = trim((string) ($h['name'] ?? ''));
if ($name !== '') {
$names[] = $name;
}
}
// 空白药材
if ($names === []) {
return true;
}
// 重复药材
$seen = [];
foreach ($names as $name) {
$key = strtolower(preg_replace('/\s+/', '', $name) ?? '');
if ($key === '') {
continue;
}
if (isset($seen[$key])) {
return true;
}
$seen[$key] = true;
}
return false;
}
/**
* @notes 搜索条件
*/
@@ -142,8 +236,16 @@ class PrescriptionLists extends BaseAdminDataLists implements ListsSearchInterfa
$this->applyCreatorIdsFilter($query);
$this->applyDataScopeByOwnerColumns($query, ['creator_id', 'assistant_id']);
$lists = $query
->whereNull('delete_time')
// 全局置顶:有业务订单且处方药材为空白/重复
$candidateIds = (clone $query)->whereNull('delete_time')->column('id');
$riskIds = $this->collectRiskPrescriptionIds(is_array($candidateIds) ? $candidateIds : []);
$listQuery = $query->whereNull('delete_time');
if ($riskIds !== []) {
$riskIdStr = implode(',', array_map('intval', $riskIds));
$listQuery->orderRaw("CASE WHEN id IN ({$riskIdStr}) THEN 0 ELSE 1 END ASC");
}
$lists = $listQuery
->order('id', 'desc')
->limit($this->limitOffset, $this->limitLength)
->select()
@@ -71,13 +71,7 @@ class PrescriptionOrderLists extends BaseAdminDataLists implements ListsSearchIn
$this->applyDoctorAssistantFilters($query);
$this->applyPatientKeywordFilter($query);
if (!PrescriptionOrderLogic::canSeeAllPrescriptionOrders($this->adminInfo)) {
$diagIds = Diagnosis::where('assistant_id', $this->adminId)->whereNull('delete_time')->column('id');
$query->where(function ($q) use ($diagIds) {
$q->where('creator_id', $this->adminId);
if ($diagIds !== []) {
$q->whereOr('diagnosis_id', 'in', $diagIds);
}
});
$query->where('creator_id', $this->adminId);
}
$this->applyDataScopeForPrescriptionOrder($query);
}
@@ -145,13 +139,7 @@ class PrescriptionOrderLists extends BaseAdminDataLists implements ListsSearchIn
return $query;
}
if (!PrescriptionOrderLogic::canSeeAllPrescriptionOrders($this->adminInfo)) {
$diagIds = Diagnosis::where('assistant_id', $this->adminId)->whereNull('delete_time')->column('id');
$query->where(function ($q) use ($diagIds) {
$q->where('creator_id', $this->adminId);
if ($diagIds !== []) {
$q->whereOr('diagnosis_id', 'in', $diagIds);
}
});
$query->where('creator_id', $this->adminId);
}
$this->applyDataScopeForPrescriptionOrder($query);
@@ -319,9 +307,13 @@ class PrescriptionOrderLists extends BaseAdminDataLists implements ListsSearchIn
'stats_order_amount' => $s['order_amount'],
'stats_order_amount_not_cancelled' => $s['order_amount_not_cancelled'],
'stats_order_amount_cancelled' => $s['order_amount_cancelled'],
/** 业绩:业务订单金额,剔除履约已取消(fulfillment_status=4),其余状态全部计入 */
'stats_order_amount_performance' => $s['order_amount_not_cancelled'],
'stats_linked_pay_amount' => $s['linked_pay_amount'],
'stats_linked_pay_amount_not_cancelled' => $s['linked_pay_amount_not_cancelled'],
'stats_linked_pay_amount_cancelled' => $s['linked_pay_amount_cancelled'],
/** 业绩-关联实付:剔除履约已取消订单的关联支付金额 */
'stats_linked_pay_amount_performance' => $s['linked_pay_amount_not_cancelled'],
'stats_time_start' => $s['label_start'],
'stats_time_end' => $s['label_end'],
/** 统计口径:all=支付审核白名单全量;assistant=医助仅诊单协助业绩;restricted=与列表同域 */