tjx
4 天以前 b3839a0f70927243c0ba797cb64777c8a53c9f3b
整合钉钉和企业微信
已修改4个文件
已添加23个文件
1574 ■■■■■ 文件已修改
pom.xml 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/gs/dingtalk/config/DataAcquisitionConfiguration.java 34 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/gs/dingtalk/controller/DingtalkController.java 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/gs/dingtalk/dto/NumbericalDto.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/gs/dingtalk/entity/DingtalkInfo.java 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/gs/dingtalk/entity/DingtalkMsg.java 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/gs/dingtalk/entity/MesStaff.java 125 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/gs/dingtalk/entity/VwCjScSjTsBb.java 83 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/gs/dingtalk/entity/VwCjScSjTsBbMonth.java 83 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/gs/dingtalk/mapper/DingtalkInfoMapper.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/gs/dingtalk/mapper/DingtalkMsgMapper.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/gs/dingtalk/mapper/MesStaffMapper.java 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/gs/dingtalk/mapper/VwCjScSjTsBbMapper.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/gs/dingtalk/mapper/VwCjScSjTsBbMonthMapper.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/gs/dingtalk/service/DingtalkInfoService.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/gs/dingtalk/service/VwCjScSjTsBbMonthService.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/gs/dingtalk/service/VwCjScSjTsBbService.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/gs/dingtalk/service/impl/DingtalkInfoServiceImpl.java 552 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/gs/dingtalk/service/impl/VwCjScSjTsBbMonthServiceImpl.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/gs/dingtalk/service/impl/VwCjScSjTsBbServiceImpl.java 193 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/gs/dingtalk/task/ScheduledTasks.java 96 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application.yml 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/DingtalkInfoMapper.xml 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/DingtalkMsgMapper.xml 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/MesStaffMapper.xml 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/VwCjScSjTsBbMapper.xml 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/VwCjScSjTsBbMonthMapper.xml 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pom.xml
@@ -102,10 +102,11 @@
            <version>5.8.18</version>
        </dependency>
        <!-- Apache POI for Excel (required by Hutool) -->
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>4.1.2</version> <!-- æˆ–更高版本 -->
            <version>5.2.3</version>
        </dependency>
    </dependencies>
src/main/java/com/gs/dingtalk/config/DataAcquisitionConfiguration.java
@@ -3,24 +3,48 @@
public class DataAcquisitionConfiguration {
    //测试环境
    public static final String TEST_COMPANY_CODE = "55958795";
    public static final String TEST_APP_KEY = "ab2d86b6dffabcc81dca6855c727c246";
    public static final String TEST_APP_SECRET = "01b17babe2a96d5ebd802e67709f33d1";
    public static final String TEST_ERP_CODE = "Z106";
    //正式环境
    public static final String COMPANY_CODE = "72505985";
    public static final String APP_KEY = "a2866f03bb7f76387bfb1a98001f0e31";
    public static final String APP_SECRET = "f13bd1bcb130f0090ed92dc021e5f4e1";
    public static final String ERP_CODE = "Z106";
    /**
     * åº”用的 AgentId
     */
    public static final Long AGENT_ID = 3303296035L;
    public static final Long AGENT_ID = 4104598880L;
    /**
     * åº”用的 AppKey
     */
    public static final String APP_KEY = "dingyfqkfjecy4cjfyxa";
    public static final String TALK_APP_KEY = "dinggglb3pttl1x0gn0c";
    /**
     * åº”用的 AppSecret
     */
    public static final String APP_SECRET = "nCwmyBw8K-EqAvkuhrhhqFonbLp455awtMa4D4Q-VRaY8U2EDEVsnYSYYfPvjiAX";
    public static final String CORP_ID = "ding1dd72cd1d6adf70aa1320dcb25e91351";
    public static final String TALK_APP_SECRET = "Zc7r0Mb7bPsC_xy7ryrWoEnE5OzHEXibUMPDCA2LBusJ9pYzIolRk_OdZuLvNExf";
    //群聊机器人相关
    public static final String CUSTOM_ROBOT_TOKEN = "c2849e46cb0d91b0721c377742938b8ac5ef57e3c9eeab918e2cd5dd9c3aad2a";
    public static final String SECRET = "SEC382027a5c81ea5152b71b687fb2c1ebf26acbde035355da6ab2fb37306454134";
    //企业微信
    public static final String CORPID = "wwabe21b935901a7d8";
    public static final String CORPSECRET = "Z-7fNbZjrd80ypz69U14j8FMxI_fpUxcT6PksxlvKaY";
    public static final String TXL_CORPSECRET = "T64bdcV7fo0hvW10W3NJYmGUmlBYxYMfiW6EiUJ9VPM";
}
src/main/java/com/gs/dingtalk/controller/DingtalkController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,62 @@
package com.gs.dingtalk.controller;
import com.gs.dingtalk.config.ResultMessage;
import com.gs.dingtalk.dto.NumbericalDto;
import com.gs.dingtalk.service.DingtalkInfoService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
 * é’‰é’‰æ¶ˆæ¯å‘送控制器
 *
 * @author 28567
 * @description æä¾›é’‰é’‰æ¶ˆæ¯å‘送相关的REST接口
 * @createDate 2025-01-27
 */
@RestController
@RequestMapping("/api/dingtalk")
@RequiredArgsConstructor
@Slf4j
public class DingtalkController {
    private final DingtalkInfoService dingtalkInfoService;
    /**
     * å‘送钉钉消息
     *
     * @param numbericalDto åŒ…含检验单号的请求参数
     * @return ResultMessage æ“ä½œç»“æžœ
     */
    @PostMapping("/sendMessage")
    public ResultMessage sendMessage(@RequestBody NumbericalDto numbericalDto) {
        try {
            log.info("开始发送钉钉消息,检验单号:{}", numbericalDto.getReleaseNo());
            // å‚数校验
            if (numbericalDto == null || numbericalDto.getReleaseNo() == null || numbericalDto.getReleaseNo().trim().isEmpty()) {
                log.warn("检验单号不能为空");
                return ResultMessage.error("检验单号不能为空");
            }
            // è°ƒç”¨æœåŠ¡å±‚å‘é€æ¶ˆæ¯
            boolean result = dingtalkInfoService.sendMessage(numbericalDto.getReleaseNo());
            if (result) {
                log.info("钉钉消息发送成功,检验单号:{}", numbericalDto.getReleaseNo());
                return ResultMessage.ok();
            } else {
                log.warn("钉钉消息发送失败,检验单号:{}", numbericalDto.getReleaseNo());
                return ResultMessage.error("钉钉消息发送失败");
            }
        } catch (Exception e) {
            log.error("发送钉钉消息时发生异常,检验单号:{}", numbericalDto != null ? numbericalDto.getReleaseNo() : "null", e);
            return ResultMessage.error(e);
        }
    }
}
src/main/java/com/gs/dingtalk/dto/NumbericalDto.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,14 @@
package com.gs.dingtalk.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class NumbericalDto {
    private String releaseNo;
}
src/main/java/com/gs/dingtalk/entity/DingtalkInfo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,43 @@
package com.gs.dingtalk.entity;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serializable;
/**
 * @TableName DINGTALK_INFO
 */
@TableName(value = "DINGTALK_INFO")
@Data
public class DingtalkInfo implements Serializable {
    @TableField(exist = false)
    private static final long serialVersionUID = 1L;
    /**
     * SEQ_DingTalk
     */
    @TableId
    private Long id;
    /**
     * èŒå·¥id
     */
    private Long sid;
    /**
     * ç”µè¯
     */
    private String phone;
    /**
     * é’‰é’‰id
     */
    private String dingtalkId;
    /**
     * é’‰é’‰é€šçŸ¥
     */
    private Integer isSendDingtalk;
    /**
     * å›ºå®šæŽ¨é€ï¼Œ1表示是固定推送的
     */
    private Integer isHead;
}
src/main/java/com/gs/dingtalk/entity/DingtalkMsg.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,54 @@
package com.gs.dingtalk.entity;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
/**
 * @TableName DINGTALK_MSG
 */
@TableName(value = "DINGTALK_MSG")
@Data
public class DingtalkMsg implements Serializable {
    @TableField(exist = false)
    private static final long serialVersionUID = 1L;
    /**
     * æ£€éªŒå•号
     */
    private String releaseNo;
    /**
     * ä¾›åº”商
     */
    private String suppName;
    /**
     * æ¥æ–™æ—¥æœŸ
     */
    private Date createDate;
    /**
     * é¡¹ç›®
     */
    private String projectCodes;
    /**
     * æ–™å·
     */
    private String itemNo;
    /**
     * å®¡æ ¸äººä¸­æ–‡
     */
    private String fname;
    /**
     * å¤„理方式
     */
    private String fngHandle;
    /**
     * é‡‡è´­äººå‘˜
     */
    private String employeeName;
    /**
     * å®¡æ ¸äºº
     */
    private String modify1By;
}
src/main/java/com/gs/dingtalk/entity/MesStaff.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,125 @@
package com.gs.dingtalk.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
/**
 * å‘˜å·¥ä¿¡æ¯è¡¨
 *
 * @TableName MES_STAFF
 */
@TableName(value = "MES_STAFF")
@Data
@KeySequence(value = "SEQ_MES_STAFF", dbType = DbType.ORACLE)
public class MesStaff implements Serializable {
    @TableField(exist = false)
    private static final long serialVersionUID = 1L;
    /**
     * ID
     */
    @TableId
    private Long id;
    /**
     * å‘˜å·¥ç¼–码
     */
    private String staffNo;
    /**
     * å‘˜å·¥å§“名
     */
    private String staffName;
    /**
     * éƒ¨é—¨ç¼–码
     */
    private String departmentNo;
    /**
     * éƒ¨é—¨åç§°
     */
    private String departmentName;
    /**
     * å²—位编码
     */
    private String positionCode;
    /**
     * å²—位名称
     */
    private String positionName;
    /**
     * æ‰‹æœºå·
     */
    private String phoneNumber;
    /**
     * ä»»å²—开始日期
     */
    private Date startDate;
    /**
     * å¤‡æ³¨
     */
    private String remark;
    /**
     * åˆ›å»ºç»„织
     */
    private Long createOrg;
    /**
     * ä½¿ç”¨ç»„织
     */
    private String useOrg;
    /**
     * è‡ªå®šä¹‰å­—段3
     */
    private String remark3;
    /**
     * è‡ªå®šä¹‰å­—段4
     */
    private String remark4;
    /**
     * è‡ªå®šä¹‰å­—段5
     */
    private String remark5;
    /**
     * åˆ›å»ºäºº
     */
    private String createBy;
    /**
     * åˆ›å»ºæ—¶é—´
     */
    private Date createDate;
    /**
     * æ›´æ–°äºº
     */
    private String lastupdateBy;
    /**
     * æ›´æ–°æ—¶é—´
     */
    private Date lastupdateDate;
    /**
     * å¤‡æ³¨
     */
    private String memo;
    /**
     * ç”Ÿäº§çº¿
     */
    private String lineNo;
    /**
     * ç”Ÿäº§å‘˜å·¥æ ‡è¯†
     */
    private Long workMk;
    /**
     * æ˜¯å¦ç¦ç”¨
     */
    private String fforbidStatus;
    /**
     * ä¸šåŠ¡å‘˜ç±»åž‹
     */
    private String operatorType;
    /**
     * ç¦»èŒæ—¶é—´
     */
    private Date separationTime;
    /**
     * æ˜¯å¦æ˜¯å…³é”®å²—位 0:否,1:是
     */
    private String keyPosts;
}
src/main/java/com/gs/dingtalk/entity/VwCjScSjTsBb.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,83 @@
package com.gs.dingtalk.entity;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serializable;
/**
 *
 * @TableName VW_CJ_SC_SJ_TS_BB
 */
@TableName(value ="VW_CJ_SC_SJ_TS_BB")
@Data
public class VwCjScSjTsBb implements Serializable {
    /**
     * ç‰©æ–™åç§°
     */
    private String itemName;
    /**
     * ç‰©æ–™ç¼–码
     */
    private String itemNo;
    /**
     * è½¦é—´åç§°
     */
    private String departmentname;
    /**
     * è½¦é—´ç¼–码
     */
    private String daa001;
    /**
     * å·¥å•号
     */
    private String lineName;
    /**
     * çº¿ä½“名称
     */
    private String daa008;
    /**
     * é¢„计开工
     */
    private String yjkg;
    /**
     * å®žé™…开工
     */
    private String sjkg;
    /**
     *
     */
    private String sq;
    /**
     * å…¥åº“
     */
    private String rk;
    /**
     * ç”³è¯·æœªå®Œå·¥æ•°
     */
    private String sqwwg;
    /**
     * å…¥åº“未完工
     */
    private String rkwwg;
    /**
     * ç”³è¯·æœªå…¥åº“
     */
    private String sqwrk;
    @TableField(exist = false)
    private static final long serialVersionUID = 1L;
}
src/main/java/com/gs/dingtalk/entity/VwCjScSjTsBbMonth.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,83 @@
package com.gs.dingtalk.entity;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serializable;
/**
 *
 * @TableName VW_CJ_SC_SJ_TS_BB_MONTH
 */
@TableName(value ="VW_CJ_SC_SJ_TS_BB_MONTH")
@Data
public class VwCjScSjTsBbMonth implements Serializable {
    /**
     *
     */
    private String itemName;
    /**
     *
     */
    private String itemNo;
    /**
     *
     */
    private String departmentname;
    /**
     *
     */
    private String daa001;
    /**
     *
     */
    private String lineName;
    /**
     *
     */
    private String daa008;
    /**
     *
     */
    private String yjkg;
    /**
     *
     */
    private String sjkg;
    /**
     *
     */
    private String sq;
    /**
     *
     */
    private String rk;
    /**
     *
     */
    private String sqwwg;
    /**
     *
     */
    private String rkwwg;
    /**
     *
     */
    private String sqwrk;
    @TableField(exist = false)
    private static final long serialVersionUID = 1L;
}
src/main/java/com/gs/dingtalk/mapper/DingtalkInfoMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,18 @@
package com.gs.dingtalk.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.gs.dingtalk.entity.DingtalkInfo;
/**
 * @author 28567
 * @description é’ˆå¯¹è¡¨ã€DINGTALK_INFO】的数据库操作Mapper
 * @createDate 2025-06-20 16:12:48
 * @Entity com.gs.xky.entity.DingtalkInfo
 */
public interface DingtalkInfoMapper extends BaseMapper<DingtalkInfo> {
}
src/main/java/com/gs/dingtalk/mapper/DingtalkMsgMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,18 @@
package com.gs.dingtalk.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.gs.dingtalk.entity.DingtalkMsg;
/**
 * @author 28567
 * @description é’ˆå¯¹è¡¨ã€DINGTALK_MSG】的数据库操作Mapper
 * @createDate 2025-06-21 10:07:15
 * @Entity com.gs.xky.entity.DingtalkMsg
 */
public interface DingtalkMsgMapper extends BaseMapper<DingtalkMsg> {
}
src/main/java/com/gs/dingtalk/mapper/MesStaffMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,23 @@
package com.gs.dingtalk.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.gs.dingtalk.entity.MesStaff;
/**
 * @author 28567
 * @description é’ˆå¯¹è¡¨ã€MES_STAFF(员工信息表)】的数据库操作Mapper
 * @createDate 2025-02-17 20:59:36
 * @Entity com.gs.xky.entity.MesStaff
 */
public interface MesStaffMapper extends BaseMapper<MesStaff> {
    long getNextVal();
    int updateStaff();
    int deleteStaff();
}
src/main/java/com/gs/dingtalk/mapper/VwCjScSjTsBbMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,18 @@
package com.gs.dingtalk.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.gs.dingtalk.entity.VwCjScSjTsBb;
/**
* @author Administrator
* @description é’ˆå¯¹è¡¨ã€VW_CJ_SC_SJ_TS_BB】的数据库操作Mapper
* @createDate 2025-11-12 19:44:46
* @Entity generator.domain.VwCjScSjTsBb
*/
public interface VwCjScSjTsBbMapper extends BaseMapper<VwCjScSjTsBb> {
}
src/main/java/com/gs/dingtalk/mapper/VwCjScSjTsBbMonthMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,18 @@
package com.gs.dingtalk.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.gs.dingtalk.entity.VwCjScSjTsBbMonth;
/**
* @author Administrator
* @description é’ˆå¯¹è¡¨ã€VW_CJ_SC_SJ_TS_BB_MONTH】的数据库操作Mapper
* @createDate 2025-11-12 19:45:00
* @Entity com.gs.xky.entity.VwCjScSjTsBbMonth
*/
public interface VwCjScSjTsBbMonthMapper extends BaseMapper<VwCjScSjTsBbMonth> {
}
src/main/java/com/gs/dingtalk/service/DingtalkInfoService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,25 @@
package com.gs.dingtalk.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.gs.dingtalk.entity.DingtalkInfo;
/**
 * @author 28567
 * @description é’ˆå¯¹è¡¨ã€DINGTALK_INFO】的数据库操作Service
 * @createDate 2025-06-20 16:12:48
 */
public interface DingtalkInfoService extends IService<DingtalkInfo> {
    boolean sendMessage(String releaseNo);
    boolean sendActionCardMessage() throws Exception;
    /**
     * å‘送文件消息
     *
     * @param filePath æœ¬åœ°æ–‡ä»¶è·¯å¾„
     * @return æ˜¯å¦å‘送成功
     * @throws Exception å¼‚常
     */
    boolean sendFileMessage(String filePath) throws Exception;
}
src/main/java/com/gs/dingtalk/service/VwCjScSjTsBbMonthService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,13 @@
package com.gs.dingtalk.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.gs.dingtalk.entity.VwCjScSjTsBbMonth;
/**
 * @author Administrator
 * @description é’ˆå¯¹è¡¨ã€VW_CJ_SC_SJ_TS_BB_MONTH】的数据库操作Service
 * @createDate 2025-11-12 19:45:00
 */
public interface VwCjScSjTsBbMonthService extends IService<VwCjScSjTsBbMonth> {
}
src/main/java/com/gs/dingtalk/service/VwCjScSjTsBbService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,19 @@
package com.gs.dingtalk.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.gs.dingtalk.entity.VwCjScSjTsBb;
/**
 * @author Administrator
 * @description é’ˆå¯¹è¡¨ã€VW_CJ_SC_SJ_TS_BB】的数据库操作Service
 * @createDate 2025-11-12 19:44:46
 */
public interface VwCjScSjTsBbService extends IService<VwCjScSjTsBb> {
    /**
     * å¯¼å‡ºæ•°æ®åˆ°Excel并发送钉钉消息
     *
     * @return æ˜¯å¦æˆåŠŸ
     */
    boolean exportAndSendToDingtalk() throws Exception;
}
src/main/java/com/gs/dingtalk/service/impl/DingtalkInfoServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,552 @@
package com.gs.dingtalk.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dingtalk.api.DefaultDingTalkClient;
import com.dingtalk.api.DingTalkClient;
import com.dingtalk.api.request.OapiMessageCorpconversationAsyncsendV2Request;
import com.dingtalk.api.response.OapiMessageCorpconversationAsyncsendV2Response;
import com.gs.dingtalk.entity.DingtalkInfo;
import com.gs.dingtalk.entity.DingtalkMsg;
import com.gs.dingtalk.entity.MesStaff;
import com.gs.dingtalk.mapper.DingtalkInfoMapper;
import com.gs.dingtalk.mapper.DingtalkMsgMapper;
import com.gs.dingtalk.mapper.MesStaffMapper;
import com.gs.dingtalk.service.DingtalkInfoService;
import com.gs.dingtalk.service.SimpleExample;
import com.taobao.api.FileItem;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
/**
 * @author 28567
 * @description é’ˆå¯¹è¡¨ã€DINGTALK_INFO】的数据库操作Service实现
 * @createDate 2025-06-20 16:12:48
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class DingtalkInfoServiceImpl extends ServiceImpl<DingtalkInfoMapper, DingtalkInfo>
        implements DingtalkInfoService {
    private final SimpleExample simpleExample;
    private final DingtalkMsgMapper dingtalkMsgMapper;
    private final MesStaffMapper mesStaffMapper;
    @Override
    public boolean sendMessage(String releaseNo) {
        try {
            // æŸ¥è¯¢é’‰é’‰æ¶ˆæ¯å†…容
            LambdaQueryWrapper<DingtalkMsg> msgWrapper = new LambdaQueryWrapper<>();
            msgWrapper.eq(DingtalkMsg::getReleaseNo, releaseNo);
            DingtalkMsg dingtalkMsg = dingtalkMsgMapper.selectOne(msgWrapper);
            if (dingtalkMsg == null) {
                log.error("未找到检验单号为 {} çš„钉钉消息内容", releaseNo);
                return false;
            }
            // æ ¼å¼åŒ–日期
            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
            String createDateStr = (dingtalkMsg.getCreateDate() != null) ?
                    dateFormat.format(dingtalkMsg.getCreateDate()) : "未知";
            // æž„建消息内容
//            String message = String.format("供应商[%s] æ¥æ–™æ—¥æœŸ[%s] é¡¹ç›®[%s] æ–™å·[%s]的不合格检验单被[%s]审批为[%s],请查收!",
//                    dingtalkMsg.getSuppName(), createDateStr, dingtalkMsg.getProjectCodes(),
//                    dingtalkMsg.getItemNo(), dingtalkMsg.getFname(), dingtalkMsg.getFngHandle());
            String message = String.format(
                    "供应商[%s] æ¥æ–™æ—¥æœŸ[%s] é¡¹ç›®[%s] æ–™å·[%s]的不合格检验单被[%s]审批为[%s],请查收!",
                    Optional.ofNullable(dingtalkMsg.getSuppName()).orElse(" æœªçŸ¥ä¾›åº”商"),
                    createDateStr,
                    Optional.ofNullable(dingtalkMsg.getProjectCodes()).orElse(" æœªçŸ¥é¡¹ç›®"),
                    Optional.ofNullable(dingtalkMsg.getItemNo()).orElse(" æœªçŸ¥æ–™å·"),
                    Optional.ofNullable(dingtalkMsg.getFname()).orElse(" æœªçŸ¥å®¡æ‰¹äºº"),
                    Optional.ofNullable(dingtalkMsg.getFngHandle()).orElse(" æœªçŸ¥å®¡æ‰¹ç»“æžœ"));
            // æ”¶é›†éœ€è¦æŽ¨é€çš„人员sid
            List<Long> sidList = new ArrayList<>();
            // 1. å›ºå®šæŽ¨é€äººå‘˜ï¼ˆisSendDingtalk=1)
            List<DingtalkInfo> fixedList = baseMapper.selectList(
                    new LambdaQueryWrapper<DingtalkInfo>().eq(DingtalkInfo::getIsSendDingtalk, 1)
            );
            for (DingtalkInfo info : fixedList) {
                sidList.add(info.getSid());
            }
            // 2. é‡‡è´­äººå‘˜ï¼ˆemployeeName)
            if (StringUtils.hasText(dingtalkMsg.getEmployeeName())) {
                MesStaff buyer = mesStaffMapper.selectOne(
                        new LambdaQueryWrapper<MesStaff>().eq(MesStaff::getStaffName, dingtalkMsg.getEmployeeName()), false
                );
                if (buyer != null) {
                    DingtalkInfo buyerInfo = baseMapper.selectOne(
                            new LambdaQueryWrapper<DingtalkInfo>().eq(DingtalkInfo::getSid, buyer.getId()), false
                    );
                    if (buyerInfo != null) sidList.add(buyerInfo.getSid());
                }
            }
            // 3. å®¡æ ¸äººï¼ˆmodify1By)
            if (StringUtils.hasText(dingtalkMsg.getModify1By())) {
                MesStaff auditor = mesStaffMapper.selectOne(
                        new LambdaQueryWrapper<MesStaff>().eq(MesStaff::getStaffNo, dingtalkMsg.getModify1By())
                );
                if (auditor != null) {
                    DingtalkInfo auditorInfo = baseMapper.selectOne(
                            new LambdaQueryWrapper<DingtalkInfo>().eq(DingtalkInfo::getSid, auditor.getId())
                    );
                    if (auditorInfo != null) sidList.add(auditorInfo.getSid());
                }
            }
            // 4. æ ¹æ®æ”¶é›†çš„sid获取dingtalkId (复用getDingtalkUserIdList的关键逻辑)
            List<String> userIdList = getDingtalkUserIdListBySids(sidList);
            if (userIdList == null || userIdList.isEmpty()) {
                log.warn("没有需要发送钉钉消息的用户");
                return false;
            }
            // é€šè¿‡é’‰é’‰å‘送消息
            String userIdListStr = String.join(",", userIdList);
            OapiMessageCorpconversationAsyncsendV2Response rsp = sendMessage(userIdListStr, message);
            System.out.println(rsp.getBody());
            log.info("成功发送钉钉消息: {}", message);
            return true;
        } catch (Exception e) {
            log.error("发送钉钉消息失败", e);
            return false;
        }
    }
    @Override
    public boolean sendActionCardMessage() {
        try {
            // 1. èŽ·å–éœ€è¦æŽ¨é€çš„ç”¨æˆ·åˆ—è¡¨ï¼ˆå¯ä»¥æ˜¯å›ºå®šæŽ¨é€ç”¨æˆ·ï¼‰
            List<DingtalkInfo> fixedList = baseMapper.selectList(
                    new LambdaQueryWrapper<DingtalkInfo>().eq(DingtalkInfo::getIsHead, 1)
            );
            List<Long> sidList = fixedList.stream()
                    .map(DingtalkInfo::getSid)
                    .collect(Collectors.toList());
            List<String> userIdList = getDingtalkUserIdListBySids(sidList);
            if (userIdList == null || userIdList.isEmpty()) {
                log.warn("没有需要发送钉钉消息的用户");
                return false;
            }
            String userIdListStr = String.join(",", userIdList);
            // 2. æž„建消息内容
            String title = "生产数据看板";
            String markdown = "请点击下方按钮查看详细BI报表";
            String singleTitle = "查看报表";
            String singleUrl = "http://192.168.1.22:8081/design?fid=rpte6045ab079b211f0824bd3cfd50c6b93&fserid=4b198960bedd11f09f6f792bfe147b64&fsharetype=3";
            // 3. å‘送消息
            OapiMessageCorpconversationAsyncsendV2Response rsp =
                    sendActionCardMessage(userIdListStr, title, markdown, singleTitle, singleUrl);
            log.info("成功发送ActionCard消息: {}", rsp.getBody());
            return true;
        } catch (Exception e) {
            log.error("发送ActionCard消息失败", e);
            return false;
        }
    }
    @Override
    public boolean sendFileMessage(String filePath) throws Exception {
        try {
            // 1. æ£€æŸ¥æ–‡ä»¶æ˜¯å¦å­˜åœ¨
            File file = new File(filePath);
            if (!file.exists()) {
                log.error("文件不存在: {}", filePath);
                return false;
            }
            // 2. èŽ·å–éœ€è¦æŽ¨é€çš„ç”¨æˆ·åˆ—è¡¨ï¼ˆå¯æ ¹æ®å®žé™…éœ€æ±‚è°ƒæ•´ï¼Œè¿™é‡Œä½¿ç”¨isHead=1的用户)
            List<DingtalkInfo> fixedList = baseMapper.selectList(
                    new LambdaQueryWrapper<DingtalkInfo>().eq(DingtalkInfo::getIsHead, 1)
            );
            if (fixedList == null || fixedList.isEmpty()) {
                log.warn("没有需要发送文件的用户(isHead=1)");
                return false;
            }
            List<String> sidList = fixedList.stream()
                    .map(DingtalkInfo::getPhone)
                    .collect(Collectors.toList());
            List<String> userIdList = getDingtalkUserIdListByPhones(sidList);
            if (userIdList == null || userIdList.isEmpty()) {
                log.warn("没有有效的钉钉用户ID");
                return false;
            }
            String userIdListStr = String.join(",", userIdList);
            // 3. ä¸Šä¼ æ–‡ä»¶åˆ°é’‰é’‰æœåС噍
            log.info("开始上传文件: {}", filePath);
            String mediaId = uploadMedia(filePath, "file");
            // 4. å‘送文件消息
            log.info("开始发送文件消息,mediaId: {}", mediaId);
            OapiMessageCorpconversationAsyncsendV2Response response = sendFileMessageByMediaId(userIdListStr, mediaId);
            log.info("文件消息发送响应: {}", response.getBody());
            return response.getErrcode() == 0;
        } catch (Exception e) {
            log.error("发送文件消息失败", e);
            throw e;
        }
    }
    /**
     * æ ¹æ®æŒ‡å®šçš„sid列表获取钉钉用户ID列表
     *
     * @param sidList sid列表
     * @return é’‰é’‰ç”¨æˆ·ID列表
     */
    private List<String> getDingtalkUserIdListBySids(List<Long> sidList) {
        try {
            if (sidList == null || sidList.isEmpty()) {
                return new ArrayList<>();
            }
            // åŽ»é‡
            sidList = sidList.stream().distinct().collect(Collectors.toList());
            // æ ¹æ®sid查询DingtalkInfo
            List<DingtalkInfo> list = baseMapper.selectList(
                    new LambdaQueryWrapper<DingtalkInfo>().in(DingtalkInfo::getSid, sidList)
            );
            if (list == null || list.isEmpty()) {
                return new ArrayList<>();
            }
            // ä½¿ç”¨stream流过滤出list中dingtalkId为空的数据
            List<DingtalkInfo> emptyDingtalkIdList = list.stream()
                    .filter(info -> !StringUtils.hasText(info.getDingtalkId()))
                    .collect(Collectors.toList());
            // å¦‚果存在为空的数据就通过钉钉的接口获取,为dingtalkId赋值,并且更新数据库
            if (!emptyDingtalkIdList.isEmpty()) {
                String accessToken = simpleExample.getAccessToken();
                for (DingtalkInfo info : emptyDingtalkIdList) {
                    if (StringUtils.hasText(info.getPhone())) {
                        try {
                            // é€šè¿‡æ‰‹æœºå·èŽ·å–é’‰é’‰ç”¨æˆ·ID
                            com.dingtalk.api.response.OapiV2UserGetbymobileResponse response =
                                    simpleExample.getOapiV2UserGetbymobileResponse(info.getPhone(), accessToken);
                            if (response != null && response.getResult() != null) {
                                info.setDingtalkId(response.getResult().getUserid());
                                // æ›´æ–°æ•°æ®åº“
                                updateById(info);
                            }
                        } catch (Exception e) {
                            log.error("获取钉钉用户ID失败,手机号:{}", info.getPhone(), e);
                        }
                    }
                }
            }
            // ä¸å­˜åœ¨ä¸ºç©ºçš„æ•°æ®æˆ–者处理完空数据后,返回所有有效的dingtalkId列表
            return list.stream()
                    .map(DingtalkInfo::getDingtalkId)
                    .filter(StringUtils::hasText)
                    .distinct()
                    .collect(Collectors.toList());
        } catch (Exception e) {
            log.error("获取钉钉用户列表失败", e);
            return new ArrayList<>();
        }
    }
    private List<String> getDingtalkUserIdListByPhones(List<String> phoneList) {
        try {
            if (phoneList == null || phoneList.isEmpty()) {
                return new ArrayList<>();
            }
            // åŽ»é‡
            phoneList = phoneList.stream().distinct().collect(Collectors.toList());
            // æ ¹æ®sid查询DingtalkInfo
            List<DingtalkInfo> list = baseMapper.selectList(
                    new LambdaQueryWrapper<DingtalkInfo>().in(DingtalkInfo::getPhone, phoneList)
            );
            if (list == null || list.isEmpty()) {
                return new ArrayList<>();
            }
            // ä½¿ç”¨stream流过滤出list中dingtalkId为空的数据
            List<DingtalkInfo> emptyDingtalkIdList = list.stream()
                    .filter(info -> !StringUtils.hasText(info.getDingtalkId()))
                    .collect(Collectors.toList());
            // å¦‚果存在为空的数据就通过钉钉的接口获取,为dingtalkId赋值,并且更新数据库
            if (!emptyDingtalkIdList.isEmpty()) {
                String accessToken = simpleExample.getAccessToken();
                for (DingtalkInfo info : emptyDingtalkIdList) {
                    if (StringUtils.hasText(info.getPhone())) {
                        try {
                            // é€šè¿‡æ‰‹æœºå·èŽ·å–é’‰é’‰ç”¨æˆ·ID
                            com.dingtalk.api.response.OapiV2UserGetbymobileResponse response =
                                    simpleExample.getOapiV2UserGetbymobileResponse(info.getPhone(), accessToken);
                            if (response != null && response.getResult() != null) {
                                info.setDingtalkId(response.getResult().getUserid());
                                // æ›´æ–°æ•°æ®åº“
                                updateById(info);
                            }
                        } catch (Exception e) {
                            log.error("获取钉钉用户ID失败,手机号:{}", info.getPhone(), e);
                        }
                    }
                }
            }
            // ä¸å­˜åœ¨ä¸ºç©ºçš„æ•°æ®æˆ–者处理完空数据后,返回所有有效的dingtalkId列表
            return list.stream()
                    .map(DingtalkInfo::getDingtalkId)
                    .filter(StringUtils::hasText)
                    .distinct()
                    .collect(Collectors.toList());
        } catch (Exception e) {
            log.error("获取钉钉用户列表失败", e);
            return new ArrayList<>();
        }
    }
    private OapiMessageCorpconversationAsyncsendV2Response sendMessage(String userIdListStr, String message) throws Exception {
        String accessToken = simpleExample.getAccessToken();
        DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/message/corpconversation/asyncsend_v2");
        OapiMessageCorpconversationAsyncsendV2Request request = new OapiMessageCorpconversationAsyncsendV2Request();
        request.setAgentId(4104598880L);
        request.setUseridList(userIdListStr);
        request.setToAllUser(false);
        OapiMessageCorpconversationAsyncsendV2Request.Msg msg = new OapiMessageCorpconversationAsyncsendV2Request.Msg();
        msg.setMsgtype("text");
        msg.setText(new OapiMessageCorpconversationAsyncsendV2Request.Text());
        msg.getText().setContent(message);
        request.setMsg(msg);
        return client.execute(request, accessToken);
    }
    /**
     * å‘送link消息(在钉钉内置浏览器中打开)
     *
     * @param userIdListStr ç”¨æˆ·ID列表,逗号分隔
     * @param title         æ¶ˆæ¯æ ‡é¢˜
     * @param text          æ¶ˆæ¯å†…容
     * @param messageUrl    ç‚¹å‡»æ¶ˆæ¯åŽè·³è½¬çš„URL
     * @param picUrl        å›¾ç‰‡URL(可选)
     * @return å“åº”结果
     * @throws Exception å¼‚常
     */
    private OapiMessageCorpconversationAsyncsendV2Response sendLinkMessage(String userIdListStr, String title, String text, String messageUrl, String picUrl) throws Exception {
        String accessToken = simpleExample.getAccessToken();
        DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/message/corpconversation/asyncsend_v2");
        OapiMessageCorpconversationAsyncsendV2Request request = new OapiMessageCorpconversationAsyncsendV2Request();
        request.setAgentId(4104598880L);
        request.setUseridList(userIdListStr);
        request.setToAllUser(false);
        OapiMessageCorpconversationAsyncsendV2Request.Msg msg = new OapiMessageCorpconversationAsyncsendV2Request.Msg();
        msg.setMsgtype("link");
        msg.setLink(new OapiMessageCorpconversationAsyncsendV2Request.Link());
        msg.getLink().setTitle(title);
        msg.getLink().setText(text);
        msg.getLink().setMessageUrl(messageUrl);
        if (StringUtils.hasText(picUrl)) {
            msg.getLink().setPicUrl(picUrl);
        }
        request.setMsg(msg);
        return client.execute(request, accessToken);
    }
    /**
     * å‘送ActionCard消息(在外部浏览器中打开链接,适合BI等外部系统)
     *
     * @param userIdListStr ç”¨æˆ·ID列表,逗号分隔
     * @param title         æ¶ˆæ¯æ ‡é¢˜
     * @param markdown      æ¶ˆæ¯å†…容(支持Markdown格式)
     * @param singleTitle   æŒ‰é’®æ–‡å­—,例如:"查看详情"
     * @param singleUrl     ç‚¹å‡»æŒ‰é’®åŽè·³è½¬çš„URL(外部链接)
     * @return å“åº”结果
     * @throws Exception å¼‚常
     */
    private OapiMessageCorpconversationAsyncsendV2Response sendActionCardMessage(String userIdListStr, String title, String markdown, String singleTitle, String singleUrl) throws Exception {
        String accessToken = simpleExample.getAccessToken();
        DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/message/corpconversation/asyncsend_v2");
        OapiMessageCorpconversationAsyncsendV2Request request = new OapiMessageCorpconversationAsyncsendV2Request();
        request.setAgentId(4104598880L);
        request.setUseridList(userIdListStr);
        request.setToAllUser(false);
        OapiMessageCorpconversationAsyncsendV2Request.Msg msg = new OapiMessageCorpconversationAsyncsendV2Request.Msg();
        msg.setMsgtype("action_card");
        msg.setActionCard(new OapiMessageCorpconversationAsyncsendV2Request.ActionCard());
        msg.getActionCard().setTitle(title);
        msg.getActionCard().setMarkdown(markdown);
        msg.getActionCard().setSingleTitle(singleTitle);
        msg.getActionCard().setSingleUrl(singleUrl);
        request.setMsg(msg);
        return client.execute(request, accessToken);
    }
    /**
     * ä¸Šä¼ æ–‡ä»¶åˆ°é’‰é’‰æœåŠ¡å™¨ï¼ŒèŽ·å–media_id
     *
     * @param filePath æœ¬åœ°æ–‡ä»¶è·¯å¾„
     * @param fileType æ–‡ä»¶ç±»åž‹ï¼šfile(普通文件), voice(语音文件), video(视频文件), image(图片文件)
     * @return media_id
     * @throws Exception å¼‚常
     */
    private String uploadMedia(String filePath, String fileType) throws Exception {
        String accessToken = simpleExample.getAccessToken();
        DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/media/upload");
        com.dingtalk.api.request.OapiMediaUploadRequest request = new com.dingtalk.api.request.OapiMediaUploadRequest();
        request.setType(fileType);
        // ä½¿ç”¨ FileItem åŒ…装文件
        File file = new File(filePath);
        FileItem fileItem = new FileItem(file);
        request.setMedia(fileItem);
        com.dingtalk.api.response.OapiMediaUploadResponse response = client.execute(request, accessToken);
        if (response.getErrcode() == 0) {
            log.info("文件上传成功,media_id: {}", response.getMediaId());
            return response.getMediaId();
        } else {
            log.error("文件上传失败,错误码: {}, é”™è¯¯ä¿¡æ¯: {}", response.getErrcode(), response.getErrmsg());
            throw new Exception("文件上传失败: " + response.getErrmsg());
        }
    }
    /**
     * é€šè¿‡media_id发送文件消息
     *
     * @param userIdListStr ç”¨æˆ·ID列表,逗号分隔
     * @param mediaId       æ–‡ä»¶çš„media_id(通过uploadMedia方法获取)
     * @return å“åº”结果
     * @throws Exception å¼‚常
     */
    private OapiMessageCorpconversationAsyncsendV2Response sendFileMessageByMediaId(String userIdListStr, String mediaId) throws Exception {
        String accessToken = simpleExample.getAccessToken();
        DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/message/corpconversation/asyncsend_v2");
        OapiMessageCorpconversationAsyncsendV2Request request = new OapiMessageCorpconversationAsyncsendV2Request();
        request.setAgentId(4104598880L);
        request.setUseridList(userIdListStr);
        request.setToAllUser(false);
        OapiMessageCorpconversationAsyncsendV2Request.Msg msg = new OapiMessageCorpconversationAsyncsendV2Request.Msg();
        msg.setMsgtype("file");
        msg.setFile(new OapiMessageCorpconversationAsyncsendV2Request.File());
        msg.getFile().setMediaId(mediaId);
        request.setMsg(msg);
        return client.execute(request, accessToken);
    }
    /**
     * èŽ·å–isSendDingtalk=1的用户的钉钉用户ID列表(保留原有方法,向后兼容)
     */
    private List<String> getDingtalkUserIdList() {
        try {
            LambdaQueryWrapper<DingtalkInfo> wrapper = new LambdaQueryWrapper<>();
            wrapper.ge(DingtalkInfo::getIsSendDingtalk, 1);
            List<DingtalkInfo> list = list(wrapper);
            if (list == null || list.isEmpty()) {
                return new ArrayList<>();
            }
            // ä½¿ç”¨stream流过滤出list中dingtalkId为空的数据
            List<DingtalkInfo> emptyDingtalkIdList = list.stream()
                    .filter(info -> !StringUtils.hasText(info.getDingtalkId()))
                    .collect(Collectors.toList());
            // å¦‚果存在为空的数据就通过钉钉的接口获取,为dingtalkId赋值,并且更新数据库
            if (!emptyDingtalkIdList.isEmpty()) {
                String accessToken = simpleExample.getAccessToken();
                for (DingtalkInfo info : emptyDingtalkIdList) {
                    if (StringUtils.hasText(info.getPhone())) {
                        try {
                            // é€šè¿‡æ‰‹æœºå·èŽ·å–é’‰é’‰ç”¨æˆ·ID
                            com.dingtalk.api.response.OapiV2UserGetbymobileResponse response =
                                    simpleExample.getOapiV2UserGetbymobileResponse(info.getPhone(), accessToken);
                            if (response != null && response.getResult() != null) {
                                info.setDingtalkId(response.getResult().getUserid());
                                // æ›´æ–°æ•°æ®åº“
                                updateById(info);
                            }
                        } catch (Exception e) {
                            log.error("获取钉钉用户ID失败,手机号:{}", info.getPhone(), e);
                        }
                    }
                }
            }
            // ä¸å­˜åœ¨ä¸ºç©ºçš„æ•°æ®æˆ–者处理完空数据后,返回所有有效的dingtalkId列表
            return list.stream()
                    .map(DingtalkInfo::getDingtalkId)
                    .filter(StringUtils::hasText)
                    .distinct()
                    .collect(Collectors.toList());
        } catch (Exception e) {
            log.error("获取钉钉用户列表失败", e);
            return new ArrayList<>();
        }
    }
}
src/main/java/com/gs/dingtalk/service/impl/VwCjScSjTsBbMonthServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,22 @@
package com.gs.dingtalk.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.gs.dingtalk.entity.VwCjScSjTsBbMonth;
import com.gs.dingtalk.mapper.VwCjScSjTsBbMonthMapper;
import com.gs.dingtalk.service.VwCjScSjTsBbMonthService;
import org.springframework.stereotype.Service;
/**
 * @author Administrator
 * @description é’ˆå¯¹è¡¨ã€VW_CJ_SC_SJ_TS_BB_MONTH】的数据库操作Service实现
 * @createDate 2025-11-12 19:45:00
 */
@Service
public class VwCjScSjTsBbMonthServiceImpl extends ServiceImpl<VwCjScSjTsBbMonthMapper, VwCjScSjTsBbMonth>
        implements VwCjScSjTsBbMonthService {
}
src/main/java/com/gs/dingtalk/service/impl/VwCjScSjTsBbServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,193 @@
package com.gs.dingtalk.service.impl;
import cn.hutool.core.io.FileUtil;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.gs.dingtalk.entity.VwCjScSjTsBb;
import com.gs.dingtalk.entity.VwCjScSjTsBbMonth;
import com.gs.dingtalk.mapper.VwCjScSjTsBbMapper;
import com.gs.dingtalk.service.DingtalkInfoService;
import com.gs.dingtalk.service.VwCjScSjTsBbMonthService;
import com.gs.dingtalk.service.VwCjScSjTsBbService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.List;
/**
 * @author Administrator
 * @description é’ˆå¯¹è¡¨ã€VW_CJ_SC_SJ_TS_BB】的数据库操作Service实现
 * @createDate 2025-11-12 16:42:06
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class VwCjScSjTsBbServiceImpl extends ServiceImpl<VwCjScSjTsBbMapper, VwCjScSjTsBb>
        implements VwCjScSjTsBbService {
    private final DingtalkInfoService dingtalkInfoService;
    private final VwCjScSjTsBbMonthService vwCjScSjTsBbMonthService;
    @Override
    public boolean exportAndSendToDingtalk() throws Exception {
        String exportFilePath = null;
        try {
            // 2. å‡†å¤‡å¯¼å‡ºæ–‡ä»¶è·¯å¾„
            String timestamp = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
            String fileName = "生产数据报表_" + timestamp + ".xlsx";
            exportFilePath = "D:\\BIFile\\" + fileName;
            // ç¡®ä¿ç›®å½•存在
            FileUtil.mkdir("D:\\BIFile");
            // 3. å¯¼å‡ºåˆ°Excel
            log.info("开始导出Excel文件: {}", exportFilePath);
            exportToExcel(exportFilePath);
            log.info("Excel文件导出成功");
            // 4. å‘送钉钉消息
            log.info("开始发送钉钉文件消息...");
            boolean sendResult = dingtalkInfoService.sendFileMessage(exportFilePath);
            if (sendResult) {
                log.info("钉钉文件消息发送成功");
            } else {
                log.warn("钉钉文件消息发送失败");
            }
            return sendResult;
        } catch (Exception e) {
            log.error("导出并发送失败", e);
            throw e;
        } finally {
            // å¯é€‰ï¼šå‘送后删除临时文件
            // if (exportFilePath != null && FileUtil.exist(exportFilePath)) {
            //     FileUtil.del(exportFilePath);
            //     log.info("临时文件已删除: {}", exportFilePath);
            // }
        }
    }
    /**
     * å¯¼å‡ºæ•°æ®åˆ°Excel(两个sheet页)
     *
     * @param filePath æ–‡ä»¶è·¯å¾„
     */
    private void exportToExcel(String filePath) {
        // è®¡ç®—昨天的日期
        LocalDate yesterday = LocalDate.now().minusDays(1);
        String yesterdayStr = yesterday.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
        // è®¡ç®—本月的起止日期
        LocalDate today = LocalDate.now();
        LocalDate firstDayOfMonth = today.withDayOfMonth(1);
        LocalDate lastDayOfMonth = today.withDayOfMonth(today.lengthOfMonth());
        String firstDayStr = firstDayOfMonth.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
        String lastDayStr = lastDayOfMonth.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
        log.info("昨天日期: {}", yesterdayStr);
        log.info("本月范围: {} è‡³ {}", firstDayStr, lastDayStr);
        // è¿‡æ»¤æ•°æ®ï¼šé¢„计开工在昨天的数据
        List<VwCjScSjTsBb> yesterdayData = list();
        // è¿‡æ»¤æ•°æ®ï¼šé¢„计开工在本月的数据
        List<VwCjScSjTsBbMonth> thisMonthData = vwCjScSjTsBbMonthService.list();
        log.info("昨天数据: {} æ¡", yesterdayData.size());
        log.info("本月数据: {} æ¡", thisMonthData.size());
        // åˆ›å»ºExcel写入器(第一个sheet)
        ExcelWriter writer = ExcelUtil.getWriter(filePath, "昨天入库工单申请");
        // å†™å…¥ç¬¬ä¸€ä¸ªsheet:昨天预计开工的数据
        writeSheetData(writer, "昨天入库工单申请数据(" + yesterdayStr + ")", yesterdayData);
        // åˆ›å»ºç¬¬äºŒä¸ªsheet
        writer.setSheet("本月入库工单申请");
        // å†™å…¥ç¬¬äºŒä¸ªsheet:本月预计开工的数据
        writeSheetData(writer, thisMonthData, "本月入库工单申请数据(" + firstDayStr + " è‡³ " + lastDayStr + ")");
        // å…³é—­writer,释放内存
        writer.close();
    }
    /**
     * å†™å…¥å•个sheet的数据
     *
     * @param writer   Excel写入器
     * @param dataList æ•°æ®åˆ—表
     * @param title    æ ‡é¢˜
     */
    private void writeSheetData(ExcelWriter writer, String title, List<VwCjScSjTsBb> dataList) {
        // æ¸…空之前的别名设置
        writer.clearHeaderAlias();
        // è®¾ç½®è¡¨å¤´åˆ«åï¼ˆä¸­æ–‡åˆ—名)
        writer.addHeaderAlias("departmentname", "车间");
        writer.addHeaderAlias("lineName", "线体名称");
        writer.addHeaderAlias("itemNo", "产品编码");
        writer.addHeaderAlias("itemName", "产品名称");
        writer.addHeaderAlias("daa001", "工单号");
        writer.addHeaderAlias("daa008", "工单数");
        writer.addHeaderAlias("sq", "申请数");
        writer.addHeaderAlias("rk", "入库数");
        writer.addHeaderAlias("sqwwg", "申请未完工数");
        writer.addHeaderAlias("rkwwg", "入库未完工");
        writer.addHeaderAlias("sqwrk", "申请未入库");
        writer.addHeaderAlias("yjkg", "预计开工");
        writer.addHeaderAlias("sjkg", "实际开工");
        // åˆå¹¶å•元格作为标题行
        writer.merge(14, title);
        // å†™å…¥æ•°æ®ï¼Œé»˜è®¤ä¼šä½¿ç”¨åˆ«åä½œä¸ºè¡¨å¤´
        writer.write(dataList, true);
        // è®¾ç½®åˆ—宽自适应
        writer.autoSizeColumnAll();
    }
    private void writeSheetData(ExcelWriter writer, List<VwCjScSjTsBbMonth> dataList, String title) {
        // æ¸…空之前的别名设置
        writer.clearHeaderAlias();
        // è®¾ç½®è¡¨å¤´åˆ«åï¼ˆä¸­æ–‡åˆ—名)
        writer.addHeaderAlias("departmentname", "车间");
        writer.addHeaderAlias("lineName", "线体名称");
        writer.addHeaderAlias("itemNo", "产品编码");
        writer.addHeaderAlias("itemName", "产品名称");
        writer.addHeaderAlias("daa001", "工单号");
        writer.addHeaderAlias("daa008", "工单数");
        writer.addHeaderAlias("sq", "申请数");
        writer.addHeaderAlias("rk", "入库数");
        writer.addHeaderAlias("sqwwg", "申请未完工数");
        writer.addHeaderAlias("rkwwg", "入库未完工");
        writer.addHeaderAlias("sqwrk", "申请未入库");
        writer.addHeaderAlias("yjkg", "预计开工");
        writer.addHeaderAlias("sjkg", "实际开工");
        // åˆå¹¶å•元格作为标题行
        writer.merge(14, title);
        // å†™å…¥æ•°æ®ï¼Œé»˜è®¤ä¼šä½¿ç”¨åˆ«åä½œä¸ºè¡¨å¤´
        writer.write(dataList, true);
        // è®¾ç½®åˆ—宽自适应
        writer.autoSizeColumnAll();
    }
}
src/main/java/com/gs/dingtalk/task/ScheduledTasks.java
@@ -2,54 +2,90 @@
import com.gs.dingtalk.service.SendDingtalkService;
import com.gs.dingtalk.service.VwCjScSjTsBbService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.util.concurrent.atomic.AtomicBoolean;
@Component
@RequiredArgsConstructor
@Slf4j
public class ScheduledTasks {
    private final SendDingtalkService sendDingtalkService;
//    private final SendDingtalkService sendDingtalkService;
    private final VwCjScSjTsBbService vwCjScSjTsBbService;
    private final AtomicBoolean exportTaskRunning = new AtomicBoolean(false);
//    /**
//     * æ¯ä¸¤åˆ†é’Ÿæ‰§è¡Œä¸€æ¬¡
//     * èŽ·å–è®¾å¤‡æœ€è¿‘çš„ä¸€æ¡è®°å½•
//     *
//     * @return void
//     * @author tjx
//     * @description TODO
//     * @date 2024/9/27 21:48
//     */
//    @Scheduled(cron = "0 0/2 * * * ?")
//    public void getDeviceRealTimeData() {
//        try {
//            sendDingtalkService.sendDingTalkFiveMinute();
//            log.info("定时任务 getDeviceRealTimeData æ‰§è¡ŒæˆåŠŸ");
//        } catch (Exception e) {
//            log.error("定时任务 getDeviceRealTimeData æ‰§è¡Œå¤±è´¥: ", e);
//        }
//    }
//
//    @Scheduled(cron = "0 0/3 * * * ?")
//    public void sendDingTalkFifteenMinute() {
//        try {
//            sendDingtalkService.sendDingTalkFifteenMinute();
//            log.info("定时任务 sendDingTalkFifteenMinute æ‰§è¡ŒæˆåŠŸ");
//        } catch (Exception e) {
//            log.error("定时任务 sendDingTalkFifteenMinute æ‰§è¡Œå¤±è´¥: ", e);
//        }
//    }
//
//    @Scheduled(cron = "0 0/4 * * * ?")
//    public void sendDingTalkThirtyMinute() {
//        try {
//            sendDingtalkService.sendDingTalkthirtyMinute();
//            log.info("定时任务 sendDingTalkThirtyMinute æ‰§è¡ŒæˆåŠŸ");
//        } catch (Exception e) {
//            log.error("定时任务 sendDingTalkThirtyMinute æ‰§è¡Œå¤±è´¥: ", e);
//        }
//    }
    /**
     * æ¯ä¸¤åˆ†é’Ÿæ‰§è¡Œä¸€æ¬¡
     * èŽ·å–è®¾å¤‡æœ€è¿‘çš„ä¸€æ¡è®°å½•
     *
     * @return void
     * @author tjx
     * @description TODO
     * @date 2024/9/27 21:48
     * æ¯å¤©ä¸Šåˆ9点执行
     * å¯¼å‡ºç”Ÿäº§æ•°æ®å¹¶å‘送钉钉消息
     */
    @Scheduled(cron = "0 0/2 * * * ?")
    public void getDeviceRealTimeData() {
        try {
            sendDingtalkService.sendDingTalkFiveMinute();
            log.info("定时任务 getDeviceRealTimeData æ‰§è¡ŒæˆåŠŸ");
        } catch (Exception e) {
            log.error("定时任务 getDeviceRealTimeData æ‰§è¡Œå¤±è´¥: ", e);
    @Async("taskExecutor")
    @Scheduled(cron = "0 0 12,16 * * ?")
    public void exportAndSendProductionDataTask() {
        if (!exportTaskRunning.compareAndSet(false, true)) {
            log.warn("生产数据导出任务正在执行中,跳过本次执行");
            return;
        }
    }
    @Scheduled(cron = "0 0/3 * * * ?")
    public void sendDingTalkFifteenMinute() {
        try {
            sendDingtalkService.sendDingTalkFifteenMinute();
            log.info("定时任务 sendDingTalkFifteenMinute æ‰§è¡ŒæˆåŠŸ");
        } catch (Exception e) {
            log.error("定时任务 sendDingTalkFifteenMinute æ‰§è¡Œå¤±è´¥: ", e);
        }
    }
            log.info("开始执行生产数据导出并发送钉钉任务");
            boolean result = vwCjScSjTsBbService.exportAndSendToDingtalk();
    @Scheduled(cron = "0 0/4 * * * ?")
    public void sendDingTalkThirtyMinute() {
        try {
            sendDingtalkService.sendDingTalkthirtyMinute();
            log.info("定时任务 sendDingTalkThirtyMinute æ‰§è¡ŒæˆåŠŸ");
            if (result) {
                log.info("生产数据导出并发送钉钉任务执行成功");
            } else {
                log.error("生产数据导出并发送钉钉任务执行失败");
            }
        } catch (Exception e) {
            log.error("定时任务 sendDingTalkThirtyMinute æ‰§è¡Œå¤±è´¥: ", e);
            log.error("生产数据导出并发送钉钉任务执行异常", e);
        } finally {
            exportTaskRunning.set(false);
        }
    }
}
src/main/resources/application.yml
@@ -5,9 +5,9 @@
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: oracle.jdbc.OracleDriver
    url: jdbc:oracle:thin:@192.168.0.100:1521/orcl
    username: yc_dev
    password: ycdev
    url: jdbc:oracle:thin:@192.168.1.22:1521/orcl
    username: hm_prd
    password: hmprd
    druid:
      initialSize: 5
      minIdle: 5
src/main/resources/mapper/DingtalkInfoMapper.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.gs.dingtalk.mapper.DingtalkInfoMapper">
</mapper>
src/main/resources/mapper/DingtalkMsgMapper.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.gs.dingtalk.mapper.DingtalkMsgMapper">
</mapper>
src/main/resources/mapper/MesStaffMapper.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.gs.dingtalk.mapper.MesStaffMapper">
    <update id="updateStaff">
        update SYS_USER a
        set SID = (select ID from MES_STAFF where STAFF_NO = a.FCODE and STAFF_NAME = a.FNAME)
        where 1 = 1
    </update>
    <delete id="deleteStaff">
        delete
        from MES_STAFF
        where 1 = 1
    </delete>
    <select id="getNextVal" resultType="java.lang.Long">
        select SEQ_MES_STAFF.NextVal
        from dual
    </select>
</mapper>
src/main/resources/mapper/VwCjScSjTsBbMapper.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.gs.dingtalk.mapper.VwCjScSjTsBbMapper">
</mapper>
src/main/resources/mapper/VwCjScSjTsBbMonthMapper.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.gs.dingtalk.mapper.VwCjScSjTsBbMonthMapper">
</mapper>