原来取的是中台库 dbo.flow_flow,那份数据停在 2026-07-16,7 月之后的申请 (比如 shp20260716165004772 京喜-清木居工厂店 关店流程)审批状态全是空的, 而工作平台显示「流程中」。 jiansun.dbo.flow_flow 是实时表(今天还在更新,10543 行),同名单据里 flowStatus=1、当前审批人=山时。已登记并完成首次全量 + 建 flowId 索引; 中台那份旧副本改为停止同步(数据保留)。 流程统计页与店铺账号密码的「流程类型 / 审批状态 / 流程状态」两处取值都跟着换成实时表。
217 lines
9.0 KiB
PHP
217 lines
9.0 KiB
PHP
<?php
|
||
|
||
namespace Modules\EcommerceFinance\Repositories;
|
||
|
||
use Illuminate\Database\Query\Builder;
|
||
use Modules\EcommerceFinance\Services\BusinessMirrorDatasetRegistry;
|
||
use RuntimeException;
|
||
|
||
/**
|
||
* 财务经营风险管控 › 流程统计:店铺申请流程(开店 / 变更 / 关店)只读镜像。
|
||
*
|
||
* 表单字段来自 jiansun.dbo.fys_shopInfo_flow(聚水潭侧申请单,含店铺/主体/变更明细),
|
||
* 审批状态与当前审批人来自中台库的 OA 流程表 dbo.flow_flow(按 flowId 关联)。
|
||
*/
|
||
class ShopInfoFlowMirrorRepository
|
||
{
|
||
public const FORM_DATASET = 'jiansun.dbo.fys_shopInfo_flow';
|
||
public const FLOW_DATASET = 'jiansun.dbo.flow_flow';
|
||
|
||
/** OA 流程类型(flowType)到页面文案。 */
|
||
public const FLOW_TYPES = [
|
||
8 => '店铺开店',
|
||
9 => '店铺变更',
|
||
10 => '店铺关店',
|
||
12 => '主体注销自动流程',
|
||
];
|
||
|
||
/** OA 审批状态(flow_flow.flowStatus),取值与文案取自 OA 前端。 */
|
||
public const APPROVAL_STATUSES = [
|
||
0 => '暂存',
|
||
1 => '流程中',
|
||
2 => '拒绝',
|
||
3 => '完成',
|
||
4 => '撤回',
|
||
5 => '待签署',
|
||
6 => '已签署',
|
||
7 => '待审批',
|
||
];
|
||
|
||
/** 店铺档案状态(fys_shopInfo.shopState 口径)。 */
|
||
private const SHOP_STATES = [0 => '正常', 3 => '关闭中', 6 => '已关闭'];
|
||
|
||
public function __construct(
|
||
private readonly BusinessMirrorDatasetRegistry $datasets,
|
||
private readonly GenericBusinessMirrorRepository $mirror,
|
||
) {
|
||
}
|
||
|
||
public function ready(): bool
|
||
{
|
||
try {
|
||
return $this->mirror->ready($this->datasets->get(self::FORM_DATASET));
|
||
} catch (\Throwable) {
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @param array<string,mixed> $filters
|
||
* @return array{data:list<array<string,mixed>>,total:int}
|
||
*/
|
||
public function paginate(int $page, int $perPage, array $filters = []): array
|
||
{
|
||
if (! $this->ready()) {
|
||
throw new RuntimeException('shop_info_flow_mirror_not_ready');
|
||
}
|
||
|
||
$page = max($page, 1);
|
||
$perPage = min(max($perPage, 1), 100);
|
||
$connection = $this->mirror->connection();
|
||
$form = $this->datasets->get(self::FORM_DATASET)->targetTable();
|
||
$flow = $this->datasets->get(self::FLOW_DATASET)->targetTable();
|
||
|
||
$query = $connection->table($form . ' as f')
|
||
->leftJoin($flow . ' as o', 'o.flowId', '=', 'f.flowId');
|
||
|
||
$this->applyFilters($query, $filters);
|
||
|
||
$total = (clone $query)->count();
|
||
$rows = $query
|
||
->select([
|
||
'f.Id as id', 'f.flowId as flow_id', 'f.flowType as flow_type', 'f.applyUser as apply_user',
|
||
'f.applyTime as apply_time', 'f.applyDepartment as apply_department', 'f.remark as remark',
|
||
'f.platform as platform', 'f.responsibleDepartment as responsible_department',
|
||
'f.shopHead as shop_head', 'f.customerManager as customer_manager', 'f.shopType as shop_type',
|
||
'f.shopState as shop_state', 'f.shopName as shop_name', 'f.erpShopName as erp_shop_name',
|
||
'f.trademarkNumber as trademark_number', 'f.unifiedSocialCreditCode as subject_code',
|
||
'f.informationChangeType as information_change_type', 'f.changeReason as change_reason',
|
||
'f.changeSuccessful as change_successful', 'f.closeReason as close_reason',
|
||
'f.expectedClosingTime as expected_closing_time', 'f.refundType as refund_type',
|
||
'f.refundAmount as refund_amount', 'f.refundIssue as refund_issue',
|
||
'o.flowStatus as approval_status_code', 'o.approver as current_approver',
|
||
'o.subject as flow_subject', 'o.updateTime as flow_updated_at',
|
||
])
|
||
->orderByDesc('f.applyTime')->orderByDesc('f.Id')
|
||
->forPage($page, $perPage)
|
||
->get()
|
||
->map(fn (object $row): array => $this->mapRow($row))
|
||
->all();
|
||
|
||
return ['data' => $rows, 'total' => $total];
|
||
}
|
||
|
||
/** @param array<string,mixed> $filters */
|
||
private function applyFilters(Builder $query, array $filters): void
|
||
{
|
||
foreach ([
|
||
'flow_id' => 'f.flowId',
|
||
'apply_user' => 'f.applyUser',
|
||
'erp_shop_name' => 'f.erpShopName',
|
||
'shop_name' => 'f.shopName',
|
||
'shop_head' => 'f.shopHead',
|
||
'current_approver' => 'o.approver',
|
||
'responsible_department' => 'f.responsibleDepartment',
|
||
'trademark_number' => 'f.trademarkNumber',
|
||
] as $key => $column) {
|
||
$value = trim((string) ($filters[$key] ?? ''));
|
||
if ($value !== '') {
|
||
$query->where($column, 'like', '%' . addcslashes($value, '%_\\') . '%');
|
||
}
|
||
}
|
||
|
||
foreach ([['from', '>='], ['to', '<=']] as [$suffix, $operator]) {
|
||
$date = trim((string) ($filters['apply_' . $suffix] ?? ''));
|
||
if (preg_match('/^\d{4}-\d{2}-\d{2}$/D', $date)) {
|
||
$query->whereDate('f.applyTime', $operator, $date);
|
||
}
|
||
}
|
||
|
||
// 多选筛选:流程类型 / 审批状态 / 平台,多个取值取并集。
|
||
$flowTypes = $this->filterValues($filters['flow_type'] ?? '');
|
||
if ($flowTypes !== []) {
|
||
$codes = [];
|
||
foreach ($flowTypes as $label) {
|
||
$code = array_search($label, self::FLOW_TYPES, true);
|
||
if ($code !== false) {
|
||
$codes[] = (int) $code;
|
||
}
|
||
}
|
||
$query->whereIn('f.flowType', $codes === [] ? [-1] : $codes);
|
||
}
|
||
|
||
$statuses = $this->filterValues($filters['approval_status'] ?? '');
|
||
if ($statuses !== []) {
|
||
$codes = [];
|
||
foreach ($statuses as $label) {
|
||
$code = array_search($label, self::APPROVAL_STATUSES, true);
|
||
if ($code !== false) {
|
||
$codes[] = (int) $code;
|
||
}
|
||
}
|
||
$query->whereIn('o.flowStatus', $codes === [] ? [-1] : $codes);
|
||
}
|
||
|
||
$platforms = $this->filterValues($filters['platform'] ?? '');
|
||
if ($platforms !== []) {
|
||
$query->whereIn('f.platform', $platforms);
|
||
}
|
||
}
|
||
|
||
/** @return list<string> */
|
||
private function filterValues(mixed $value): array
|
||
{
|
||
$values = [];
|
||
foreach ((array) $value as $item) {
|
||
if (! is_scalar($item)) {
|
||
continue;
|
||
}
|
||
$item = trim((string) $item);
|
||
if ($item !== '') {
|
||
$values[$item] = true;
|
||
}
|
||
}
|
||
|
||
return array_keys($values);
|
||
}
|
||
|
||
/** @return array<string,mixed> */
|
||
private function mapRow(object $row): array
|
||
{
|
||
$flowType = (int) ($row->flow_type ?? 0);
|
||
$statusCode = $row->approval_status_code !== null ? (int) $row->approval_status_code : null;
|
||
|
||
return [
|
||
'id' => $row->id,
|
||
'flow_id' => (string) $row->flow_id,
|
||
'flow_type' => self::FLOW_TYPES[$flowType] ?? ('其它(' . $flowType . ')'),
|
||
'flow_subject' => (string) ($row->flow_subject ?? ''),
|
||
'approval_status' => $statusCode === null ? '' : (self::APPROVAL_STATUSES[$statusCode] ?? (string) $statusCode),
|
||
'current_approver' => (string) ($row->current_approver ?? ''),
|
||
'apply_user' => (string) ($row->apply_user ?? ''),
|
||
'apply_time' => (string) ($row->apply_time ?? ''),
|
||
'apply_department' => (string) ($row->apply_department ?? ''),
|
||
'remark' => (string) ($row->remark ?? ''),
|
||
'platform' => (string) ($row->platform ?? ''),
|
||
'responsible_department' => (string) ($row->responsible_department ?? ''),
|
||
'shop_head' => (string) ($row->shop_head ?? ''),
|
||
'customer_manager' => (string) ($row->customer_manager ?? ''),
|
||
'shop_type' => (string) ($row->shop_type ?? ''),
|
||
'shop_state' => $row->shop_state === null ? '' : (self::SHOP_STATES[(int) $row->shop_state] ?? (string) $row->shop_state),
|
||
'shop_name' => (string) ($row->shop_name ?? ''),
|
||
'erp_shop_name' => (string) ($row->erp_shop_name ?? ''),
|
||
'trademark_number' => (string) ($row->trademark_number ?? ''),
|
||
'subject_code' => (string) ($row->subject_code ?? ''),
|
||
'information_change_type' => $row->information_change_type === null ? '' : (string) $row->information_change_type,
|
||
'change_reason' => (string) ($row->change_reason ?? ''),
|
||
'change_successful' => $row->change_successful === null ? null : (bool) $row->change_successful,
|
||
'close_reason' => (string) ($row->close_reason ?? ''),
|
||
'expected_closing_time' => (string) ($row->expected_closing_time ?? ''),
|
||
'refund_type' => (string) ($row->refund_type ?? ''),
|
||
'refund_amount' => $row->refund_amount === null ? null : (float) $row->refund_amount,
|
||
'refund_issue' => (string) ($row->refund_issue ?? ''),
|
||
'flow_updated_at' => (string) ($row->flow_updated_at ?? ''),
|
||
];
|
||
}
|
||
}
|