6 次代碼提交 1ba343fbf5 ... e41a0222e4

作者 SHA1 備註 提交日期
  wukai e41a0222e4 修改MES员工打卡同步规则 3 周之前
  wukai 2e749ff0c0 解决驾驶仓手动填报数据 3 周之前
  wukai cda7e5481e 统计经编生产长度时增加规格 4 周之前
  wukai 5e3116a9e4 统计经编生产长度时增加规格 4 周之前
  wukai 61c8396762 送经量统计增加设定米长 4 周之前
  wukai 7318864f7a 修改可按ids查询设备列表 4 周之前

+ 3 - 3
jjt-admin/src/test/java/com/jjt/task/TaskEmpTest.java

@@ -25,10 +25,10 @@ public class TaskEmpTest {
 
     @Test
     public void test() throws Exception {
-        String date = "2025-07-23";
+        String date = "2025-08-03";
         LocalDate localDate = LocalDate.parse(date);
-        empService.sync(DateUtils.toDate(localDate));
-//        calcService.calc(DateUtils.toDate(localDate));
+//        empService.sync(DateUtils.toDate(localDate));
+        calcService.calc(DateUtils.toDate(localDate));
     }
 
 }

+ 10 - 6
jjt-admin/src/test/java/com/jjt/task/TaskTest.java

@@ -38,12 +38,16 @@ public class TaskTest {
     public void curr() {
         apiService.curr();
     }
-
+    @Test
+    public void last(){
+        iotService.setToken();
+        calcHourService.last();
+    }
 
     @Test
-    public void last() {
+    public void time() {
         iotService.setToken();
-        String date = "2025-07-11";
+        String date = "2025-07-29";
         LocalDate localDate = LocalDate.parse(date);
 //        dayService.day(localDate);
 //        dayService.day(LocalDate.parse("2025-02-23"));
@@ -55,7 +59,7 @@ public class TaskTest {
 //        }
 ////        empCalcService.calc(DateUtils.toDate(localDate));
         LocalDateTime ldt = LocalDateTime.of(localDate, LocalTime.MIN);
-        for (int i = 23; i < 24; i++) {
+        for (int i = 7; i < 10; i++) {
             LocalDateTime tt = ldt.plusHours(i);
             System.err.println(tt.toLocalDate().toString() + tt.getHour());
             calcHourService.calc(tt.toLocalDate().toString(), tt.getHour());
@@ -69,8 +73,8 @@ public class TaskTest {
     @Test
     public void test() {
         iotService.setToken();
-        String st = "2025-03-16";
-        String ed = "2025-03-16";
+        String st = "2025-07-28";
+        String ed = "2025-07-28";
         LocalDate localDate = LocalDate.parse(st);
         LocalDate endDate = LocalDate.parse(ed);
         LocalDateTime start = LocalDateTime.of(localDate, LocalTime.MIN).plusHours(7);

+ 28 - 0
jjt-admin/src/test/java/com/jjt/wk/EmpCalcTest.java

@@ -1,13 +1,21 @@
 package com.jjt.wk;
 
+import com.alibaba.fastjson2.JSON;
 import com.jjt.JjtApplication;
+import com.jjt.biz.vo.KnittingEmpVO;
 import com.jjt.common.utils.DateUtils;
 import com.jjt.emp.service.ITwinEmpCalcService;
+import com.jjt.wkEmp.domain.TwinWkEmpSync;
+import com.jjt.wkEmp.service.ITwinWkEmpRotaService;
+import com.jjt.wkEmp.service.ITwinWkEmpSyncService;
 import org.junit.jupiter.api.Test;
 import org.springframework.boot.test.context.SpringBootTest;
 
 import javax.annotation.Resource;
 import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
 
 /**
  * DataProcess$
@@ -19,6 +27,10 @@ import java.util.Date;
 public class EmpCalcTest {
     @Resource
     private ITwinEmpCalcService empCalcService;
+    @Resource
+    private ITwinWkEmpRotaService rotaService;
+    @Resource
+    private ITwinWkEmpSyncService syncService;
 
     @Test
     public void test() {
@@ -26,5 +38,21 @@ public class EmpCalcTest {
         Date date = DateUtils.parseDate(s);
         empCalcService.calcNew(date);
     }
+
+    @Test
+    public void sync() {
+        Map<String, Object> params = new HashMap<>();
+        params.put("beginRecordTime", "2025-08-03 00:00:00");
+        params.put("endRecordTime", "2025-08-03 23:59:59");
+        TwinWkEmpSync search = new TwinWkEmpSync();
+        search.setParams(params);
+        List<TwinWkEmpSync> list = syncService.selectTwinWkEmpSyncList(search);
+        for (TwinWkEmpSync sync : list) {
+            String content = sync.getContent();
+            System.err.println(content);
+            KnittingEmpVO data = JSON.parseObject(content, KnittingEmpVO.class);
+            rotaService.sync(data);
+        }
+    }
 }
 

+ 54 - 9
jjt-biz/src/main/java/com/jjt/biz/controller/ApiDriverController.java

@@ -1,12 +1,17 @@
 package com.jjt.biz.controller;
 
+import com.jjt.biz.vo.CostVO;
+import com.jjt.biz.vo.GlobalRevenueVO;
+import com.jjt.biz.vo.SalesVO;
 import com.jjt.common.core.controller.BaseController;
 import com.jjt.common.core.domain.R;
+import com.jjt.common.core.redis.RedisCache;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.web.bind.annotation.*;
 
+import javax.annotation.Resource;
 import java.util.*;
 
 /**
@@ -19,6 +24,9 @@ import java.util.*;
 @RequestMapping("/api/driver")
 @Slf4j
 public class ApiDriverController extends BaseController {
+    @Resource
+    private RedisCache redisCache;
+
     @ApiOperation("全球营收")
     @GetMapping("/rev")
     @CrossOrigin(origins = "*")
@@ -139,7 +147,21 @@ public class ApiDriverController extends BaseController {
             forecastList.add(forecastMap);
         }
         result.put("forecastList", forecastList);
-        return R.ok(result);
+        Object obj = redisCache.getCacheObject("driver:rev");
+        if (obj != null) {
+            return R.ok(obj);
+        } else {
+            return R.ok(result);
+        }
+    }
+
+    @ApiOperation("全球营收")
+    @PostMapping("/rev")
+    @CrossOrigin(origins = "*")
+    @ResponseBody
+    public R<?> saveRev(@RequestBody GlobalRevenueVO vo) {
+        redisCache.setCacheObject("driver:rev", vo);
+        return R.ok();
     }
 
     @ApiOperation("销售")
@@ -260,8 +282,21 @@ public class ApiDriverController extends BaseController {
             specRanking.add(specData);
         }
         result.put("bom", specRanking);
+        Object obj = redisCache.getCacheObject("driver:sales");
+        if (obj != null) {
+            return R.ok(obj);
+        } else {
+            return R.ok(result);
+        }
+    }
 
-        return R.ok(result);
+    @ApiOperation("销售")
+    @PostMapping("/sales")
+    @CrossOrigin(origins = "*")
+    @ResponseBody
+    public R<?> saveSales(@RequestBody SalesVO vo) {
+        redisCache.setCacheObject("driver:sales", vo);
+        return R.ok();
     }
 
     @ApiOperation("成本")
@@ -344,10 +379,6 @@ public class ApiDriverController extends BaseController {
                 put("month", month);
                 put("value", Math.round(v3 * 100) / 100.0);
             }});
-            n3List.add(new HashMap<String, Object>() {{
-                put("month", month);
-                put("value", Math.round(v3 * 100) / 100.0);
-            }});
             n4List.add(new HashMap<String, Object>() {{
                 put("month", month);
                 put("value", Math.round(v4 * 100) / 100.0);
@@ -396,8 +427,8 @@ public class ApiDriverController extends BaseController {
         // 成本明细数据
         List<Map<String, Object>> costDetails = new ArrayList<>();
         String[] costItems = {"产量", "人工工资", "计时工资", "社会保险费", "职工福利费", "委外加工费", "机物料(含柴油)",
-                            "修理费", "制版材料", "染料", "助剂", "辅料", "水", "水电费", "药剂", "其他", "折旧", "加工成本"};
-        String[] proc = {"加弹","经编", "前整", "印染", "后整", "成品"};
+                "修理费", "制版材料", "染料", "助剂", "辅料", "水", "水电费", "药剂", "其他", "折旧", "加工成本"};
+        String[] proc = {"加弹", "经编", "前整", "印染", "后整", "成品"};
 
         // 生成各成本项数据
         for (String item : costItems) {
@@ -429,7 +460,21 @@ public class ApiDriverController extends BaseController {
         }
         result.put("detail", costDetails);
 
-        return R.ok(result);
+        Object obj = redisCache.getCacheObject("driver:cogs");
+        if (obj != null) {
+            return R.ok(obj);
+        } else {
+            return R.ok(result);
+        }
+    }
+
+    @ApiOperation("成本")
+    @PostMapping("/cogs")
+    @CrossOrigin(origins = "*")
+    @ResponseBody
+    public R<?> saveCogs(@RequestBody CostVO vo) {
+        redisCache.setCacheObject("driver:cogs", vo);
+        return R.ok();
     }
 
     @ApiOperation("采购")

+ 2 - 4
jjt-biz/src/main/java/com/jjt/biz/controller/ApiProductivityExportController.java

@@ -26,10 +26,7 @@ import java.math.BigDecimal;
 import java.math.RoundingMode;
 import java.net.URLEncoder;
 import java.time.LocalDate;
-import java.util.Arrays;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
 import java.util.stream.Collectors;
 
 /**
@@ -67,6 +64,7 @@ public class ApiProductivityExportController extends BaseController {
             bkp.setVerticalAlignment(VerticalAlignment.CENTER);
 
             List<TwinEmpCalc> list = empCalcService.selectTwinEmpCalcListByDate(DateUtils.parseDate(date));
+            list.sort(Comparator.comparing(TwinEmpCalc::getDeviceId));
             Sheet sheet = wb.getSheetAt(0);
             Sheet bSheet = wb.cloneSheet(0);
             Cell title = sheet.getRow(0).getCell(0);

+ 240 - 0
jjt-biz/src/main/java/com/jjt/biz/vo/CostVO.java

@@ -0,0 +1,240 @@
+package com.jjt.biz.vo;
+
+import lombok.Data;
+import java.util.List;
+
+/**
+ * 成本数据VO类
+ *
+ * @author lingma
+ */
+@Data
+public class CostVO {
+
+    /**
+     * 当前成本数据
+     */
+    private CurrentCost curr;
+
+    /**
+     * 历史成本数据
+     */
+    private HistoricalCost his;
+
+    /**
+     * 成本趋势数据
+     */
+    private List<CostTrend> trend;
+
+    /**
+     * 生产效率数据
+     */
+    private List<Productivity> prod;
+
+    /**
+     * 成本明细数据
+     */
+    private List<CostDetail> detail;
+
+
+    /**
+     * 当前成本数据内部类
+     */
+    @Data
+    public static class CurrentCost {
+        /**
+         * 总成本
+         */
+        private String total;
+
+        /**
+         * 成本项1名称 - 人工工资+社保+福利
+         */
+        private String n1;
+
+        /**
+         * 成本项1值
+         */
+        private Double v1;
+
+        /**
+         * 成本项1占比
+         */
+        private Double p1;
+
+        /**
+         * 成本项2名称 - 电+汽
+         */
+        private String n2;
+
+        /**
+         * 成本项2值
+         */
+        private Double v2;
+
+        /**
+         * 成本项2占比
+         */
+        private Double p2;
+
+        /**
+         * 成本项3名称 - 运营+管理
+         */
+        private String n3;
+
+        /**
+         * 成本项3值
+         */
+        private Double v3;
+
+        /**
+         * 成本项3占比
+         */
+        private Double p3;
+
+        /**
+         * 成本项4名称 - 机物料+修理+制版材料+染料+助剂+辅料+水
+         */
+        private String n4;
+
+        /**
+         * 成本项4值
+         */
+        private Double v4;
+
+        /**
+         * 成本项4占比
+         */
+        private Double p4;
+
+        /**
+         * 成本项5名称 - 折旧
+         */
+        private String n5;
+
+        /**
+         * 成本项5值
+         */
+        private Double v5;
+
+        /**
+         * 成本项5占比
+         */
+        private Double p5;
+
+        /**
+         * 成本项6名称 - 其他
+         */
+        private String n6;
+
+        /**
+         * 成本项6值
+         */
+        private Double v6;
+
+        /**
+         * 成本项6占比
+         */
+        private Double p6;
+    }
+
+    /**
+     * 历史成本数据内部类
+     */
+    @Data
+    public static class HistoricalCost {
+        /**
+         * 成本项1历史数据列表 - 人工工资+社保+福利
+         */
+        private List<CostItem> n1;
+
+        /**
+         * 成本项2历史数据列表 - 电+汽
+         */
+        private List<CostItem> n2;
+
+        /**
+         * 成本项3历史数据列表 - 运营+管理
+         */
+        private List<CostItem> n3;
+
+        /**
+         * 成本项4历史数据列表 - 机物料+修理+制版材料+染料+助剂+辅料+水
+         */
+        private List<CostItem> n4;
+
+        /**
+         * 成本项5历史数据列表 - 折旧
+         */
+        private List<CostItem> n5;
+
+        /**
+         * 成本项6历史数据列表 - 其他
+         */
+        private List<CostItem> n6;
+    }
+
+    /**
+     * 成本项内部类
+     */
+    @Data
+    public static class CostItem {
+        /**
+         * 月份
+         */
+        private String month;
+
+        /**
+         * 成本值
+         */
+        private Double value;
+    }
+
+    /**
+     * 成本趋势内部类
+     */
+    @Data
+    public static class CostTrend {
+        /**
+         * 月份
+         */
+        private String month;
+
+        /**
+         * 成本值(万元)
+         */
+        private Double value;
+    }
+
+    /**
+     * 生产效率内部类
+     */
+    @Data
+    public static class Productivity {
+        /**
+         * 工序名称
+         */
+        private String name;
+
+        /**
+         * 效率值(米/人/天)
+         */
+        private Double value;
+    }
+
+    /**
+     * 成本明细内部类
+     */
+    @Data
+    public static class CostDetail {
+        /**
+         * 成本项目
+         */
+        private String item;
+
+        /**
+         * 各工序值列表
+         */
+        private List<Double> values;
+    }
+}

+ 242 - 0
jjt-biz/src/main/java/com/jjt/biz/vo/GlobalRevenueVO.java

@@ -0,0 +1,242 @@
+package com.jjt.biz.vo;
+
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * 全球营收数据VO类
+ *
+ * @author wukai
+ */
+@Data
+public class GlobalRevenueVO {
+
+    /**
+     * 年度总营收(单位:万元)
+     */
+    private Integer rev;
+
+    /**
+     * 年度总成本(单位:万元)
+     */
+    private Integer cogs;
+
+    /**
+     * 年度总利润(单位:万元)
+     */
+    private Integer gp;
+
+    /**
+     * 年度毛利率(百分比)
+     */
+    private Double gpm;
+
+    /**
+     * 总资产(单位:万元)
+     */
+    private Integer assets;
+
+    /**
+     * 业务覆盖国家数量
+     */
+    private Integer country;
+
+    /**
+     * 年度订单总量
+     */
+    private Integer order;
+
+    /**
+     * 营收构成明细数据
+     */
+    private List<RevenueDetail> revList;
+
+    /**
+     * 成本构成明细数据
+     */
+    private List<CostDetail> cogsList;
+
+    /**
+     * 各国业务数据
+     */
+    private List<CountryBusiness> orderList;
+
+    /**
+     * 产品线利润数据
+     */
+    private List<ProductProfit> gpList;
+
+    /**
+     * 产品线毛利率数据
+     */
+    private List<ProductGrossMargin> gpmList;
+
+    /**
+     * 月度经营数据
+     */
+    private List<MonthlyOperation> tradeList;
+
+    /**
+     * 月度预测数据
+     */
+    private List<MonthlyForecast> forecastList;
+
+
+    /**
+     * 营收构成明细内部类
+     */
+    @Data
+    public static class RevenueDetail {
+        /**
+         * 收入类型
+         */
+        private String type;
+
+        /**
+         * 收入金额(万元)
+         */
+        private Double value;
+
+    }
+
+    /**
+     * 成本构成明细内部类
+     */
+    @Data
+    public static class CostDetail {
+        /**
+         * 成本类型
+         */
+        private String type;
+
+        /**
+         * 成本金额(万元)
+         */
+        private Double value;
+
+    }
+
+    /**
+     * 各国业务数据内部类
+     */
+    @Data
+    public static class CountryBusiness {
+        /**
+         * 国家名称
+         */
+        private String country;
+
+        /**
+         * 订单数量
+         */
+        private Integer num;
+
+        /**
+         * 收入(万元)
+         */
+        private Integer rev;
+
+        /**
+         * 利润(万元)
+         */
+        private Double gp;
+    }
+
+    /**
+     * 产品线利润数据内部类
+     */
+    @Data
+    public static class ProductProfit {
+        /**
+         * 产品类型
+         */
+        private String type;
+
+        /**
+         * 利润金额(万元)
+         */
+        private Double value;
+
+        /**
+         * 毛利率百分比
+         */
+        private Double gmp;
+
+    }
+
+    /**
+     * 产品线毛利率数据内部类
+     */
+    @Data
+    public static class ProductGrossMargin {
+        /**
+         * 产品类型
+         */
+        private String type;
+
+        /**
+         * 毛利率百分比
+         */
+        private Double value;
+
+    }
+
+    /**
+     * 月度经营数据内部类
+     */
+    @Data
+    public static class MonthlyOperation {
+        /**
+         * 月份
+         */
+        private String month;
+
+        /**
+         * 月度收入(万元)
+         */
+        private Integer revenue;
+
+        /**
+         * 月度成本(万元)
+         */
+        private Integer cost;
+
+        /**
+         * 月度利润(万元)
+         */
+        private Integer profit;
+
+        /**
+         * 月度利润率(%)
+         */
+        private Double profitRate;
+
+    }
+
+    /**
+     * 月度预测数据内部类
+     */
+    @Data
+    public static class MonthlyForecast {
+        /**
+         * 预测月份
+         */
+        private String month;
+
+        /**
+         * 预测收入(万元)
+         */
+        private Integer revenue;
+
+        /**
+         * 预测利润率(%)
+         */
+        private Double profitRate;
+
+        /**
+         * 预测资产(万元)
+         */
+        private Integer assets;
+    }
+}

+ 166 - 0
jjt-biz/src/main/java/com/jjt/biz/vo/SalesVO.java

@@ -0,0 +1,166 @@
+package com.jjt.biz.vo;
+
+import lombok.Data;
+import java.util.List;
+
+/**
+ * 销售数据VO类
+ *
+ * @author lingma
+ */
+@Data
+public class SalesVO {
+
+    /**
+     * 月度销售数据
+     */
+    private List<MonthlySales> sales;
+
+    /**
+     * 渠道排名数据
+     */
+    private List<ChannelRanking> channel;
+
+    /**
+     * 花色排名数据
+     */
+    private List<PatternRanking> pattern;
+
+    /**
+     * 区域销售数据
+     */
+    private List<SalesTypeRegion> type;
+
+    /**
+     * 规格占比数据
+     */
+    private List<SpecRanking> bom;
+
+
+    /**
+     * 月度销售数据内部类
+     */
+    @Data
+    public static class MonthlySales {
+        /**
+         * 月份
+         */
+        private String month;
+
+        /**
+         * 销售收入
+         */
+        private Double revenue;
+    }
+
+    /**
+     * 渠道排名数据内部类
+     */
+    @Data
+    public static class ChannelRanking {
+        /**
+         * 渠道名称
+         */
+        private String channel;
+
+        /**
+         * 销售金额
+         */
+        private Double amount;
+
+        /**
+         * 占比百分比
+         */
+        private Double percentage;
+    }
+
+    /**
+     * 花色排名数据内部类
+     */
+    @Data
+    public static class PatternRanking {
+        /**
+         * 花色名称
+         */
+        private String pattern;
+
+        /**
+         * 销售金额
+         */
+        private Double amount;
+    }
+
+    /**
+     * 销售类型区域数据内部类
+     */
+    @Data
+    public static class SalesTypeRegion {
+        /**
+         * 销售类型
+         */
+        private String salesType;
+
+        /**
+         * 区域销售详情列表
+         */
+        private List<RegionDetail> regions;
+    }
+
+    /**
+     * 区域详情内部类
+     */
+    @Data
+    public static class RegionDetail {
+        /**
+         * 区域名称
+         */
+        private String region;
+
+        /**
+         * 销售金额
+         */
+        private Double amount;
+
+        /**
+         * 月度销售趋势
+         */
+        private List<MonthlyTrend> trend;
+    }
+
+    /**
+     * 月度趋势内部类
+     */
+    @Data
+    public static class MonthlyTrend {
+        /**
+         * 月份
+         */
+        private String month;
+
+        /**
+         * 销售金额
+         */
+        private Double amount;
+    }
+
+    /**
+     * 规格排名数据内部类
+     */
+    @Data
+    public static class SpecRanking {
+        /**
+         * 规格名称
+         */
+        private String bom;
+
+        /**
+         * 销售金额
+         */
+        private Double amount;
+
+        /**
+         * 占比百分比
+         */
+        private Double percentage;
+    }
+}

+ 9 - 5
jjt-biz/src/main/java/com/jjt/biz/vo/WarpRunIn.java

@@ -36,6 +36,8 @@ public class WarpRunIn {
     private Float density;
     @ApiModelProperty("卷曲张力系数")
     private Float coefficient;
+    @ApiModelProperty("设定落布米数")
+    private Integer length;
 
     public WarpRunIn(boolean isSmall, Map<String, Object> map) {
         TwinDevice twinDevice = (TwinDevice) map.get("device");
@@ -54,16 +56,18 @@ public class WarpRunIn {
         this.l3 = (int) map.get("Formula_data_21") + "";
         this.l4 = (int) map.get("Formula_data_22") + "";
         this.l5 = (int) map.get("Formula_data_23") + "";
+        this.length = (int) map.get("Capacity_data_4");
         this.density = (float) map.get("Formula_data_24");
         this.coefficient = (float) map.get("Formula_data_25");
     }
 
     private void setBig(Map<String, Object> map) {
-        this.l1 = ((Number) map.get("Formula_data_1")).intValue()+"";
-        this.l2 = ((Number) map.get("Formula_data_2")).intValue()+"";
-        this.l3 = ((Number)map.get("Formula_data_3")).intValue()+"";
-        this.l4 = ((Number) map.get("Formula_data_4")).intValue()+"";
-        this.l5 = ((Number) map.get("Formula_data_5")).intValue()+"";
+        this.l1 = ((Number) map.get("Formula_data_1")).intValue() + "";
+        this.l2 = ((Number) map.get("Formula_data_2")).intValue() + "";
+        this.l3 = ((Number) map.get("Formula_data_3")).intValue() + "";
+        this.l4 = ((Number) map.get("Formula_data_4")).intValue() + "";
+        this.l5 = ((Number) map.get("Formula_data_5")).intValue() + "";
+        this.length = ((Number) map.get("Capacity_data_176")).intValue();
 //        this.l1 = String.format("%d,%d",
 //                ((Number) map.get("Formula_data_1")).intValue(),
 //                ((Number) map.get("Formula_data_2")).intValue());

+ 3 - 0
jjt-biz/src/main/java/com/jjt/calc/service/impl/TwinCalcHourServiceImpl.java

@@ -189,6 +189,9 @@ public class TwinCalcHourServiceImpl implements ITwinCalcHourService {
         search.setOnline("1");
         //TODO 临时设置单个设备,上线需取消
 //        search.setDeviceId(175L);
+//        Map<String,Object> params = new HashMap<>();
+//        params.put("ids", new int[]{120});
+//        search.setParams(params);
         List<TwinDevice> list = deviceService.selectTwinDeviceList(search);
         List<TwinDevice> errList = exec(list, start, end);
         //重试2次

+ 22 - 3
jjt-biz/src/main/java/com/jjt/emp/service/impl/TwinEmpCalcServiceImpl.java

@@ -133,7 +133,7 @@ public class TwinEmpCalcServiceImpl implements ITwinEmpCalcService {
         List<TwinCalcHourSpec> specHourList = hourSpecService.selectTwinCalcHourSpecListByDate(date);
         specHourList.forEach(TwinCalcHourSpec::setTeam);
         // 按照deviceId、density、mick、team,并统计每组的length总和
-        Map<String, List<TwinCalcHourSpec>> specHourMap = specHourList.stream().collect(Collectors.groupingBy(o -> o.getDeviceId() + "-" + o.getTeam(), LinkedHashMap::new, Collectors.toList()));
+//        Map<String, List<TwinCalcHourSpec>> specHourMap = specHourList.stream().collect(Collectors.groupingBy(o -> o.getDeviceId() + "-" + o.getTeam(), LinkedHashMap::new, Collectors.toList()));
         List<TwinCalcDay> twinCalcDays = twinCalcDayService.selectTwinCalcDayListByTime(date, date);
         Map<Long, TwinCalcDay> calcMap = twinCalcDays.stream().collect(Collectors.toMap(TwinCalcDay::getDeviceId, o -> o));
         Map<Long, BigDecimal> effMap = twinCalcDays.stream().collect(Collectors.toMap(TwinCalcDay::getDeviceId, TwinCalcDay::getEfficiency));
@@ -146,6 +146,20 @@ public class TwinEmpCalcServiceImpl implements ITwinEmpCalcService {
 //        process(true, emp, calcMap, configMap, calcList, specHourMap);
 //        process(false, emp, calcMap, configMap, calcList, specHourMap);
         List<TwinEmpCalc> calcList = process(emp, effMap, configMap, specHourList);
+        //最后还需要合并,否则数据会重复
+        calcList = calcList.stream().collect(Collectors.groupingBy(o -> o.getDeviceId() + "-" + o.getDensity() + "-" + o.getMick() + "-" + o.getEmpTeam())).entrySet().stream().map(entry -> {
+            List<TwinEmpCalc> group = entry.getValue();
+            TwinEmpCalc calc = group.get(0);
+            // 合并产量
+            BigDecimal totalLength = group.stream().map(TwinEmpCalc::getLength).reduce(BigDecimal.ZERO, BigDecimal::add);
+            calc.setLength(totalLength);
+
+            // 合并总价
+            BigDecimal totalPrice = group.stream().map(TwinEmpCalc::getTotalPrice).reduce(BigDecimal.ZERO, BigDecimal::add);
+            calc.setTotalPrice(totalPrice);
+            return calc;
+        }).collect(Collectors.toList());
+
         if (calcList.size() > 0) {
             try (SqlSession sqlSession = factory.openSession(ExecutorType.BATCH, false)) {
                 TwinEmpCalcMapper mapper = sqlSession.getMapper(TwinEmpCalcMapper.class);
@@ -241,9 +255,9 @@ public class TwinEmpCalcServiceImpl implements ITwinEmpCalcService {
 //                    List<TwinCalcHourSpec> hourList = specs.stream().filter(t -> t.getHour() == hour).collect(Collectors.toList());
 //                }
                 //按密度和米克重、毛高分组统计
-                Map<String, BigDecimal> resultMap = hourList.stream().collect(Collectors.groupingBy(t -> t.getDensity() + "-" + t.getMick() + "-" + t.getHeight(), Collectors.reducing(BigDecimal.ZERO, TwinCalcHourSpec::getLength, BigDecimal::add)));
+                Map<String, BigDecimal> resultMap = hourList.stream().collect(Collectors.groupingBy(t -> t.getDensity() + "||" + t.getMick() + "||" + t.getHeight() + "||" + t.getSpec(), Collectors.reducing(BigDecimal.ZERO, TwinCalcHourSpec::getLength, BigDecimal::add)));
                 for (String ss : resultMap.keySet()) {
-                    String[] temp = ss.split("-");
+                    String[] temp = ss.split("\\|\\|");
                     BigDecimal density = new BigDecimal(temp[0]).setScale(2, RoundingMode.HALF_UP);
                     Integer mick = Integer.parseInt(temp[1]);
                     TwinEmpCalc calc = new TwinEmpCalc();
@@ -252,6 +266,11 @@ public class TwinEmpCalcServiceImpl implements ITwinEmpCalcService {
                         BigDecimal height = new BigDecimal(temp[2]).setScale(2, RoundingMode.HALF_UP);
                         calc.setHeight(height);
                     }
+                    if (temp.length == 4 && StringUtils.isNotEmpty(temp[3]) && !"null".equals(temp[3])) {
+                        calc.setSpec(temp[3]);
+                    } else {
+                        calc.setSpec("");
+                    }
                     calc.setEfficiency(effMap.get(deviceId));
                     calc.setEmpDate(emp.getEmpDate());
                     BigDecimal length = resultMap.get(ss);

+ 17 - 6
jjt-biz/src/main/java/com/jjt/utils/AsyncService.java

@@ -45,7 +45,7 @@ public class AsyncService {
             "Alarm_unit_2", "Alarm_unit_20", "Alarm_unit_21", "Alarm_unit_22", "Alarm_unit_23",
             "Alarm_unit_24", "Alarm_unit_25", "Alarm_unit_26", "Alarm_unit_27", "Alarm_unit_3",
             "Alarm_unit_4", "Alarm_unit_5", "Alarm_unit_6", "Alarm_unit_7", "Alarm_unit_8",
-            "Alarm_unit_9"
+            "Alarm_unit_9", "Formula_data_36"
     };
 
     /**
@@ -232,6 +232,8 @@ public class AsyncService {
         float[] total = new float[6];
         //米克重
         int lastMkz = 0;
+        //规格
+        String lastSpec = "";
         //毛高
         float lastHeight = 0f;
         //上次密度记录的米长
@@ -245,7 +247,9 @@ public class AsyncService {
         for (int i = 0; i < values.size(); i++) {
             JSONArray da = values.getJSONArray(i);
             //当前时间数据 米长,正向电能,牵拉密度 2025-06-19 取消电量计算没必要了
+            //0.米长 1.已废弃 2.密度 3.规格
             float[] curr = {da.getFloat(fieldList.indexOf("Capacity_data_2")), 0f, da.getFloat(fieldList.indexOf("Formula_data_24"))};
+            String spec = da.getStr(fieldList.indexOf("Formula_data_36"));
             int curr48 = da.getInt(fieldList.indexOf("Capacity_data_48"));
 
             if (i == 0) {
@@ -258,12 +262,15 @@ public class AsyncService {
                 if (StringUtils.isNotEmpty(da.getStr(fieldList.indexOf("Formula_data_15")))) {
                     lastHeight = da.getFloat(fieldList.indexOf("Formula_data_15"));
                 }
+                if (StringUtils.isNotEmpty(da.getStr(fieldList.indexOf("Formula_data_36")))) {
+                    lastSpec = da.getStr(fieldList.indexOf("Formula_data_36"));
+                }
                 last48 = curr48;
                 continue;
             }
 
-            if (last[2] != curr[2]) {
-                calcSpec(total[0], lastSpecLength, last[2], lastMkz, lastHeight, specList);
+            if (last[2] != curr[2] || (StringUtils.isNotEmpty(lastSpec) && !lastSpec.equals(spec))) {
+                calcSpec(total[0], lastSpecLength, last[2], lastMkz, lastHeight, lastSpec, specList);
                 lastSpecLength = total[0];
             }
             //如果当前值为小于上一个,且上一个值不为0,则计算
@@ -290,11 +297,14 @@ public class AsyncService {
             if (StringUtils.isNotEmpty(da.getStr(fieldList.indexOf("Formula_data_15")))) {
                 lastHeight = da.getFloat(fieldList.indexOf("Formula_data_15"));
             }
+            if (StringUtils.isNotEmpty(da.getStr(fieldList.indexOf("Formula_data_36")))) {
+                lastSpec = da.getStr(fieldList.indexOf("Formula_data_36"));
+            }
             last48 = curr48;
         }
         //还是只计算米长
         calcTotal(0, last, first, total, lastMkz, coefficient);
-        calcSpec(total[0], lastSpecLength, last[2], lastMkz, lastHeight, specList);
+        calcSpec(total[0], lastSpecLength, last[2], lastMkz, lastHeight, lastSpec, specList);
         total[3] = 3600 - total[4];
         total[5] = lastHeight;
         Map<String, Object> result = new HashMap<>(16);
@@ -377,15 +387,16 @@ public class AsyncService {
      * @param lastMkz     米克重
      * @param specList    列表
      */
-    private void calcSpec(float len, float lastLen, float lastDensity, int lastMkz, float lastHeight, List<TwinCalcHourSpec> specList) {
+    private void calcSpec(float len, float lastLen, float lastDensity, int lastMkz, float lastHeight, String lastSpec, List<TwinCalcHourSpec> specList) {
         //计算规格米长
-        //如果密度有变化,就记录下来
+        //如果密度或者规格有变化,就记录下来
         TwinCalcHourSpec spec = new TwinCalcHourSpec();
         spec.setDensity(BigDecimal.valueOf(lastDensity));
         float len1 = len - lastLen;
         spec.setLength(BigDecimal.valueOf(len1));
         spec.setMick(lastMkz);
         spec.setHeight(BigDecimal.valueOf(lastHeight));
+        spec.setSpec(lastSpec);
         specList.add(spec);
     }
 

+ 5 - 3
jjt-biz/src/main/java/com/jjt/wkEmp/service/impl/TwinWkEmpRotaServiceImpl.java

@@ -117,8 +117,6 @@ public class TwinWkEmpRotaServiceImpl implements ITwinWkEmpRotaService {
         rota.setEmpId(data.getId());
         rota.setEmpName(data.getName());
         rota.setEmpTeam(data.getTeam());
-        rota.setInTime(data.getIn());
-        rota.setOutTime(data.getOut());
         Set<Integer> deviceSet = new TreeSet<>();
         for (String s : data.getDevices()) {
             String id = s.replace("J", "");
@@ -126,9 +124,13 @@ public class TwinWkEmpRotaServiceImpl implements ITwinWkEmpRotaService {
         }
         String devices = deviceSet.stream().map(String::valueOf).collect(Collectors.joining(","));
         rota.setDevices(devices);
-        if (rota.getShiftId() != null) {
+        //2025-08-04 修改同步规则,之前是后面一条记录覆盖前一条记录,现在需要判断上下班时间
+        if (rota.getShiftId() != null && rota.getInTime().equals(data.getIn()) && rota.getOutTime().equals(data.getOut())) {
             updateTwinWkEmpRota(rota);
         } else {
+            rota.setShiftId(null);
+            rota.setInTime(data.getIn());
+            rota.setOutTime(data.getOut());
             insertTwinWkEmpRota(rota);
         }
     }

+ 6 - 0
jjt-biz/src/main/resources/mapper/biz/TwinDeviceMapper.xml

@@ -34,6 +34,12 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="updatedBy != null  and updatedBy != ''"> and UPDATED_BY = #{updatedBy}</if>
             <if test="updatedTime != null "> and UPDATED_TIME = #{updatedTime}</if>
             <if test="remark != null  and remark != ''"> and REMARK = #{remark}</if>
+            <if test="params.ids != null and params.ids !=''">
+                and DEVICE_ID in
+                <foreach item="id" collection="params.ids" open="(" separator="," close=")">
+                    #{id}
+                </foreach>
+            </if>
         </where>
     </select>