feat: enhance agent settlement features and improve data access controls
- Added new section in AGENTS.md detailing learned workspace facts for better understanding of settlement processes. - Updated AgentNodeDestroyController to remove unnecessary checks for admin users. - Enhanced AgentSettlement controllers to assert permissions for finance adjustments and bill operations. - Improved query scopes in AgentSettlement services to ensure proper data access based on admin roles. - Refactored methods in SettlementPartyEnrichment for better bill row enrichment and data handling. - Introduced new methods in AdminAgentSettlementScope for managing agent node visibility and finance adjustments.
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
use App\Support\AdminAuthorizationRegistry;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
/**
|
||||
* 开账建议 API 注册到 admin_api_resources(已有库增量同步,避免 api_resource_not_configured)。
|
||||
*/
|
||||
return new class extends Migration
|
||||
{
|
||||
private const RESOURCE_CODE = 'admin.settlement-periods.open-hints';
|
||||
|
||||
public function up(): void
|
||||
{
|
||||
$now = Carbon::now();
|
||||
$menuActionIds = DB::table('admin_menu_actions')->pluck('id', 'permission_code');
|
||||
$resource = collect(AdminAuthorizationRegistry::resources())
|
||||
->firstWhere('code', self::RESOURCE_CODE);
|
||||
|
||||
if (! is_array($resource)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$resourceId = DB::table('admin_api_resources')
|
||||
->where('code', $resource['code'])
|
||||
->value('id');
|
||||
|
||||
$payload = [
|
||||
'module_code' => $resource['module_code'],
|
||||
'name' => $resource['name'],
|
||||
'http_method' => $resource['http_method'],
|
||||
'uri_pattern' => $resource['uri_pattern'],
|
||||
'route_name' => $resource['route_name'],
|
||||
'auth_mode' => $resource['auth_mode'],
|
||||
'is_audit_required' => $resource['is_audit_required'],
|
||||
'status' => 1,
|
||||
'meta_json' => null,
|
||||
'updated_at' => $now,
|
||||
];
|
||||
|
||||
if ($resourceId === null) {
|
||||
$resourceId = DB::table('admin_api_resources')->insertGetId($payload + [
|
||||
'code' => $resource['code'],
|
||||
'created_at' => $now,
|
||||
]);
|
||||
} else {
|
||||
DB::table('admin_api_resources')
|
||||
->where('id', (int) $resourceId)
|
||||
->update($payload);
|
||||
}
|
||||
|
||||
DB::table('admin_api_resource_bindings')
|
||||
->where('api_resource_id', (int) $resourceId)
|
||||
->delete();
|
||||
|
||||
foreach ($resource['permission_codes'] as $permissionCode) {
|
||||
$menuActionId = $menuActionIds[$permissionCode] ?? null;
|
||||
if ($menuActionId === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
DB::table('admin_api_resource_bindings')->insert([
|
||||
'api_resource_id' => (int) $resourceId,
|
||||
'menu_action_id' => (int) $menuActionId,
|
||||
'created_at' => $now,
|
||||
'updated_at' => $now,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
$resourceId = DB::table('admin_api_resources')
|
||||
->where('code', self::RESOURCE_CODE)
|
||||
->value('id');
|
||||
|
||||
if ($resourceId === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
DB::table('admin_api_resource_bindings')->where('api_resource_id', (int) $resourceId)->delete();
|
||||
DB::table('admin_api_resources')->where('id', (int) $resourceId)->delete();
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
use App\Models\AdminRole;
|
||||
use App\Models\AgentNode;
|
||||
use App\Support\AgentDefaultRolePermissions;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
AgentDefaultRolePermissions::ensurePlatformAgentRole();
|
||||
|
||||
AgentNode::query()->each(static function (AgentNode $node): void {
|
||||
$ownerRole = AdminRole::query()
|
||||
->where('owner_agent_id', $node->id)
|
||||
->where('slug', 'agent_owner_'.$node->id)
|
||||
->first();
|
||||
|
||||
if ($ownerRole === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$ownerRole->syncLegacyPermissionSlugs(AgentDefaultRolePermissions::ownerSlugsForNode($node));
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
// 产品策略调整,回滚不恢复报表中心权限。
|
||||
}
|
||||
};
|
||||
145
database/seeders/AgentSettlementAdjustmentDemoSeeder.php
Normal file
145
database/seeders/AgentSettlementAdjustmentDemoSeeder.php
Normal file
@@ -0,0 +1,145 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use App\Services\AgentSettlement\AgentSettlementBillAdjustmentService;
|
||||
use Illuminate\Database\Seeder;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
/**
|
||||
* 为结算中心「调账 / 冲正」Tab 写入代理账单补差、冲正演示数据。
|
||||
*
|
||||
* ```bash
|
||||
* php artisan db:seed --class="Database\\Seeders\\AgentSettlementAdjustmentDemoSeeder"
|
||||
* ```
|
||||
*/
|
||||
final class AgentSettlementAdjustmentDemoSeeder extends Seeder
|
||||
{
|
||||
private const DEMO_REASON_PREFIX = '[demo]';
|
||||
|
||||
public function run(): void
|
||||
{
|
||||
if (app()->environment('production') && ! config('app.debug')) {
|
||||
$this->command?->warn('跳过:production 且 APP_DEBUG=false 时不写入演示调账数据。');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$adminUserId = (int) (DB::table('admin_users')->where('username', 'admin')->value('id') ?? 0);
|
||||
/** @var AgentSettlementBillAdjustmentService $adjustments */
|
||||
$adjustments = app(AgentSettlementBillAdjustmentService::class);
|
||||
|
||||
$scenarios = [
|
||||
[
|
||||
'original_bill_id' => 35,
|
||||
'amount' => 12000,
|
||||
'adjustment_type' => 'adjustment',
|
||||
'reason' => self::DEMO_REASON_PREFIX.' 代理 good 回水漏计,人工补差',
|
||||
'confirm' => false,
|
||||
],
|
||||
[
|
||||
'original_bill_id' => 34,
|
||||
'amount' => -5600,
|
||||
'adjustment_type' => 'reversal',
|
||||
'reason' => self::DEMO_REASON_PREFIX.' 代理 321321 占成分摊复核有误,冲正',
|
||||
'confirm' => true,
|
||||
],
|
||||
[
|
||||
'original_bill_id' => 31,
|
||||
'amount' => 800,
|
||||
'adjustment_type' => 'adjustment',
|
||||
'reason' => self::DEMO_REASON_PREFIX.' 代理 good 平台四舍五入补差',
|
||||
'confirm' => false,
|
||||
],
|
||||
];
|
||||
|
||||
DB::transaction(function () use ($scenarios, $adjustments, $adminUserId): void {
|
||||
foreach ($scenarios as $scenario) {
|
||||
$this->seedScenario($adjustments, $adminUserId, $scenario);
|
||||
}
|
||||
});
|
||||
|
||||
$this->command?->info('代理结算补差/冲正演示数据已写入(前缀 '.self::DEMO_REASON_PREFIX.')。');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array{original_bill_id:int,amount:int,adjustment_type:string,reason:string,confirm:bool} $scenario
|
||||
*/
|
||||
private function seedScenario(
|
||||
AgentSettlementBillAdjustmentService $adjustments,
|
||||
int $adminUserId,
|
||||
array $scenario,
|
||||
): void {
|
||||
$originalBillId = (int) $scenario['original_bill_id'];
|
||||
$reason = (string) $scenario['reason'];
|
||||
|
||||
if ($this->demoAdjustmentExists($originalBillId, $reason)) {
|
||||
$this->command?->line("跳过 bill #{$originalBillId}:已存在相同演示记录。");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$original = DB::table('settlement_bills')->where('id', $originalBillId)->first();
|
||||
if ($original === null) {
|
||||
$this->command?->warn("跳过 bill #{$originalBillId}:原账单不存在。");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ((string) $original->bill_type !== 'agent') {
|
||||
$this->command?->warn("跳过 bill #{$originalBillId}:非代理账单。");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($original->locked_at === null) {
|
||||
DB::table('settlement_bills')->where('id', $originalBillId)->update([
|
||||
'locked_at' => now(),
|
||||
'updated_at' => now(),
|
||||
]);
|
||||
}
|
||||
|
||||
if ((string) $original->status === 'pending_confirm') {
|
||||
DB::table('settlement_bills')->where('id', $originalBillId)->update([
|
||||
'status' => 'confirmed',
|
||||
'confirmed_at' => now(),
|
||||
'locked_at' => now(),
|
||||
'updated_at' => now(),
|
||||
]);
|
||||
}
|
||||
|
||||
$newBillId = $adjustments->createAdjustment(
|
||||
$originalBillId,
|
||||
(int) $scenario['amount'],
|
||||
(string) $scenario['adjustment_type'],
|
||||
$reason,
|
||||
$adminUserId,
|
||||
);
|
||||
|
||||
if ($scenario['confirm']) {
|
||||
$now = now();
|
||||
DB::table('settlement_bills')->where('id', $newBillId)->update([
|
||||
'status' => 'confirmed',
|
||||
'confirmed_at' => $now,
|
||||
'locked_at' => $now,
|
||||
'updated_at' => $now,
|
||||
]);
|
||||
}
|
||||
|
||||
$this->command?->line(sprintf(
|
||||
'已创建 %s bill #%d ← 原代理账单 #%d(%s)',
|
||||
(string) $scenario['adjustment_type'],
|
||||
$newBillId,
|
||||
$originalBillId,
|
||||
$reason,
|
||||
));
|
||||
}
|
||||
|
||||
private function demoAdjustmentExists(int $originalBillId, string $reason): bool
|
||||
{
|
||||
return DB::table('settlement_adjustments')
|
||||
->where('original_bill_id', $originalBillId)
|
||||
->where('reason', $reason)
|
||||
->exists();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user