本文目录导读:

我来为你详细介绍一下PHP供应商管理的实现方案,这是一个典型的企业级应用场景,我会从架构设计到具体实现逐步展开。
整体架构设计
数据库设计
-- 供应商主表
CREATE TABLE suppliers (
id INT PRIMARY KEY AUTO_INCREMENT,
supplier_code VARCHAR(50) UNIQUE NOT NULL COMMENT '供应商编码',
supplier_name VARCHAR(200) NOT NULL COMMENT '供应商名称',
supplier_type ENUM('material', 'service', 'equipment') DEFAULT 'material',
contact_person VARCHAR(100),
contact_phone VARCHAR(20),
contact_email VARCHAR(100),
address VARCHAR(500),
tax_number VARCHAR(100),
bank_name VARCHAR(200),
bank_account VARCHAR(100),
payment_terms VARCHAR(200),
credit_limit DECIMAL(15,2) DEFAULT 0,
status TINYINT DEFAULT 1 COMMENT '1-启用 0-停用',
rating TINYINT DEFAULT 3 COMMENT '1-5星评价',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_supplier_name (supplier_name),
INDEX idx_supplier_type (supplier_type),
INDEX idx_status (status)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 供应商联系人表
CREATE TABLE supplier_contacts (
id INT PRIMARY KEY AUTO_INCREMENT,
supplier_id INT NOT NULL,
name VARCHAR(100) NOT NULL,
position VARCHAR(100),
phone VARCHAR(20),
email VARCHAR(100),
is_primary BOOLEAN DEFAULT FALSE,
FOREIGN KEY (supplier_id) REFERENCES suppliers(id) ON DELETE CASCADE
);
-- 供应商资质表
CREATE TABLE supplier_qualifications (
id INT PRIMARY KEY AUTO_INCREMENT,
supplier_id INT NOT NULL,
cert_type VARCHAR(50) COMMENT '证书类型',
cert_number VARCHAR(100),
issue_date DATE,
expiry_date DATE,
cert_file_path VARCHAR(500),
status TINYINT DEFAULT 1,
FOREIGN KEY (supplier_id) REFERENCES suppliers(id) ON DELETE CASCADE
);
项目目录结构
supplier-management/
├── app/
│ ├── Models/
│ │ ├── Supplier.php
│ │ └── SupplierQualification.php
│ ├── Controllers/
│ │ ├── SupplierController.php
│ │ └── Api/
│ │ └── SupplierApiController.php
│ ├── Services/
│ │ └── SupplierService.php
│ ├── Repositories/
│ │ └── SupplierRepository.php
│ └── Middleware/
│ └── SupplierAuthMiddleware.php
├── routes/
│ ├── web.php
│ └── api.php
├── resources/
│ ├── views/
│ │ └── suppliers/
│ └── assets/
└── public/
└── uploads/
核心代码实现
供应商模型
<?php
// app/Models/Supplier.php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class Supplier extends Model
{
use SoftDeletes;
protected $table = 'suppliers';
protected $primaryKey = 'id';
protected $fillable = [
'supplier_code',
'supplier_name',
'supplier_type',
'contact_person',
'contact_phone',
'contact_email',
'address',
'tax_number',
'bank_name',
'bank_account',
'payment_terms',
'credit_limit',
'status',
'rating'
];
protected $casts = [
'credit_limit' => 'decimal:2',
'status' => 'integer',
'rating' => 'integer'
];
// 关联联系人
public function contacts()
{
return $this->hasMany(SupplierContact::class);
}
// 关联资质
public function qualifications()
{
return $this->hasMany(SupplierQualification::class);
}
// 获取主要联系人
public function getPrimaryContactAttribute()
{
return $this->contacts()->where('is_primary', true)->first();
}
// 活动供应商
public function scopeActive($query)
{
return $query->where('status', 1);
}
// 生成供应商编码
public static function generateSupplierCode()
{
$prefix = 'SUP';
$year = date('Y');
$lastRecord = self::whereYear('created_at', $year)
->orderBy('id', 'DESC')
->first();
if ($lastRecord) {
$sequence = intval(substr($lastRecord->supplier_code, -8)) + 1;
} else {
$sequence = 1;
}
return sprintf($prefix . $year . '%08d', $sequence);
}
}
供应商服务类
<?php
// app/Services/SupplierService.php
namespace App\Services;
use App\Models\Supplier;
use App\Repositories\SupplierRepository;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Facades\DB;
class SupplierService
{
protected $supplierRepository;
public function __construct(SupplierRepository $supplierRepository)
{
$this->supplierRepository = $supplierRepository;
}
/**
* 创建供应商
* @param array $data
* @return Supplier
*/
public function createSupplier(array $data)
{
$validator = Validator::make($data, [
'supplier_name' => 'required|max:200',
'supplier_type' => 'required|in:material,service,equipment',
'contact_person' => 'required|max:100',
'contact_phone' => 'required|max:20',
'contact_email' => 'required|email|max:100',
'address' => 'required|max:500',
'tax_number' => 'required|max:100',
'bank_name' => 'required|max:200',
'bank_account' => 'required|max:100',
'payment_terms' => 'required|max:200',
'credit_limit' => 'numeric|min:0',
]);
if ($validator->fails()) {
throw new \InvalidArgumentException($validator->errors()->first());
}
return DB::transaction(function () use ($data) {
// 生成供应商编码
$data['supplier_code'] = Supplier::generateSupplierCode();
// 创建供应商
$supplier = $this->supplierRepository->create($data);
// 如果有联系人信息
if (isset($data['contacts']) && is_array($data['contacts'])) {
foreach ($data['contacts'] as $contact) {
$supplier->contacts()->create($contact);
}
}
// 记录日志
activity('supplier')
->performedOn($supplier)
->log('创建供应商: ' . $data['supplier_name']);
return $supplier;
});
}
/**
* 更新供应商
* @param int $id
* @param array $data
* @return Supplier
*/
public function updateSupplier(int $id, array $data)
{
$supplier = $this->supplierRepository->find($id);
if (!$supplier) {
throw new \Exception('供应商不存在');
}
return DB::transaction(function () use ($supplier, $data) {
// 更新基本资料
$this->supplierRepository->update($supplier->id, $data);
// 更新联系人(可选)
if (isset($data['contacts'])) {
$supplier->contacts()->delete();
foreach ($data['contacts'] as $contact) {
$supplier->contacts()->create($contact);
}
}
return $supplier->fresh();
});
}
/**
* 获取供应商列表
* @param array $filters
* @return LengthAwarePaginator
*/
public function getSuppliers(array $filters = [])
{
return $this->supplierRepository
->search($filters)
->paginate($filters['per_page'] ?? 15);
}
/**
* 审批资质
* @param int $qualificationId
* @param string $status
* @param string $remark
*/
public function approveQualification($qualificationId, $status, $remark = '')
{
$qualification = SupplierQualification::findOrFail($qualificationId);
return DB::transaction(function () use ($qualification, $status, $remark) {
$qualification->update([
'status' => $status,
'approve_remark' => $remark,
'approved_at' => now(),
'approved_by' => auth()->id()
]);
// 记录审批日志
activity('qualification')
->performedOn($qualification)
->withProperties(['status' => $status])
->log('资质审批: ' . $qualification->cert_number);
return $qualification;
});
}
}
控制器实现
<?php
// app/Controllers/SupplierController.php
namespace App\Controllers;
use App\Services\SupplierService;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
class SupplierController extends Controller
{
protected $supplierService;
public function __construct(SupplierService $supplierService)
{
$this->supplierService = $supplierService;
$this->middleware('auth');
}
/**
* 供应商列表页
*/
public function index(Request $request)
{
$suppliers = $this->supplierService->getSuppliers($request->all());
return view('suppliers.index', compact('suppliers'));
}
/**
* 创建供应商表单
*/
public function create()
{
return view('suppliers.create');
}
/**
* 保存新供应商
*/
public function store(Request $request)
{
try {
$supplier = $this->supplierService->createSupplier($request->all());
return redirect()
->route('suppliers.show', $supplier->id)
->with('success', '供应商创建成功');
} catch (\InvalidArgumentException $e) {
return back()
->withErrors(['message' => $e->getMessage()])
->withInput();
} catch (\Exception $e) {
return back()
->withErrors(['message' => '创建失败: ' . $e->getMessage()])
->withInput();
}
}
/**
* 供应商详情
*/
public function show($id)
{
$supplier = $this->supplierService->getSupplierWithDetails($id);
return view('suppliers.show', compact('supplier'));
}
/**
* 编辑供应商
*/
public function edit($id)
{
$supplier = $this->supplierService->getSupplierWithDetails($id);
return view('suppliers.edit', compact('supplier'));
}
/**
* 更新供应商
*/
public function update(Request $request, $id)
{
try {
$supplier = $this->supplierService->updateSupplier($id, $request->all());
return redirect()
->route('suppliers.show', $supplier->id)
->with('success', '供应商信息更新成功');
} catch (\Exception $e) {
return back()
->withErrors(['message' => '更新失败: ' . $e->getMessage()])
->withInput();
}
}
/**
* 删除供应商(软删除)
*/
public function destroy($id)
{
try {
$this->supplierService->deleteSupplier($id);
if (request()->wantsJson()) {
return response()->json(['message' => '供应商已删除'], 200);
}
return redirect()
->route('suppliers.index')
->with('success', '供应商已删除');
} catch (\Exception $e) {
return response()->json(['message' => '删除失败'], 500);
}
}
/**
* 供应商资质审核页
*/
public function qualificationApproval($supplierId)
{
$supplier = $this->supplierService->getSupplierWithDetails($supplierId);
return view('suppliers.qualifications', compact('supplier'));
}
/**
* 导入供应商数据
*/
public function import(Request $request)
{
$request->validate([
'file' => 'required|file|mimes:xlsx,xls,csv'
]);
try {
$result = $this->supplierService->importSuppliers($request->file('file'));
return redirect()
->route('suppliers.index')
->with('success', "成功导入 {$result['success']} 条记录");
} catch (\Exception $e) {
return back()->withErrors(['message' => '导入失败: ' . $e->getMessage()]);
}
}
}
供应商视图界面
<!-- resources/views/suppliers/index.blade.php -->
@extends('layouts.app')
@section('content')
<div class="container">
<div class="row">
<div class="col-md-12">
<div class="card">
<div class="card-header">
<h4>供应商管理</h4>
<div class="card-tools">
<a href="{{ route('suppliers.create') }}" class="btn btn-primary">
新增供应商
</a>
<button class="btn btn-secondary" onclick="importSuppliers()">
批量导入
</button>
</div>
</div>
<div class="card-body">
<!-- 搜索过滤 -->
<form method="GET" action="{{ route('suppliers.index') }}" class="mb-4">
<div class="row">
<div class="col-md-4">
<input type="text" name="keyword"
value="{{ request('keyword') }}"
class="form-control"
placeholder="搜索供应商名称/编码">
</div>
<div class="col-md-3">
<select name="type" class="form-control">
<option value="">全部类型</option>
<option value="material" {{ request('type') == 'material' ? 'selected' : '' }}>
物料供应商
</option>
<option value="service" {{ request('type') == 'service' ? 'selected' : '' }}>
服务供应商
</option>
<option value="equipment" {{ request('type') == 'equipment' ? 'selected' : '' }}>
设备供应商
</option>
</select>
</div>
<div class="col-md-2">
<select name="status" class="form-control">
<option value="">全部状态</option>
<option value="1" {{ request('status') === '1' ? 'selected' : '' }}>
启用
</option>
<option value="0" {{ request('status') === '0' ? 'selected' : '' }}>
停用
</option>
</select>
</div>
<div class="col-md-3">
<button type="submit" class="btn btn-primary">搜索</button>
<a href="{{ route('suppliers.index') }}" class="btn btn-default">重置</a>
</div>
</div>
</form>
<!-- 数据表格 -->
<table class="table table-striped table-bordered">
<thead>
<tr>
<th>编码</th>
<th>名称</th>
<th>类型</th>
<th>联系人</th>
<th>联系电话</th>
<th>状态</th>
<th>评分</th>
<th>操作</th>
</tr>
</thead>
<tbody>
@forelse($suppliers as $supplier)
<tr>
<td>{{ $supplier->supplier_code }}</td>
<td>{{ $supplier->supplier_name }}</td>
<td>
<span class="badge badge-info">
{{ $supplier->supplier_type }}
</span>
</td>
<td>{{ $supplier->contact_person }}</td>
<td>{{ $supplier->contact_phone }}</td>
<td>
@if($supplier->status)
<span class="badge badge-success">启用</span>
@else
<span class="badge badge-danger">停用</span>
@endif
</td>
<td>
@for($i = 1; $i <= 5; $i++)
@if($i <= $supplier->rating)
<span class="text-warning">★</span>
@else
<span class="text-muted">☆</span>
@endif
@endfor
</td>
<td>
<a href="{{ route('suppliers.show', $supplier->id) }}"
class="btn btn-sm btn-info">查看</a>
<a href="{{ route('suppliers.edit', $supplier->id) }}"
class="btn btn-sm btn-primary">编辑</a>
<form action="{{ route('suppliers.destroy', $supplier->id) }}"
method="POST"
style="display:inline">
@csrf
@method('DELETE')
<button type="submit" class="btn btn-sm btn-danger"
onclick="return confirm('确定删除该供应商吗?')">
删除
</button>
</form>
</td>
</tr>
@empty
<tr>
<td colspan="8" class="text-center">暂无供应商数据</td>
</tr>
@endforelse
</tbody>
</table>
<!-- 分页 -->
<div class="d-flex justify-content-end">
{{ $suppliers->links() }}
</div>
</div>
</div>
</div>
</div>
</div>
<script>
function importSuppliers() {
// 使用 Bootstrap Modal 实现文件上传
$('#importModal').modal('show');
}
</script>
@endsection
API接口设计
RESTful API路由
// routes/api.php
Route::group(['prefix' => 'v1/suppliers'], function () {
// 公开API
Route::get('active', 'Api\SupplierApiController@getActiveSuppliers');
// 需要认证的API
Route::group(['middleware' => ['auth:api']], function () {
// 供应商CRUD
Route::get('/', 'Api\SupplierApiController@index');
Route::post('/', 'Api\SupplierApiController@store');
Route::get('{id}', 'Api\SupplierApiController@show');
Route::put('{id}', 'Api\SupplierApiController@update');
Route::delete('{id}', 'Api\SupplierApiController@destroy');
// 供应商操作
Route::post('{id}/contacts', 'Api\SupplierApiController@addContact');
Route::put('{id}/status', 'Api\SupplierApiController@toggleStatus');
Route::get('{id}/transactions', 'Api\SupplierApiController@getTransactions');
// 批量导入导出
Route::post('import', 'Api\SupplierApiController@import');
Route::get('export', 'Api\SupplierApiController@export');
});
});
API控制器示例
<?php
// app/Controllers/Api/SupplierApiController.php
namespace App\Controllers\Api;
use App\Services\SupplierService;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\Http\Resources\SupplierResource;
class SupplierApiController extends Controller
{
protected $supplierService;
public function __construct(SupplierService $supplierService)
{
$this->supplierService = $supplierService;
}
/**
* 供应商列表API
* @param Request $request
* @return JsonResponse
*/
public function index(Request $request)
{
try {
$suppliers = $this->supplierService->getSuppliers($request->all());
return response()->json([
'success' => true,
'data' => SupplierResource::collection($suppliers),
'meta' => [
'current_page' => $suppliers->currentPage(),
'per_page' => $suppliers->perPage(),
'total' => $suppliers->total()
]
], 200);
} catch (\Exception $e) {
return response()->json([
'success' => false,
'message' => '获取供应商列表失败: ' . $e->getMessage()
], 500);
}
}
/**
* 新增供应商API
* @param Request $request
* @return JsonResponse
*/
public function store(Request $request)
{
try {
$supplier = $this->supplierService->createSupplier($request->all());
return response()->json([
'success' => true,
'message' => '供应商创建成功',
'data' => new SupplierResource($supplier)
], 201);
} catch (\InvalidArgumentException $e) {
return response()->json([
'success' => false,
'message' => $e->getMessage()
], 422);
} catch (\Exception $e) {
return response()->json([
'success' => false,
'message' => '创建失败: ' . $e->getMessage()
], 500);
}
}
/**
* 获取供应商详情API
* @param int $id
* @return JsonResponse
*/
public function show($id)
{
try {
$supplier = $this->supplierService->getSupplierWithDetails($id);
return response()->json([
'success' => true,
'data' => new SupplierResource($supplier)
], 200);
} catch (\Exception $e) {
return response()->json([
'success' => false,
'message' => '获取供应商信息失败: ' . $e->getMessage()
], 404);
}
}
}
安全与性能优化
安全措施
<?php
// app/Middleware/SupplierAuthMiddleware.php
namespace App\Middleware;
use Closure;
use Illuminate\Http\Request;
class SupplierAuthMiddleware
{
public function handle(Request $request, Closure $next)
{
// IP限制
$allowedIp = config('supplier.allowed_ips');
if ($allowedIp && !in_array($request->ip(), $allowedIp)) {
return response()->json(['error' => 'IP不能访问'], 403);
}
// 权限检查
if (!$request->user()->hasPermission('manage_suppliers')) {
return response()->json(['error' => '权限不足'], 403);
}
// 记录访问日志
\Log::info('供应商API访问', [
'user_id' => $request->user()->id,
'uri' => $request->fullUrl(),
'method' => $request->method()
]);
return $next($request);
}
}
缓存策略
<?php
// 在Repository中使用缓存
namespace App\Repositories;
use App\Models\Supplier;
use Illuminate\Support\Facades\Cache;
class SupplierRepository
{
protected $model;
public function __construct(Supplier $model)
{
$this->model = $model;
}
public function find(int $id)
{
return Cache::remember(
"supplier:{$id}",
config('cache.duration'),
function () use ($id) {
return $this->model->with(['contacts', 'qualifications'])->find($id);
}
);
}
public function allActive()
{
return Cache::remember('suppliers_active', 3600, function () {
return $this->model->where('status', 1)->get();
});
}
public function clearCache($id = null)
{
Cache::forget('suppliers_active');
if ($id) {
Cache::forget("supplier:{$id}");
}
}
}
数据验证与过滤
<?php
// App\Http\Requests\SupplierRequest.php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;
class SupplierRequest extends FormRequest
{
public function authorize()
{
return true;
}
public function rules()
{
$rules = [
'supplier_name' => 'required|max:200',
'supplier_type' => ['required', Rule::in(['material', 'service', 'equipment'])],
'contact_person' => 'required|max:100',
'contact_phone' => 'required|max:20',
'contact_email' => 'required|email|max:100',
'address' => 'required|max:500',
'credit_limit' => 'numeric|min:0|max:99999999.99',
'bank_account' => 'required|max:100',
'payment_terms' => 'required|max:200'
];
// 更新时,允许修改供应商编码
if ($this->isMethod('PUT') || $this->isMethod('PATCH')) {
$rules['supplier_code'] = [
'required',
Rule::unique('suppliers', 'supplier_code')->ignore($this->route('supplier'))
];
}
return $rules;
}
public function messages()
{
return [
'supplier_name.required' => '供应商名称不能为空',
'supplier_name.max' => '供应商名称不能超过200个字符',
'contact_phone.required' => '联系电话不能为空',
'contact_email.email' => '邮箱格式不正确',
'credit_limit.max' => '信用额度超出限制'
];
}
}
统计与报表功能
供应商分析
-- 供应商类型统计
SELECT
supplier_type,
COUNT(*) as total,
AVG(rating) as avg_rating,
SUM(CASE WHEN status = 1 THEN 1 ELSE 0 END) as active_count
FROM suppliers
GROUP BY supplier_type;
-- 供应商活跃度分析
SELECT
supplier_id,
COUNT(*) as transaction_count,
SUM(total_amount) as total_spend,
AVG(delivery_days) as avg_delivery_days,
SUM(CASE WHEN on_time = 1 THEN 1 ELSE 0 END) / COUNT(*) * 100 as on_time_rate
FROM purchase_orders
WHERE created_at >= DATE_SUB(NOW(), INTERVAL 6 MONTH)
GROUP BY supplier_id
ORDER BY total_spend DESC;
供应商评估
<?php
// app/Services/SupplierEvaluationService.php
namespace App\Services;
class SupplierEvaluationService
{
/**
* 计算供应商综合评分
* @param int $supplierId
* @return array
*/
public function evaluateSupplier($supplierId)
{
// 质量评分
$qualityScore = $this->calculateQualityScore($supplierId);
// 交期评分
$deliveryScore = $this->calculateDeliveryScore($supplierId);
// 价格评分
$priceScore = $this->calculatePriceScore($supplierId);
// 服务评分
$serviceScore = $this->calculateServiceScore($supplierId);
$weights = config('supplier.evaluation_weights');
$totalScore = $qualityScore * $weights['quality']
+ $deliveryScore * $weights['delivery']
+ $priceScore * $weights['price']
+ $serviceScore * $weights['service'];
return [
'total_score' => round($totalScore, 2),
'quality_score' => $qualityScore,
'delivery_score' => $deliveryScore,
'price_score' => $priceScore,
'service_score' => $serviceScore,
'evaluation_date' => now()
];
}
private function calculateQualityScore($supplierId)
{
// 计算订单合格率
$stats = DB::table('purchase_orders as po')
->join('inbound_inspections as ii', 'po.id', '=', 'ii.po_id')
->where('po.supplier_id', $supplierId)
->whereDate('po.created_at', '>=', now()->subMonths(6))
->selectRaw('AVG(ii.pass_rate) as pass_rate, COUNT(*) as total_check')
->first();
if ($stats->total_check == 0) {
return 100; // 没有数据时给满分
}
return $stats->pass_rate;
}
private function calculateDeliveryScore($supplierId)
{
// 计算准时交货率
$stats = DB::table('purchase_orders')
->where('supplier_id', $supplierId)
->whereDate('created_at', '>=', now()->subMonths(6))
->selectRaw('AVG(CASE WHEN delivery_date <= promised_date THEN 1 ELSE 0 END) as on_time_rate')
->first();
return $stats->on_time_rate * 100;
}
}
部署与维护建议
项目配置
// config/supplier.php
return [
'code_prefix' => 'SUP',
'max_upload_size' => 5 * 1024 * 1024, // 5MB
'allowed_uploads' => ['pdf', 'jpg', 'png', 'jpeg'],
'status_map' => [
0 => '停用',
1 => '启用',
],
'type_map' => [
'material' => '物料供应商',
'service' => '服务供应商',
'equipment' => '设备供应商',
],
'evaluation_weights' => [
'quality' => 0.4, // 质量权重
'delivery' => 0.2, // 交期权重
'price' => 0.2, // 价格权重
'service' => 0.2, // 服务权重
],
'cache_time' => 3600,
'per_page_default' => 15,
'api_rate_limit' => 30,
];
数据库索引优化
-- 添加组合索引优化查询
ALTER TABLE suppliers
ADD INDEX idx_type_status (supplier_type, status),
ADD INDEX idx_name_code (supplier_name, supplier_code);
-- 定时清理过期数据
CREATE EVENT clean_expired_data
ON SCHEDULE EVERY 1 DAY
DO
BEGIN
DELETE FROM supplier_qualifications
WHERE expiry_date < DATE_SUB(NOW(), INTERVAL 1 YEAR);
DELETE FROM supplier_contacts
WHERE created_at < DATE_SUB(NOW(), INTERVAL 3 YEAR);
END;
测试用例示例
<?php
// tests/Feature/SupplierTest.php
namespace Tests\Feature;
use App\Models\Supplier;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithFaker;
use Tests\TestCase;
class SupplierTest extends TestCase
{
use RefreshDatabase, WithFaker;
/** @test */
public function can_create_supplier()
{
$user = User::factory()->create();
$response = $this->actingAs($user)
->post('/suppliers', [
'supplier_name' => $this->faker->company,
'supplier_type' => 'material',
'contact_person' => $this->faker->name,
'contact_phone' => $this->faker->phoneNumber,
'contact_email' => $this->faker->email,
'address' => $this->faker->address,
'bank_name' => $this->faker->company,
'bank_account' => $this->faker->bankAccountNumber,
'payment_terms' => '月结30天',
'credit_limit' => 100000
]);
$response->assertStatus(302);
$this->assertDatabaseHas('suppliers', [
'supplier_name' => $response['supplier_name']
]);
}
/** @test */
public function can_update_supplier()
{
$supplier = Supplier::factory()->create();
$response = $this->actingAs(User::factory()->create())
->put("/suppliers/{$supplier->id}", [
'supplier_name' => '更新后的名称',
'supplier_type' => 'service',
'contact_person' => '联系人',
'contact_phone' => '12345678901',
'contact_email' => 'test@example.com',
'address' => '测试地址',
'tax_number' => '123456789',
'bank_name' => '银行名称',
'bank_account' => '12345678901',
'payment_terms' => '月结',
'credit_limit' => 500000
]);
$response->assertStatus(302);
$this->assertEquals('更新后的名称', $supplier->fresh()->supplier_name);
}
}
这个PHP供应商管理系统的核心特点:
- 模块化设计 - 清晰的分层架构,便于维护和扩展
- 完整的CRUD操作 - 支持供应商的全生命周期管理
- 权限控制 - 基于角色的访问控制
- 数据分析 - 供应商评分和绩效分析
- 安全防护 - 数据验证、SQL注入防护、文件上传安全
- 性能优化 - 缓存策略、索引优化、批量处理
- 日志记录 - 完整的操作日志和审计跟踪
根据实际需求,你还可以扩展:
- 供应商合同管理
- 采购订单关联
- 付款审批流程
- 供应商协同平台
- 微信/邮件通知
如果还有其他具体需求,欢迎继续提问!
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播