Browse Source

Merge branch 'master' of https://git.7in6.com/liuhairui/mes-dc-server-api

liuhairui 1 week ago
parent
commit
c5d4232ebe

+ 0 - 678
application/api/controller/EmployeeDailySalary.php

@@ -1,678 +0,0 @@
-<?php
-
-namespace app\api\controller;
-
-use app\common\controller\Api;
-use \think\Request;
-use \think\Db;
-/**
- * 员工日工资查询接口
- */
-class EmployeeDailySalary extends Api
-{
-    protected $noNeedLogin = ['*'];
-    protected $noNeedRight = ['*'];
-
-    /**
-     * 首页
-     *
-     */
-    public function index()
-    {
-        $this->success('请求成功');
-    }
-
-    /**
-     * 员工日工资查询侧边栏
-     * @ApiMethod (GET)
-     */
-    public function getTab()
-    {
-        //get请求
-        if(!$this->request->isGet()){
-            $this->error('请求方式错误');
-        }
-        $res=db('绩效工资汇总')
-            ->field('DATE_FORMAT(`sczl_rq`, "%Y%m") as sys_ny,bh')
-            ->group('DATE_FORMAT(`sczl_rq`, "%Y%m"),bh')
-            ->whereTime('sczl_rq', '>=', strtotime('-14 months'))
-            ->where('bh','<>','000000')
-            ->order('sczl_rq desc')
-            ->select();
-        foreach($res as $v){
-            $arr[$v['sys_ny'].'-'][]=$v['bh'];
-        }
-        $rs = db('人事_基本资料')->column('员工编号,所在部门');
-        foreach($arr as $k=>$v){
-            foreach($v as $key=>$value){
-                if(array_key_exists($value,$rs)){
-                    $data[$k][$key]=rtrim($rs[$value]);
-                }
-            }
-            $data[$k]=array_unique($data[$k]);
-            usort($data[$k], function($a, $b) {
-                $order = array(
-                    '胶印车间',
-                    '凹印车间',
-                    '丝印车间',
-                    '模切车间',
-                    '检验车间',
-                    '精品试验车间',
-                    '品保部',
-                    '人力资源部',
-                    '生产部',
-                    '营销部'
-                );
-                $a_index = array_search($a, $order);
-                $b_index = array_search($b, $order);
-                return $a_index - $b_index;
-            });
-        }
-        $i=0;
-        foreach($data as $k=>$v){
-            $datas[$i]['label']=str_replace('-','',$k);
-            foreach($v as $value){
-                $datas[$i]['children'][]['label']=$value;
-            }
-            $i++;
-        }
-        $this->success('成功',$datas);
-    }
-    
-
-    /**
-     * 员工日工资查询上侧列表
-     * @ApiMethod (GET)
-     * @param string $date 年月
-     * @param string $department 部门
-     */
-    public function getList()
-    {
-        //get请求
-        if(!$this->request->isGet()){
-            $this->error('请求方式错误');
-        }
-        $req = $this->request->param();
-        //获取工资汇总表数据
-        $res=db('绩效工资汇总')
-            ->join('人事_基本资料','人事_基本资料.员工编号=绩效工资汇总.bh')
-            ->group('绩效工资汇总.bh,LEFT(sczl_rq, 10)')
-            ->where('sys_ny',$req['date'])
-            ->where(['人事_基本资料.所在部门'=>['like',$req['department'].'%']])
-            ->column('CONCAT(bh, "-", DATE_FORMAT(sczl_rq,"%Y.%m.%d")) AS rq_bh,bh,rtrim(xm) as 员工姓名,DATE_FORMAT(sczl_rq,"%Y.%m.%d") as sczl_rq,sum(个人计件工资) as 计件工资,
-            sum(个人加班工资) as 加班工资,sum(异常停机工时) as 计时时数');
-
-        $year=substr($req['date'],0,4);
-        $month=substr($req['date'],-2);
-        $start_time = $year . '-' . $month . '-01 00:00:00';
-        $end_time = date('Y-m-t', strtotime("$year-$month-01")) . ' 23:59:59';
-        
-        //查询wgjs表获取数据,以供后续计算
-        $arr = [];
-        $columns = ['wgjs_bh1', 'wgjs_bh2', 'wgjs_bh3', 'wgjs_bh4', 'wgjs_bh5', 'wgjs_bh6'];
-        for ($i = 1; $i <= 6; $i++) {
-            $column = $columns[$i-1];
-            $r = db('db_wgjs')
-                ->field("DATE_FORMAT(wgjs_rq,'%Y.%m.%d') as wgjs_rq, $column as wgjs_bh, trim(员工姓名) as 员工姓名, wgjs_js$i as wgjs_js, wgjs_冲定额$i as wgjs_冲定额")
-                ->join('人事_基本资料', "人事_基本资料.员工编号=db_wgjs.$column")
-                ->where(['wgjs_rq' => ['between', "$start_time,$end_time"], '人事_基本资料.所在部门' => ['like', $req['department'] . '%']])
-                ->select();
-
-            $arr = array_merge($arr, $r);
-        }
-        
-        //计算wgjs总计时时数
-        $list = [];
-        foreach($arr as $v){
-            if(isset($list[$v['wgjs_bh'].'-'.$v['wgjs_rq']])){
-                $list[$v['wgjs_bh'].'-'.$v['wgjs_rq']]['计时时数']+=$v['wgjs_js'];
-                $list[$v['wgjs_bh'].'-'.$v['wgjs_rq']]['计时工资']=floatval(number_format($list[$v['wgjs_bh'].'-'.$v['wgjs_rq']]['计时时数']*10,2, '.', ''));
-                $list[$v['wgjs_bh'].'-'.$v['wgjs_rq']]['日工资合计']=floatval(number_format($list[$v['wgjs_bh'].'-'.$v['wgjs_rq']]['计时时数']*10,2, '.', ''));
-            }else{
-                $list[$v['wgjs_bh'].'-'.$v['wgjs_rq']]['bh']=$v['wgjs_bh'];
-                $list[$v['wgjs_bh'].'-'.$v['wgjs_rq']]['员工姓名']=$v['员工姓名'];
-                $list[$v['wgjs_bh'].'-'.$v['wgjs_rq']]['sczl_rq']=$v['wgjs_rq'];
-                $list[$v['wgjs_bh'].'-'.$v['wgjs_rq']]['计件工资']=0;
-                $list[$v['wgjs_bh'].'-'.$v['wgjs_rq']]['加班工资']=0;
-                $list[$v['wgjs_bh'].'-'.$v['wgjs_rq']]['计时时数']=$v['wgjs_js'];
-                $list[$v['wgjs_bh'].'-'.$v['wgjs_rq']]['计时工资']=floatval(number_format($v['wgjs_js']*10,2, '.', ''));
-                $list[$v['wgjs_bh'].'-'.$v['wgjs_rq']]['日工资合计']=floatval(number_format($v['wgjs_js']*10,2, '.', ''));
-            }
-        };
-
-        //计算当月冲定额时长
-        $list1 = [];
-        foreach($arr as $v){
-            if(isset($list1[$v['wgjs_bh']])){
-                if($v['wgjs_冲定额']=='是'){
-                    $list1[$v['wgjs_bh']]['计时时数']+=$v['wgjs_js'];
-                }
-            }else{
-                if($v['wgjs_冲定额']=='是'){
-                    $list1[$v['wgjs_bh']]['计时时数']=$v['wgjs_js'];
-                }else{
-                    $list1[$v['wgjs_bh']]['计时时数']=0;
-                }
-            }
-        };
-
-        //获取每个员工计时补差天数
-        $arr1 = db('绩效工资汇总')->alias('j')
-            ->field('j.bh,j.sczl_rq')
-            ->join('人事_基本资料 r','r.员工编号=j.bh')
-            ->group('j.bh,j.sczl_rq')
-            ->where('j.sys_ny',$req['date'])
-            // ->where('j.达标定额','<>',0)
-            ->where(['r.所在部门'=>['like',$req['department'].'%']])
-            ->buildSql();
-        $arr1 = db()->table($arr1 . ' t')
-            ->group('t.bh, LEFT(t.sczl_rq, 7)')
-            ->column('t.bh,count(LEFT(t.sczl_rq, 7)) as num');
-
-        //查询该员工当月停机工时
-        $res1=db('绩效工资汇总')
-            ->join('人事_基本资料','人事_基本资料.员工编号=绩效工资汇总.bh')
-            ->group('绩效工资汇总.bh,LEFT(sczl_rq, 7)')
-            ->where('sys_ny',$req['date'])
-            ->where(['人事_基本资料.所在部门'=>['like',$req['department'].'%']])
-            ->column('bh,rtrim(xm) as 员工姓名,DATE_FORMAT(sczl_rq,"%Y.%m.%d") as sczl_rq,
-            sum(异常停机工时) as 计时时数');
-
-        //查询每条记录
-        $arr2 = db('绩效工资汇总')->alias('j')
-            ->field('j.bh, DATE_FORMAT(j.sczl_rq,"%Y.%m.%d") as sczl_rq,
-            sum(j.达标定额) as 达标定额, j.Rate, j.千件工价, sum(j.异常停机工时) as 异常停机工时')
-            ->join('人事_基本资料 r','r.员工编号=j.bh')
-            ->where('j.sys_ny',$req['date'])
-            // ->where('j.达标定额','<>',0)
-            ->where(['r.所在部门'=>['like',$req['department'].'%']])
-            ->group('j.bh,j.sczl_rq,j.sczl_gdbh')
-            ->select();
-        //计算每条记录的计时补差
-        $brr = [];
-        foreach($arr2 as $v){
-            $yjsss1 = array_key_exists($v['bh'],$list1) ? $list1[$v['bh']]['计时时数'] : 0;
-            $yjsss2 = array_key_exists($v['bh'],$res1) ? $res1[$v['bh']]['计时时数'] : 0;
-            $yjsss = $yjsss1 + $yjsss2;
-
-            $a = $yjsss/$arr1[$v['bh']]/11*0.6*$v['达标定额']*$v['Rate']*0.5/1000*$v['千件工价'];
-                
-            if(array_key_exists($v['bh'].'-'.$v['sczl_rq'],$brr)){
-                $brr[$v['bh'].'-'.$v['sczl_rq']] += $a;
-            }else{
-                $brr[$v['bh'].'-'.$v['sczl_rq']] = $a;
-            }
-        }
-
-        //工资汇总表和wgjs表中共有数据汇总 
-        foreach($res as $k=>&$v){
-            if(array_key_exists($k,$list)){
-                $v['计时时数'] += $list[$k]['计时时数'];
-                unset($list[$k]);
-            }
-            $v['计时工资'] = array_key_exists($k,$brr) ? floatval(number_format($v['计时时数']*10+$brr[$k],2, '.', '')) : floatval(number_format($v['计时时数']*10,2, '.', ''));
-            $v['日工资合计'] = floatval(number_format($v['计件工资'] + $v['加班工资'] + $v['计时工资'],2, '.', ''));
-            
-            unset($v['rq_bh']);
-        }
-
-        //工资汇总表和wgjs表中不共有数据合并 
-        $rows = array_merge($res,$list);
-        ksort($rows);
-        $rows = array_values($rows);
-        $this->success('成功',$rows);
-    }
-    /**
-     * 员工日工资查询下侧列表
-     * @ApiMethod (GET)
-     * @param string $date 年月
-     * @param string $code 员工编号
-     */
-    public function getAllList()
-    {
-        //get请求
-        if(!$this->request->isGet()){
-            $this->error('请求方式错误');
-        }
-        $req = $this->request->param();
-        $res = db('绩效工资汇总')
-            ->field('LEFT(sczl_rq, 10) as sczl_rq, sczl_gdbh, rtrim(g.yj_yjmc) as Gd_cpmc, 
-            sczl_yjno, sczl_gxh, sczl_type, sczl_jtbh,sum(班组车头产量) as 班组车头产量, 
-            sum(班组换算产量) as 班组换算产量, 工序难度系数, sum(个人计件工资) as 个人计件工资,
-            sum(个人加班工资) as 个人加班工资, sum(装版工时) as 装版工时, 
-            sum(保养工时) as 保养工时, sum(打样工时) as 打样工时, 
-            sum(异常停机工时) as 异常停机工时, sum(车头产量占用机时) as 车头产量占用机时, 日定额,
-            sum(达标定额) as 达标定额, 千件工价, 补产标准, bh, rtrim(xm) as xm, Rate')
-            ->join('工单_印件资料 g','g.Yj_Gdbh = 绩效工资汇总.sczl_gdbh AND g.yj_Yjno = 绩效工资汇总.sczl_yjno','LEFT')
-            ->group('LEFT(sczl_rq, 10),sczl_gdbh,CONCAT(sczl_yjno, "-", sczl_gxh, "-", sczl_type),千件工价')
-            ->where(['sys_ny'=>$req['date'],'bh'=>$req['code']])
-            ->select();
-        if(!$res){
-            $this->error('失败');
-        }
-        foreach($res as &$v){
-            $v['sczl_rq']=str_replace('-','.',$v['sczl_rq']);
-            if($v['sczl_yjno']>9){
-                $sczl_yjno=$v['sczl_yjno'];
-            }else{
-                $sczl_yjno='0'.$v['sczl_yjno'];
-            }
-            unset($v['sczl_yjno']);
-            if($v['sczl_gxh']>9){
-                $sczl_gxh=$v['sczl_gxh'];
-            }else{
-                $sczl_gxh='0'.$v['sczl_gxh'];
-            }
-            unset($v['sczl_gxh']);
-            $v['sczl_type']=$sczl_yjno.'-'.$sczl_gxh.'-->'.$v['sczl_type'];
-        }
-        $this->success('成功',$res);
-    }
-     /**
-     * 员工日工资查询详情
-     * @ApiMethod (GET)
-     * @param string $date 日期
-     * @param string $code 员工编号
-     */
-    public function getDetail()
-    {
-        //get请求
-        if(!$this->request->isGet()){
-            $this->error('请求方式错误');
-        }
-        $req = $this->request->param();
-        $start_time=$req['date'].' 00:00:00';
-        $end_time=$req['date'].' 23:59:59';
-        $res=db('绩效工资汇总')
-            ->field('LEFT(sczl_rq, 10) as sczl_rq,sczl_gdbh,rtrim(g.yj_yjmc) as Gd_cpmc,sczl_yjno,sczl_gxh,sczl_type,
-            sczl_jtbh,sum(班组车头产量) as 班组车头产量,sum(班组换算产量) as 班组换算产量,工序难度系数,sum(个人计件工资)
-            as 个人计件工资,sum(个人加班工资) as 个人加班工资,sum(装版工时) as 装版工时,sum(保养工时) as 保养工时,
-            sum(打样工时) as 打样工时,sum(异常停机工时) as 异常停机工时,sum(车头产量占用机时) as 车头产量占用机时,日定额,
-            sum(达标定额) as 达标定额,千件工价,补产标准,bh,rtrim(xm) as xm,Rate')
-            ->join('工单_印件资料 g','g.Yj_Gdbh = 绩效工资汇总.sczl_gdbh AND g.yj_Yjno = 绩效工资汇总.sczl_yjno','LEFT')
-            ->group('LEFT(sczl_rq, 10),sczl_gdbh')
-            ->where(['sczl_rq'=>['between',"$start_time,$end_time"],'bh'=>$req['code']])
-            ->select();
-        if(!$res){
-            $this->error('失败');
-        }
-        foreach($res as &$v){
-            $v['sczl_rq']=str_replace('-','.',$v['sczl_rq']);
-            if($v['sczl_yjno']>9){
-                $sczl_yjno=$v['sczl_yjno'];
-            }else{
-                $sczl_yjno='0'.$v['sczl_yjno'];
-            }
-            unset($v['sczl_yjno']);
-            if($v['sczl_gxh']>9){
-                $sczl_gxh=$v['sczl_gxh'];
-            }else{
-                $sczl_gxh='0'.$v['sczl_gxh'];
-            }
-            unset($v['sczl_gxh']);
-            $v['sczl_type']=$sczl_yjno.'-'.$sczl_gxh.'-->'.$v['sczl_type'];
-        }
-        $this->success('成功',$res);
-    }
-    /**
-     * 定位
-     * @ApiMethod GET
-    */
-    public function search(){
-        
-        //get请求
-        if(!$this->request->isGet()){
-            $this->error('请求方式错误');
-        }
-        $req = $this->request->param();
-        $yg = db('人事_基本资料')->where('员工姓名',$req['search'])->value('员工编号');
-        if($yg){
-            $req['search']=$yg;
-        }
-        $res=db('绩效工资汇总')
-            ->join('人事_基本资料','人事_基本资料.员工编号=绩效工资汇总.bh')
-            ->group('绩效工资汇总.bh,LEFT(sczl_rq, 10)')
-            ->where('sys_ny',$req['date'])
-            ->where('bh',$req['search'])
-            // ->where(['人事_基本资料.所在部门'=>['like',$req['department'].'%']])
-            ->column('CONCAT(bh, "-", DATE_FORMAT(sczl_rq,"%Y.%m.%d")) AS rq_bh,bh,rtrim(xm) as 员工姓名,DATE_FORMAT(sczl_rq,"%Y.%m.%d") as sczl_rq,sum(个人计件工资) as 计件工资,
-            sum(个人加班工资) as 加班工资,sum(异常停机工时) as 计时时数');
-
-        $year=substr($req['date'],0,4);
-        $month=substr($req['date'],-2);
-        $start_time = $year . '-' . $month . '-01 00:00:00';
-        $end_time = date('Y-m-t', strtotime("$year-$month-01")) . ' 23:59:59';
-        
-        $arr = [];
-        $columns = ['wgjs_bh1', 'wgjs_bh2', 'wgjs_bh3', 'wgjs_bh4', 'wgjs_bh5', 'wgjs_bh6'];
-        for ($i = 1; $i <= 6; $i++) {
-            $column = $columns[$i-1];
-            $r = db('db_wgjs')
-                ->field("DATE_FORMAT(wgjs_rq,'%Y.%m.%d') as wgjs_rq, $column as wgjs_bh, trim(员工姓名) as 员工姓名, wgjs_js$i as wgjs_js, wgjs_冲定额$i as wgjs_冲定额")
-                ->join('人事_基本资料', "人事_基本资料.员工编号=db_wgjs.$column")
-                ->where($column,$req['search'])
-                ->where(['wgjs_rq' => ['between', "$start_time,$end_time"]])
-                ->select();
-
-            $arr = array_merge($arr, $r);
-        }
-        
-        $list = [];
-        foreach($arr as $v){
-            if(isset($list[$v['wgjs_bh'].'-'.$v['wgjs_rq']])){
-                $list[$v['wgjs_bh'].'-'.$v['wgjs_rq']]['计时时数']+=$v['wgjs_js'];
-                $list[$v['wgjs_bh'].'-'.$v['wgjs_rq']]['计时工资']=floatval(number_format($list[$v['wgjs_bh'].'-'.$v['wgjs_rq']]['计时时数']*10,2, '.', ''));
-                $list[$v['wgjs_bh'].'-'.$v['wgjs_rq']]['日工资合计']=floatval(number_format($list[$v['wgjs_bh'].'-'.$v['wgjs_rq']]['计时时数']*10,2, '.', ''));
-            }else{
-                $list[$v['wgjs_bh'].'-'.$v['wgjs_rq']]['bh']=$v['wgjs_bh'];
-                $list[$v['wgjs_bh'].'-'.$v['wgjs_rq']]['员工姓名']=$v['员工姓名'];
-                $list[$v['wgjs_bh'].'-'.$v['wgjs_rq']]['sczl_rq']=$v['wgjs_rq'];
-                $list[$v['wgjs_bh'].'-'.$v['wgjs_rq']]['计件工资']=0;
-                $list[$v['wgjs_bh'].'-'.$v['wgjs_rq']]['加班工资']=0;
-                $list[$v['wgjs_bh'].'-'.$v['wgjs_rq']]['计时时数']=$v['wgjs_js'];
-                $list[$v['wgjs_bh'].'-'.$v['wgjs_rq']]['计时工资']=floatval(number_format($v['wgjs_js']*10,2, '.', ''));
-                $list[$v['wgjs_bh'].'-'.$v['wgjs_rq']]['日工资合计']=floatval(number_format($v['wgjs_js']*10,2, '.', ''));
-            }
-        };
-        $list1 = [];
-        foreach($arr as $v){
-            if(isset($list1[$v['wgjs_bh']])){
-                if($v['wgjs_冲定额']=='是'){
-                    $list1[$v['wgjs_bh']]['计时时数']+=$v['wgjs_js'];
-                }
-            }else{
-                if($v['wgjs_冲定额']=='是'){
-                    $list1[$v['wgjs_bh']]['计时时数']=$v['wgjs_js'];
-                }else{
-                    $list1[$v['wgjs_bh']]['计时时数']=0;
-                }
-            }
-        };
-
-        //获取每个员工计时补差天数
-        $arr1 = db('绩效工资汇总')->alias('j')
-            ->field('j.bh,j.sczl_rq')
-            ->join('人事_基本资料 r','r.员工编号=j.bh')
-            ->group('j.bh,j.sczl_rq')
-            ->where('j.sys_ny',$req['date'])
-            // ->where('j.达标定额','<>',0)
-            ->where('j.bh',$req['search'])
-            // ->where(['r.所在部门'=>['like',$req['department'].'%']])
-            ->buildSql();
-        $arr1 = db()->table($arr1 . ' t')
-            ->group('t.bh, LEFT(t.sczl_rq, 7)')
-            ->column('t.bh,count(LEFT(t.sczl_rq, 7)) as num');
-
-        //查询该员工当月停机工时
-        $res1=db('绩效工资汇总')
-            ->join('人事_基本资料','人事_基本资料.员工编号=绩效工资汇总.bh')
-            ->group('绩效工资汇总.bh,LEFT(sczl_rq, 7)')
-            ->where('sys_ny',$req['date'])
-            ->where('bh',$req['search'])
-            // ->where(['人事_基本资料.所在部门'=>['like',$req['department'].'%']])
-            ->column('bh,rtrim(xm) as 员工姓名,DATE_FORMAT(sczl_rq,"%Y.%m.%d") as sczl_rq,
-            sum(异常停机工时) as 计时时数');
-
-        //查询每条记录
-        $arr2 = db('绩效工资汇总')->alias('j')
-            ->field('j.bh, DATE_FORMAT(j.sczl_rq,"%Y.%m.%d") as sczl_rq,
-            sum(j.达标定额) as 达标定额, j.Rate, j.千件工价, sum(j.异常停机工时) as 异常停机工时')
-            ->join('人事_基本资料 r','r.员工编号=j.bh')
-            ->where('j.sys_ny',$req['date'])
-            // ->where('j.达标定额','<>',0)
-            ->where('j.bh',$req['search'])
-            // ->where(['r.所在部门'=>['like',$req['department'].'%']])
-            ->group('j.bh,j.sczl_rq,j.sczl_gdbh')
-            ->select();
-        //计算每条记录的计时补差
-        $brr = [];
-        foreach($arr2 as $v){
-            $yjsss1 = array_key_exists($v['bh'],$list1) ? $list1[$v['bh']]['计时时数'] : 0;
-            $yjsss2 = array_key_exists($v['bh'],$res1) ? $res1[$v['bh']]['计时时数'] : 0;
-            $yjsss = $yjsss1 + $yjsss2;
-
-            $a = $yjsss/$arr1[$v['bh']]/11*0.6*$v['达标定额']*$v['Rate']*0.5/1000*$v['千件工价'];
-                
-            if(array_key_exists($v['bh'].'-'.$v['sczl_rq'],$brr)){
-                $brr[$v['bh'].'-'.$v['sczl_rq']] += $a;
-            }else{
-                $brr[$v['bh'].'-'.$v['sczl_rq']] = $a;
-            }
-        }
-
-        foreach($res as $k=>&$v){
-            if(array_key_exists($k,$list)){
-                $v['计时时数'] += $list[$k]['计时时数'];
-                unset($list[$k]);
-            }
-            $v['计时工资'] = array_key_exists($k,$brr) ? floatval(number_format($v['计时时数']*10+$brr[$k],2, '.', '')) : floatval(number_format($v['计时时数']*10,2, '.', ''));
-            $v['日工资合计'] = floatval(number_format($v['计件工资'] + $v['加班工资'] + $v['计时工资'],2, '.', ''));
-            
-            unset($v['rq_bh']);
-        }
-
-        $rows = array_merge($res,$list);
-        ksort($rows);
-        $rows = array_values($rows);
-        $this->success('成功',$rows);
-    }
-     /**
-     * 员工计件明细
-     * @ApiMethod (GET)
-     * @param string $start_time
-     * @param string $end_time
-     * @param string $code 员工编号
-     */
-    public function getPieceDetail()
-    {
-        //get请求
-        if(!$this->request->isGet()){
-            $this->error('请求方式错误');
-        }
-        $req = $this->request->param();
-        $req['start_time']=str_replace('.','-',$req['start_time']);
-        $req['end_time']=str_replace('.','-',$req['end_time']);
-        $start_time=$req['start_time'].' 00:00:00';
-        $end_time=$req['end_time'].' 23:59:59';
-        $res=db('绩效工资汇总')
-           ->field('LEFT(sczl_rq, 10) as sczl_rq,sczl_gdbh,rtrim(g.员工姓名) as 员工资料,sczl_yjno,sczl_gxh,sczl_type,
-            sczl_jtbh,班组车头产量,保养工时,装版工时,打样工时,异常停机工时,补产标准,计时补差额工资 as 班组补产,日定额,达标定额,千件工价,
-            个人计件工资,个人加班工资,Rate as 个人比例')
-            ->join('人事_基本资料 g','人事_基本资料.员工编号=绩效工资汇总.bh','LEFT')
-            ->where(['sczl_rq'=>['between',"$start_time,$end_time"],'bh'=>$req['code']])
-            ->select();
-        if(!$res){
-            $this->error('失败');
-        }
-        foreach($res as &$v){
-            $v['sczl_rq']=str_replace('-','.',$v['sczl_rq']);
-            if($v['sczl_yjno']>9){
-                $sczl_yjno=$v['sczl_yjno'];
-            }else{
-                $sczl_yjno='0'.$v['sczl_yjno'];
-            }
-            unset($v['sczl_yjno']);
-            if($v['sczl_gxh']>9){
-                $sczl_gxh=$v['sczl_gxh'];
-            }else{
-                $sczl_gxh='0'.$v['sczl_gxh'];
-            }
-            unset($v['sczl_gxh']);
-            $v['sczl_type']=$sczl_yjno.'-'.$sczl_gxh.'-->'.$v['sczl_type'];
-            $v['班组补产']=$v['补产标准']*($v['保养工时']+$v['装版工时']+$v['打样工时']+$v['异常停机工时']);
-        }
-        $this->success('成功',$res);
-    }
-     /**
-     * 每日工资统计
-     * @ApiMethod (GET)
-     * @param string $date 年月
-     * @param string $department 部门
-     */
-    public function statistics()
-    {
-        //get请求
-        if(!$this->request->isGet()){
-            $this->error('请求方式错误');
-        }
-        $req = $this->request->param();
-        $res=db('绩效工资汇总')
-            ->join('人事_基本资料','人事_基本资料.员工编号=绩效工资汇总.bh','LEFT')
-            ->field('bh,rtrim(xm) as 员工姓名,LEFT(sczl_rq, 10) as sczl_rq,sum(个人计件工资) as 计件工资,
-            sum(个人加班工资) as 加班工资')
-            ->group('绩效工资汇总.bh,LEFT(sczl_rq, 10)')
-            ->where('sys_ny',$req['date'])
-            ->where(['人事_基本资料.所在部门'=>['like',$req['department'].'%']])
-            ->select();
-        if(!$res){
-            $this->error('失败');
-        }
-        $year=substr($req['date'],0,4);
-        $month=substr($req['date'],-2);
-        $start_time = $year . '-' . $month . '-01 00:00:00';
-        $end_time = date('Y-m-t', strtotime("$year-$month-01")) . ' 23:59:59';
-        $res1=db('db_wgjs')->cache(true,86400)
-        ->field('LEFT(wgjs_rq, 10) as wgjs_rq,wgjs_bh1 as wgjs_bh,wgjs_js1 as wgjs_js')
-        ->join('人事_基本资料','人事_基本资料.员工编号=db_wgjs.wgjs_bh1','LEFT')
-        ->where(['wgjs_rq'=>['between',"$start_time,$end_time"],'人事_基本资料.所在部门'=>['like',$req['department'].'%']])
-        ->select();
-        $res2=db('db_wgjs')->cache(true,86400)
-        ->field('LEFT(wgjs_rq, 10) as wgjs_rq,wgjs_bh2 as wgjs_bh,wgjs_js2 as wgjs_js')
-        ->join('人事_基本资料','人事_基本资料.员工编号=db_wgjs.wgjs_bh2','LEFT')
-        ->where(['wgjs_rq'=>['between',"$start_time,$end_time"],'人事_基本资料.所在部门'=>['like',$req['department'].'%']])
-        ->select();
-        $res3=db('db_wgjs')->cache(true,86400)
-        ->field('LEFT(wgjs_rq, 10) as wgjs_rq,wgjs_bh3 as wgjs_bh,wgjs_js3 as wgjs_js')
-        ->join('人事_基本资料','人事_基本资料.员工编号=db_wgjs.wgjs_bh3','LEFT')
-        ->where(['wgjs_rq'=>['between',"$start_time,$end_time"],'人事_基本资料.所在部门'=>['like',$req['department'].'%']])
-        ->select();
-        $res4=db('db_wgjs')->cache(true,86400)
-        ->field('LEFT(wgjs_rq, 10) as wgjs_rq,wgjs_bh4 as wgjs_bh,wgjs_js4 as wgjs_js')
-        ->join('人事_基本资料','人事_基本资料.员工编号=db_wgjs.wgjs_bh4','LEFT')
-        ->where(['wgjs_rq'=>['between',"$start_time,$end_time"],'人事_基本资料.所在部门'=>['like',$req['department'].'%']])
-        ->select();
-        $res5=db('db_wgjs')->cache(true,86400)
-        ->field('LEFT(wgjs_rq, 10) as wgjs_rq,wgjs_bh5 as wgjs_bh,wgjs_js5  as wgjs_js')
-        ->join('人事_基本资料','人事_基本资料.员工编号=db_wgjs.wgjs_bh5','LEFT')
-        ->where(['wgjs_rq'=>['between',"$start_time,$end_time"],'人事_基本资料.所在部门'=>['like',$req['department'].'%']])
-        ->select();
-        $res6=db('db_wgjs')->cache(true,86400)
-        ->field('LEFT(wgjs_rq, 10) as wgjs_rq,wgjs_bh6 as wgjs_bh,wgjs_js6 as wgjs_js')
-        ->join('人事_基本资料','人事_基本资料.员工编号=db_wgjs.wgjs_bh6','LEFT')
-        ->where(['wgjs_rq'=>['between',"$start_time,$end_time"],'人事_基本资料.所在部门'=>['like',$req['department'].'%']])
-        ->select();
-        $arr=array_merge($res1,$res2,$res3,$res4,$res5,$res6);
-        foreach($arr as $v){
-            if(isset($list[$v['wgjs_rq']][$v['wgjs_bh']])){
-                $list[$v['wgjs_rq']][$v['wgjs_bh']]+=$v['wgjs_js'];
-            }else{
-                $list[$v['wgjs_rq']][$v['wgjs_bh']]=$v['wgjs_js'];
-            }
-        }
-        foreach($res as &$v){
-            if(isset($list[$v['sczl_rq']][$v['bh']])){
-                $v['计时工资']=$list[$v['sczl_rq']][$v['bh']];
-            }else{
-                $v['计时工资']='';
-            } 
-            $v['部门']=$req['department']; 
-            $v['sczl_rq']=str_replace('-','.',$v['sczl_rq']);
-        }
-        $this->success('成功',$res);
-    }
-       /**
-     * 月度计时工资调整
-     * @ApiMethod (GET)
-     * @param string $date 年月
-     * @param string $code 员工编号
-     */
-    public function wageAdjustment()
-    {
-        //get请求
-        if(!$this->request->isGet()){
-            $this->error('请求方式错误');
-        }
-        $req = $this->request->param();
-        $res=db('绩效加班工资月度修正')->cache(true,86400)
-            ->field('人事_基本资料.所在部门,bh,rtrim(xm) as xm,月计时时数,DATE_FORMAT(sczl_rq,"%Y.%m.%d") as sczl_rq,sczl_gdbh,sczl_yjno,sczl_gxh,
-            千件工价,Rate,sum(达标定额) as 达标定额,sum(个人计件工资) as 个人计件工资,sum(个人加班工资) as 个人加班工资,计时冲定额,sum(New_计件工资) as
-            New_计件工资,sum(New_加班工资) as New_加班工资,调整量_合计')
-            ->join('人事_基本资料','人事_基本资料.员工编号=绩效加班工资月度修正.bh','LEFT')
-            ->group('LEFT(sczl_rq, 10),sczl_gdbh')
-            ->where(['sys_ny'=>$req['date'],'bh'=>$req['code']])
-            ->select();
-        if(!$res){
-            $this->error('失败');
-        }
-        foreach($res as &$v){
-            if($v['sczl_yjno']>9){
-                $sczl_yjno=$v['sczl_yjno'];
-            }else{
-                $sczl_yjno='0'.$v['sczl_yjno'];
-            }
-            unset($v['sczl_yjno']);
-            if($v['sczl_gxh']>9){
-                $sczl_gxh=$v['sczl_gxh'];
-            }else{
-                $sczl_gxh='0'.$v['sczl_gxh'];
-            }
-            unset($v['sczl_gxh']);
-            $v['sczl_gdbh']=$v['sczl_gdbh'].'-'.$sczl_yjno.'-'.$sczl_gxh;
-            $v['bh']=$v['bh'].' '.$v['xm'];
-            unset($v['xm']); 
-        }
-        $this->success('成功',$res);
-    }
-
-    /**
-     * 员工工资明细查询
-     * @ApiMethod (GET)
-     * @param string $date 年月
-     * @param string $code 员工编号
-     */
-    public function getGzByYgbh()
-    {
-        //get请求
-        if(!$this->request->isGet()){
-            $this->error('请求方式错误');
-        }
-        $req = $this->request->param();
-        $res = db('绩效工资汇总')
-            ->field('LEFT(sczl_rq, 10) as sczl_rq, sczl_gdbh, rtrim(g.yj_yjmc) as Gd_cpmc, 
-            sczl_yjno, sczl_gxh, sczl_type, sczl_jtbh,班组车头产量, 
-            班组车头产量*工序难度系数 as 计件产量,
-            班组换算产量 as 补产产量,
-            (班组车头产量*工序难度系数+班组换算产量) as 核算产量,
-            工序难度系数, 个人计件工资,
-            个人加班工资, 装版工时, 
-            保养工时, 打样工时, 
-            异常停机工时, 车头产量占用机时, 日定额,
-            达标定额, 千件工价, 补产标准, bh, rtrim(xm) as xm, Rate')
-            ->join('工单_印件资料 g','g.Yj_Gdbh = 绩效工资汇总.sczl_gdbh AND g.yj_Yjno = 绩效工资汇总.sczl_yjno','LEFT')
-            ->where(['sys_ny'=>$req['date'],'bh'=>$req['code']])
-            ->select();
-        if($res===false){
-            $this->error('失败');
-        }
-        foreach($res as &$v){
-            $v['sczl_rq']=str_replace('-','.',$v['sczl_rq']);
-            if($v['sczl_yjno']>9){
-                $sczl_yjno=$v['sczl_yjno'];
-            }else{
-                $sczl_yjno='0'.$v['sczl_yjno'];
-            }
-            unset($v['sczl_yjno']);
-            if($v['sczl_gxh']>9){
-                $sczl_gxh=$v['sczl_gxh'];
-            }else{
-                $sczl_gxh='0'.$v['sczl_gxh'];
-            }
-            $v['计件产量'] = floatval(number_format($v['计件产量'], 2, '.', ''));
-            $v['核算产量'] = floatval(number_format($v['核算产量'], 2, '.', ''));
-            unset($v['sczl_gxh']);
-            $v['sczl_type']=$sczl_yjno.'-'.$sczl_gxh.'-->'.$v['sczl_type'];
-        }
-        $this->success('成功',$res);
-    }
-
-}

+ 0 - 25
application/api/controller/RelatedSalaryAccounting.php

@@ -1,25 +0,0 @@
-<?php
-
-namespace app\api\controller;
-
-use app\common\controller\Api;
-use \think\Request;
-use \think\Db;
-/**
- * 关联工资核算接口
- */
-class RelatedSalaryAccounting extends Api
-{
-    protected $noNeedLogin = ['*'];
-    protected $noNeedRight = ['*'];
-
-    /**
-     * 首页
-     *
-     */
-    public function index()
-    {
-        $this->success('请求成功');
-    }
-
-}

+ 928 - 8
application/api/controller/Synchronization.php

@@ -4,22 +4,942 @@ namespace app\api\controller;
 
 
 use app\common\controller\Api;
 use app\common\controller\Api;
 use think\Db;
 use think\Db;
-use Overtrue\Pinyin;
+use think\exception\HttpResponseException;
 
 
 /**
 /**
- * 中间表数据同步
+ * MES-ERP 工单同步
  */
  */
 class Synchronization extends Api
 class Synchronization extends Api
 {
 {
     protected $noNeedLogin = ['*'];
     protected $noNeedLogin = ['*'];
     protected $noNeedRight = ['*'];
     protected $noNeedRight = ['*'];
 
 
-    public function SynchronizationData()
+    /** @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 ($this->request->isGet() === false){
-            $this->error('请求错误');
+        if (empty($value)) {
+            return null;
         }
         }
-        $db2 = Db::connect(config('database.db2'));
-        
+        $timestamp = strtotime($value);
+        return $timestamp ? date('Y-m-d', $timestamp) : null;
     }
     }
-}
+}