调整ä功能
This commit is contained in:
@@ -43,6 +43,7 @@ class UserGroupLogic
|
||||
$userGroup->title = $data['title'];
|
||||
$userGroup->sort = $data['sort'] ?? 0;
|
||||
$userGroup->status = $data['status'];
|
||||
$userGroup->scenario_code = $data['scenario_code'] ?? '';
|
||||
$userGroup->save();
|
||||
|
||||
// 删除所有成员
|
||||
|
||||
@@ -75,6 +75,7 @@ class ApprovalInstances extends Model
|
||||
// ====== ====== ====== 映射业务表 ====== ====== ======
|
||||
protected const BIZ_CLASS = [
|
||||
'stock_orders' => \app\common\model\StockOrders::class,
|
||||
'stock_orders_nullify' => \app\common\model\StockOrders::class,
|
||||
'purchase_orders' => \app\common\model\PurchaseOrders::class,
|
||||
];
|
||||
|
||||
@@ -85,7 +86,7 @@ class ApprovalInstances extends Model
|
||||
|
||||
public function bizInfo()
|
||||
{
|
||||
return $this->morphTo('bizInfo', 'biz_type', 'biz_id');
|
||||
return $this->morphTo('bizInfo', 'biz_type', 'biz_id')->withTrashed();
|
||||
}
|
||||
// ====== ====== ====== 映射表业务 ====== ====== ======
|
||||
|
||||
|
||||
@@ -4,9 +4,13 @@ namespace app\common\model;
|
||||
|
||||
use support\Model;
|
||||
use app\common\traits\HasMoneyJournal;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
class StockOrders extends Model
|
||||
{
|
||||
|
||||
use SoftDeletes;
|
||||
|
||||
/**
|
||||
* The connection name for the model.
|
||||
*
|
||||
|
||||
@@ -40,7 +40,7 @@ class UserGroup extends Model
|
||||
/**
|
||||
* 允许自动填充模型字段
|
||||
*/
|
||||
protected $fillable = ['id', 'title', 'sort', 'status', 'created_at', 'updated_at'];
|
||||
protected $fillable = ['id', 'title', 'scenario_code', 'sort', 'status', 'created_at', 'updated_at'];
|
||||
|
||||
/**
|
||||
* 不允许自动填充模型字段
|
||||
|
||||
@@ -136,6 +136,7 @@ class ApprovalRecordsService
|
||||
$bizInfo = [];
|
||||
$service = match ($data['flow_type']) {
|
||||
'stock_orders' => new \app\common\service\StockOrdersService(),
|
||||
'stock_orders_nullify' => new \app\common\service\StockOrdersService(),
|
||||
'purchase_orders' => new \app\common\service\PurchaseOrdersService(),
|
||||
default => new \app\common\service\StockOrdersService()
|
||||
};
|
||||
@@ -154,6 +155,14 @@ class ApprovalRecordsService
|
||||
'instance.bizInfo.moneyValues',
|
||||
'instance.bizInfo.salesperson',
|
||||
],
|
||||
'stock_orders_nullify' => [
|
||||
'instance.bizInfo.items',
|
||||
'instance.bizInfo.items.options',
|
||||
'instance.bizInfo.items.moneyValues',
|
||||
'instance.bizInfo.accounts',
|
||||
'instance.bizInfo.moneyValues',
|
||||
'instance.bizInfo.salesperson',
|
||||
],
|
||||
'purchase_orders' => [
|
||||
'instance.bizInfo.items',
|
||||
'instance.bizInfo.items.stockOrder',
|
||||
|
||||
@@ -410,7 +410,7 @@ class StockOrdersService
|
||||
public function approvalNullify($item)
|
||||
{
|
||||
$appro = new \app\common\utils\approval\service\ApprovalFlowService();
|
||||
$appro->startNullify('stock_orders_nullify', 'stock_orders_nullify', $item->id, [], $item->salesperson_id);
|
||||
$appro->startNullify('stock_orders_nullify', 'stock_orders', $item->id, [], $item->salesperson_id);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -74,16 +74,16 @@ class WsEvents
|
||||
}
|
||||
|
||||
// 连接是否满足条件
|
||||
// if ($payload['server']['HTTP_ORIGIN'] != 'https://wstool.js.org') {
|
||||
// flg('[x] [TOKEN认证] HTTP_ORIGIN不满足条件', false);
|
||||
// Gateway::sendToClient($client_id, WsResponse::error(
|
||||
// 'system.auth',
|
||||
// 1001,
|
||||
// '[x] [TOKEN认证] HTTP_ORIGIN不满足条件'
|
||||
// ));
|
||||
// Gateway::closeClient($client_id);
|
||||
// return;
|
||||
// }
|
||||
if (getenv('ENVIRONMENT') === 'master' && $payload['server']['HTTP_ORIGIN'] === getenv('MAIN_DOMAIN')) {
|
||||
flg('[x] [TOKEN认证] HTTP_ORIGIN不满足条件', false);
|
||||
Gateway::sendToClient($client_id, WsResponse::error(
|
||||
'system.auth',
|
||||
1001,
|
||||
'[x] [TOKEN认证] HTTP_ORIGIN不满足条件'
|
||||
));
|
||||
Gateway::closeClient($client_id);
|
||||
return;
|
||||
}
|
||||
|
||||
// 签名检查
|
||||
if (!isset($payload['get']['token'])) {
|
||||
@@ -99,9 +99,14 @@ class WsEvents
|
||||
|
||||
// 解析TOKEN
|
||||
$user = (new Jwt())->verifyToken($payload['get']['token']);
|
||||
|
||||
// 绑定用户ID
|
||||
Gateway::bindUid($client_id, $user->uid);
|
||||
|
||||
// 绑定SESSION
|
||||
$_SESSION['uid'] = $user->uid;
|
||||
$_SESSION['name'] = $user->uname;
|
||||
|
||||
// 响应
|
||||
Gateway::sendToClient($client_id, WsResponse::success(
|
||||
'system.auth',
|
||||
@@ -119,6 +124,13 @@ class WsEvents
|
||||
}
|
||||
}
|
||||
|
||||
public function logout(string $client_id)
|
||||
{
|
||||
if ($_SESSION !== null) {
|
||||
var_dump($_SESSION);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 系统心跳
|
||||
*
|
||||
|
||||
@@ -3,6 +3,7 @@ namespace app\common\utils\approval\service;
|
||||
|
||||
use support\exception\BusinessException;
|
||||
use support\Db;
|
||||
use support\Log;
|
||||
|
||||
class ApprovalFlowService
|
||||
{
|
||||
@@ -174,14 +175,18 @@ class ApprovalFlowService
|
||||
$nextIndex = $this->getNextStepIndex($steps, $formData, $idx);
|
||||
|
||||
if ($nextIndex === null) {
|
||||
$finalStatus = $this->normalizeFinalStatus((string)$instance->flow_type, 'approved');
|
||||
$notifyAction = ($finalStatus === 'voided') ? 'voided' : 'approve';
|
||||
|
||||
Db::table('approval_instances')->where('id', $instanceId)->update([
|
||||
'status' => 'approved',
|
||||
'status' => $finalStatus,
|
||||
'finished_at' => $now,
|
||||
'updated_at' => $now,
|
||||
]);
|
||||
$this->onFlowFinalized($instance, 'approved');
|
||||
$this->sendNotification([$instance->initiator_id], $instance, 'approve', false);
|
||||
return ['status' => 'completed', 'msg' => '同意'];
|
||||
$this->onFlowFinalized($instance, $finalStatus);
|
||||
$this->sendNotification([$instance->initiator_id], $instance, $notifyAction, false);
|
||||
|
||||
return ['status' => 'completed', 'msg' => ($finalStatus === 'voided' ? '已作废' : '同意')];
|
||||
}
|
||||
|
||||
// 乐观推进 current_step
|
||||
@@ -206,13 +211,16 @@ class ApprovalFlowService
|
||||
});
|
||||
}
|
||||
|
||||
/** ★ 新增:流程完结后的业务回调(此处处理“作废”落库) */
|
||||
private function onFlowFinalized(object $instance, string $final /* 'approved'|'rejected' */): void
|
||||
// 流程完结后的业务回调(approved|rejected|revoked|timeout?)
|
||||
private function onFlowFinalized(object $instance, string $final): void
|
||||
{
|
||||
try {
|
||||
switch ($instance->flow_type) {
|
||||
case 'stock_orders': {
|
||||
$order = \app\common\model\StockOrders::find($instance->biz_id);
|
||||
if (!$order) {
|
||||
throw new BusinessException('订单不存在');
|
||||
}
|
||||
if ($final === 'approved') {
|
||||
$order->approval_status = 2;
|
||||
$order->save();
|
||||
@@ -225,30 +233,27 @@ class ApprovalFlowService
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 'stock_orders_nullify':
|
||||
// 仅当作废审批“通过”时,真正把业务单置为作废
|
||||
if ($final === 'approved') {
|
||||
// 这里按你的模型更新,状态字段请替换成你系统实际枚举
|
||||
$final = 'voided';
|
||||
Db::table('approval_instances')->where('id', $instance->id)->update([
|
||||
'status' => 'voided',
|
||||
]);
|
||||
}
|
||||
if (in_array($final, ['voided','approved'], true)) {
|
||||
$order = \app\common\model\StockOrders::find($instance->biz_id);
|
||||
if ($order && $order->approval_status !== 4) {
|
||||
$order->approval_status = 4;
|
||||
$order->approval_status = 4;
|
||||
$order->save();
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
// case 'purchase_orders_nullify':
|
||||
// ...
|
||||
// break;
|
||||
|
||||
default:
|
||||
// 非作废流程,无需处理
|
||||
break;
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
// 避免回写异常影响审批主流程,可按需记录日志
|
||||
// logger()->error('onFlowFinalized error: '.$e->getMessage());
|
||||
Log::error('onFlowFinalized error: '.$e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -361,8 +366,9 @@ class ApprovalFlowService
|
||||
$flow = Db::table('approval_flows')->where('type', $instance->flow_type)->first();
|
||||
if (!$flow) return;
|
||||
|
||||
$final = ($flow->timeout_action === 'auto_approve') ? 'approved' : 'rejected';
|
||||
$finalAction = ($flow->timeout_action === 'auto_approve') ? 'approve' : 'reject';
|
||||
$final = ($flow->timeout_action === 'auto_approve') ? 'approved' : 'rejected';
|
||||
$final = $this->normalizeFinalStatus((string)$instance->flow_type, $final);
|
||||
$finalAction = ($final === 'voided') ? 'voided' : (($final === 'approved') ? 'approve' : 'reject');
|
||||
|
||||
Db::table('approval_instances')->where('id', $instance->id)->update([
|
||||
'status' => $final,
|
||||
@@ -490,6 +496,28 @@ class ApprovalFlowService
|
||||
$head = $this->getDeptHeadId((int)$val);
|
||||
if ($head) $uids[] = (int)$head;
|
||||
break;
|
||||
case 'group_members': {
|
||||
$gid = (int)($val ?? 0);
|
||||
if ($gid <= 0) throw new BusinessException('group_members 需要 group_id');
|
||||
$uids = $this->getGroupMemberIds($gid); // A 方案
|
||||
break;
|
||||
}
|
||||
case 'scenario_leader_of_initiator': {
|
||||
$formData = json_decode($instance->form_data ?? '{}', true) ?: [];
|
||||
$scenario = $val ?? ($formData[$cc['scenario_field']] ?? null);
|
||||
if (!$scenario) throw new BusinessException('缺少业务场景(scenario 或 scenario_field)');
|
||||
|
||||
$mustBelong = array_key_exists('must_belong', $cc) ? (bool)$cc['must_belong'] : true;
|
||||
$preferPrimary = array_key_exists('prefer_primary', $cc) ? (bool)$cc['prefer_primary'] : true;
|
||||
|
||||
$uids = $this->getLeadersByScenarioForInitiator(
|
||||
(int)$instance->initiator_id,
|
||||
(string)$scenario,
|
||||
$mustBelong,
|
||||
$preferPrimary
|
||||
); // B 方案(按场景)
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
@@ -553,10 +581,6 @@ class ApprovalFlowService
|
||||
|
||||
/**
|
||||
* 聚合本步骤结论(按类型)
|
||||
* 返回:'pending' | 'approve' | 'reject'
|
||||
*
|
||||
* any:有 reject => reject;否则有 approve => approve;否则 pending
|
||||
* all:有 reject => reject;有 pending => pending;否则(全 approved 或 approved+skipped/timeout)=> approve
|
||||
*/
|
||||
private function getStepOutcome(int $instanceId, int $round, int $stepIndex, string $type): string
|
||||
{
|
||||
@@ -566,22 +590,28 @@ class ApprovalFlowService
|
||||
->where('step_index', $stepIndex)
|
||||
->get();
|
||||
|
||||
$pending = 0; $approved = 0; $rejected = 0;
|
||||
$pending = $approved = $rejected = $timeout = $skipped = 0;
|
||||
foreach ($records as $r) {
|
||||
if ($r->status === 'pending') $pending++;
|
||||
elseif ($r->status === 'approved') $approved++;
|
||||
elseif ($r->status === 'rejected') $rejected++;
|
||||
switch ($r->status) {
|
||||
case 'pending': $pending++; break;
|
||||
case 'approved': $approved++; break;
|
||||
case 'rejected': $rejected++; break;
|
||||
case 'timeout': $timeout++; break;
|
||||
case 'skipped': $skipped++; break;
|
||||
}
|
||||
}
|
||||
|
||||
$type = strtolower($type);
|
||||
if ($type === 'any') {
|
||||
if ($rejected > 0) return 'reject';
|
||||
if ($approved > 0) return 'approve';
|
||||
// 全为 skipped/timeout 且无 pending/rejected ⇒ 通过
|
||||
if ($pending === 0 && $rejected === 0 && $approved === 0) return 'approve';
|
||||
return 'pending';
|
||||
}
|
||||
if ($rejected > 0) return 'reject';
|
||||
if ($pending > 0) return 'pending';
|
||||
return $approved > 0 ? 'approve' : 'pending';
|
||||
return 'approve';
|
||||
}
|
||||
|
||||
/** 关闭本步其余待办为 skipped */
|
||||
@@ -637,6 +667,7 @@ class ApprovalFlowService
|
||||
'approve' => '已通过',
|
||||
'reject' => '已拒绝',
|
||||
'revoked' => '已撤回',
|
||||
'voided' => '已作废',
|
||||
];
|
||||
$prefix = $isCC ? '【抄送】' : '【审批】';
|
||||
$text = $map[$action] ?? $action;
|
||||
@@ -644,6 +675,10 @@ class ApprovalFlowService
|
||||
// 业务标题/内容
|
||||
$approvalType = array_flip(\app\common\service\SysDictService::pluckKeyValueByCode('approval_type'));
|
||||
|
||||
$flowTxt = $approvalType[$instance->flow_type] ?? (string)$instance->flow_type;
|
||||
$title = $prefix.'审批通知';
|
||||
$content = "{$prefix}你有一条审批 {$text}:类型 {$flowTxt},业务标识 {$instance->biz_id}";
|
||||
|
||||
switch ($instance->flow_type) {
|
||||
case 'stock_orders':
|
||||
// 备货单
|
||||
@@ -1589,6 +1624,7 @@ class ApprovalFlowService
|
||||
->where('status', 'approved')
|
||||
->orderByDesc('id')
|
||||
->first();
|
||||
|
||||
if (!$latestApproved) {
|
||||
throw new BusinessException('仅已通过的单据可发起作废');
|
||||
}
|
||||
@@ -1603,6 +1639,26 @@ class ApprovalFlowService
|
||||
throw new BusinessException('已存在进行中的作废审批');
|
||||
}
|
||||
|
||||
// A. 基于历史“作废流程已通过”的实例拦截
|
||||
$voidApproved = Db::table('approval_instances')
|
||||
->where('flow_type', $voidFlowType)
|
||||
->where('biz_id', $bizId)
|
||||
->whereIn('status', ['approved','voided'])
|
||||
->exists();
|
||||
if ($voidApproved) {
|
||||
throw new BusinessException('该单据已作废,无需重复作废');
|
||||
}
|
||||
|
||||
// B.(可选)直接查业务表状态拦截(若你用 approval_status=4 表示已作废)
|
||||
/*
|
||||
$orderVoided = \app\common\model\StockOrders::where('id', $bizId)
|
||||
->where('approval_status', 4)
|
||||
->exists();
|
||||
if ($orderVoided) {
|
||||
throw new BusinessException('该单据已作废,无需重复作废');
|
||||
}
|
||||
*/
|
||||
|
||||
// 4) 计算首步
|
||||
// 注:作废流程可以有独立条件;若不需要条件,直接第一步
|
||||
$formData = array_merge([
|
||||
@@ -1735,4 +1791,23 @@ class ApprovalFlowService
|
||||
return $ids;
|
||||
}
|
||||
|
||||
//
|
||||
private function isNullifyFlow(string $flowType): bool
|
||||
{
|
||||
$t = strtolower(trim($flowType));
|
||||
return str_ends_with($t, '_nullify') || in_array($t, [
|
||||
// 所有“作废”类流程的类型
|
||||
'stock_orders_nullify',
|
||||
], true);
|
||||
}
|
||||
|
||||
// 放到工具方法区
|
||||
private function normalizeFinalStatus(string $flowType, string $final): string
|
||||
{
|
||||
if ($this->isNullifyFlow($flowType) && $final === 'approved') {
|
||||
return 'voided';
|
||||
}
|
||||
return $final;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user