CJ hai 1 ano
pai
achega
5de0cf960c

+ 2 - 2
_sidebar.md

@@ -1,8 +1,7 @@
 <!-- docs/_sidebar.md -->
 
 * [个人简历](resume.html)
-* [软件发布及追溯系统 2021-08](/project/软件发布及追溯系统.md)
-<!-- * [商贸进销存软件](/project/商贸进销存软件.md) -->
+* [Java-软件发布及追溯系统 2021-08](/project/软件发布及追溯系统.md)
 * [MES前端操作站 2020-2021](/project/MES前端操作站.md)
 * [PanaCIM松下扣料机服务 2020](/project/PanaCIM松下扣料机服务.md)
 * [Andon系统 2020-2021](/project/Andon系统.md)
@@ -11,6 +10,7 @@
 * [BI报表 2022-2023](/project/BI报表.md)
 * [MI系统 2022-2023](/project/MI系统.md)
 * [小程序&微信网页 2022](/project/小程序合集.md)
+* [Java商贸进销存软件 2019](/project/商贸进销存软件.md)
 * [计划任务 2023](/project/计划任务.md)
 * [云智高级仓储PDA(Android开发) 2018](/project/云智高级仓储PDA(Android开发).md)
 * [代码生成器 2022](/project/代码生成器.md)

+ 156 - 1
project/Andon系统.md

@@ -38,4 +38,159 @@ Andon广播呼叫程序: JavaFx Lame转码 JAVA Native Interface SAPI
 ![Alt text](image-8.png)
 
 WMS基础数据
-![Alt text](image-9.png)
+![Alt text](image-9.png)
+
+
+
+代码预览
+
+```java
+package cn.ciemis.util;
+
+import cn.ciemis.base.CommandUtil;
+import cn.ciemis.entity.PlayModel;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.*;
+import java.util.*;
+
+public class AudioUtil4 {
+    public static String path = null;
+    public static String lame = null;
+    public static String tail = "F.mp3";
+    static MSTTSSpeech speech = new MSTTSSpeech();
+
+    static Logger LOG = LoggerFactory.getLogger(AudioUtil4.class);
+
+    public static File mergeMp3(List<PlayModel> playModels) throws IOException {
+        Vector<FileInputStream> fileInputStreams = new Vector<>();
+        for (PlayModel playModel : playModels) {
+            if (playModel.isMp3()) {
+                playModel.setPath(path + "\\" + playModel.getContent());
+            } else {
+                playModel.setPath(AudioUtil4.textToMp3(playModel.getContent()));
+            }
+            fileInputStreams.add(new FileInputStream(playModel.getPath()));
+            LOG.info("合并文件:" + playModel.getPath());
+        }
+        SequenceInputStream sequenceInputStream = new SequenceInputStream(fileInputStreams.elements());
+        String mergePath = path + "\\" + UUID.randomUUID() + tail;
+        File afterMergeMp3 = new File(mergePath);
+        boolean isSuccess = afterMergeMp3.createNewFile();
+        if (isSuccess) {
+            FileOutputStream fileOutputStream = new FileOutputStream(afterMergeMp3);//destinationfile
+            int temp;
+
+            while ((temp = sequenceInputStream.read()) != -1) {
+                // System.out.print( (char) temp ); // to print at DOS prompt
+                fileOutputStream.write(temp);   // to write to file
+            }
+            for (FileInputStream f : fileInputStreams) {
+                f.close();
+            }
+            fileOutputStream.close();
+            sequenceInputStream.close();
+            playModels.forEach(playModel -> {
+                if (!playModel.isMp3()) {
+                    new File(playModel.getPath()).delete();
+                }
+            });
+        } else {
+            throw new IOException("mp3创建失败! " + mergePath);
+        }
+        return new File(mergePath);
+    }
+
+    public static List<PlayModel> getPlayList(String message) {
+        int i = 0;
+        char[] messageArr = message.toCharArray();
+        List<PlayModel> playModels = new ArrayList<>();
+        StringBuilder temp = new StringBuilder();
+        while (i < messageArr.length) {
+            if (messageArr[i] == '[') {
+                if (temp.length() > 0) {
+                    playModels.add(new PlayModel(false, temp.toString(), null));
+                    temp = new StringBuilder();
+                }
+                Map<String, Object> mp3 = getEndIndex(i, messageArr);
+                i = (int) mp3.get("endIndex");
+                playModels.add(new PlayModel(true, mp3.get("mp3").toString(), null));
+            } else {
+                temp.append(messageArr[i]);
+            }
+            i += 1;
+        }
+        if (temp.length() > 0) {
+            playModels.add(new PlayModel(false, temp.toString(), null));
+        }
+        return playModels;
+    }
+
+    public static Map<String, Object> getEndIndex(int index, char[] chars) {
+        StringBuilder mp3 = new StringBuilder();
+        int mp3EndIndex = -1;
+        int endIndex = -1;
+        for (int i = index + 3; i < chars.length; i++) {
+            if (chars[i] == ']') {
+                endIndex = i;
+                break;
+            } else if (chars[i] == ';') {
+                mp3EndIndex = i;
+            } else {
+                if (mp3EndIndex == -1) {
+                    mp3.append(chars[i]);
+                }
+            }
+        }
+        Map<String, Object> result = new HashMap<>();
+        result.put("endIndex", endIndex);
+        result.put("mp3", mp3);
+        return result;
+    }
+
+    public static String textToMp3(String text) throws FileNotFoundException {
+        LOG.info("转语音:" + text);
+        File wavFile = new File(textToWav(text));
+        if (!wavFile.exists()) {
+            throw new FileNotFoundException("转语音失败!");
+        } else {
+            //to mp3 use lame
+            String wavPathStr = wavFile.getPath();
+            String mp3PathStr = wavFile.getPath().
+                    replace(".wav", ".mp3");
+            String cmds1 = lame +
+                    String.format(" -b128 --resample 24 -m j  %s %s", wavPathStr, mp3PathStr);
+//                Arguments = string.Format("-b128 --resample 24 -m j \"{0}\" \"{1}\"", str3, str4)
+            //                    String.format(" --silent -b 160 -m m -q 9-resample -tt  %s %s", wavPathStr, mp3PathStr);
+
+            String cmds2 = lame +
+                    String.format(" --scale 4 --mp3input %s %s", mp3PathStr, mp3PathStr.replace(".mp3", "L.mp3"));
+
+            LOG.info("lame to mp3 : " + cmds1);
+            LOG.info("large voice : " + cmds2);
+            CommandUtil.exec(cmds1);
+            CommandUtil.exec(cmds2);
+            new File(wavPathStr).delete();
+            new File(mp3PathStr).delete();
+            return mp3PathStr.replace(".mp3", "L.mp3");
+        }
+    }
+
+
+    private static String textToWav(String text) {
+        speech.setFormatType(16);
+        File wavFile = new File(path);
+        if (!wavFile.exists()) {
+            wavFile.getParentFile().mkdirs();
+        }
+        LOG.info(path);
+        String filePath = path + "\\" + UUID.randomUUID() + ".wav";
+        LOG.info("wav文件保存到 : " + filePath);
+        speech.saveToWav(text, filePath);
+        return filePath;
+    }
+}
+
+```
+

+ 2 - 0
project/BI报表.md

@@ -13,6 +13,8 @@
 前台: Vue2
 数据库: SqlServer
 
+相关链接:甘特图实现 [Gantt/BTS 生产计划电子看板甘特图](https://cybersicko.net/article/10.html)
+
 #### **功能介绍**
 BI报表服务多家企业,通过WMS提供统一的BI接口。由我负责开发BI报表系统的基础功能及报表展示层。
 权限:

+ 4 - 1
project/EDI.md

@@ -27,10 +27,13 @@
                     QRCodeGenerator qrGenerator = new QRCoder.QRCodeGenerator();
                     QRCodeData qrCodeData = qrGenerator.CreateQrCode(sdHu.HuId, QRCodeGenerator.ECCLevel.M);
                     QRCode qrcode = new QRCode(qrCodeData);
-![Alt text](image-49.png)
+
 ```
 
+![Alt text](image-49.png)
+
 #### **财务税票模块开发**
+
 拥有可结算清单,采购账单,采购发票,未开票明细等功能,作为项目亮点,增加了发票识别导入功能,通过在阿里云函数计算编写Python脚本,利用正则等方式将图片或PDF识别出的文本关键字筛选出来。
 我负责开发Vue界面功能和Python脚本。
 ![Alt text](image-51.png)

+ 167 - 1
project/MI系统.md

@@ -24,4 +24,170 @@ MQTT: 阿里云MQTT服务\
 采集程序: C# Hslcommuniation Kepware
 
 #### **功能介绍**
-![Alt text](image-24.png)
+![Alt text](image-24.png)
+
+注塑机IM Controller:
+
+```java
+package com.ciemis.api.controller.im;
+
+import cn.hutool.json.JSONArray;
+import com.aliyun.oss.OSSClient;
+import com.ciemis.entity.*;
+import com.ciemis.entity.postgres.OpcImData;
+import com.ciemis.entity.postgres.OpcImWarningRecord;
+import com.ciemis.framework.aop.CompanyInject;
+import com.ciemis.framework.exception.BusinessException;
+import com.ciemis.framework.exception.ServiceException;
+import com.ciemis.framework.properties.AliyunOSSProperties;
+import com.ciemis.framework.response.ResponseData;
+import com.ciemis.framework.util.OssClient;
+import com.ciemis.repository.CraftProcessRepository;
+import com.ciemis.repository.CraftRepository;
+import com.ciemis.repository.MachineRepository;
+import com.ciemis.repository.MqAccountRepository;
+import com.ciemis.service.PostgresqlService;
+
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.net.URL;
+import java.sql.SQLException;
+import java.util.*;
+
+@RestController
+@RequestMapping("/im")
+public class IMController {
+
+    @Autowired
+    PostgresqlService postgresqlService;
+
+    @Autowired
+    CraftRepository craftRepository;
+
+    @Autowired
+    CraftProcessRepository craftProcessRepository;
+
+    @Autowired
+    MachineRepository machineRepository;
+
+    @Autowired
+    MqAccountRepository mqAccountRepository;
+
+
+    @Autowired
+    AliyunOSSProperties ossProperties;
+
+    @ApiOperation(value = "概览接口", notes = "")
+    @RequestMapping("/dashboard")
+    public ResponseData dashboard(@CompanyInject Company company, @RequestParam(value = "machineId", required = false)
+            String machineId) throws SQLException {
+        List<BaseMachine> machines = machineRepository.findByCompanyCode(company.getCode());
+        Map<String, Object> result = new HashMap<>();
+        result.put("machineList", machines);
+        result.put("warnRules", new JSONArray());
+        List<MqAccount> mqAccounts = mqAccountRepository.findMqAccountByIsSuper(true);
+        result.put("mqAccounts", mqAccounts);
+
+        List<OpcImData> data;
+        if (Objects.isNull(machineId) || machineId.isEmpty()) {
+            if (machines.isEmpty()) {
+                throw new BusinessException(500, "请维护注塑机数据");
+            } else {
+                BaseMachine machine = machines.get(0);
+                data = postgresqlService.findAllByMachineId(company, machine.getMachineId());
+                if (data.size() == 0) {
+                    data.add(new OpcImData());
+                }
+                if (machine.getImage() != null) {
+                    Date expiration = new Date(new Date().getTime() + 3600 * 1000);
+                    OSSClient ossClient = OssClient.initOSS(ossProperties);
+                    URL url = ossClient.generatePresignedUrl(ossProperties.getBucket(), machine.getImage(), expiration);
+                    machine.setImage(url.toString());
+                }
+                result.put("machineCurrent", machine);
+                result.put("lastMachineData", data.get(0));
+                result.put("lastWarningRecord", postgresqlService.getLastWarningRecord(company, machine.getMachineId()));
+            }
+        } else {
+            BaseMachine machine = machineRepository.findByMachineIdAndCompanyCode(machineId, company.getCode());
+            if (Objects.isNull(machine)) {
+                throw new NullPointerException("未找到machineId");
+            }
+            data = postgresqlService.findAllByMachineId(company, machine.getMachineId());
+            if (data.size() == 0) {
+                data.add(new OpcImData());
+            }
+            if (machine.getImage() != null) {
+                Date expiration = new Date(new Date().getTime() + 3600 * 1000);
+                OSSClient ossClient = OssClient.initOSS(ossProperties);
+                URL url = ossClient.generatePresignedUrl(ossProperties.getBucket(), machine.getImage(), expiration);
+                machine.setImage(url.toString());
+            }
+            result.put("machineCurrent", machine);
+            result.put("lastMachineData", data.get(0));
+
+            result.put("lastWarningRecord", postgresqlService.getLastWarningRecord(company, machine.getMachineId()));
+
+        }
+
+        BaseCraft craft = craftRepository.findByCompanyCodeAndCraftId(company.getCode(), data.get(0).getTm_craft_id());
+
+        if (!Objects.isNull(craft)) {
+            List<BaseCraftProcess> craftProcesses = craftProcessRepository.findByCompanyCodeAndCraftId(company.getCode(), data.get(0).getTm_craft_id());
+            result.put("craft", craft);
+            result.put("warnRules", craftProcesses);
+        }
+
+        //告警
+
+        return ResponseData.success(result);
+    }
+
+
+    @ApiOperation(value = "查询告警", notes = "")
+    @RequestMapping(value = "/searchWarningRecord", method = RequestMethod.GET)
+    public ResponseData searchWarningRecord(@CompanyInject Company company,
+                                            @RequestParam(value = "machineId", required = false) String machineId,
+                                            @RequestParam(value = "alarmId", required = false) String alarmId,
+                                            @RequestParam(value = "description", required = false) String description,
+                                            @RequestParam(value = "startTime", required = false) String startTime,
+                                            @RequestParam(value = "endTime", required = false) String endTime,
+                                            @RequestParam(value = "level", required = false) Integer level) {
+
+        try {
+            List<OpcImWarningRecord> opcImWarningRecords = postgresqlService.searchImWarningRecord(company,
+                    machineId, alarmId, description, startTime, endTime, level);
+            return ResponseData.success(opcImWarningRecords);
+        } catch (SQLException e) {
+            e.printStackTrace();
+            throw new ServiceException(500, e.getMessage());
+        }
+    }
+
+    @ApiOperation(value = "查询周期时间", notes = "")
+    @RequestMapping(value = "/searchImOpcCycleTime", method = RequestMethod.GET)
+    public ResponseData searchImOpcCycleTime(@CompanyInject Company company,
+                                             @RequestParam(value = "machineId", required = false) String machineId,
+                                             @RequestParam(value = "craftId", required = false) String craftId,
+                                             @RequestParam(value = "startTime", required = false) String startTime,
+                                             @RequestParam(value = "endTime", required = false) String endTime) {
+
+        try {
+            List<OpcImData> results = postgresqlService.searchImOpcDataTimeOfCycle(company, machineId, craftId, startTime, endTime);
+            return ResponseData.success(results);
+        } catch (SQLException e) {
+            e.printStackTrace();
+            throw new ServiceException(500, e.getMessage());
+        }
+    }
+
+
+}
+
+```
+

BIN=BIN
project/image-54.png


+ 21 - 0
project/商贸进销存软件.md

@@ -0,0 +1,21 @@
+## **软件发布及追溯系统**
+
+#### **项目背景**
+2019年作为合伙人为安徽合肥的一家软件公司开发项目,该项目服务于商贸快销品行业,有基本的进销存功能和基本的财务功能。
+
+#### **功能预览**
+![Alt text](image-54.png)
+
+#### **人员**
+由我负责框架搭建,主要功能开发,管理两位软件开发人员进行项目开发。
+
+#### **项目介绍**
+商贸型企业采购下单商品,无TMS,下单后进行入库,可进行退货,盘点,调拨,同时记录库存信息,库存事务等。
+财务模块拥有付款单,收款单等功能,可进行预付款和直接付款等方式进行付款。
+同时开发小程序,小程序拥有客户拜访以及基础进销存功能。
+
+#### **技术栈**
+后端:Java SpringBoot Mybatis
+前端:Jquery Layui
+数据库: MySql7.5
+中间件: Redis

+ 619 - 126
project/软件发布及追溯系统.md

@@ -50,142 +50,635 @@
 ![Alt text](./img/image-4.png)
 
 #### **代码片段**
-<p>C# 下载软件包</p>
-
-```html
-using System;
-using System.Collections.Generic;
-using System.Configuration;
-using System.IO;
-using System.Threading;
-using System.Threading.Tasks;
-using Aliyun.OSS;
-using Aliyun.OSS.Common;
-using CiemisDownload.Entity;
-using CiemisDownload.Helper;
-using Microsoft.Extensions.Hosting;
-using Microsoft.Extensions.Logging;
-using Newtonsoft.Json;
-
-namespace CiemisDownload
-{
-    public class DownloadWorker : BackgroundService
-    {
-        private readonly ILogger<DownloadWorker> _logger;
-
-        private ClientService clientService;
-
-
-        public DownloadWorker(ILogger<DownloadWorker> logger)
-        {
-            _logger = logger;
-            clientService = ClientService.GetInstance();
-        }
-
-        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
-        {
-            while (!stoppingToken.IsCancellationRequested)
-            {
-                _logger.LogInformation("ping...");
-
-                try
-                {
-                    //List<DownloadItem> result = await clientService.GetDownloadUrls().ConfigureAwait(result => _logger.LogInformation()) ;
-                    List<DownloadItem> result = await clientService.GetDownloadUrls();
-                    if (result.Count > 0)
-                    {
-                        Sts sts = await clientService.GetStsAsync();
-                        DownloadFile(sts, result);
-                    }
-                }
-                catch (Exception e)
-                {
-                    _logger.LogError(e.Message);
-                }
+<p>软件发布</p>
+
+```java 
+
+package com.ciemis.svms.service.impl;
+
+import cn.hutool.core.bean.BeanUtil;
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.aliyun.oss.OSSClient;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ciemis.svms.base.exception.BusinessException;
+import com.ciemis.svms.base.util.DateUtil;
+import com.ciemis.svms.base.util.OssClient;
+import com.ciemis.svms.base.ws.SocketManager;
+import com.ciemis.svms.entity.*;
+import com.ciemis.svms.mapper.SoftItemSoftwareMapper;
+import com.ciemis.svms.param.ApprovalSoftwareParam;
+import com.ciemis.svms.param.FileParam;
+import com.ciemis.svms.param.SoftwareParam;
+import com.ciemis.svms.param.SoftwareQuery;
+import com.ciemis.svms.properties.OssProperties;
+import com.ciemis.svms.properties.RepositoryProperties;
+import com.ciemis.svms.service.*;
+import com.ciemis.svms.vo.SoftItemSoftwareVo;
+import com.ciemis.svms.vo.SoftwareServiceResultVo;
+import com.ciemis.svms.vo.SysUserVo;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.messaging.simp.SimpMessagingTemplate;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.socket.WebSocketSession;
+
+import java.net.URL;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * <p>
+ * 软件ITEM 服务实现类
+ * </p>
+ *
+ * @author 杨超杰
+ * @since 2021-04-09
+ */
+@Service
+public class SoftItemSoftwareServiceImpl extends ServiceImpl<SoftItemSoftwareMapper, SoftItemSoftware> implements ISoftItemSoftwareService {
+
+    @Autowired
+    RedisService redisService;
+
+    @Autowired
+    ISoftItemSoftwareService softwareService; //废弃
+
+    @Autowired
+    ISoftSoftwareRelationService softwareRelationService;
+
+    @Autowired
+    IOssFileRecordService ossFileRecordService;
+
+    @Autowired
+    private SimpMessagingTemplate template;
+
+    @Autowired
+    private ISysMenuService menuService;
+
+    @Autowired
+    private ISysNoticeService noticeService;
+
+    @Autowired
+    private ISysUserService userService;
+
+    @Autowired
+    OssProperties properties;
+
+    @Autowired
+    IDataPlantService plantService;
+
+    @Autowired
+    RepositoryProperties repositoryProperties;
+
+    @Autowired
+    IDataProjectService projectService;
+
+    @Autowired
+    ISysItemApprovalService approvalService;
+
+    @Autowired
+    ISoftPushMachineService pushMachineService;
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void saveReleaseSoftware(SysUserVo currentUser, SoftwareParam softwareParam) {
+        SoftItemSoftware softItemSoftware = new SoftItemSoftware();
+        QueryWrapper<SoftItemSoftware> checkId = new QueryWrapper<>();
+        checkId.eq("SoftwareCode", softwareParam.getSoftwareCode());
+        if (softwareService.count(checkId) > 0) {
+            throw new BusinessException(500, "软件ID重复");
+        }
 
-                await Task.Delay(1000, stoppingToken);
-            }
+        BeanUtil.copyProperties(softwareParam, softItemSoftware);
+
+        SysUser user = userService.getAccount(softwareParam.getReceiveUser());
+
+        if (Objects.isNull(user)) {
+            throw new BusinessException(500, "没有找到制造工程师用户");
+        }
+
+        softItemSoftware.setFlag(SoftItemSoftware.Status.CREATE.name());
+        softItemSoftware.setPublishDate(LocalDateTime.now());
+        softItemSoftware.setPublisher(currentUser.getCode());
+        softItemSoftware.setPublisherName(currentUser.getName());
+        softItemSoftware.setUploadType(softwareParam.getUploadType());
+        softItemSoftware.setServiceStatus(SoftItemSoftware.WindowsServiceStatus.PENDING.name());
+        softItemSoftware.setTargetPlant(user.getPlant());
+        softItemSoftware.setReceiveUser(user.getCode());
+        softItemSoftware.setReceiveName(user.getName());
+        softwareService.save(softItemSoftware);
+
+        SysNotice sysNotice = new SysNotice();
+        sysNotice.setCreateTime(LocalDateTime.now());
+        sysNotice.setFromUser(currentUser.getCode());
+        sysNotice.setStatus(SysNotice.Status.CREATE.name());
+        sysNotice.setToUser(softItemSoftware.getUploadFileUser());
+        sysNotice.setRelationField(String.valueOf(softItemSoftware.getSoftwareId()));
+        sysNotice.setType(SysNotice.Type.A.name());
+        if (SoftItemSoftware.Type.UPGRADE.name().equals(softwareParam.getUploadType())) {
+            sysNotice.setSubject(softItemSoftware.getSoftwareName() + "USB升级包上传");
+            sysNotice.setMessage(softItemSoftware.getSoftwareName() + "USB升级包 已经创建,请上传文件");
+        } else {
+            sysNotice.setSubject(softItemSoftware.getSoftwareName() + "软件上传");
+            sysNotice.setMessage(softItemSoftware.getSoftwareName() + "已经创建,请上传文件");
+        }
+
+        sysNotice.setFromUsername(currentUser.getName());
+        QueryWrapper<SysMenu> menuQueryWrapper = new QueryWrapper<>();
+        menuQueryWrapper.eq("Code", "uploadSoftware");
+        SysMenu sysMenu = menuService.getOne(menuQueryWrapper);
+
+        sysNotice.setHandleUrl(sysMenu.getPath() + "/" + softItemSoftware.getSoftwareId());
+
+        noticeService.save(sysNotice);
+
+        redisService.addNotice(softItemSoftware.getUploadFileUser(), sysNotice);
+        //通知
+        WebSocketSession webSocketSession = SocketManager.get(softItemSoftware.getUploadFileUser());
+        if (webSocketSession != null) {
+            /**
+             * 主要防止broken pipe
+             */
+            JSONObject noticeResult = new JSONObject();
+            noticeResult.put("add", sysNotice);
+            template.convertAndSendToUser(softItemSoftware.getUploadFileUser(), "/queue/notice", noticeResult.toJSONString());
+        }
+
+
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void uploadFileBySoftware(SysUserVo currentUser, SoftwareParam softwareParam) {
+
+
+        if (softwareParam.getFiles().length <= 0) {
+            throw new BusinessException(500, "文件不能为空");
+        }
+
+        if (!currentUser.getCode().equals(softwareParam.getUploadFileUser())) {
+            throw new BusinessException(500, "当前上传用户与指定上传用户不一致");
+        }
+
+        //添加文件至OSS File
+        SoftItemSoftware softItemSoftware = softwareService.getById(softwareParam.getSoftwareId());
+
+
+        if (Objects.isNull(softItemSoftware)) {
+            throw new BusinessException(500, "软件信息不存在!");
+        }
+        BeanUtil.copyProperties(softwareParam, softItemSoftware);
+        List<OssFileRecord> ossFileRecordList = new ArrayList<>();
+        List<SoftSoftwareRelation> softSoftwareRelations = new ArrayList<>();
+        for (int i = 0; i < softwareParam.getFiles().length; i++) {
+            FileParam fileParam = softwareParam.getFiles()[i];
+            OssFileRecord ossFileRecord = new OssFileRecord();
+            ossFileRecord.setFileId(fileParam.getSignature());
+            ossFileRecord.setFileName(fileParam.getFileName());
+            ossFileRecord.setCreateTime(softItemSoftware.getPublishDate());
+            ossFileRecord.setCreateUser(currentUser.getCode());
+            ossFileRecord.setSize(fileParam.getSize());
+            ossFileRecord.setStatus(OssFileRecord.FileStatus.CREATE.name());
+            ossFileRecord.setFileType(fileParam.getSuffix());
+            ossFileRecord.setOssUrl(fileParam.getUrl());
+            ossFileRecordList.add(ossFileRecord);
+
+            //关系映射
+
+            SoftSoftwareRelation relation = new SoftSoftwareRelation();
+            relation.setFileId(ossFileRecord.getFileId());
+            relation.setSoftwareCode(softItemSoftware.getSoftwareCode());
+            relation.setType(fileParam.getType());
+            softSoftwareRelations.add(relation);
         }
 
-        /**
-         * 创建下载信息文件
-         */
-        private void CreateDownloadFile(DownloadItem item)
-        {
-            string downloadPath = ConfigurationManager.AppSettings["DownloadPath"];
-            string savePath = downloadPath + item.burnAfterItemCode + "\\";
-            string filePath = savePath + "\\" + "point.d";
-            Directory.CreateDirectory(savePath);
-            if (!File.Exists(filePath))
-            {
-                FileStream fs1 = new FileStream(filePath, FileMode.Create, FileAccess.Write);
-                StreamWriter sw = new StreamWriter(fs1);
-                sw.WriteLine(JsonConvert.SerializeObject(item.point)); //开始写入值
-                sw.Close();
-                fs1.Close();
+        softItemSoftware.setFlag(SoftItemSoftware.Status.UPLOADING.name());
+
+        softwareService.updateById(softItemSoftware);
+
+        //保存oss file record
+        ossFileRecordService.saveBatch(ossFileRecordList);
+
+        //保存文件映射
+        softwareRelationService.saveBatch(softSoftwareRelations);
+
+        //保存软件信息
+        //缓存待上传文件
+        List<FileParam> currentWaitFiles = Arrays.stream(softwareParam.getFiles()).collect(Collectors.toList());
+        currentWaitFiles.forEach((fileParam -> {
+            fileParam.setSoftwareCode(softItemSoftware.getSoftwareCode());
+        }));
+        redisService.addFile(currentUser.getCode(), currentWaitFiles);
+    }
+
+    @Override
+    public List<SoftItemSoftwareVo> selectSoftwareItem(SoftwareQuery softItemSoftware) {
+        return baseMapper.selectSoftwareItem(softItemSoftware);
+    }
+
+    @Override
+    public List<SoftItemSoftwareVo> selectSoftwareManagement(SoftwareQuery condition) {
+        return baseMapper.selectSoftwareManagement(condition);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void deleteSoftware(String userCode, Integer id) {
+        SoftItemSoftware softItemSoftware = softwareService.getById(id);
+        if (Objects.isNull(softItemSoftware)) {
+            throw new BusinessException(500, "找不到软件");
+        }
+        if (softItemSoftware.getFlag().equals(SoftItemSoftware.Status.CREATE.name()) ||
+                softItemSoftware.getFlag().equals(SoftItemSoftware.Status.PENDING.name())) {
+            //删除关联
+//            QueryWrapper<SoftSoftwareRelation> delSoftRelationWrapper = new QueryWrapper<>();
+//            delSoftRelationWrapper.eq("SoftwareCode", softItemSoftware.getSoftwareCode());
+//            softwareRelationService.remove(delSoftRelationWrapper);
+
+            //删除软件记录
+            softItemSoftware.setFlag(SoftItemSoftware.Status.DELETE.name());
+
+            softwareService.updateById(softItemSoftware);
+            //删除OSS文件
+
+            noticeService.deleteNoticeByRelation(String.valueOf(softItemSoftware.getSoftwareId()));
+
+            //删除通知
+            redisService.removeNoticeBySoftwareId(String.valueOf(softItemSoftware.getSoftwareId()), softItemSoftware.getUploadFileUser());
+
+
+            //通知
+            WebSocketSession webSocketSession = SocketManager.get(softItemSoftware.getUploadFileUser());
+            if (webSocketSession != null) {
+                /**
+                 * 主要防止broken pipe
+                 */
+                List<SysNotice> sysNotices = redisService.getAllNotice(userCode);
+                sysNotices.removeIf((file) -> file.getRelationField().equals(String.valueOf(softItemSoftware.getSoftwareId())));
+                JSONObject noticeResult = new JSONObject();
+                noticeResult.put("override", sysNotices);
+                template.convertAndSendToUser(softItemSoftware.getUploadFileUser(), "/queue/notice", noticeResult.toJSONString());
             }
+            //TODO 软件删除后文件记录不删除 待定
+//            ossFileRecordService.remove()
+
+        } else {
+            throw new BusinessException(500, "当前状态不允许删除");
         }
+    }
 
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void updateSoftware(SysUserVo currentUser, SoftwareParam softwareParam) {
+        SoftItemSoftware softItemSoftware = softwareService.getById(softwareParam.getSoftwareId());
+        if (Objects.isNull(softItemSoftware)) {
+            throw new BusinessException(500, "软件信息不存在");
+        }
 
-        private void DownloadFile(Sts sts, List<DownloadItem> result)
-        {
-            new Task(() =>
-            {
-                var client = new OssClient("oss-cn-shanghai.aliyuncs.com", "LTAI5tQgUn4M9Pw8otJX4Evq",
-                    "vLtqLvGhEeYmhJS5P5ypO78W1xccd6");
-
-                string downloadPath = ConfigurationManager.AppSettings["DownloadPath"];
-                try
-                {
-                    // 
-                    foreach (DownloadItem item in result)
-                    {
-                        CreateDownloadFile(item);
-                        item.urls.ForEach((url) =>
-                        {
-                            _logger.LogInformation("/" + url.url);
-                            _logger.LogInformation(downloadPath + url.fileName);
-                            var savePath = downloadPath + item.burnAfterItemCode + "\\";
-
-
-                            Directory.CreateDirectory(savePath);
-
-
-                            DownloadObjectRequest request = new DownloadObjectRequest(sts.Bucket, url.url,
-                                savePath + url.fileName, "E:\\ciemis\\checkpoint\\")
-                            {
-                                // 指定下载的分片大小。
-                                PartSize = 8 * 1024 * 1024,
-                                // 指定并发线程数。
-                                ParallelThreadCount = 3
-                            };
-                            request.StreamTransferProgress += StreamProgressCallback;
-
-                            // 断点续传下载。
-                            client.ResumableDownloadObject(request);
-                        });
-                        int success = clientService.DownloadCompleteCallback(item.point.softwareId.ToString())
-                            .Result;
-                        _logger.LogInformation("下载完成:{0}", success);
-                    }
-                }
-                catch (OssException ex)
-                {
-                    _logger.LogError("Failed with error code: {0}; Error info: {1}. \nRequestID:{2}\tHostID:{3}",
-                        ex.ErrorCode, ex.Message, ex.RequestId, ex.HostId);
-                }
-                catch (Exception ex)
-                {
-                    _logger.LogError("Failed with error info: {0}", ex.Message);
+        if (softwareParam.getFiles().length == 0) {
+            BeanUtil.copyProperties(softwareParam, softItemSoftware);
+            softwareService.updateById(softItemSoftware);
+        } else {
+            if (!currentUser.getCode().equals(softwareParam.getUploadFileUser())) {
+                throw new BusinessException(500, "当前上传用户与指定上传用户不一致");
+            }
+//            if (!softItemSoftware.getFlag().equals(SoftItemSoftware.Status.CREATE.name()) ||
+//                    !softItemSoftware.getFlag().equals(SoftItemSoftware.Status.PENDING.name())) {
+//                throw new BusinessException(500, "当前状态不允许修改");
+//            }
+//
+            if (softItemSoftware.getFlag().equals(SoftItemSoftware.Status.CREATE.name()) ||
+                    softItemSoftware.getFlag().equals(SoftItemSoftware.Status.PENDING.name())) {
+                List<FileParam> newFiles = Arrays.stream(softwareParam.getFiles()).filter(FileParam::isCreate).collect(Collectors.toList());
+                List<FileParam> removeFiles = Arrays.stream(softwareParam.getFiles()).filter(FileParam::isDelete).collect(Collectors.toList());
+                List<String> removeRecords = removeFiles.stream().map(FileParam::getSignature).collect(Collectors.toList());
+                List<SoftSoftwareRelation> newRelations = new ArrayList<>();
+                List<OssFileRecord> newRecords = new ArrayList<>();
+                newFiles.forEach((item) -> {
+                    SoftSoftwareRelation relation = new SoftSoftwareRelation();
+                    relation.setFileId(item.getSignature());
+                    relation.setSoftwareCode(softItemSoftware.getSoftwareCode());
+                    relation.setType(item.getType());
+                    newRelations.add(relation);
+                    OssFileRecord ossFileRecord = new OssFileRecord();
+                    ossFileRecord.setFileId(item.getSignature());
+                    ossFileRecord.setFileName(item.getFileName());
+                    ossFileRecord.setCreateTime(softItemSoftware.getPublishDate());
+                    ossFileRecord.setCreateUser(currentUser.getCode());
+                    ossFileRecord.setSize(item.getSize());
+                    ossFileRecord.setStatus(OssFileRecord.FileStatus.CREATE.name());
+                    ossFileRecord.setFileType(item.getSuffix());
+                    ossFileRecord.setOssUrl(item.getUrl());
+                    newRecords.add(ossFileRecord);
+                });
+
+                if (!removeFiles.isEmpty()) {
+                    softwareRelationService.removeByIds(removeRecords);
+                }//添加新的记录
+                softwareRelationService.saveBatch(newRelations);
+
+                ossFileRecordService.saveBatch(newRecords);
+
+                if (!removeFiles.isEmpty()) {
+                    //删除阿里云OSS旧文件
+                    OSSClient client = OssClient.initOSS(properties);
+                    List<String> urls = new ArrayList<>();
+                    removeFiles.forEach(oss -> {
+                        urls.add(softItemSoftware.getBurnAfterItemCode() + '/' + oss.getFileName());
+                    });
+                    urls.forEach((url) -> {
+                        client.deleteObject(properties.getBucket(), url);
+                    });
+                    //删除旧文件记录
+                    ossFileRecordService.removeByIds(removeRecords);
                 }
-            }).Start();
+                redisService.addFile(currentUser.getCode(), newFiles);
+            } else {
+                throw new BusinessException(500, "当前状态不允许修改");
+            }
+        }
+    }
+
+    @Override
+    public SoftItemSoftware selectSoftwareByCode(String code) {
+        return baseMapper.selectSoftwareByCode(code);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void publishTest(SysUserVo currentUser, Integer id) {
+        SoftItemSoftware softItemSoftware = softwareService.getById(id);
+        if (Objects.isNull(softItemSoftware)) {
+            throw new NullPointerException("软件不存在");
+        }
+        if (!softItemSoftware.getFlag().equals(SoftItemSoftware.Status.PENDING.name())) {
+            throw new BusinessException(500, "当前状态不允许发布");
+        }
+        softItemSoftware.setFlag(SoftItemSoftware.Status.TEST.name());
+        softItemSoftware.setServiceStatus(SoftItemSoftware.WindowsServiceStatus.TEST.name());
+
+        SysNotice sysNotice = new SysNotice();
+        sysNotice.setType(SysNotice.Type.B.name());
+//        sysNotice.setHandleUrl("/FullScene/ReceiveSoftware/" + softItemSoftware.getSoftwareId());
+        sysNotice.setHandleUrl(null);
+        sysNotice.setSubject(softItemSoftware.getSoftwareName() + " 已发布");
+        sysNotice.setToUser(softItemSoftware.getReceiveUser());
+        sysNotice.setStatus(SysNotice.Status.CREATE.name());
+        sysNotice.setMessage(softItemSoftware.getSoftwareName() + " 已发布,请接收");
+        sysNotice.setFromUsername(currentUser.getName());
+        sysNotice.setFromUser(currentUser.getCode());
+        sysNotice.setToUser(softItemSoftware.getReceiveUser());
+        sysNotice.setCreateTime(LocalDateTime.now());
+        sysNotice.setRelationField(String.valueOf(softItemSoftware.getSoftwareId()));
+        //审批单
+
+        SysItemApproval approval = new SysItemApproval();
+        approval.setFlag(SysItemApproval.Status.TEST.name());
+        approval.setSoftwareId(softItemSoftware.getSoftwareId());
+        approval.setTopFgItem(softItemSoftware.getBurnAfterItemCode());
+        approval.setLastModifyDate(LocalDateTime.now());
+        approval.setLastModifyUser(currentUser.getCode());
+        approvalService.save(approval);
+
+        softItemSoftware.setApprovalId(approval.getId());
+
+        softwareService.updateById(softItemSoftware);
+
+        noticeService.save(sysNotice);
+        //通知
+        WebSocketSession webSocketSession = SocketManager.get(softItemSoftware.getReceiveUser());
+        if (webSocketSession != null) {
+            /**
+             * 主要防止broken pipe
+             */
+            JSONObject noticeResult = new JSONObject();
+            noticeResult.put("add", sysNotice);
+            template.convertAndSendToUser(softItemSoftware.getUploadFileUser(), "/queue/notice", noticeResult.toJSONString());
+        }
+        redisService.addNotice(softItemSoftware.getReceiveUser(), sysNotice);
+    }
+
+    @Override
+    public List<SoftItemSoftware> selectTestSoftwareByME(String status, String userCode) {
+        return baseMapper.selectTestSoftwareByME(status, userCode);
+    }
+
+    @Override
+    public List<SoftItemSoftware> selectSoftwareByPlant(String status, String plant) {
+        return baseMapper.selectSoftwareByPlant(status, plant);
+    }
+
+
+    @Override
+    public JSONObject getReceiveSoftwareDetail(SysUserVo sysUserVo, Integer softwareId) {
+        //检查软件状态
+        SoftItemSoftware softItemSoftware = softwareService.getById(softwareId);
+        if (Objects.isNull(softItemSoftware)) {
+            throw new NullPointerException("没有找到软件信息");
+        }
+        if (!softItemSoftware.getReceiveUser().equals(sysUserVo.getCode())) {
+            throw new BusinessException(500, "你不是指定的接收人");
+        }
+        if (!softItemSoftware.getFlag().equals(SoftItemSoftware.Status.TEST.name())) {
+            throw new BusinessException(500, "该软件当前不能接收");
         }
 
-        private  void StreamProgressCallback(object sender, StreamTransferProgressArgs args)
-        {
-           _logger.LogInformation("ProgressCallback - Progress: {0}%, TotalBytes:{1}, TransferredBytes:{2} ",
-                args.TransferredBytes * 100 / args.TotalBytes, args.TotalBytes, args.TransferredBytes);
+        DataPlant plant = plantService.getById(sysUserVo.getPlant());
+        if (Objects.isNull(plant)) {
+            throw new NullPointerException("接收人工厂没有配置");
         }
+        Date expiration = new Date(new Date().getTime() + 3600 * 1000);
+        //生成文件下载地址
+        List<FileParam> fileParams = ossFileRecordService.selectOssFileParam(softwareId);
+        JSONArray files = new JSONArray();
+        for (FileParam fileParam : fileParams) {
+            JSONObject fileItem = JSON.parseObject(JSON.toJSONString(fileParam));
+            //oss 下载地址
+            URL ossUrl = OssClient.initOSS(properties).generatePresignedUrl(properties.getBucket(), softItemSoftware.getBurnAfterItemCode() + '/' + fileParam.getFileName(), expiration);
+            //本地下载地址
+            fileItem.put("onlineUrl", ossUrl.toString());
+//            String localUrl = repositoryProperties.getProtocol() +
+//                    "://" +
+//                    plant.getFileServerIp() + ":" +
+//                    repositoryProperties.getPort() +
+//                    "/" +
+//                    softItemSoftware.getBurnAfterItemCode() +
+//                    "/" +
+//                    fileParam.getFileName();
+//            fileItem.put("localUrl", localUrl);
+            files.add(fileItem);
+        }
+        JSONObject result = new JSONObject();
+        JSONObject softItemSoftwareObj = JSON.parseObject(JSON.toJSONString(softItemSoftware));
+        DataProject project = projectService.getById(softItemSoftware.getProjectId());
+        softItemSoftwareObj.put("projectName", project.getCode() + "/" + project.getName());
+
+        result.put("software", softItemSoftwareObj);
+        result.put("files", files);
+
+        return result;
     }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void approveSoftware(SysUserVo currentUser, ApprovalSoftwareParam param) {
+        SoftItemSoftware softItemSoftware = softwareService.getById(param.getSoftwareId());
+        if (Objects.isNull(softItemSoftware)) {
+            throw new NullPointerException("没有找到软件信息");
+        }
+        SysItemApproval approval = approvalService.getById(softItemSoftware.getApprovalId());
+        if (Objects.isNull(approval)) {
+            throw new NullPointerException("审批单不存在");
+        }
+        if (!StringUtils.isEmpty(param.getRemark())) {
+            approval.setComment(param.getRemark());
+        }
+        if (!StringUtils.isEmpty(param.getTryOutFileName())) {
+            approval.setTestReportFileName(param.getTryOutFileName());
+        }
+        approval.setLastModifyUser(currentUser.getCode());
+        approval.setLastModifyDate(LocalDateTime.now());
+        if (param.getStatus().equals(SysItemApproval.Status.REJECT.name())) {
+            approval.setFlag(SysItemApproval.Status.REJECT.name());
+            softItemSoftware.setFlag(SoftItemSoftware.Status.PENDING.name());
+            softItemSoftware.setServiceStatus(SoftItemSoftware.WindowsServiceStatus.DELETE.name());
+            softItemSoftware.setLastModifyPushDate(null);
+            SysNotice notice = new SysNotice();
+            notice.setType(SysNotice.Type.B.name());
+            notice.setFromUser(currentUser.getCode());
+            notice.setFromUsername(currentUser.getName());
+            notice.setToUser(softItemSoftware.getPublisher());
+            notice.setStatus(SysNotice.Status.CREATE.name());
+            notice.setSubject(softItemSoftware.getSoftwareName() + " 审批被退回");
+            notice.setMessage("退回原因 : " + param.getRemark());
+            notice.setRelationField(softItemSoftware.getSoftwareId().toString());
+            notice.setCreateTime(LocalDateTime.now());
+            pushMachineService.deleteBySoftwareId(softItemSoftware.getSoftwareId());
+            approvalService.updateById(approval);
+            softwareService.updateById(softItemSoftware);
+            noticeService.save(notice);
+            redisService.addNotice(softItemSoftware.getPublisher(), notice);
+            WebSocketSession webSocketSession = SocketManager.get(softItemSoftware.getReceiveUser());
+            if (webSocketSession != null) {
+                /**
+                 * 主要防止broken pipe
+                 */
+                JSONObject noticeResult = new JSONObject();
+                noticeResult.put("add", notice);
+                template.convertAndSendToUser(softItemSoftware.getUploadFileUser(), "/queue/notice", noticeResult.toJSONString());
+            }
+        } else if (param.getStatus().equals(SysItemApproval.Status.APPROVE.name())) {
+            //审批通过
+            if (param.getEffectiveDate().isEmpty()) {
+                throw new NullPointerException("发布时间不能为空");
+            }
+            if (param.getTryOutFileName().isEmpty()) {
+                throw new NullPointerException("试装结论不能为空");
+            }
+            softItemSoftware.setFlag(SoftItemSoftware.Status.PASS.name());
+            softItemSoftware.setEffectiveDate(DateUtil.strToLocalDateTime(param.getEffectiveDate()));
+            approval.setFlag(SysItemApproval.Status.APPROVE.name());
+
+            approval.setReleaseDate(DateUtil.strToLocalDateTime(param.getEffectiveDate()));
+            approval.setTestReportFileName(param.getTryOutFileName());
+
+            SysNotice notice = new SysNotice();
+            notice.setType(SysNotice.Type.B.name());
+            notice.setFromUser(currentUser.getCode());
+            notice.setFromUsername(currentUser.getName());
+            notice.setToUser(softItemSoftware.getPublisher());
+            notice.setStatus(SysNotice.Status.CREATE.name());
+            notice.setSubject(softItemSoftware.getSoftwareName() + " 已被接收");
+            notice.setMessage("正式发布时间:" + param.getEffectiveDate());
+            notice.setRelationField(softItemSoftware.getSoftwareId().toString());
+            notice.setCreateTime(LocalDateTime.now());
+            softwareService.updateById(softItemSoftware);
+            approvalService.updateById(approval);
+            noticeService.save(notice);
+            redisService.addNotice(softItemSoftware.getPublisher(), notice);
+            WebSocketSession webSocketSession = SocketManager.get(softItemSoftware.getReceiveUser());
+            if (webSocketSession != null) {
+                /**
+                 * 主要防止broken pipe
+                 */
+                JSONObject noticeResult = new JSONObject();
+                noticeResult.put("add", notice);
+                template.convertAndSendToUser(softItemSoftware.getUploadFileUser(), "/queue/notice", noticeResult.toJSONString());
+            }
+        } else {
+            throw new BusinessException(500, "状态异常");
+        }
+    }
+
+    @Override
+    public List<SoftwareServiceResultVo> pingServiceMachine(String plant, String windowsStatus, String status) {
+        return baseMapper.pingServiceMachine(plant, windowsStatus, status);
+    }
+
+    @Override
+    public List<SoftwareServiceResultVo> pingServiceMachine(String plant, String status) {
+        return getBaseMapper().pingServiceMachineRelease(plant, status);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void pushMachine(SysUserVo currentUser, Integer softwareId, Integer[] machineIds) {
+        SoftItemSoftware softItemSoftware = softwareService.getById(softwareId);
+        if (Objects.isNull(softItemSoftware)) {
+            throw new NullPointerException("软件信息不存在");
+        }
+        softItemSoftware.setSoftwareId(softwareId);
+        List<SoftPushMachine> softPushMachines = new ArrayList<>();
+        for (Integer machineId : machineIds) {
+            SoftPushMachine machine = new SoftPushMachine();
+            machine.setMachineId(machineId);
+            machine.setSoftwareId(softwareId);
+            machine.setPublishDate(LocalDateTime.now());
+            machine.setPublishName(currentUser.getName());
+            machine.setPublisher(currentUser.getCode());
+            machine.setPlant(softItemSoftware.getTargetPlant());
+            softPushMachines.add(machine);
+        }
+        softItemSoftware.setLastModifyPushDate(LocalDateTime.now());
+        pushMachineService.deleteBySoftwareId(softwareId);
+        pushMachineService.batchInsert(softPushMachines);
+        softwareService.updateById(softItemSoftware);
+
+    }
+
+    @Override
+    public List<String> selectBurnItemCode() {
+        return baseMapper.selectBurnItemCode();
+    }
+
+    @Override
+    public List<SoftwareServiceResultVo> pingServiceDelete(String plant, String windowsServiceStatus) {
+        return baseMapper.pingServiceDelete(plant, windowsServiceStatus);
+    }
+
+    @Override
+    public List<SoftwareServiceResultVo> pingServiceMachineRelease(String plant, String status) {
+        return baseMapper.pingServiceMachineRelease(plant, status);
+    }
+
+    @Override
+    public SoftItemSoftware selectPreviousVersionSoftware(String plant, String itemCode) {
+        return baseMapper.selectPreviousVersionSoftware(plant, itemCode);
+    }
+
+    @Override
+    public void firstSerialItemCode(Integer softwareId, String firstSerialNumber) {
+        SoftItemSoftware softItemSoftware = softwareService.getById(softwareId);
+        softItemSoftware.setFirstProductSerial(firstSerialNumber);
+        softwareService.updateById(softItemSoftware);
+    }
+
+
 }
-```
+```