feat: 员工自助注册+审核流程

- 新增 registration_packages 表:管理员配置注册套餐(名称/角色)
- AuthController 新增 register() 和 registrationPackages() 公开接口
- UserController 新增 approve() / reject() 审核接口
- StoreController 新增 publicList() 公开门店列表
- 前端 /register 注册页:选门店→选岗位套餐→填信息→提交
- 前端 system/registration-packages:套餐 CRUD
- 用户管理页:待审核状态展示 + 通过/拒绝快捷操作
- 登录页底部加「申请注册」跳转链接
- 路由白名单加 /register
This commit is contained in:
li
2026-03-14 17:39:50 +08:00
parent 4a68855a88
commit ee2b1757d0
105 changed files with 7859 additions and 2469 deletions
@@ -85,4 +85,62 @@ class AuthController extends Controller
'menus' => $user->getMenuTree(),
]);
}
/**
* 获取可用注册套餐列表(公开接口)
*/
public function registrationPackages(Request $request): JsonResponse
{
$storeId = $request->input('store_id');
if (!$storeId) {
return $this->error('请选择门店', 42200);
}
$packages = \App\Models\System\RegistrationPackage::withoutGlobalScope('store')
->where('store_id', $storeId)
->where('status', 1)
->orderBy('sort')
->get(['id', 'name', 'description']);
return $this->success($packages);
}
/**
* 自助注册(公开接口)
* 注册后 status=2(待审核),需管理员审批后才能登录
*/
public function register(Request $request): JsonResponse
{
$data = $request->validate([
'store_id' => 'required|exists:stores,id',
'registration_package_id' => 'required|exists:registration_packages,id',
'username' => 'required|string|max:50|unique:users,username',
'password' => 'required|string|min:6',
'name' => 'required|string|max:50',
'phone' => 'required|string|max:20',
]);
// 验证套餐属于该门店
$package = \App\Models\System\RegistrationPackage::withoutGlobalScope('store')
->where('id', $data['registration_package_id'])
->where('store_id', $data['store_id'])
->where('status', 1)
->firstOrFail();
$user = \App\Models\User::create([
'store_id' => $data['store_id'],
'username' => $data['username'],
'password' => $data['password'],
'name' => $data['name'],
'phone' => $data['phone'],
'status' => 2, // Pending
]);
// 绑定套餐角色
if (!empty($package->role_ids)) {
$user->roles()->sync($package->role_ids);
}
return $this->success(null, '注册成功,请等待管理员审核后登录');
}
}
@@ -0,0 +1,80 @@
<?php
namespace App\Http\Controllers\Admin\Inventory;
use App\Http\Controllers\Controller;
use App\Models\Inventory\InventoryCheck;
use App\Models\Inventory\Inventory;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
class InventoryCheckController extends Controller
{
public function index(Request $request): JsonResponse
{
$query = InventoryCheck::query()
->with(['warehouse', 'operator'])
->when($request->warehouse_id, fn($q, $v) => $q->where('warehouse_id', $v))
->when($request->has('status'), fn($q) => $q->where('status', $request->status));
return $this->paginate($query->latest()->paginate($request->input('per_page', 15)));
}
public function store(Request $request): JsonResponse
{
$v = $request->validate([
'check_no' => 'required|string|max:50|unique:inventory_checks',
'warehouse_id' => 'required|exists:warehouses,id',
'items' => 'nullable|array',
'remark' => 'nullable|string|max:500',
]);
$v['status'] = 0;
$v['operator_id'] = auth()->id();
return $this->success(InventoryCheck::create($v));
}
public function show(InventoryCheck $inventoryCheck): JsonResponse
{
return $this->success($inventoryCheck->load(['warehouse', 'operator']));
}
/**
* 完成盘点 可选择按实际数量修正库存
*/
public function complete(InventoryCheck $inventoryCheck, Request $request): JsonResponse
{
if ($inventoryCheck->status === 2) {
return $this->error('已完成', 40001);
}
$v = $request->validate([
'items' => 'nullable|array',
'finish_remark' => 'nullable|string|max:500',
]);
DB::transaction(function () use ($inventoryCheck, $v) {
$items = $v['items'] ?? $inventoryCheck->items ?? [];
// 如果有差异且需要调整库存
foreach ($items as $item) {
if (!isset($item['actual_qty'])) continue;
$diff = ($item['actual_qty'] ?? 0) - ($item['book_qty'] ?? 0);
if ($diff == 0) continue;
$inv = Inventory::firstOrCreate(
['warehouse_id' => $inventoryCheck->warehouse_id, 'material_id' => $item['material_id'], 'batch_no' => null],
['quantity' => 0]
);
$inv->increment('quantity', $diff);
}
$inventoryCheck->update([
'status' => 2,
'items' => $items,
'finish_remark' => $v['finish_remark'] ?? null,
]);
});
return $this->success($inventoryCheck->fresh(['warehouse']));
}
}
@@ -0,0 +1,50 @@
<?php
namespace App\Http\Controllers\Admin\Inventory;
use App\Http\Controllers\Controller;
use App\Models\Inventory\Inventory;
use App\Models\Inventory\Material;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
class StockController extends Controller
{
/**
* 库存台账 汇总 inventories 表,关联 material 信息
* GET /inventory/stock
*/
public function index(Request $request): JsonResponse
{
$query = Inventory::query()
->with(['warehouse', 'material'])
->when($request->warehouse_id, fn($q, $v) => $q->where('warehouse_id', $v))
->when($request->material_id, fn($q, $v) => $q->where('material_id', $v))
->when($request->low_stock, fn($q) => $q->whereRaw('quantity <= (SELECT min_stock FROM materials WHERE materials.id = inventories.material_id AND min_stock IS NOT NULL)'));
return $this->paginate($query->paginate($request->input('per_page', 20)));
}
/**
* 导出库存台账
* GET /inventory/stock/export
*/
public function export(Request $request): JsonResponse
{
$rows = Inventory::with(['warehouse', 'material'])
->when($request->warehouse_id, fn($q, $v) => $q->where('warehouse_id', $v))
->get()
->map(fn($inv) => [
'warehouse' => $inv->warehouse?->name,
'material_code' => $inv->material?->code,
'material_name' => $inv->material?->name,
'unit' => $inv->material?->unit,
'quantity' => $inv->quantity,
'batch_no' => $inv->batch_no,
'expire_date' => $inv->expire_date?->toDateString(),
'safety_stock' => $inv->material?->min_stock,
'updated_at' => $inv->updated_at?->toDateTimeString(),
]);
return $this->success(['rows' => $rows, 'total' => $rows->count()]);
}
}
@@ -0,0 +1,82 @@
<?php
namespace App\Http\Controllers\Admin\Inventory;
use App\Http\Controllers\Controller;
use App\Models\Inventory\TransferOrder;
use App\Models\Inventory\Inventory;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
class TransferOrderController extends Controller
{
public function index(Request $request): JsonResponse
{
$query = TransferOrder::query()
->with(['fromWarehouse', 'toWarehouse', 'operator'])
->when($request->transfer_no, fn($q, $v) => $q->where('transfer_no', 'like', "%{$v}%"))
->when($request->has('status'), fn($q) => $q->where('status', $request->status))
->when($request->from_warehouse_id, fn($q, $v) => $q->where('from_warehouse_id', $v));
return $this->paginate($query->latest()->paginate($request->input('per_page', 15)));
}
public function store(Request $request): JsonResponse
{
$v = $request->validate([
'transfer_no' => 'required|string|max:50|unique:transfer_orders',
'from_warehouse_id' => 'required|exists:warehouses,id',
'to_warehouse_id' => 'required|exists:warehouses,id|different:from_warehouse_id',
'items' => 'nullable|array',
'remark' => 'nullable|string|max:500',
]);
$v['status'] = 0;
$v['operator_id'] = auth()->id();
return $this->success(TransferOrder::create($v));
}
public function show(TransferOrder $transferOrder): JsonResponse
{
return $this->success($transferOrder->load(['fromWarehouse', 'toWarehouse', 'operator']));
}
public function confirm(TransferOrder $transferOrder): JsonResponse
{
if ($transferOrder->status !== 0) {
return $this->error('当前状态不可确认', 40001);
}
DB::transaction(function () use ($transferOrder) {
$transferOrder->update(['status' => 1]);
// 调拨:减源仓库、加目标仓库
foreach (($transferOrder->items ?? []) as $item) {
$materialId = $item['material_id'];
$qty = $item['quantity'] ?? 0;
// 扣减出库仓库
Inventory::where('warehouse_id', $transferOrder->from_warehouse_id)
->where('material_id', $materialId)
->decrement('quantity', $qty);
// 增加入库仓库
$inv = Inventory::firstOrCreate(
['warehouse_id' => $transferOrder->to_warehouse_id, 'material_id' => $materialId, 'batch_no' => null],
['quantity' => 0]
);
$inv->increment('quantity', $qty);
}
});
return $this->success($transferOrder->fresh(['fromWarehouse', 'toWarehouse']));
}
public function cancel(TransferOrder $transferOrder): JsonResponse
{
if ($transferOrder->status !== 0) {
return $this->error('只有待确认状态可取消', 40001);
}
$transferOrder->update(['status' => 2]);
return $this->success($transferOrder);
}
}
@@ -4,8 +4,11 @@ namespace App\Http\Controllers\Admin\Office;
use App\Http\Controllers\Controller;
use App\Models\Office\Approval;
use App\Models\Office\ApprovalNode;
use App\Models\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
class ApprovalController extends Controller
{
@@ -21,16 +24,59 @@ class ApprovalController extends Controller
public function store(Request $request): JsonResponse
{
$validated = $request->validate([
'title' => 'required|string|max:255',
'template_id' => 'nullable|exists:approval_templates,id',
'form_data' => 'nullable|array',
'title' => 'required|string|max:255',
'template_id' => 'nullable|exists:approval_templates,id',
'form_data' => 'nullable|array',
'related_type' => 'nullable|string|max:50',
'related_id' => 'nullable|integer',
'related_id' => 'nullable|integer',
]);
$validated['applicant_id'] = auth()->id();
$validated['status'] = 0;
return $this->success(Approval::create($validated));
$validated['applicant_id'] = auth()->id();
$validated['status'] = 0;
$validated['current_node'] = 1;
$approval = null;
DB::transaction(function () use ($validated, &$approval) {
$approval = Approval::create($validated);
// 根据模板 flow_nodes 自动创建多级审批节点
if ($approval->template_id && $approval->template) {
$flowNodes = $approval->template->flow_nodes ?? [];
foreach ($flowNodes as $node) {
$level = $node['level'] ?? 1;
$role = $node['role'] ?? null;
$label = $node['label'] ?? "{$level}级审批";
// 按 role 找同门店下首个匹配用户
$approverId = null;
if ($role) {
$approverId = User::whereHas('roles', fn($q) => $q->where('slug', $role))
->where('store_id', $approval->store_id)
->value('id');
}
ApprovalNode::create([
'approval_id' => $approval->id,
'node_order' => $level,
'node_label' => $label,
'role' => $role,
'approver_id' => $approverId,
'action' => 0,
]);
}
} else {
// 无模板:创建单一审批节点
ApprovalNode::create([
'approval_id' => $approval->id,
'node_order' => 1,
'node_label' => '审批',
'action' => 0,
]);
}
});
return $this->success($approval->load(['template', 'applicant', 'nodes']));
}
public function show(Approval $approval): JsonResponse
@@ -44,7 +90,7 @@ class ApprovalController extends Controller
return $this->error('该审批已完结,无法修改', 40001);
}
$validated = $request->validate([
'title' => 'sometimes|string|max:255',
'title' => 'sometimes|string|max:255',
'form_data' => 'nullable|array',
]);
$approval->update($validated);
@@ -62,43 +108,28 @@ class ApprovalController extends Controller
return $this->success(null);
}
public function approve(Approval $approval): JsonResponse
public function approve(Request $request, Approval $approval): JsonResponse
{
if ($approval->status >= 2) {
return $this->error('该审批已完结', 40001);
}
$currentUser = auth()->user();
$userRoles = $currentUser->roles()->pluck('slug')->toArray();
// 当前节点匹配:approver_id 精确匹配 或 role 匹配当前用户角色
$node = $approval->nodes()
->where('approver_id', auth()->id())
->where('action', 0)
->where('node_order', $approval->current_node)
->where('action', 0)
->where(function ($q) use ($currentUser, $userRoles) {
$q->where('approver_id', $currentUser->id)
->orWhereIn('role', $userRoles)
->orWhereNull('approver_id'); // 无指定审批人时任何人可审
})
->first();
if (!$node) {
return $this->error('无待处理的审批节点', 40001);
}
$node->update(['action' => 1, 'acted_at' => now()]);
$pendingNodes = $approval->nodes()->where('action', 0)->exists();
if (!$pendingNodes) {
$approval->update(['status' => 2]);
} else {
$approval->update([
'status' => 1,
'current_node' => $approval->current_node + 1,
]);
}
return $this->success($approval->load('nodes'));
}
public function reject(Request $request, Approval $approval): JsonResponse
{
$node = $approval->nodes()
->where('approver_id', auth()->id())
->where('action', 0)
->where('node_order', $approval->current_node)
->first();
if (!$node) {
return $this->error('无待处理的审批节点', 40001);
return $this->error('无待处理的审批节点或您无权审批此节点', 40001);
}
$validated = $request->validate([
@@ -106,13 +137,67 @@ class ApprovalController extends Controller
]);
$node->update([
'action' => 2,
'comment' => $validated['comment'] ?? null,
'acted_at' => now(),
'action' => 1,
'approver_id' => $currentUser->id,
'comment' => $validated['comment'] ?? null,
'acted_at' => now(),
]);
$approval->update(['status' => 3]);
return $this->success($approval->load('nodes'));
// 检查是否还有下一节点
$hasNext = $approval->nodes()
->where('node_order', '>', $approval->current_node)
->where('action', 0)
->exists();
if ($hasNext) {
$approval->update([
'status' => 1, // 审批中
'current_node' => $approval->current_node + 1,
]);
} else {
$approval->update(['status' => 2]); // 已通过
}
return $this->success($approval->load(['nodes.approver']));
}
public function reject(Request $request, Approval $approval): JsonResponse
{
if ($approval->status >= 2) {
return $this->error('该审批已完结', 40001);
}
$currentUser = auth()->user();
$userRoles = $currentUser->roles()->pluck('slug')->toArray();
$node = $approval->nodes()
->where('node_order', $approval->current_node)
->where('action', 0)
->where(function ($q) use ($currentUser, $userRoles) {
$q->where('approver_id', $currentUser->id)
->orWhereIn('role', $userRoles)
->orWhereNull('approver_id');
})
->first();
if (!$node) {
return $this->error('无待处理的审批节点或您无权操作', 40001);
}
$validated = $request->validate([
'comment' => 'nullable|string|max:255',
]);
$node->update([
'action' => 2,
'approver_id' => $currentUser->id,
'comment' => $validated['comment'] ?? null,
'acted_at' => now(),
]);
$approval->update(['status' => 3]); // 已驳回
return $this->success($approval->load(['nodes.approver']));
}
public function withdraw(Approval $approval): JsonResponse
@@ -120,6 +205,9 @@ class ApprovalController extends Controller
if ($approval->status > 1) {
return $this->error('该审批已完结,无法撤回', 40001);
}
if ($approval->applicant_id !== auth()->id()) {
return $this->error('只能撤回自己发起的审批', 40001);
}
$approval->update(['status' => 4]);
return $this->success($approval);
@@ -0,0 +1,66 @@
<?php
namespace App\Http\Controllers\Admin\System;
use App\Http\Controllers\Controller;
use App\Models\System\RegistrationPackage;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class RegistrationPackageController extends Controller
{
public function index(Request $request): JsonResponse
{
$packages = RegistrationPackage::when($request->keyword, fn($q) =>
$q->where('name', 'like', "%{$request->keyword}%"))
->when($request->has('status'), fn($q) =>
$q->where('status', $request->status))
->orderBy('sort')->orderByDesc('id')
->paginate($request->input('page_size', 20));
return $this->paginate($packages);
}
public function store(Request $request): JsonResponse
{
$data = $request->validate([
'name' => 'required|string|max:50',
'description' => 'nullable|string|max:255',
'role_ids' => 'array',
'role_ids.*' => 'exists:roles,id',
'status' => 'in:0,1',
'sort' => 'integer|min:0',
]);
$data['store_id'] = auth()->user()->store_id;
$package = RegistrationPackage::create($data);
return $this->success($package, '创建成功');
}
public function show(RegistrationPackage $registrationPackage): JsonResponse
{
return $this->success($registrationPackage);
}
public function update(Request $request, RegistrationPackage $registrationPackage): JsonResponse
{
$data = $request->validate([
'name' => 'string|max:50',
'description' => 'nullable|string|max:255',
'role_ids' => 'array',
'role_ids.*' => 'exists:roles,id',
'status' => 'in:0,1',
'sort' => 'integer|min:0',
]);
$registrationPackage->update($data);
return $this->success($registrationPackage, '更新成功');
}
public function destroy(RegistrationPackage $registrationPackage): JsonResponse
{
$registrationPackage->delete();
return $this->success(null, '删除成功');
}
}
@@ -83,4 +83,16 @@ class StoreController extends Controller
$store->delete();
return $this->success(null, '删除成功');
}
/**
* 公开门店列表(注册页用,仅返回启用门店的 id/name
*/
public function publicList(): JsonResponse
{
$stores = Store::withoutGlobalScope('store')
->where('status', 1)
->orderByDesc('id')
->get(['id', 'name']);
return $this->success($stores);
}
}
@@ -112,6 +112,30 @@ class UserController extends Controller
return $this->success(null, '密码重置成功');
}
/**
* 审核通过 激活待审核用户
*/
public function approve(User $user): JsonResponse
{
if ($user->status->value !== 2) {
return $this->error('该用户不在待审核状态');
}
$user->update(['status' => 1]);
return $this->success(null, '已审核通过');
}
/**
* 审核拒绝 禁用该用户
*/
public function reject(User $user): JsonResponse
{
if ($user->status->value !== 2) {
return $this->error('该用户不在待审核状态');
}
$user->update(['status' => 0]);
return $this->success(null, '已拒绝注册');
}
public function destroy(User $user): JsonResponse
{
if ($user->is_super) {
@@ -0,0 +1,16 @@
<?php
namespace App\Models\Inventory;
use App\Models\User;
use App\Traits\BelongsToStore;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class InventoryCheck extends Model
{
use BelongsToStore;
protected $table = 'inventory_checks';
protected $fillable = ['store_id','check_no','warehouse_id','items','status','finish_remark','operator_id'];
protected function casts(): array { return ['items'=>'array','status'=>'integer']; }
public function warehouse(): BelongsTo { return $this->belongsTo(Warehouse::class); }
public function operator(): BelongsTo { return $this->belongsTo(User::class, 'operator_id'); }
}
@@ -0,0 +1,17 @@
<?php
namespace App\Models\Inventory;
use App\Models\User;
use App\Traits\BelongsToStore;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class TransferOrder extends Model
{
use BelongsToStore;
protected $table = 'transfer_orders';
protected $fillable = ['store_id','transfer_no','from_warehouse_id','to_warehouse_id','items','status','operator_id','remark'];
protected function casts(): array { return ['items'=>'array','status'=>'integer']; }
public function fromWarehouse(): BelongsTo { return $this->belongsTo(Warehouse::class, 'from_warehouse_id'); }
public function toWarehouse(): BelongsTo { return $this->belongsTo(Warehouse::class, 'to_warehouse_id'); }
public function operator(): BelongsTo { return $this->belongsTo(User::class, 'operator_id'); }
}
+2 -1
View File
@@ -11,7 +11,8 @@ class ApprovalNode extends Model
const UPDATED_AT = null;
protected $fillable = [
'approval_id', 'node_order', 'approver_id', 'action', 'comment', 'acted_at',
'approval_id', 'node_order', 'node_label', 'role',
'approver_id', 'action', 'comment', 'acted_at',
];
protected function casts(): array
@@ -0,0 +1,26 @@
<?php
namespace App\Models\System;
use App\Traits\BelongsToStore;
use Illuminate\Database\Eloquent\Model;
class RegistrationPackage extends Model
{
use BelongsToStore;
protected $table = 'registration_packages';
protected $fillable = [
'store_id', 'name', 'description', 'role_ids', 'status', 'sort',
];
protected function casts(): array
{
return [
'role_ids' => 'array',
'status' => 'integer',
'sort' => 'integer',
];
}
}
+1 -1
View File
@@ -19,7 +19,7 @@ return Application::configure(basePath: dirname(__DIR__))
'oplog' => \App\Http\Middleware\OperationLog::class,
]);
$middleware->statefulApi();
// Removed statefulApi() — app uses Bearer token auth, not cookie SPA auth
})
->withExceptions(function (Exceptions $exceptions): void {
//
@@ -0,0 +1,25 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::table('approval_nodes', function (Blueprint $table) {
$table->string('node_label', 100)->nullable()->after('node_order')->comment('节点名称');
$table->string('role', 50)->nullable()->after('node_label')->comment('节点角色标识');
// approver_id 改为可空(无模板时无法预指定审批人)
$table->unsignedBigInteger('approver_id')->nullable()->change();
});
}
public function down(): void
{
Schema::table('approval_nodes', function (Blueprint $table) {
$table->dropColumn(['node_label', 'role']);
});
}
};
@@ -0,0 +1,47 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('transfer_orders', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('store_id')->index();
$table->string('transfer_no', 50)->unique();
$table->unsignedBigInteger('from_warehouse_id');
$table->unsignedBigInteger('to_warehouse_id');
$table->json('items')->nullable();
$table->tinyInteger('status')->default(0)->comment('0待确认1已确认2已取消');
$table->unsignedBigInteger('operator_id')->nullable();
$table->string('remark', 500)->nullable();
$table->timestamps();
$table->foreign('from_warehouse_id')->references('id')->on('warehouses');
$table->foreign('to_warehouse_id')->references('id')->on('warehouses');
});
Schema::create('inventory_checks', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('store_id')->index();
$table->string('check_no', 50)->unique();
$table->unsignedBigInteger('warehouse_id');
$table->json('items')->nullable()->comment('[{material_id,book_qty,actual_qty,diff_qty,remark}]');
$table->tinyInteger('status')->default(0)->comment('0草稿1盘点中2已完成');
$table->string('finish_remark', 500)->nullable();
$table->unsignedBigInteger('operator_id')->nullable();
$table->timestamps();
$table->foreign('warehouse_id')->references('id')->on('warehouses');
});
}
public function down(): void
{
Schema::dropIfExists('inventory_checks');
Schema::dropIfExists('transfer_orders');
}
};
@@ -0,0 +1,29 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('registration_packages', function (Blueprint $table) {
$table->id();
$table->foreignId('store_id')->constrained()->comment('所属门店');
$table->string('name', 50)->comment('套餐名称,如:销售、店长');
$table->string('description', 255)->nullable()->comment('套餐说明');
$table->json('role_ids')->nullable()->comment('关联角色ID数组');
$table->tinyInteger('status')->default(1)->comment('1启用 0停用');
$table->unsignedSmallInteger('sort')->default(0)->comment('排序');
$table->timestamps();
$table->index('store_id');
});
}
public function down(): void
{
Schema::dropIfExists('registration_packages');
}
};
+30 -1
View File
@@ -44,6 +44,9 @@ use App\Http\Controllers\Admin\Inventory\MaterialController;
use App\Http\Controllers\Admin\Inventory\PurchaseOrderController;
use App\Http\Controllers\Admin\Inventory\StockMovementController;
use App\Http\Controllers\Admin\Inventory\InventoryController;
use App\Http\Controllers\Admin\Inventory\StockController;
use App\Http\Controllers\Admin\Inventory\TransferOrderController;
use App\Http\Controllers\Admin\Inventory\InventoryCheckController;
use App\Http\Controllers\Admin\Finance\FinanceCategoryController;
use App\Http\Controllers\Admin\Finance\FinanceRecordController;
use App\Http\Controllers\Admin\Finance\CustomerAccountController;
@@ -64,6 +67,7 @@ use App\Http\Controllers\Admin\Report\ReportController;
use App\Http\Controllers\Admin\Report\ReportExportController;
use App\Http\Controllers\Admin\Kb\KbCategoryController;
use App\Http\Controllers\Admin\Kb\KbArticleController;
use App\Http\Controllers\Admin\System\RegistrationPackageController;
/*
|--------------------------------------------------------------------------
@@ -74,6 +78,9 @@ use App\Http\Controllers\Admin\Kb\KbArticleController;
// ===== 公开路由(无需认证) =====
Route::post('/auth/login', [AuthController::class, 'login']);
Route::post('/auth/register', [AuthController::class, 'register']);
Route::get('/auth/register/packages', [AuthController::class, 'registrationPackages']);
Route::get('/auth/stores', [\App\Http\Controllers\Admin\System\StoreController::class, 'publicList']);
// ===== 需要认证的路由 =====
Route::middleware(['auth:sanctum', 'store', 'oplog'])->group(function () {
@@ -98,6 +105,11 @@ Route::middleware(['auth:sanctum', 'store', 'oplog'])->group(function () {
Route::apiResource('users', UserController::class);
Route::put('users/{user}/status', [UserController::class, 'updateStatus']);
Route::put('users/{user}/password', [UserController::class, 'resetPassword']);
Route::put('users/{user}/approve', [UserController::class, 'approve']);
Route::put('users/{user}/reject', [UserController::class, 'reject']);
// 注册套餐管理
Route::apiResource('registration-packages', RegistrationPackageController::class);
// 角色管理
Route::apiResource('roles', RoleController::class);
@@ -264,8 +276,25 @@ Route::middleware(['auth:sanctum', 'store', 'oplog'])->group(function () {
Route::get('stock-movements/{stockMovement}', [StockMovementController::class, 'show']);
Route::put('stock-movements/{stockMovement}/confirm', [StockMovementController::class, 'confirm']);
// 库存查询
// 库存查询(按物料/仓库汇总)
Route::get('inventories', [InventoryController::class, 'index']);
// 库存台账
Route::get('stock', [StockController::class, 'index']);
Route::get('stock/export', [StockController::class, 'export']);
// 调拨管理
Route::get('transfers', [TransferOrderController::class, 'index']);
Route::post('transfers', [TransferOrderController::class, 'store']);
Route::get('transfers/{transferOrder}', [TransferOrderController::class, 'show']);
Route::put('transfers/{transferOrder}/confirm', [TransferOrderController::class, 'confirm']);
Route::put('transfers/{transferOrder}/cancel', [TransferOrderController::class, 'cancel']);
// 盘点管理
Route::get('checks', [InventoryCheckController::class, 'index']);
Route::post('checks', [InventoryCheckController::class, 'store']);
Route::get('checks/{inventoryCheck}', [InventoryCheckController::class, 'show']);
Route::put('checks/{inventoryCheck}/complete', [InventoryCheckController::class, 'complete']);
});
// --- 财务管理模块 ---