PHP 怎么供应商管理

wen PHP项目 8

本文目录导读:

PHP 怎么供应商管理

  1. 整体架构设计
  2. 核心代码实现
  3. API接口设计
  4. 安全与性能优化
  5. 统计与报表功能
  6. 部署与维护建议

我来为你详细介绍一下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供应商管理系统的核心特点:

  1. 模块化设计 - 清晰的分层架构,便于维护和扩展
  2. 完整的CRUD操作 - 支持供应商的全生命周期管理
  3. 权限控制 - 基于角色的访问控制
  4. 数据分析 - 供应商评分和绩效分析
  5. 安全防护 - 数据验证、SQL注入防护、文件上传安全
  6. 性能优化 - 缓存策略、索引优化、批量处理
  7. 日志记录 - 完整的操作日志和审计跟踪

根据实际需求,你还可以扩展:

  • 供应商合同管理
  • 采购订单关联
  • 付款审批流程
  • 供应商协同平台
  • 微信/邮件通知

如果还有其他具体需求,欢迎继续提问!

抱歉,评论功能暂时关闭!