feat: add article and production modules

This commit is contained in:
2026-08-31 13:16:46 +08:00
parent ac3da246b2
commit 5c586a1a95
334 changed files with 34419 additions and 1 deletions
@@ -0,0 +1,37 @@
<?php
declare(strict_types=1);
namespace plugin\produce\app\model;
use app\model\system\Admin;
use core\base\BaseModel as CoreBaseModel;
use Illuminate\Database\Eloquent\Relations\HasOne;
use Illuminate\Database\Eloquent\SoftDeletes;
abstract class BaseModel extends CoreBaseModel
{
use SoftDeletes;
protected $appends = ['created_date', 'updated_date', 'created_name', 'updated_name'];
public function createds(): HasOne
{
return $this->hasOne(Admin::class, 'id', 'created_by')->select('id', 'real_name as created_name');
}
public function updateds(): HasOne
{
return $this->hasOne(Admin::class, 'id', 'updated_by')->select('id', 'real_name as updated_name');
}
public function getCreatedNameAttribute(): string
{
return $this->createds?->created_name ?? '';
}
public function getUpdatedNameAttribute(): string
{
return $this->updateds?->updated_name ?? '';
}
}
@@ -0,0 +1,81 @@
<?php
declare(strict_types=1);
namespace plugin\produce\app\model;
class ExceptionRecord extends BaseModel
{
protected $table = 'produce_exception_record';
protected $appends = ['reported_date', 'handled_date', 'status_name'];
protected static function booted(): void
{
static::creating(function (self $model) {
if (empty($model->reported_at)) {
$model->reported_at = time();
}
});
}
protected $fillable = [
'id',
'order_no',
'user_code',
'reporter',
'reporter_id',
'type_id',
'type_code',
'type_name',
'reason_id',
'reason_code',
'reason_name',
'issue_id',
'issue_code',
'issue_name',
'images',
'remark',
'status',
'handler',
'handler_id',
'handled_at',
'reported_at',
'created_by',
'updated_by',
'created_at',
'updated_at',
'deleted_at',
];
protected $casts = [
'images' => 'array',
'status' => 'integer',
'handled_at' => 'integer',
'reported_at' => 'integer',
'created_by' => 'integer',
'updated_by' => 'integer',
'created_at' => 'integer',
'updated_at' => 'integer',
'deleted_at' => 'integer',
];
public function getReportedDateAttribute(): ?string
{
return $this->reported_at ? date('Y-m-d H:i:s', (int) $this->reported_at) : null;
}
public function getHandledDateAttribute(): ?string
{
return $this->handled_at ? date('Y-m-d H:i:s', (int) $this->handled_at) : null;
}
public function getStatusNameAttribute(): string
{
return match ((int) $this->status) {
1 => '处理中',
2 => '已处理',
default => '待处理',
};
}
}
@@ -0,0 +1,50 @@
<?php
declare(strict_types=1);
namespace plugin\produce\app\model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class ExceptionSetting extends BaseModel
{
protected $table = 'produce_exception_setting';
protected $appends = ['parent_name'];
protected $fillable = [
'id',
'parent_id',
'setting_type',
'code',
'name',
'sort',
'is_enabled',
'remark',
'created_by',
'updated_by',
'created_at',
'updated_at',
'deleted_at',
];
protected $casts = [
'sort' => 'integer',
'is_enabled' => 'integer',
'created_by' => 'integer',
'updated_by' => 'integer',
'created_at' => 'integer',
'updated_at' => 'integer',
'deleted_at' => 'integer',
];
public function parent(): BelongsTo
{
return $this->belongsTo(self::class, 'parent_id', 'id')->withTrashed();
}
public function getParentNameAttribute(): string
{
return (string) ($this->parent?->name ?? '');
}
}
@@ -0,0 +1,39 @@
<?php
declare(strict_types=1);
namespace plugin\produce\app\model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use plugin\produce\app\model\BaseModel;
use plugin\produce\app\model\MaterialCategory;
class HardwareFaultDescription extends BaseModel
{
protected $table = 'produce_hardware_fault_description';
protected $appends = ['created_date', 'updated_date', 'category_name'];
protected $fillable = [
'id', 'category_id', 'description', 'use_count', 'is_enabled', 'remark',
'created_by', 'updated_by', 'created_at', 'updated_at', 'deleted_at',
];
protected $casts = [
'use_count' => 'integer',
'is_enabled' => 'integer',
'created_at' => 'integer',
'updated_at' => 'integer',
'deleted_at' => 'integer',
];
public function category(): BelongsTo
{
return $this->belongsTo(MaterialCategory::class, 'category_id', 'id')->withTrashed();
}
public function getCategoryNameAttribute(): string
{
return (string) ($this->category?->category_name ?? '');
}
}
@@ -0,0 +1,42 @@
<?php
declare(strict_types=1);
namespace plugin\produce\app\model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class Material extends BaseModel
{
protected $table = 'produce_material';
protected $appends = ['created_date', 'updated_date', 'category_name'];
protected $fillable = [
'id',
'category_id',
'product_code',
'product_name',
'created_by',
'updated_by',
'created_at',
'updated_at',
'deleted_at',
];
protected $casts = [
'created_at' => 'integer',
'updated_at' => 'integer',
'deleted_at' => 'integer',
];
public function category(): BelongsTo
{
return $this->belongsTo(MaterialCategory::class, 'category_id', 'id')->withTrashed();
}
public function getCategoryNameAttribute(): string
{
return (string) ($this->category?->category_name ?? '');
}
}
@@ -0,0 +1,38 @@
<?php
declare(strict_types=1);
namespace plugin\produce\app\model;
use Illuminate\Database\Eloquent\Relations\HasMany;
class MaterialCategory extends BaseModel
{
protected $table = 'produce_material_category';
protected $fillable = [
'id',
'category_name',
'created_by',
'updated_by',
'created_at',
'updated_at',
'deleted_at',
];
protected $casts = [
'created_at' => 'integer',
'updated_at' => 'integer',
'deleted_at' => 'integer',
];
public function getNameAttribute(): string
{
return (string) $this->category_name;
}
public function materials(): HasMany
{
return $this->hasMany(Material::class, 'category_id', 'id');
}
}
@@ -0,0 +1,46 @@
<?php
declare(strict_types=1);
namespace plugin\produce\app\model;
use Illuminate\Database\Eloquent\Relations\HasMany;
class Process extends BaseModel
{
protected $table = 'produce_process';
protected $fillable = [
'id',
'process_code',
'process_name',
'description',
'sort',
'status',
'step_count',
'created_by',
'updated_by',
'created_at',
'updated_at',
'deleted_at',
];
protected $casts = [
'sort' => 'integer',
'status' => 'integer',
'step_count' => 'integer',
'created_at' => 'integer',
'updated_at' => 'integer',
'deleted_at' => 'integer',
];
public function getNameAttribute(): string
{
return (string) $this->process_name;
}
public function steps(): HasMany
{
return $this->hasMany(ProcessStep::class, 'process_id', 'id');
}
}
@@ -0,0 +1,55 @@
<?php
declare(strict_types=1);
namespace plugin\produce\app\model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class ProcessStep extends BaseModel
{
protected $table = 'produce_process_step';
protected $fillable = [
'id',
'process_id',
'step_code',
'step_name',
'description',
'sort',
'status',
'prerequisite_steps',
'prerequisite_mode',
'prerequisite_require_final',
'allow_repeat_scan',
'repeat_scan_statuses',
'created_by',
'updated_by',
'created_at',
'updated_at',
'deleted_at',
];
protected $casts = [
'process_id' => 'integer',
'sort' => 'integer',
'status' => 'integer',
'prerequisite_steps' => 'array',
'prerequisite_require_final' => 'integer',
'allow_repeat_scan' => 'integer',
'repeat_scan_statuses' => 'array',
'created_at' => 'integer',
'updated_at' => 'integer',
'deleted_at' => 'integer',
];
public function getNameAttribute(): string
{
return (string) $this->step_name;
}
public function process(): BelongsTo
{
return $this->belongsTo(Process::class, 'process_id', 'id');
}
}
@@ -0,0 +1,39 @@
<?php
declare(strict_types=1);
namespace plugin\produce\app\model;
class ProduceConfig extends BaseModel
{
protected $table = 'produce_config';
protected $fillable = [
'id',
'group_code',
'code',
'name',
'content',
'sort',
'remark',
'created_by',
'updated_by',
'created_at',
'updated_at',
'deleted_at',
];
protected $casts = [
'sort' => 'integer',
'created_by' => 'integer',
'updated_by' => 'integer',
'created_at' => 'integer',
'updated_at' => 'integer',
'deleted_at' => 'integer',
];
public function getNameAttribute(): string
{
return (string) $this->name;
}
}
@@ -0,0 +1,82 @@
<?php
declare(strict_types=1);
namespace plugin\produce\app\model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class ProductRecord extends BaseModel
{
protected $table = 'produce_product_record';
protected $appends = ['created_date', 'updated_date', 'process_name', 'step_name', 'status_name'];
protected $fillable = [
'id',
'process_order_no',
'product_code',
'quantity',
'process_id',
'step_id',
'status_id',
'user_code',
'operator',
'operator_id',
'scan_result',
'remark',
'created_by',
'updated_by',
'created_at',
'updated_at',
'deleted_at',
];
protected $casts = [
'quantity' => 'integer',
'scan_result' => 'integer',
'created_at' => 'integer',
'updated_at' => 'integer',
'deleted_at' => 'integer',
];
public function process(): BelongsTo
{
return $this->belongsTo(Process::class, 'process_id', 'id')->withTrashed();
}
public function step(): BelongsTo
{
return $this->belongsTo(ProcessStep::class, 'step_id', 'id')->withTrashed();
}
public function status(): BelongsTo
{
return $this->belongsTo(StepStatus::class, 'status_id', 'id')->withTrashed();
}
public function getProcessNameAttribute(): string
{
return (string) ($this->process?->process_name ?? '');
}
public function getStepNameAttribute(): string
{
return (string) ($this->step?->step_name ?? '');
}
public function getStatusNameAttribute(): string
{
return (string) ($this->status?->status_name ?? '');
}
public function getCreatedDateAttribute(): ?string
{
return $this->created_at ? date('Y-m-d H:i:s', (int) $this->created_at) : null;
}
public function getUpdatedDateAttribute(): ?string
{
return $this->updated_at ? date('Y-m-d H:i:s', (int) $this->updated_at) : null;
}
}
@@ -0,0 +1,160 @@
<?php
declare(strict_types=1);
namespace plugin\produce\app\model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use plugin\produce\app\model\BaseModel;
use plugin\produce\app\model\MaterialCategory;
class RepairRecord extends BaseModel
{
protected $table = 'produce_repair_record';
protected $appends = [
'created_date', 'updated_date', 'fault_description_name', 'fault_description_ids', 'fault_descriptions', 'repaired_date', 'exception_type_name',
'part_category', 'part_code', 'part_name', 'part_fault', 'part_quantity', 'part_source',
'order_product', 'repair_result_name', 'responsible_operator_names',
];
protected $with = ['exception'];
protected $fillable = [
'id', 'repair_no', 'exception_id', 'exception_reason', 'exception_issue', 'order_no', 'product_code', 'product_name', 'category_id', 'category_ids', 'category_name', 'category_names',
'fault_description_id', 'fault_description', 'repair_result', 'parts', 'images', 'operation_fault_steps', 'responsible_operators', 'operator', 'repaired_at',
'remark', 'created_by', 'updated_by', 'created_at', 'updated_at', 'deleted_at',
];
protected $casts = [
'category_ids' => 'array',
'category_names' => 'array',
'parts' => 'array',
'images' => 'array',
'operation_fault_steps' => 'array',
'responsible_operators' => 'array',
'repaired_at' => 'integer',
'created_at' => 'integer',
'updated_at' => 'integer',
'deleted_at' => 'integer',
];
public function category(): BelongsTo
{
return $this->belongsTo(MaterialCategory::class, 'category_id', 'id')->withTrashed();
}
public function exception(): BelongsTo
{
return $this->belongsTo(ExceptionRecord::class, 'exception_id', 'id')->withTrashed();
}
public function getExceptionTypeNameAttribute(): string
{
return (string) ($this->exception?->type_name ?? '');
}
public function faultDescription(): BelongsTo
{
return $this->belongsTo(HardwareFaultDescription::class, 'fault_description_id', 'id')->withTrashed();
}
public function getFaultDescriptionNameAttribute(): string
{
return (string) $this->fault_description;
}
public function getOrderProductAttribute(): string
{
return implode('/', array_filter([(string) $this->order_no, (string) $this->product_code]));
}
public function getRepairResultNameAttribute(): string
{
return match ($this->repair_result) {
'material_defect' => '物料不良',
'operation_defect' => '作业不良',
'assembly_mismatch' => '搭配错误',
'process_defect' => '工艺不良',
'no_defect' => '不良不现',
default => (string) $this->repair_result,
};
}
public function getResponsibleOperatorNamesAttribute(): string
{
return implode("\n", array_filter(array_map(
static function (array $snapshot): string {
$step = (string) ($snapshot['step_name'] ?? '');
$operator = (string) ($snapshot['operator'] ?? $snapshot['user_code'] ?? '');
return implode('-', array_filter([$step, $operator]));
},
is_array($this->responsible_operators) ? $this->responsible_operators : [],
)));
}
public function getPartCategoryAttribute(): string
{
return $this->formatPartValues(static fn (array $part): string => (string) ($part['category_name'] ?? ''));
}
public function getPartCodeAttribute(): string
{
return $this->formatPartValues(static fn (array $part): string => (string) ($part['product_code'] ?? ''));
}
public function getPartNameAttribute(): string
{
return $this->formatPartValues(static fn (array $part): string => (string) ($part['product_name'] ?? ''));
}
public function getPartFaultAttribute(): string
{
return $this->formatPartValues(static function (array $part): string {
return implode('、', array_filter(array_map(
static fn (array $fault): string => (string) ($fault['description'] ?? ''),
is_array($part['fault_descriptions'] ?? null) ? $part['fault_descriptions'] : [],
)));
});
}
public function getPartQuantityAttribute(): string
{
return $this->formatPartValues(static fn (array $part): string => (string) ($part['quantity'] ?? 1));
}
public function getPartSourceAttribute(): string
{
return $this->formatPartValues(static function (array $part): string {
return match ($part['source'] ?? '') {
'new' => '新料',
'old', 'after_sale', 'after_sales', 'after-sale' => '售后',
'repair', 'refurbished' => '返修',
default => (string) ($part['source'] ?? '-'),
};
});
}
private function formatPartValues(callable $callback): string
{
return implode("\n", array_map(
static fn (array $part): string => $callback($part) ?: '-',
is_array($this->parts) ? $this->parts : [],
));
}
public function getFaultDescriptionIdsAttribute(): array
{
return $this->fault_description_id ? [$this->fault_description_id] : [];
}
public function getFaultDescriptionsAttribute(): array
{
return $this->fault_description ? [$this->fault_description] : [];
}
public function getRepairedDateAttribute(): ?string
{
return $this->repaired_at ? date('Y-m-d H:i:s', (int) $this->repaired_at) : null;
}
}
@@ -0,0 +1,50 @@
<?php
declare(strict_types=1);
namespace plugin\produce\app\model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class RepairSetting extends BaseModel
{
protected $table = 'produce_repair_setting';
protected $appends = ['parent_name'];
protected $fillable = [
'id',
'parent_id',
'setting_type',
'code',
'name',
'sort',
'is_enabled',
'remark',
'created_by',
'updated_by',
'created_at',
'updated_at',
'deleted_at',
];
protected $casts = [
'sort' => 'integer',
'is_enabled' => 'integer',
'created_by' => 'integer',
'updated_by' => 'integer',
'created_at' => 'integer',
'updated_at' => 'integer',
'deleted_at' => 'integer',
];
public function parent(): BelongsTo
{
return $this->belongsTo(self::class, 'parent_id', 'id')->withTrashed();
}
public function getParentNameAttribute(): string
{
return (string) ($this->parent?->name ?? '');
}
}
@@ -0,0 +1,88 @@
<?php
declare(strict_types=1);
namespace plugin\produce\app\model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class ScanData extends BaseModel
{
protected $table = 'produce_scan_data';
protected $appends = ['scan_date', 'process_name', 'step_name', 'status_name'];
protected static function booted(): void
{
static::creating(function (self $model) {
if (empty($model->scan_time)) {
$model->scan_time = time();
}
});
}
protected $fillable = [
'id',
'order_no',
'quantity',
'user_code',
'operator',
'operator_id',
'process_id',
'step_id',
'status_id',
'scan_result',
'scan_time',
'remark',
'created_by',
'updated_by',
'created_at',
'updated_at',
'deleted_at',
];
protected $casts = [
'quantity' => 'integer',
'scan_result' => 'integer',
'scan_time' => 'integer',
'created_at' => 'integer',
'updated_at' => 'integer',
'deleted_at' => 'integer',
];
public function process(): BelongsTo
{
return $this->belongsTo(Process::class, 'process_id', 'id')->withTrashed();
}
public function step(): BelongsTo
{
return $this->belongsTo(ProcessStep::class, 'step_id', 'id')->withTrashed();
}
public function status(): BelongsTo
{
return $this->belongsTo(StepStatus::class, 'status_id', 'id')->withTrashed();
}
public function getProcessNameAttribute(): string
{
return (string) ($this->process?->process_name ?? '');
}
public function getStepNameAttribute(): string
{
return (string) ($this->step?->step_name ?? '');
}
public function getStatusNameAttribute(): string
{
return (string) ($this->status?->status_name ?? '');
}
public function getScanDateAttribute(): ?string
{
return $this->scan_time ? date('Y-m-d H:i:s', (int) $this->scan_time) : null;
}
}
@@ -0,0 +1,47 @@
<?php
declare(strict_types=1);
namespace plugin\produce\app\model;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
class SpecificRule extends BaseModel
{
protected $table = 'produce_specific_rule';
protected $fillable = [
'id',
'rule_name',
'rule_pattern',
'rule_type',
'apply_mode',
'match_field',
'is_enabled',
'step_ids',
'sort',
'remark',
'created_by',
'updated_by',
'created_at',
'updated_at',
'deleted_at',
];
protected $casts = [
'rule_type' => 'integer',
'is_enabled' => 'integer',
'step_ids' => 'array',
'sort' => 'integer',
'created_by' => 'integer',
'updated_by' => 'integer',
'created_at' => 'integer',
'updated_at' => 'integer',
'deleted_at' => 'integer',
];
public function getNameAttribute(): string
{
return (string) $this->rule_name;
}
}
@@ -0,0 +1,64 @@
<?php
declare(strict_types=1);
namespace plugin\produce\app\model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class StatusTransition extends BaseModel
{
protected $table = 'produce_status_transition';
protected $fillable = [
'id',
'step_id',
'from_status_id',
'to_status_id',
'trigger_event',
'event_config',
'auto_trigger',
'priority',
'transition_type',
'side_effect_type',
'side_effect_config',
'description',
'sort',
'status',
'created_by',
'updated_by',
'created_at',
'updated_at',
'deleted_at',
];
protected $casts = [
'step_id' => 'integer',
'from_status_id' => 'integer',
'to_status_id' => 'integer',
'event_config' => 'array',
'auto_trigger' => 'integer',
'priority' => 'integer',
'side_effect_config' => 'array',
'sort' => 'integer',
'status' => 'integer',
'created_at' => 'integer',
'updated_at' => 'integer',
'deleted_at' => 'integer',
];
public function step(): BelongsTo
{
return $this->belongsTo(ProcessStep::class, 'step_id', 'id');
}
public function fromStatus(): BelongsTo
{
return $this->belongsTo(StepStatus::class, 'from_status_id', 'id');
}
public function toStatus(): BelongsTo
{
return $this->belongsTo(StepStatus::class, 'to_status_id', 'id');
}
}
@@ -0,0 +1,51 @@
<?php
declare(strict_types=1);
namespace plugin\produce\app\model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class StepRelation extends BaseModel
{
protected $table = 'produce_step_relation';
protected $fillable = [
'id',
'from_step_id',
'to_step_id',
'relation_type',
'flow_condition',
'priority',
'description',
'sort',
'status',
'created_by',
'updated_by',
'created_at',
'updated_at',
'deleted_at',
];
protected $casts = [
'from_step_id' => 'integer',
'to_step_id' => 'integer',
'flow_condition' => 'array',
'priority' => 'integer',
'sort' => 'integer',
'status' => 'integer',
'created_at' => 'integer',
'updated_at' => 'integer',
'deleted_at' => 'integer',
];
public function fromStep(): BelongsTo
{
return $this->belongsTo(ProcessStep::class, 'from_step_id', 'id');
}
public function toStep(): BelongsTo
{
return $this->belongsTo(ProcessStep::class, 'to_step_id', 'id');
}
}
@@ -0,0 +1,50 @@
<?php
declare(strict_types=1);
namespace plugin\produce\app\model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class StepStatus extends BaseModel
{
protected $table = 'produce_step_status';
protected $fillable = [
'id',
'step_id',
'status_code',
'status_name',
'is_initial',
'is_final',
'description',
'sort',
'status',
'created_by',
'updated_by',
'created_at',
'updated_at',
'deleted_at',
];
protected $casts = [
'step_id' => 'integer',
'is_initial' => 'integer',
'is_final' => 'integer',
'sort' => 'integer',
'status' => 'integer',
'created_at' => 'integer',
'updated_at' => 'integer',
'deleted_at' => 'integer',
];
public function getNameAttribute(): string
{
return (string) $this->status_name;
}
public function step(): BelongsTo
{
return $this->belongsTo(ProcessStep::class, 'step_id', 'id');
}
}
@@ -0,0 +1,72 @@
<?php
declare(strict_types=1);
namespace plugin\produce\app\model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class TransitionLog extends BaseModel
{
const UPDATED_AT = null;
protected $table = 'produce_transition_log';
protected $appends = ['created_date', 'created_name'];
protected $fillable = [
'id',
'work_order_id',
'work_order_no',
'step_id',
'transition_id',
'from_status_id',
'to_status_id',
'trigger_event',
'trigger_data',
'trigger_user',
'trigger_ip',
'is_auto',
'is_success',
'error_message',
'duration',
'remark',
'created_by',
'created_at',
'deleted_at',
];
protected $casts = [
'work_order_id' => 'integer',
'step_id' => 'integer',
'transition_id' => 'integer',
'from_status_id' => 'integer',
'to_status_id' => 'integer',
'trigger_data' => 'array',
'is_auto' => 'integer',
'is_success' => 'integer',
'duration' => 'integer',
'created_at' => 'integer',
'deleted_at' => 'integer',
];
public function step(): BelongsTo
{
return $this->belongsTo(ProcessStep::class, 'step_id', 'id');
}
public function transition(): BelongsTo
{
return $this->belongsTo(StatusTransition::class, 'transition_id', 'id');
}
public function fromStatus(): BelongsTo
{
return $this->belongsTo(StepStatus::class, 'from_status_id', 'id');
}
public function toStatus(): BelongsTo
{
return $this->belongsTo(StepStatus::class, 'to_status_id', 'id');
}
}