本文目录导读:

我来为你详细介绍一下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注入防护、文件上传安全
- 性能优化 - 缓存策略、索引优化、批量处理
- 日志记录 - 完整的操作日志和审计跟踪
根据实际需求,你还可以扩展:
- 供应商合同管理
- 采购订单关联
- 付款审批流程
- 供应商协同平台
- 微信/邮件通知
如果还有其他具体需求,欢迎继续提问!