修复生产统计与维修记录用户数据
This commit is contained in:
@@ -19,6 +19,7 @@ use plugin\produce\app\model\ExceptionSetting;
|
||||
use plugin\produce\app\model\HardwareFaultDescription;
|
||||
use plugin\produce\app\model\Material;
|
||||
use plugin\produce\app\model\MaterialCategory;
|
||||
use plugin\produce\app\model\MemberNotification;
|
||||
use plugin\produce\app\model\Process;
|
||||
use plugin\produce\app\model\ProcessStep;
|
||||
use plugin\produce\app\model\ProductRecord;
|
||||
@@ -367,6 +368,8 @@ final class DataEntryController extends Base
|
||||
'today_total' => $todayTotal,
|
||||
'success_total' => $successTotal,
|
||||
'failed_total' => $failedTotal,
|
||||
'process_breakdown' => $this->buildTodayProcessStepBreakdown(clone $query),
|
||||
'daily_trend' => $this->buildDailyTrend(clone $query, 'scan_time'),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -422,9 +425,166 @@ final class DataEntryController extends Base
|
||||
'today_total' => $todayTotal,
|
||||
'success_total' => $successTotal,
|
||||
'failed_total' => $failedTotal,
|
||||
'process_breakdown' => $this->buildTodayProcessStepBreakdown(clone $query),
|
||||
'daily_trend' => $this->buildDailyTrend(clone $query, 'created_at'),
|
||||
]);
|
||||
}
|
||||
|
||||
#[OA\Get(path: '/produce/dashboard/today', summary: '获取首页今日生产总览', tags: ['produce-web'])]
|
||||
#[DataResponse(description: '获取成功')]
|
||||
public function todayDashboard(): Response
|
||||
{
|
||||
$member = $this->currentMember->user();
|
||||
if (!$member) {
|
||||
return Json::fail('请先登录', [], 401);
|
||||
}
|
||||
|
||||
$todayStart = strtotime(date('Y-m-d 00:00:00'));
|
||||
$tomorrowStart = $todayStart + 86400;
|
||||
$monthStart = strtotime(date('Y-m-01 00:00:00'));
|
||||
$nextMonthStart = strtotime('+1 month', $monthStart);
|
||||
$userCode = (string) $member->username;
|
||||
$orders = ScanData::query()
|
||||
->where('user_code', $userCode)
|
||||
->whereBetween('scan_time', [$todayStart, $tomorrowStart - 1]);
|
||||
$products = ProductRecord::query()
|
||||
->where('user_code', $userCode)
|
||||
->whereBetween('created_at', [$todayStart, $tomorrowStart - 1]);
|
||||
$exceptions = ExceptionRecord::query()
|
||||
->where('user_code', $userCode)
|
||||
->whereBetween('reported_at', [$todayStart, $tomorrowStart - 1]);
|
||||
$operatorNames = array_values(array_unique(array_filter([
|
||||
trim((string) ($member->nickname ?? '')),
|
||||
$userCode,
|
||||
])));
|
||||
$repairs = RepairRecord::query()
|
||||
->where(function ($query) use ($operatorNames, $userCode): void {
|
||||
$query->whereIn('operator', $operatorNames)
|
||||
->orWhereHas('exception', static fn ($exceptionQuery) => $exceptionQuery->where('user_code', $userCode));
|
||||
})
|
||||
->whereBetween('repaired_at', [$todayStart, $tomorrowStart - 1]);
|
||||
$exceptionTotal = (int) (clone $exceptions)->count();
|
||||
$responsibilityData = $this->buildMonthlyResponsibilityOrders($monthStart, $nextMonthStart, $userCode);
|
||||
return Json::success('获取成功', [
|
||||
'summary' => [
|
||||
'order_scan' => (int) (clone $orders)->sum('quantity'),
|
||||
'product_scan' => (int) (clone $products)->sum('quantity'),
|
||||
'exception' => $exceptionTotal,
|
||||
'repair' => (int) (clone $repairs)->count(),
|
||||
],
|
||||
'order_breakdown' => $this->buildTodayProcessStepBreakdown(clone $orders),
|
||||
'repair_breakdown' => $this->buildTodayProcessStepBreakdown(clone $repairs),
|
||||
'product_breakdown' => $this->buildTodayProcessStepBreakdown(clone $products),
|
||||
'responsibility_order_total' => $responsibilityData['total'],
|
||||
'responsibility_orders' => $responsibilityData['items'],
|
||||
'trend' => $this->buildTodayHourlyTrend($orders, $products, $todayStart, $tomorrowStart),
|
||||
'exceptions' => [
|
||||
'pending' => (int) (clone $exceptions)->where('status', 0)->count(),
|
||||
'processing' => (int) (clone $exceptions)->where('status', 1)->count(),
|
||||
'completed' => (int) (clone $exceptions)->where('status', 2)->count(),
|
||||
'total' => $exceptionTotal,
|
||||
],
|
||||
'updated_at' => time(),
|
||||
]);
|
||||
}
|
||||
|
||||
/** 统计本月当前会员承担责任的不良订单,并返回最近五条。 */
|
||||
private function buildMonthlyResponsibilityOrders(int $monthStart, int $nextMonthStart, string $userCode): array
|
||||
{
|
||||
$repairs = RepairRecord::query()
|
||||
->where('repair_result', 'operation_defect')
|
||||
->whereBetween('repaired_at', [$monthStart, $nextMonthStart - 1])
|
||||
->orderByDesc('repaired_at')
|
||||
->orderByDesc('id')
|
||||
->get(['id', 'order_no', 'exception_reason', 'repaired_at', 'responsible_operators']);
|
||||
$items = [];
|
||||
foreach ($repairs as $repair) {
|
||||
$snapshots = is_array($repair->responsible_operators) ? $repair->responsible_operators : [];
|
||||
$isResponsible = collect($snapshots)->contains(static fn (array $snapshot): bool => trim((string) ($snapshot['user_code'] ?? '')) === $userCode);
|
||||
if (!$isResponsible) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$items[] = [
|
||||
'id' => (string) $repair->id,
|
||||
'order_no' => (string) $repair->order_no,
|
||||
'exception_reason' => (string) $repair->exception_reason,
|
||||
'quantity' => 1,
|
||||
'repaired_at' => (int) $repair->repaired_at,
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'total' => count($items),
|
||||
'items' => array_slice($items, 0, 5),
|
||||
];
|
||||
}
|
||||
|
||||
/** 统计最近六个整点时段的扫码数量。 */
|
||||
private function buildTodayHourlyTrend($orders, $products, int $todayStart, int $tomorrowStart): array
|
||||
{
|
||||
$trend = [];
|
||||
$currentHour = strtotime(date('Y-m-d H:00:00'));
|
||||
for ($offset = 5; $offset >= 0; $offset--) {
|
||||
$hourStart = $currentHour - ($offset * 3600);
|
||||
$rangeStart = max($todayStart, $hourStart);
|
||||
$rangeEnd = min($tomorrowStart - 1, $hourStart + 3599);
|
||||
$orderCount = (int) (clone $orders)->whereBetween('scan_time', [$rangeStart, $rangeEnd])->sum('quantity');
|
||||
$productCount = (int) (clone $products)->whereBetween('created_at', [$rangeStart, $rangeEnd])->sum('quantity');
|
||||
$trend[] = ['label' => date('H:i', $hourStart), 'value' => $orderCount + $productCount];
|
||||
}
|
||||
return $trend;
|
||||
}
|
||||
|
||||
/** 汇总今日记录的工序与步骤数量。 */
|
||||
private function buildTodayProcessStepBreakdown($query): array
|
||||
{
|
||||
$isRepairRecord = $query->getModel()->getTable() === 'produce_repair_record';
|
||||
$quantityExpression = $isRepairRecord ? 'COUNT(*)' : 'SUM(quantity)';
|
||||
$snapshotColumns = $isRepairRecord ? ', MAX(process_name) as record_process_name, MAX(step_name) as record_step_name' : '';
|
||||
$rows = $query
|
||||
->selectRaw("process_id, step_id{$snapshotColumns}, {$quantityExpression} as quantity")
|
||||
->groupBy('process_id', 'step_id')
|
||||
->orderByDesc('quantity')
|
||||
->get();
|
||||
if ($rows->isEmpty()) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$processNames = Process::query()
|
||||
->whereIn('id', $rows->pluck('process_id')->filter()->all())
|
||||
->pluck('process_name', 'id');
|
||||
$stepNames = ProcessStep::query()
|
||||
->whereIn('id', $rows->pluck('step_id')->filter()->all())
|
||||
->pluck('step_name', 'id');
|
||||
|
||||
return $rows->map(static function ($row) use ($processNames, $stepNames): array {
|
||||
$processId = (string) ($row->process_id ?? '');
|
||||
$stepId = (string) ($row->step_id ?? '');
|
||||
return [
|
||||
'process_id' => $processId,
|
||||
'step_id' => $stepId,
|
||||
'process_name' => (string) (($row->record_process_name ?? '') ?: $processNames->get($row->process_id) ?: '未记录工序'),
|
||||
'step_name' => (string) (($row->record_step_name ?? '') ?: $stepNames->get($row->step_id) ?: '未记录步骤'),
|
||||
'quantity' => (int) $row->quantity,
|
||||
];
|
||||
})->values()->all();
|
||||
}
|
||||
|
||||
/** 汇总查询区间内每日数量。 */
|
||||
private function buildDailyTrend($query, string $dateColumn): array
|
||||
{
|
||||
$rows = $query
|
||||
->selectRaw("FROM_UNIXTIME({$dateColumn}, '%Y-%m-%d') as day, SUM(quantity) as quantity")
|
||||
->groupBy('day')
|
||||
->orderBy('day')
|
||||
->get();
|
||||
return $rows->map(static fn ($row): array => [
|
||||
'date' => (string) $row->day,
|
||||
'quantity' => (int) $row->quantity,
|
||||
])->values()->all();
|
||||
}
|
||||
|
||||
#[OA\Get(path: '/produce/product-record/user-code-stats', summary: '前台整机用户统计', tags: ['produce-web'])]
|
||||
#[DataResponse(description: '获取成功')]
|
||||
#[AllowAnonymous(requireToken: false, requirePermission: false, description: '公共接口')]
|
||||
@@ -860,13 +1020,24 @@ final class DataEntryController extends Base
|
||||
public function repairStatistics(Request $request): Response
|
||||
{
|
||||
$query = RepairRecord::query();
|
||||
$todayStart = strtotime(date('Y-m-d 00:00:00'));
|
||||
$todayEnd = $todayStart + 86400;
|
||||
$userCode = trim((string) $request->input('user_code', ''));
|
||||
$startAt = $this->parseQueryDate($request->input('start_date')) ?: strtotime(date('Y-m-d 00:00:00'));
|
||||
$endAt = $this->parseQueryDate($request->input('end_date'), true) ?: strtotime(date('Y-m-d 23:59:59'));
|
||||
if ($userCode !== '') {
|
||||
$member = Member::query()->where('username', $userCode)->first();
|
||||
$operators = array_values(array_unique(array_filter([$userCode, (string) ($member?->nickname ?? '')])));
|
||||
$query->where(function ($builder) use ($operators, $userCode): void {
|
||||
$builder->whereIn('operator', $operators)
|
||||
->orWhereHas('exception', static fn ($exceptionQuery) => $exceptionQuery->where('user_code', $userCode));
|
||||
});
|
||||
}
|
||||
$query->whereBetween('repaired_at', [$startAt, $endAt]);
|
||||
|
||||
return Json::success('获取成功', [
|
||||
'pending' => ExceptionRecord::query()->where('status', 0)->count(),
|
||||
'processing' => ExceptionRecord::query()->where('status', 1)->count(),
|
||||
'completed_today' => (clone $query)->whereBetween('repaired_at', [$todayStart, $todayEnd - 1])->count(),
|
||||
'completed_today' => (clone $query)->count(),
|
||||
'process_breakdown' => $this->buildTodayProcessStepBreakdown(clone $query),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -1097,6 +1268,16 @@ final class DataEntryController extends Base
|
||||
'handled_at' => $now,
|
||||
])->save();
|
||||
|
||||
if ($repairResult === 'operation_defect') {
|
||||
$this->createRepairResponsibilityNotifications(
|
||||
$repair,
|
||||
$exception,
|
||||
$responsibleOperatorSnapshots,
|
||||
$member,
|
||||
$now
|
||||
);
|
||||
}
|
||||
|
||||
return $repair;
|
||||
});
|
||||
} catch (\Throwable $e) {
|
||||
@@ -1106,6 +1287,74 @@ final class DataEntryController extends Base
|
||||
return Json::success('提交成功', ['id' => (string) $record->id, 'repair_no' => $record->repair_no]);
|
||||
}
|
||||
|
||||
#[OA\Get(path: '/produce/member-notification', summary: '获取当前会员生产通知', tags: ['produce-web'])]
|
||||
#[DataResponse(description: '获取成功')]
|
||||
public function memberNotifications(Request $request): Response
|
||||
{
|
||||
$memberId = (int) $this->currentMember->id();
|
||||
if ($memberId <= 0) {
|
||||
return Json::fail('请先登录', [], 401);
|
||||
}
|
||||
|
||||
$page = max(1, (int) $request->input('page', 1));
|
||||
$limit = min(50, max(1, (int) $request->input('limit', 20)));
|
||||
$query = MemberNotification::query()
|
||||
->where('member_id', $memberId)
|
||||
->orderByDesc('created_at')
|
||||
->orderByDesc('id');
|
||||
$total = $query->count();
|
||||
|
||||
return Json::success('ok', [
|
||||
'items' => $query->forPage($page, $limit)->get(),
|
||||
'total' => $total,
|
||||
]);
|
||||
}
|
||||
|
||||
#[OA\Get(path: '/produce/member-notification/unread-count', summary: '获取当前会员未读生产通知数', tags: ['produce-web'])]
|
||||
#[DataResponse(description: '获取成功')]
|
||||
public function memberNotificationUnreadCount(): Response
|
||||
{
|
||||
$memberId = (int) $this->currentMember->id();
|
||||
if ($memberId <= 0) {
|
||||
return Json::fail('请先登录', [], 401);
|
||||
}
|
||||
|
||||
$count = MemberNotification::query()
|
||||
->where('member_id', $memberId)
|
||||
->where('status', 0)
|
||||
->count();
|
||||
|
||||
return Json::success('ok', ['unread_count' => $count]);
|
||||
}
|
||||
|
||||
#[OA\Put(path: '/produce/member-notification/read', summary: '标记当前会员生产通知已读', tags: ['produce-web'])]
|
||||
#[SimpleResponse(description: '操作成功')]
|
||||
public function readMemberNotification(Request $request): Response
|
||||
{
|
||||
$memberId = (int) $this->currentMember->id();
|
||||
$notificationId = trim((string) $request->input('id', ''));
|
||||
if ($memberId <= 0) {
|
||||
return Json::fail('请先登录', [], 401);
|
||||
}
|
||||
if ($notificationId === '') {
|
||||
return Json::fail('通知ID不能为空');
|
||||
}
|
||||
|
||||
$notification = MemberNotification::query()
|
||||
->where('member_id', $memberId)
|
||||
->whereKey($notificationId)
|
||||
->first();
|
||||
if (!$notification) {
|
||||
return Json::fail('通知不存在');
|
||||
}
|
||||
if ((int) $notification->status === 1) {
|
||||
return Json::success('ok');
|
||||
}
|
||||
|
||||
$notification->fill(['status' => 1, 'read_at' => time()])->save();
|
||||
return Json::success('ok');
|
||||
}
|
||||
|
||||
#[OA\Post(path: '/produce/exception/record', summary: '提交异常记录', tags: ['produce-web'])]
|
||||
#[SimpleResponse(description: '提交成功')]
|
||||
#[AllowAnonymous(requireToken: false, requirePermission: false, description: '公共接口')]
|
||||
@@ -1382,9 +1631,17 @@ final class DataEntryController extends Base
|
||||
protected function applyExceptionRecordFilter($query, Request $request): void
|
||||
{
|
||||
$userCode = trim((string) $request->input('user_code', ''));
|
||||
$startAt = $this->parseQueryDate($request->input('start_date'));
|
||||
$endAt = $this->parseQueryDate($request->input('end_date'), true);
|
||||
if ($userCode !== '') {
|
||||
$query->where('user_code', $userCode);
|
||||
}
|
||||
if ($startAt !== null) {
|
||||
$query->where('reported_at', '>=', $startAt);
|
||||
}
|
||||
if ($endAt !== null) {
|
||||
$query->where('reported_at', '<=', $endAt);
|
||||
}
|
||||
}
|
||||
|
||||
protected function applyScanDataFilter($query, Request $request): void
|
||||
@@ -1395,6 +1652,8 @@ final class DataEntryController extends Base
|
||||
$stepId = (int) $request->input('step_id', 0);
|
||||
$statusId = (int) $request->input('status_id', 0);
|
||||
$scanResult = $request->input('scan_result', '');
|
||||
$startAt = $this->parseQueryDate($request->input('start_date'));
|
||||
$endAt = $this->parseQueryDate($request->input('end_date'), true);
|
||||
|
||||
if ($orderNo !== '') {
|
||||
$query->where('order_no', $orderNo);
|
||||
@@ -1415,12 +1674,19 @@ final class DataEntryController extends Base
|
||||
$query->where('scan_result', (int) $scanResult);
|
||||
}
|
||||
|
||||
if ($startAt !== null) {
|
||||
$query->where('scan_time', '>=', $startAt);
|
||||
}
|
||||
if ($endAt !== null) {
|
||||
$query->where('scan_time', '<=', $endAt);
|
||||
}
|
||||
|
||||
$recentOnly = (int) ProduceConfig::query()
|
||||
->where('group_code', 'basic_setting')
|
||||
->where('code', 'recent_records_today_only')
|
||||
->value('content');
|
||||
|
||||
if ($recentOnly === 1 && $orderNo === '') {
|
||||
if ($recentOnly === 1 && $orderNo === '' && $startAt === null && $endAt === null) {
|
||||
$start = strtotime(date('Y-m-d 00:00:00'));
|
||||
$end = strtotime(date('Y-m-d 23:59:59'));
|
||||
$query->whereBetween('scan_time', [$start, $end]);
|
||||
@@ -1429,10 +1695,17 @@ final class DataEntryController extends Base
|
||||
|
||||
protected function applyProductRecordFilter($query, Request $request): void
|
||||
{
|
||||
$userCode = trim((string) $request->input('user_code', ''));
|
||||
$processId = (int) $request->input('process_id', 0);
|
||||
$stepId = (int) $request->input('step_id', 0);
|
||||
$statusId = (int) $request->input('status_id', 0);
|
||||
$scanResult = $request->input('scan_result', '');
|
||||
$startAt = $this->parseQueryDate($request->input('start_date'));
|
||||
$endAt = $this->parseQueryDate($request->input('end_date'), true);
|
||||
|
||||
if ($userCode !== '') {
|
||||
$query->where('user_code', $userCode);
|
||||
}
|
||||
|
||||
if ($processId > 0) {
|
||||
$query->where('process_id', $processId);
|
||||
@@ -1447,12 +1720,19 @@ final class DataEntryController extends Base
|
||||
$query->where('scan_result', (int) $scanResult);
|
||||
}
|
||||
|
||||
if ($startAt !== null) {
|
||||
$query->where('created_at', '>=', $startAt);
|
||||
}
|
||||
if ($endAt !== null) {
|
||||
$query->where('created_at', '<=', $endAt);
|
||||
}
|
||||
|
||||
$recentOnly = (int) ProduceConfig::query()
|
||||
->where('group_code', 'basic_setting')
|
||||
->where('code', 'recent_records_today_only')
|
||||
->value('content');
|
||||
|
||||
if ($recentOnly === 1) {
|
||||
if ($recentOnly === 1 && $startAt === null && $endAt === null) {
|
||||
$start = strtotime(date('Y-m-d 00:00:00'));
|
||||
$end = strtotime(date('Y-m-d 23:59:59'));
|
||||
$query->whereBetween('created_at', [$start, $end]);
|
||||
@@ -1475,6 +1755,71 @@ final class DataEntryController extends Base
|
||||
return $data;
|
||||
}
|
||||
|
||||
/** 创建作业不良责任通知,每名责任人仅保留一条通知。 */
|
||||
private function createRepairResponsibilityNotifications(
|
||||
RepairRecord $repair,
|
||||
ExceptionRecord $exception,
|
||||
array $snapshots,
|
||||
Member $repairer,
|
||||
int $now
|
||||
): void {
|
||||
$snapshotsByUserCode = [];
|
||||
foreach ($snapshots as $snapshot) {
|
||||
$userCode = trim((string) ($snapshot['user_code'] ?? ''));
|
||||
if ($userCode !== '') {
|
||||
$snapshotsByUserCode[$userCode][] = $snapshot;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($snapshotsByUserCode as $userCode => $userSnapshots) {
|
||||
$responsibleMember = Member::query()->where('username', $userCode)->first();
|
||||
if (!$responsibleMember) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$steps = array_values(array_unique(array_filter(array_map(static function (array $snapshot): string {
|
||||
return implode(' / ', array_filter([
|
||||
trim((string) ($snapshot['process_name'] ?? '')),
|
||||
trim((string) ($snapshot['step_name'] ?? '')),
|
||||
]));
|
||||
}, $userSnapshots))));
|
||||
MemberNotification::query()->create([
|
||||
'id' => Snowflake::generate(),
|
||||
'member_id' => $responsibleMember->id,
|
||||
'user_code' => $userCode,
|
||||
'type' => 'repair_responsibility',
|
||||
'title' => '维修作业不良责任通知',
|
||||
'content' => $this->formatRepairResponsibilityContent($repair, $exception, $steps, $repairer),
|
||||
'repair_id' => $repair->id,
|
||||
'repair_no' => $repair->repair_no,
|
||||
'order_no' => $repair->order_no,
|
||||
'status' => 0,
|
||||
'created_at' => $now,
|
||||
'updated_at' => $now,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/** 组装维修定责通知的原因说明。 */
|
||||
private function formatRepairResponsibilityContent(
|
||||
RepairRecord $repair,
|
||||
ExceptionRecord $exception,
|
||||
array $steps,
|
||||
Member $repairer
|
||||
): string {
|
||||
$content = [
|
||||
sprintf('订单 %s 已完成维修,维修判定为作业不良责任。', $repair->order_no),
|
||||
$steps === [] ? '' : '责任工序/步骤:' . implode('、', $steps),
|
||||
$exception->reason_name ? '异常原因:' . $exception->reason_name : '',
|
||||
$exception->issue_name ? '具体问题:' . $exception->issue_name : '',
|
||||
$repair->remark ? '维修说明:' . $repair->remark : '',
|
||||
'维修单:' . $repair->repair_no,
|
||||
'提交人:' . ($repairer->nickname ?: $repairer->username),
|
||||
];
|
||||
|
||||
return implode("\n", array_filter($content));
|
||||
}
|
||||
|
||||
protected function handleNextStepTransitions(ScanData $currentScanData, Request $request, Member $member, int $now): void
|
||||
{
|
||||
$previousRelations = \plugin\produce\app\model\StepRelation::query()
|
||||
|
||||
@@ -186,6 +186,27 @@ final class DataEntryController
|
||||
return $this->delegate->repairRecord($request, $repairRecordService);
|
||||
}
|
||||
|
||||
#[OA\Get(path: '/produce/member-notification', summary: 'APP 获取当前会员生产通知', tags: ['produce-app'])]
|
||||
#[DataResponse(description: '获取成功')]
|
||||
public function memberNotifications(Request $request): Response
|
||||
{
|
||||
return $this->delegate->memberNotifications($request);
|
||||
}
|
||||
|
||||
#[OA\Get(path: '/produce/member-notification/unread-count', summary: 'APP 获取未读生产通知数', tags: ['produce-app'])]
|
||||
#[DataResponse(description: '获取成功')]
|
||||
public function memberNotificationUnreadCount(): Response
|
||||
{
|
||||
return $this->delegate->memberNotificationUnreadCount();
|
||||
}
|
||||
|
||||
#[OA\Put(path: '/produce/member-notification/read', summary: 'APP 标记生产通知已读', tags: ['produce-app'])]
|
||||
#[SimpleResponse(description: '操作成功')]
|
||||
public function readMemberNotification(Request $request): Response
|
||||
{
|
||||
return $this->delegate->readMemberNotification($request);
|
||||
}
|
||||
|
||||
#[OA\Post(path: '/produce/exception/record', summary: 'APP 提交异常记录', tags: ['produce-app'])]
|
||||
#[SimpleResponse(description: '提交成功')]
|
||||
#[AllowAnonymous(requireToken: false, requirePermission: false, description: 'APP 公共接口')]
|
||||
@@ -208,6 +229,13 @@ final class DataEntryController
|
||||
return $this->delegate->productStatistics($request);
|
||||
}
|
||||
|
||||
#[OA\Get(path: '/produce/dashboard/today', summary: 'APP 获取首页今日生产总览', tags: ['produce-app'])]
|
||||
#[DataResponse(description: '获取成功')]
|
||||
public function todayDashboard(): Response
|
||||
{
|
||||
return $this->delegate->todayDashboard();
|
||||
}
|
||||
|
||||
#[OA\Get(path: '/produce/product-record/user-code-stats', summary: 'APP 整机用户统计', tags: ['produce-app'])]
|
||||
#[DataResponse(description: '获取成功')]
|
||||
public function productUserCodeStats(Request $request): Response
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace plugin\produce\app\model;
|
||||
|
||||
class MemberNotification extends BaseModel
|
||||
{
|
||||
protected $table = 'produce_member_notification';
|
||||
|
||||
protected $appends = ['created_date', 'read_date'];
|
||||
|
||||
protected $fillable = [
|
||||
'id', 'member_id', 'user_code', 'type', 'title', 'content', 'repair_id', 'repair_no',
|
||||
'order_no', 'status', 'read_at', 'created_at', 'updated_at', 'deleted_at',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'member_id' => 'integer',
|
||||
'repair_id' => 'integer',
|
||||
'status' => 'integer',
|
||||
'read_at' => 'integer',
|
||||
'created_at' => 'integer',
|
||||
'updated_at' => 'integer',
|
||||
'deleted_at' => 'integer',
|
||||
];
|
||||
|
||||
public function getReadDateAttribute(): ?string
|
||||
{
|
||||
return $this->read_at ? date('Y-m-d H:i:s', (int) $this->read_at) : null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user