| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945 |
- <?php
- namespace app\api\controller;
- use app\common\controller\Api;
- use think\Db;
- use think\exception\HttpResponseException;
- /**
- * MES-ERP 工单同步
- */
- class Synchronization extends Api
- {
- protected $noNeedLogin = ['*'];
- protected $noNeedRight = ['*'];
- /** @var int 同步状态:待同步 */
- const SYNC_STATUS_PENDING = 0;
- /** @var int 同步状态:成功 */
- const SYNC_STATUS_SUCCESS = 1;
- /** @var int 同步状态:失败 */
- const SYNC_STATUS_FAILED = 2;
- /** @var int 操作状态:成功 */
- const OPERATE_STATUS_SUCCESS = 1;
- /** @var int 操作状态:失败 */
- const OPERATE_STATUS_FAILED = 2;
- /** @var int 操作状态:处理中 */
- const OPERATE_STATUS_PROCESSING = 3;
- /**
- * ERP SQL Server 表映射(请按实际 ERP 表结构填写 table 与 fields)
- * fields:MES 源字段名 => ERP 目标字段名;固定值以 = 开头,如 '=M' => 'is_mfg_wrk'
- * 报工 sfb_id 规则:K+年月日(同步日期)+四位自增,如 K2606100201
- */
- protected $erpTableMap = [
- // 1-工单数据:来源 工单_基本资料
- 'work_order' => [
- 'table' => 'mps_mfg_ord',
- 'fields' => [
- '订单编号' => 'ordno',
- '=M' => 'is_mfg_wrk',
- '生产款号' => 'itemno',
- '接单日期' => 'cr_date',
- '落货日期' => 'rel_date',
- '同步日期' => 'due_date',
- '订单数量' => 'qty_ord',
- 'remark' => 'remark',
- '计划制造工分' => 'curprc',
- '=0001' => 'ocode',
- ],
- ],
- // 2-工艺数据:来源 工单_基础工艺资料
- 'process' => [
- 'table' => 'mps_mfg_routing',
- 'fields' => [
- 'work_order' => 'ordno',
- '=M' => 'is_mfg_wrk',
- '生产款号' => 'itemno',
- 'process_code' => 'op',
- 'process_name' => 'routiname',
- 'standard_score' => 'preprc',
- '=0001' => 'ocode',
- '订单数量' => 'qty_ord',
- ],
- ],
- // 3-报工数据:来源工序产量核查汇总(sfb_id 按同步日期自动生成)
- 'report' => [
- 'table' => 'wfc_shift_feedback',
- 'fields' => [
- '员工编号' => 'emp_no',
- '=1' => 'shift_id',
- '订单编号' => 'ordno',
- '生产款号' => 'itemno',
- '工序号' => 'op',
- '工序名称' => 'routiname',
- '数量' => 'qty_comp',
- '开工日期' => 'op_begdt',
- '完工日期' => 'op_enddt',
- '填报日期' => 'tran_date',
- '=0202' => 'centerno',
- '=0001.02.02' => 'detno',
- '=0001' => 'ocode',
- '工分' => 'preprc',
- ],
- ],
- ];
- /**
- * 完工工单列表(含同步状态)
- * @ApiMethod (GET)
- * @param page 页码
- * @param limit 每页条数
- * @param search 搜索(订单编号/款号/款式/客户)
- * @param sync_status 同步状态筛选:unsynced|pending|synced|failed|cancelled
- */
- public function getCompletedWorkOrderList()
- {
- if (!$this->request->isGet()) {
- $this->error('请求方法错误');
- }
- $param = $this->request->param();
- $page = max(1, (int)(isset($param['page']) ? $param['page'] : 1));
- $limit = max(15, min(100, (int)(isset($param['limit']) ? $param['limit'] : 15)));
-
- $baseWhere = function ($query) use ($param) {
- $query->whereNull('Mod_rq')
- ->whereNotNull('工单完工日期')
- ->where('工单完工日期', '<>', '');
- if (!empty($param['search'])) {
- $query->where('订单编号|生产款号|客户编号|款式|工单完工日期', 'like', '%' . trim($param['search']) . '%');
- }
- };
- $countQuery = \db('工单_基本资料');
- $baseWhere($countQuery);
- $total = (int)$countQuery->count();
- $listQuery = \db('工单_基本资料');
- $baseWhere($listQuery);
- $list = $listQuery
- ->field('Uniqid,订单编号,生产款号,款式,客户编号,工单完工日期,remark,gd_statu,Sys_id,Sys_rq')
- ->order('工单完工日期 desc,订单编号 desc')
- ->page($page, $limit)
- ->select();
- if (!empty($list)) {
- $orderNos = array_column($list, '订单编号');
- $statusMap = $this->batchResolveDisplaySyncStatus($orderNos);
- foreach ($list as &$row) {
- $orderNo = $row['订单编号'];
- $statusInfo = isset($statusMap[$orderNo]) ? $statusMap[$orderNo] : $this->defaultDisplaySyncStatus();
- $row['sync_status'] = $statusInfo['code'];
- $row['sync_status_text'] = $statusInfo['text'];
- $row['同步状态'] = $statusInfo['text'];
- $row['last_sync_time'] = $statusInfo['sync_time'];
- $row['last_sync_msg'] = $statusInfo['sync_msg'];
- $row['last_sync_log_id'] = $statusInfo['sync_log_id'];
- }
- unset($row);
- if (!empty($param['sync_status'])) {
- $filter = $param['sync_status'];
- $list = array_values(array_filter($list, function ($row) use ($filter) {
- return $row['sync_status'] === $filter;
- }));
- }
- }
- $this->success('成功', [
- 'total' => $total,
- 'page' => $page,
- 'limit' => $limit,
- 'list' => $list ?: [],
- ]);
- }
- /**
- * 同步到 ERP
- * @ApiMethod (POST)
- * @param work_order_no 工单编号
- * @param sync_module 1-工单 2-工艺 3-报工 4-全量
- * @param operator 操作人
- * @param operate_source backend|api|crontab,默认 backend
- * @param operate_type sync|manual|auto,默认 manual
- */
- public function syncWorkOrder()
- {
- $this->executeSync('sync');
- }
- /**
- * 重试同步(仅上次失败时可重试)
- * @ApiMethod (POST)
- */
- public function retrySyncWorkOrder()
- {
- $params = $this->request->post();
- $workOrderNo = isset($params['work_order_no']) ? trim($params['work_order_no']) : '';
- if ($workOrderNo === '') {
- $this->error('工单编号不能为空');
- }
- $statusInfo = $this->resolveDisplaySyncStatus($workOrderNo);
- if ($statusInfo['code'] !== 'failed') {
- $this->error('仅同步失败的工单可重试');
- }
- $this->executeSync('retry');
- }
- /**
- * 撤回同步
- * @ApiMethod (POST)
- * @param work_order_no 工单编号
- * @param operator 操作人
- * @param reason 撤回原因
- */
- public function cancelSyncWorkOrder()
- {
- if (!$this->request->isPost()) {
- $this->error('请求方法错误');
- }
- $params = $this->request->post();
- $workOrderNo = isset($params['work_order_no']) ? trim($params['work_order_no']) : '';
- $operator = isset($params['operator']) ? trim($params['operator']) : '';
- $reason = isset($params['reason']) ? trim($params['reason']) : '';
- if ($workOrderNo === '') {
- $this->error('工单编号不能为空');
- }
- if ($operator === '') {
- $this->error('操作人不能为空');
- }
- $workOrder = $this->fetchWorkOrderBasic($workOrderNo);
- if (empty($workOrder)) {
- $this->error('工单不存在');
- }
- $statusInfo = $this->resolveDisplaySyncStatus($workOrderNo);
- if ($statusInfo['code'] !== 'synced') {
- $this->error('仅已同步的工单可撤回');
- }
- $operTime = date('Y-m-d H:i:s');
- $operateParam = json_encode([
- 'work_order_no' => $workOrderNo,
- 'reason' => $reason,
- ], JSON_UNESCAPED_UNICODE);
- $operateContent = '撤回同步工单' . $workOrderNo . ($reason !== '' ? ',原因:' . $reason : '');
- $operationLogId = $this->insertOperationLog([
- 'operate_type' => 'cancel',
- 'operate_user' => $operator,
- 'operate_user_name' => isset($params['operate_user_name']) ? $params['operate_user_name'] : $operator,
- 'operate_time' => $operTime,
- 'sync_log_id' => $statusInfo['sync_log_id'],
- 'work_order_no' => $workOrderNo,
- 'style_no' => $workOrder['生产款号'],
- 'operate_content' => $operateContent,
- 'operate_param' => $operateParam,
- 'operate_status' => self::OPERATE_STATUS_PROCESSING,
- 'operate_source' => isset($params['operate_source']) ? $params['operate_source'] : 'backend',
- 'operate_ip' => $this->request->ip(),
- 'server_ip' => isset($_SERVER['SERVER_ADDR']) ? $_SERVER['SERVER_ADDR'] : '',
- ]);
- Db::startTrans();
- try {
- $this->deleteErpDataByWorkOrder($workOrderNo);
- Db::name('mes_erp_operation_log')
- ->where('id', $operationLogId)
- ->update([
- 'operate_status' => self::OPERATE_STATUS_SUCCESS,
- ]);
- if (!empty($statusInfo['sync_log_id'])) {
- Db::name('mes_erp_work_order_sync_log')
- ->where('id', $statusInfo['sync_log_id'])
- ->update([
- 'sync_msg' => '已撤回' . ($reason !== '' ? ':' . $reason : ''),
- 'update_time' => $operTime,
- ]);
- }
- Db::commit();
- } catch (HttpResponseException $e) {
- throw $e;
- } catch (\Exception $e) {
- Db::rollback();
- Db::name('mes_erp_operation_log')
- ->where('id', $operationLogId)
- ->update([
- 'operate_status' => self::OPERATE_STATUS_FAILED,
- 'error_msg' => $e->getMessage(),
- ]);
- $this->error('撤回失败:' . $e->getMessage());
- }
- $this->success('撤回成功');
- }
- /**
- * 同步日志列表
- * @ApiMethod (GET)
- * @param work_order_no 工单编号
- */
- public function getSyncLogList()
- {
- if (!$this->request->isGet()) {
- $this->error('请求方法错误');
- }
- $workOrderNo = trim($this->request->param('work_order_no', ''));
- if ($workOrderNo === '') {
- $this->error('工单编号不能为空');
- }
- $list = Db::name('mes_erp_work_order_sync_log')
- ->where('work_order_no', $workOrderNo)
- ->order('id desc')
- ->select();
- foreach ($list as &$row) {
- $row['sync_status_text'] = $this->syncStatusText($row['sync_status']);
- $row['sync_module_text'] = $this->syncModuleText($row['sync_module']);
- }
- unset($row);
- $this->success('成功', $list ?: []);
- }
- /**
- * 操作过程记录列表
- * @ApiMethod (GET)
- * @param work_order_no 工单编号
- * @param sync_log_id 同步日志ID(可选)
- */
- public function getOperationLogList()
- {
- if (!$this->request->isGet()) {
- $this->error('请求方法错误');
- }
- $workOrderNo = trim($this->request->param('work_order_no', ''));
- $syncLogId = $this->request->param('sync_log_id');
- $query = Db::name('mes_erp_operation_log')->order('id desc');
- if ($workOrderNo !== '') {
- $query->where('work_order_no', $workOrderNo);
- }
- if ($syncLogId !== null && $syncLogId !== '') {
- $query->where('sync_log_id', intval($syncLogId));
- }
- if ($workOrderNo === '' && ($syncLogId === null || $syncLogId === '')) {
- $this->error('请传入 work_order_no 或 sync_log_id');
- }
- $list = $query->select();
- foreach ($list as &$row) {
- $row['operate_status_text'] = $this->operateStatusText($row['operate_status']);
- }
- unset($row);
- $this->success('成功', $list ?: []);
- }
- /**
- * 执行同步核心逻辑
- * @param string $operateType sync|retry|manual|auto
- */
- private function executeSync($operateType)
- {
- if (!$this->request->isPost()) {
- $this->error('请求方法错误');
- }
- $params = $this->request->post();
- $workOrderNo = isset($params['work_order_no']) ? trim($params['work_order_no']) : '';
- $syncModule = isset($params['sync_module']) ? (string)$params['sync_module'] : '4';
- $operator = isset($params['operator']) ? trim($params['operator']) : '';
- if ($workOrderNo === '') {
- $this->error('工单编号不能为空');
- }
- if ($operator === '') {
- $this->error('操作人不能为空');
- }
- if (!in_array($syncModule, ['1', '2', '3', '4'], true)) {
- $this->error('同步模块参数错误');
- }
- $workOrder = $this->fetchWorkOrderBasic($workOrderNo);
- if (empty($workOrder)) {
- $this->error('工单不存在');
- }
- if (empty($workOrder['工单完工日期'])) {
- $this->error('该工单尚未完工,不能同步');
- }
- $operTime = date('Y-m-d H:i:s');
- $operateSource = isset($params['operate_source']) ? $params['operate_source'] : 'backend';
- $requestOperateType = isset($params['operate_type']) ? $params['operate_type'] : 'manual';
- if ($operateType === 'retry') {
- $requestOperateType = 'retry';
- }
- $operateParam = json_encode([
- 'work_order_no' => $workOrderNo,
- 'sync_module' => $syncModule,
- 'operator' => $operator,
- ], JSON_UNESCAPED_UNICODE);
- $operationLogId = $this->insertOperationLog([
- 'operate_type' => $requestOperateType,
- 'operate_user' => $operator,
- 'operate_user_name' => isset($params['operate_user_name']) ? $params['operate_user_name'] : $operator,
- 'operate_time' => $operTime,
- 'sync_log_id' => null,
- 'work_order_no' => $workOrderNo,
- 'style_no' => $workOrder['生产款号'],
- 'operate_content' => $this->buildSyncOperateContent($workOrderNo, $syncModule, $requestOperateType),
- 'operate_param' => $operateParam,
- 'operate_status' => self::OPERATE_STATUS_PROCESSING,
- 'operate_source' => $operateSource,
- 'operate_ip' => $this->request->ip(),
- 'server_ip' => isset($_SERVER['SERVER_ADDR']) ? $_SERVER['SERVER_ADDR'] : '',
- ]);
- $seqRange = $this->fetchReportSeqRange($workOrderNo);
- $syncLogId = $this->insertSyncLog([
- 'work_order_no' => $workOrderNo,
- 'style_no' => $workOrder['生产款号'],
- 'complete_date' => $this->formatDateValue($workOrder['工单完工日期']),
- 'operator' => $operator,
- 'sync_time' => $operTime,
- 'report_start_seq' => $seqRange['start'],
- 'report_end_seq' => $seqRange['end'],
- 'sync_status' => self::SYNC_STATUS_PENDING,
- 'sync_module' => $syncModule,
- 'sync_msg' => '同步处理中',
- 'erp_sync_id' => '',
- ]);
- Db::name('mes_erp_operation_log')
- ->where('id', $operationLogId)
- ->update(['sync_log_id' => $syncLogId]);
- try {
- $erpSyncId = $this->pushDataToErp($workOrderNo, $syncModule, $workOrder);
- Db::name('mes_erp_work_order_sync_log')
- ->where('id', $syncLogId)
- ->update([
- 'sync_status' => self::SYNC_STATUS_SUCCESS,
- 'sync_msg' => '同步成功',
- 'erp_sync_id' => $erpSyncId,
- 'update_time' => date('Y-m-d H:i:s'),
- ]);
- Db::name('mes_erp_operation_log')
- ->where('id', $operationLogId)
- ->update(['operate_status' => self::OPERATE_STATUS_SUCCESS]);
- } catch (HttpResponseException $e) {
- throw $e;
- } catch (\Exception $e) {
- Db::name('mes_erp_work_order_sync_log')
- ->where('id', $syncLogId)
- ->update([
- 'sync_status' => self::SYNC_STATUS_FAILED,
- 'sync_msg' => $e->getMessage(),
- 'update_time' => date('Y-m-d H:i:s'),
- ]);
- Db::name('mes_erp_operation_log')
- ->where('id', $operationLogId)
- ->update([
- 'operate_status' => self::OPERATE_STATUS_FAILED,
- 'error_msg' => $e->getMessage(),
- ]);
- $this->error('同步失败:' . $e->getMessage());
- }
- $this->success('同步成功', [
- 'sync_log_id' => $syncLogId,
- 'operation_log_id' => $operationLogId,
- ]);
- }
- /**
- * 推送数据到 ERP(db2 / SQL Server)
- * @param string $workOrderNo
- * @param string $syncModule
- * @param array $workOrder
- * @return string ERP 同步标识
- * @throws \Exception
- */
- private function pushDataToErp($workOrderNo, $syncModule, $workOrder)
- {
- $erpDb = $this->getErpDb();
- $modules = $syncModule === '4' ? ['1', '2', '3'] : [$syncModule];
- $inserted = [];
- $syncDate = date('Y-m-d');
- $workOrder['同步日期'] = $syncDate;
- $erpDb->startTrans();
- try {
- foreach ($modules as $module) {
- if ($module === '1') {
- $rows = [$workOrder];
- $count = $this->insertErpRows($erpDb, 'work_order', $rows);
- $inserted['work_order'] = $count;
- } elseif ($module === '2') {
- $rows = $this->fetchWorkOrderProcessList($workOrderNo);
- if (empty($rows)) {
- throw new \Exception('工单工艺数据为空');
- }
- foreach ($rows as &$processRow) {
- $processRow['生产款号'] = $workOrder['生产款号'];
- $processRow['订单数量'] = $workOrder['订单数量'];
- }
- unset($processRow);
- $count = $this->insertErpRows($erpDb, 'process', $rows);
- $inserted['process'] = $count;
- } elseif ($module === '3') {
- $rows = $this->fetchProductionCheckRows($workOrderNo, $syncDate);
- if (empty($rows)) {
- throw new \Exception('报工数据为空,请先完成报工');
- }
- $count = $this->insertErpRows($erpDb, 'report', $rows, $syncDate);
- $inserted['report'] = $count;
- }
- }
- $erpDb->commit();
- } catch (\Exception $e) {
- $erpDb->rollback();
- throw $e;
- }
- return $workOrderNo . '_' . $syncModule . '_' . time();
- }
- /**
- * 写入 ERP 表
- */
- private function insertErpRows($erpDb, $tableKey, array $sourceRows, $syncDate = null)
- {
- $config = $this->erpTableMap[$tableKey];
- if (empty($config['table'])) {
- throw new \Exception('请先在 Synchronization.php 中配置 ERP [' . $tableKey . '] 表名');
- }
- $sfbSeq = 0;
- $sfbPrefix = '';
- if ($tableKey === 'report') {
- $syncDate = $syncDate ?: date('Y-m-d');
- $sfbPrefix = 'K' . date('ymd', strtotime($syncDate));
- $sfbSeq = $this->fetchMaxSfbSeq($erpDb, $sfbPrefix);
- }
- $erpRows = [];
- foreach ($sourceRows as $sourceRow) {
- $mapped = $this->mapSourceRowToErp($sourceRow, $config['fields']);
- if ($tableKey === 'report') {
- $sfbSeq++;
- $mapped['sfb_id'] = $sfbPrefix . str_pad((string)$sfbSeq, 4, '0', STR_PAD_LEFT);
- }
- if (!empty($mapped)) {
- $erpRows[] = $mapped;
- }
- }
- if (empty($erpRows)) {
- throw new \Exception('ERP [' . $tableKey . '] 字段映射未配置或映射结果为空');
- }
- $result = $erpDb->name($config['table'])->insertAll($erpRows);
- if ($result === false || $result === 0) {
- throw new \Exception('写入 ERP [' . $tableKey . '] 失败');
- }
- return (int)$result;
- }
- /**
- * 查询 ERP 报工表当日 sfb_id 最大序号(K+年月日+四位自增)
- * 须对末四位做数值 MAX,字符串 MAX 在序号≥10 时会算错导致主键冲突
- */
- private function fetchMaxSfbSeq($erpDb, $sfbPrefix)
- {
- $row = $erpDb->query(
- 'SELECT MAX(CAST(RIGHT(sfb_id, 4) AS INT)) AS max_seq FROM wfc_shift_feedback WHERE sfb_id LIKE ?',
- [$sfbPrefix . '%']
- );
- if (empty($row) || !isset($row[0]['max_seq']) || $row[0]['max_seq'] === null) {
- return 0;
- }
- return (int)$row[0]['max_seq'];
- }
- /**
- * 撤回时删除 ERP 数据(需配置表名及工单编号字段)
- */
- private function deleteErpDataByWorkOrder($workOrderNo)
- {
- $erpDb = $this->getErpDb();
- foreach (['work_order', 'process', 'report'] as $tableKey) {
- $config = $this->erpTableMap[$tableKey];
- if (empty($config['table'])) {
- continue;
- }
- $workOrderField = $this->resolveErpWorkOrderField($tableKey, $config['fields']);
- if ($workOrderField === '') {
- continue;
- }
- $erpDb->name($config['table'])->where($workOrderField, $workOrderNo)->delete();
- }
- }
- /**
- * MES 行映射为 ERP 行
- */
- private function mapSourceRowToErp(array $sourceRow, array $fieldMap)
- {
- $erpRow = [];
- foreach ($fieldMap as $mesField => $erpField) {
- if ($erpField === '' || $erpField === null) {
- continue;
- }
- if ($mesField !== '' && $mesField[0] === '=') {
- $erpRow[$erpField] = substr($mesField, 1);
- continue;
- }
- if (array_key_exists($mesField, $sourceRow)) {
- $erpRow[$erpField] = $sourceRow[$mesField];
- }
- }
- return $erpRow;
- }
- private function resolveErpWorkOrderField($tableKey, array $fieldMap)
- {
- $candidates = ['订单编号', 'work_order', 'work_order_no'];
- if ($tableKey === 'work_order') {
- $candidates = ['订单编号', 'work_order_no'];
- }
- foreach ($candidates as $mesField) {
- if (isset($fieldMap[$mesField]) && $fieldMap[$mesField] !== '') {
- return $fieldMap[$mesField];
- }
- }
- return '';
- }
- /**
- * 工序产量核查数据(与 WorkOrderProcess::checkProcessProduction 一致,扁平化为报工行)
- */
- private function fetchProductionCheckRows($workOrderNo, $syncDate = null)
- {
- $syncDate = $syncDate ?: date('Y-m-d');
- $list = Db::name('设备_工分计酬')->alias('a')
- ->join('工单_基本资料 b', 'a.work_order = b.订单编号 AND b.Mod_rq IS NULL', 'LEFT')
- ->where('a.work_order', $workOrderNo)
- ->whereNull('a.del_rq')
- ->field('b.订单编号,b.生产款号,b.款式,a.process_code as 工序号,a.process_name as 工序名称,a.staff_no as 员工编号,a.staff_name as 员工姓名,
- SUM(a.number) as 数量,MIN(a.date) as 开工日期,MAX(a.date) as 完工日期,
- SUM(a.standard_score) as 工分,SUM(a.production_score) as 工时,SUM(a.salary) as 工资')
- ->group('b.订单编号,b.生产款号,b.款式,a.process_code,a.process_name,a.staff_no,a.staff_name')
- ->order('a.process_code asc,a.staff_no asc')
- ->select();
- if (empty($list)) {
- return [];
- }
- $rows = [];
- foreach ($list as $row) {
- $rows[] = [
- '订单编号' => $row['订单编号'],
- '生产款号' => $row['生产款号'],
- '款式' => $row['款式'],
- '工序号' => $row['工序号'],
- '工序名称' => $row['工序名称'],
- '员工编号' => $row['员工编号'],
- '员工姓名' => $row['员工姓名'],
- '数量' => floatval($row['数量']),
- '开工日期' => !empty($row['开工日期']) ? date('Y-m-d', strtotime($row['开工日期'])) : '',
- '完工日期' => !empty($row['完工日期']) ? date('Y-m-d', strtotime($row['完工日期'])) : '',
- '填报日期' => $syncDate,
- '工分' => round(floatval($row['工分']), 4),
- '工时' => round(floatval($row['工时']), 4),
- '工资' => round(floatval($row['工资']), 4),
- ];
- }
- return $rows;
- }
- private function fetchWorkOrderBasic($workOrderNo)
- {
- return Db::table('工单_基本资料')
- ->where('订单编号', $workOrderNo)
- ->whereNull('Mod_rq')
- ->find();
- }
- private function fetchWorkOrderProcessList($workOrderNo)
- {
- return Db::table('工单_基础工艺资料')
- ->where('work_order', $workOrderNo)
- ->whereNull('del_rq')
- ->field('work_order,part_code,part_name,process_code,process_name,big_process,
- standard_hour,standard_minutes,standard_score,money,coefficient,remark')
- ->order('process_code')
- ->select();
- }
- private function fetchReportSeqRange($workOrderNo)
- {
- $range = Db::table('设备_工分计酬')
- ->where('work_order', $workOrderNo)
- ->whereNull('del_rq')
- ->field('MIN(id) as min_id,MAX(id) as max_id')
- ->find();
- return [
- 'start' => !empty($range['min_id']) ? (string)$range['min_id'] : '',
- 'end' => !empty($range['max_id']) ? (string)$range['max_id'] : '',
- ];
- }
- private function insertSyncLog(array $data)
- {
- $data['create_time'] = date('Y-m-d H:i:s');
- $data['update_time'] = $data['create_time'];
- return (int)Db::name('mes_erp_work_order_sync_log')->insertGetId($data);
- }
- private function insertOperationLog(array $data)
- {
- $data['create_time'] = date('Y-m-d H:i:s');
- return (int)Db::name('mes_erp_operation_log')->insertGetId($data);
- }
- private function getErpDb()
- {
- return Db::connect(config('database.db2'));
- }
- private function batchResolveDisplaySyncStatus(array $orderNos)
- {
- $orderNos = array_values(array_unique(array_filter($orderNos)));
- if (empty($orderNos)) {
- return [];
- }
- $defaultStatus = $this->defaultDisplaySyncStatus();
- $result = [];
- foreach ($orderNos as $orderNo) {
- $result[$orderNo] = $defaultStatus;
- }
- $latestSyncRows = Db::name('mes_erp_work_order_sync_log')
- ->whereIn('work_order_no', $orderNos)
- ->order('id desc')
- ->select();
- $latestSyncMap = [];
- foreach ($latestSyncRows as $row) {
- if (!isset($latestSyncMap[$row['work_order_no']])) {
- $latestSyncMap[$row['work_order_no']] = $row;
- }
- }
- $successSyncRows = Db::name('mes_erp_work_order_sync_log')
- ->whereIn('work_order_no', $orderNos)
- ->where('sync_status', self::SYNC_STATUS_SUCCESS)
- ->order('id desc')
- ->select();
- $latestSuccessSyncMap = [];
- foreach ($successSyncRows as $row) {
- if (!isset($latestSuccessSyncMap[$row['work_order_no']])) {
- $latestSuccessSyncMap[$row['work_order_no']] = $row;
- }
- }
- $cancelRows = Db::name('mes_erp_operation_log')
- ->whereIn('work_order_no', $orderNos)
- ->where('operate_type', 'cancel')
- ->where('operate_status', self::OPERATE_STATUS_SUCCESS)
- ->order('id desc')
- ->select();
- $latestCancelMap = [];
- foreach ($cancelRows as $row) {
- if (!isset($latestCancelMap[$row['work_order_no']])) {
- $latestCancelMap[$row['work_order_no']] = $row;
- }
- }
- foreach ($orderNos as $orderNo) {
- $latestSync = isset($latestSyncMap[$orderNo]) ? $latestSyncMap[$orderNo] : null;
- if (empty($latestSync)) {
- continue;
- }
- $latestSuccessSync = isset($latestSuccessSyncMap[$orderNo]) ? $latestSuccessSyncMap[$orderNo] : null;
- if (!empty($latestSuccessSync)) {
- $cancelAfterSync = isset($latestCancelMap[$orderNo]) ? $latestCancelMap[$orderNo] : null;
- if (!empty($cancelAfterSync) && $cancelAfterSync['operate_time'] >= $latestSuccessSync['sync_time']) {
- $result[$orderNo] = [
- 'code' => 'cancelled',
- 'text' => '已撤回',
- 'sync_time' => $latestSuccessSync['sync_time'],
- 'sync_msg' => $latestSync['sync_msg'],
- 'sync_log_id' => $latestSuccessSync['id'],
- ];
- continue;
- }
- $result[$orderNo] = [
- 'code' => 'synced',
- 'text' => '已同步',
- 'sync_time' => $latestSuccessSync['sync_time'],
- 'sync_msg' => $latestSuccessSync['sync_msg'],
- 'sync_log_id' => $latestSuccessSync['id'],
- ];
- continue;
- }
- if ((int)$latestSync['sync_status'] === self::SYNC_STATUS_FAILED) {
- $result[$orderNo] = [
- 'code' => 'failed',
- 'text' => '同步失败',
- 'sync_time' => $latestSync['sync_time'],
- 'sync_msg' => $latestSync['sync_msg'],
- 'sync_log_id' => $latestSync['id'],
- ];
- continue;
- }
- $result[$orderNo] = [
- 'code' => 'pending',
- 'text' => '待同步',
- 'sync_time' => $latestSync['sync_time'],
- 'sync_msg' => $latestSync['sync_msg'],
- 'sync_log_id' => $latestSync['id'],
- ];
- }
- return $result;
- }
- private function resolveDisplaySyncStatus($workOrderNo)
- {
- $latestSync = Db::name('mes_erp_work_order_sync_log')
- ->where('work_order_no', $workOrderNo)
- ->order('id desc')
- ->find();
- if (empty($latestSync)) {
- return $this->defaultDisplaySyncStatus();
- }
- $latestSuccessSync = Db::name('mes_erp_work_order_sync_log')
- ->where('work_order_no', $workOrderNo)
- ->where('sync_status', self::SYNC_STATUS_SUCCESS)
- ->order('id desc')
- ->find();
- if (!empty($latestSuccessSync)) {
- $cancelAfterSync = Db::name('mes_erp_operation_log')
- ->where('work_order_no', $workOrderNo)
- ->where('operate_type', 'cancel')
- ->where('operate_status', self::OPERATE_STATUS_SUCCESS)
- ->where('operate_time', '>=', $latestSuccessSync['sync_time'])
- ->order('id desc')
- ->find();
- if (!empty($cancelAfterSync)) {
- return [
- 'code' => 'cancelled',
- 'text' => '已撤回',
- 'sync_time' => $latestSuccessSync['sync_time'],
- 'sync_msg' => $latestSync['sync_msg'],
- 'sync_log_id' => $latestSuccessSync['id'],
- ];
- }
- return [
- 'code' => 'synced',
- 'text' => '已同步',
- 'sync_time' => $latestSuccessSync['sync_time'],
- 'sync_msg' => $latestSuccessSync['sync_msg'],
- 'sync_log_id' => $latestSuccessSync['id'],
- ];
- }
- if ((int)$latestSync['sync_status'] === self::SYNC_STATUS_FAILED) {
- return [
- 'code' => 'failed',
- 'text' => '同步失败',
- 'sync_time' => $latestSync['sync_time'],
- 'sync_msg' => $latestSync['sync_msg'],
- 'sync_log_id' => $latestSync['id'],
- ];
- }
- return [
- 'code' => 'pending',
- 'text' => '待同步',
- 'sync_time' => $latestSync['sync_time'],
- 'sync_msg' => $latestSync['sync_msg'],
- 'sync_log_id' => $latestSync['id'],
- ];
- }
- private function defaultDisplaySyncStatus()
- {
- return [
- 'code' => 'unsynced',
- 'text' => '未同步',
- 'sync_time' => '',
- 'sync_msg' => '',
- 'sync_log_id' => null,
- ];
- }
- private function buildSyncOperateContent($workOrderNo, $syncModule, $operateType)
- {
- $moduleText = $this->syncModuleText($syncModule);
- $typeMap = [
- 'sync' => '同步',
- 'retry' => '重试同步',
- 'manual' => '手动同步',
- 'auto' => '自动同步',
- ];
- $typeText = isset($typeMap[$operateType]) ? $typeMap[$operateType] : '同步';
- return $typeText . '工单' . $workOrderNo . ',模块:' . $moduleText;
- }
- private function syncStatusText($status)
- {
- $map = [
- self::SYNC_STATUS_PENDING => '待同步',
- self::SYNC_STATUS_SUCCESS => '同步成功',
- self::SYNC_STATUS_FAILED => '同步失败',
- ];
- return isset($map[(int)$status]) ? $map[(int)$status] : '未知';
- }
- private function syncModuleText($module)
- {
- $map = [
- '1' => '工单数据',
- '2' => '工艺数据',
- '3' => '报工数据',
- '4' => '全量同步',
- ];
- return isset($map[(string)$module]) ? $map[(string)$module] : (string)$module;
- }
- private function operateStatusText($status)
- {
- $map = [
- self::OPERATE_STATUS_SUCCESS => '成功',
- self::OPERATE_STATUS_FAILED => '失败',
- self::OPERATE_STATUS_PROCESSING => '处理中',
- ];
- return isset($map[(int)$status]) ? $map[(int)$status] : '未知';
- }
- private function formatDateValue($value)
- {
- if (empty($value)) {
- return null;
- }
- $timestamp = strtotime($value);
- return $timestamp ? date('Y-m-d', $timestamp) : null;
- }
- }
|