“lqy пре 1 месец
родитељ
комит
a92b5cf936
34 измењених фајлова са 5939 додато и 0 уклоњено
  1. 147 0
      mjava-qiwang/pom.xml
  2. 112 0
      mjava-qiwang/src/main/java/com/malk/qiwang/Controller/CallBackController.java
  3. 222 0
      mjava-qiwang/src/main/java/com/malk/qiwang/Controller/CompanyTitleController.java
  4. 120 0
      mjava-qiwang/src/main/java/com/malk/qiwang/Controller/InvoiceLibraryController.java
  5. 907 0
      mjava-qiwang/src/main/java/com/malk/qiwang/Controller/QiWangController.java
  6. 18 0
      mjava-qiwang/src/main/java/com/malk/qiwang/MjavaQiwangApplication.java
  7. 19 0
      mjava-qiwang/src/main/java/com/malk/qiwang/Service/ICompanyTitleService.java
  8. 36 0
      mjava-qiwang/src/main/java/com/malk/qiwang/Service/IInvoiceLibraryService.java
  9. 7 0
      mjava-qiwang/src/main/java/com/malk/qiwang/Service/McProjectService.java
  10. 9 0
      mjava-qiwang/src/main/java/com/malk/qiwang/Service/QiWangService.java
  11. 39 0
      mjava-qiwang/src/main/java/com/malk/qiwang/Service/TXYInvoice.java
  12. 151 0
      mjava-qiwang/src/main/java/com/malk/qiwang/Service/impl/CompanyTitleServiceImpl.java
  13. 2652 0
      mjava-qiwang/src/main/java/com/malk/qiwang/Service/impl/InvoiceLibraryServiceImpl.java
  14. 29 0
      mjava-qiwang/src/main/java/com/malk/qiwang/Service/impl/McProjectServiceImpl.java
  15. 17 0
      mjava-qiwang/src/main/java/com/malk/qiwang/Service/impl/QiWangServiceImpl.java
  16. 165 0
      mjava-qiwang/src/main/java/com/malk/qiwang/Service/impl/TXYImplInvoice.java
  17. 19 0
      mjava-qiwang/src/main/java/com/malk/qiwang/config/StartupRunner.java
  18. 76 0
      mjava-qiwang/src/main/java/com/malk/qiwang/entity/CompanyTitle.java
  19. 128 0
      mjava-qiwang/src/main/java/com/malk/qiwang/entity/InvoiceLibrary.java
  20. 46 0
      mjava-qiwang/src/main/java/com/malk/qiwang/mapper/CompanyTitleMapper.java
  21. 26 0
      mjava-qiwang/src/main/java/com/malk/qiwang/mapper/InvoiceLibraryMapper.java
  22. 5 0
      mjava-qiwang/src/main/java/com/malk/qiwang/mapper/xml/CompanyTitleMapper.xml
  23. 5 0
      mjava-qiwang/src/main/java/com/malk/qiwang/mapper/xml/InvoiceLibraryMapper.xml
  24. 220 0
      mjava-qiwang/src/main/java/com/malk/qiwang/model/McInvoiceDto.java
  25. 68 0
      mjava-qiwang/src/main/java/com/malk/qiwang/model/McInvoiceKind.java
  26. 40 0
      mjava-qiwang/src/main/java/com/malk/qiwang/tencent/TXYConf.java
  27. 113 0
      mjava-qiwang/src/main/java/com/malk/qiwang/tencent/TXYConfNh.java
  28. 267 0
      mjava-qiwang/src/main/java/com/malk/qiwang/utils/HTTPHelper.java
  29. 84 0
      mjava-qiwang/src/main/java/com/malk/qiwang/utils/MPGenerator.java
  30. 60 0
      mjava-qiwang/src/main/resources/application-dev.yml
  31. 43 0
      mjava-qiwang/src/main/resources/application-prod.yml
  32. 15 0
      mjava-qiwang/src/main/resources/application.yml
  33. 61 0
      mjava-qiwang/src/main/resources/logback-spring.xml
  34. 13 0
      mjava-qiwang/src/test/java/com/malk/qiwang/MjavaQiwangApplicationTests.java

+ 147 - 0
mjava-qiwang/pom.xml

@@ -0,0 +1,147 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>mjava-qiwang</groupId>
+    <artifactId>mjava-qiwang</artifactId>
+    <version>0.0.1-SNAPSHOT</version>
+    <name>mjava-qiwang</name>
+    <description>mjava-qiwang</description>
+   <properties>
+        <java.version>1.8</java.version>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+
+    </properties>>
+    <parent>
+        <groupId>org.springframework.boot</groupId>
+        <artifactId>spring-boot-starter-parent</artifactId>
+        <version>2.7.18</version>
+        <relativePath/>
+    </parent>
+    <dependencies>
+        <!-- Spring Boot Starters -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <!-- 自定义基础组件 -->
+        <dependency>
+            <groupId>com.malk</groupId>
+            <artifactId>base</artifactId>
+            <version>1.3</version>
+        </dependency>
+
+        <!-- MySQL 驱动 -->
+        <dependency>
+            <groupId>com.mysql</groupId>
+            <artifactId>mysql-connector-j</artifactId>
+            <version>8.0.33</version>
+        </dependency>
+
+        <!-- SQL Server 驱动(如果不需要可以移除) -->
+        <dependency>
+            <groupId>com.microsoft.sqlserver</groupId>
+            <artifactId>mssql-jdbc</artifactId>
+            <version>12.4.2.jre8</version>
+        </dependency>
+
+        <!-- MyBatis-Plus -->
+        <dependency>
+            <groupId>com.baomidou</groupId>
+            <artifactId>mybatis-plus-boot-starter</artifactId>
+            <version>3.5.3.2</version>
+        </dependency>
+
+        <!-- MyBatis-Plus 代码生成器 -->
+        <dependency>
+            <groupId>com.baomidou</groupId>
+            <artifactId>mybatis-plus-generator</artifactId>
+            <version>3.5.5</version>
+        </dependency>
+
+        <!-- 模板引擎(修复版本) -->
+        <dependency>
+            <groupId>org.apache.velocity</groupId>
+            <artifactId>velocity-engine-core</artifactId>
+            <version>2.3</version>
+        </dependency>
+
+        <!-- FreeMarker(可选,与 Velocity 二选一) -->
+        <dependency>
+            <groupId>org.freemarker</groupId>
+            <artifactId>freemarker</artifactId>
+            <version>2.3.32</version>
+        </dependency>
+
+        <!-- Lombok -->
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+            <optional>true</optional>
+        </dependency>
+
+        <!-- 腾讯云 OCR -->
+        <dependency>
+            <groupId>com.tencentcloudapi</groupId>
+            <artifactId>tencentcloud-sdk-java-ocr</artifactId>
+            <version>3.1.1210</version>
+        </dependency>
+
+        <dependency>
+            <groupId>com.tencentcloudapi</groupId>
+            <artifactId>tencentcloud-sdk-java</artifactId>
+            <version>3.1.1442</version>
+        </dependency>
+
+        <!-- 图片处理 -->
+        <dependency>
+            <groupId>net.coobird</groupId>
+            <artifactId>thumbnailator</artifactId>
+            <version>0.4.8</version>
+        </dependency>
+
+        <!-- 图片处理扩展 -->
+        <dependency>
+            <groupId>com.twelvemonkeys.imageio</groupId>
+            <artifactId>imageio-tiff</artifactId>
+            <version>3.5</version>
+        </dependency>
+
+        <!-- PDF 处理 -->
+        <dependency>
+            <groupId>e-iceblue</groupId>
+            <artifactId>spire.pdf.free</artifactId>
+            <version>5.1.0</version>
+        </dependency>
+
+        <!-- 测试 -->
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <finalName>qiwang</finalName>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+                <configuration>
+                    <executable>true</executable>
+                    <includeSystemScope>true</includeSystemScope>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>

+ 112 - 0
mjava-qiwang/src/main/java/com/malk/qiwang/Controller/CallBackController.java

@@ -0,0 +1,112 @@
+package com.malk.qiwang.Controller;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.malk.controller.DDCallbackController;
+import com.malk.qiwang.Service.IInvoiceLibraryService;
+import com.malk.server.dingtalk.DDConf;
+import com.malk.server.dingtalk.crypto.DingCallbackCrypto;
+import com.malk.service.dingtalk.DDClient_Event;
+import lombok.SneakyThrows;
+import lombok.extern.slf4j.Slf4j;
+import org.slf4j.MDC;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+@Slf4j
+@RestController
+@Component
+@RequestMapping("/aa")
+public class CallBackController extends DDCallbackController {
+    @Autowired
+    private DDConf ddConf;
+    @Autowired
+    private IInvoiceLibraryService invoiceLibrary;
+    @Autowired
+    private DDClient_Event ddClient_event;
+
+
+    /**
+     * 钉钉审批回调: [依赖包方案已弃用]
+     * -
+     * DingCallbackCrypto 方案: 官网案例 DingCallbackCrypto 不在钉钉架包, 需要单独引用
+     * 在钉钉开放平台重新保存回调地址后, 所有的注册事件会被关闭:: 通过代码注册不成功; 官方回复, 要么使用调用注册的方式 要么是后台的方式, 二选一
+     */
+    @SneakyThrows
+    public Map<String, String> invokeCallback(@RequestParam(value = "signature", required = false) String signature,
+                                              @RequestParam(value = "timestamp", required = false) String timestamp,
+                                              @RequestParam(value = "nonce", required = false) String nonce,
+                                              @RequestBody(required = false) JSONObject json) {
+        log.info("signature:{}",signature);
+        log.info("timestamp:{}",timestamp);
+        log.info("nonce:{}",nonce);
+        log.info("json:{}",json);
+//        MDC.put("MDC_KEY_PID", "1003");
+        DingCallbackCrypto callbackCrypto = new DingCallbackCrypto(ddConf.getToken(), ddConf.getAesKey(), ddConf.getAppKey());
+        final String decryptMsg = callbackCrypto.getDecryptMsg(signature, timestamp, nonce, json.getString("encrypt"));
+        JSONObject eventJson = JSON.parseObject(decryptMsg);
+        Map success = callbackCrypto.getEncryptedMap(DDConf.CALLBACK_RESPONSE, System.currentTimeMillis(), DingCallbackCrypto.Utils.getRandomStr(8));
+
+        String eventType = eventJson.getString("EventType");
+
+
+
+        if (DDConf.CALLBACK_CHECK.equals(eventType)) {
+            log.info("----- [DD]验证注册 -----");
+            return success;
+        }
+        if (DDConf.BPMS_INSTANCE_CHANGE.equals(eventType)) {
+            log.info("----- [DD]验证注册 -----");
+            String processCode = eventJson.getString("processCode");
+            String type = eventJson.getString("type");
+            String result = eventJson.getString("result");
+            String processInstanceId = eventJson.getString("processInstanceId");
+            String eventId = eventJson.getString("eventId");//事件id
+            // 检查回调事件是否已经处理过,如果是,则忽略该回调
+            if (isCallbackProcessed(eventId)) {
+                log.info("----- [DD]该签到回调事件已处理过 忽略该回调 -----");
+                return success;
+            }else {
+                // 将回调事件和当前时间戳添加到已处理集合中
+                long currentTime = System.currentTimeMillis();
+                eventList.put(eventId, currentTime);
+            }
+            if("terminate".equals(type) || "finish".equals(type) && "refuse".equals(result)){
+                log.info("----- [DD]审批回调, {}", eventJson);
+                invoiceLibrary.deleteAllByOaId(processInstanceId);
+            }
+
+            return success;
+        }
+
+        log.info("----- [DD]已注册, 未处理的其它回调 -----, {}", eventJson);
+        return success;
+    }
+
+    //保存10s内已处理的回调事件
+    private Map<String, Long> eventList = new HashMap<>();
+
+    /**
+     * 检查该回调事件在10s内是否处理过,应对钉钉瞬间重复回调
+     *
+     * @param eventId 回调事件id
+     * @return 是否处理过
+     */
+    private boolean isCallbackProcessed(String eventId) {
+        // 清理超过10s的回调事件
+        long currentTime = System.currentTimeMillis();
+        long expirationTime = currentTime - TimeUnit.MINUTES.toMillis(10);
+        eventList.entrySet().removeIf(entry -> entry.getValue() < expirationTime);
+
+        return eventList.containsKey(eventId);
+    }
+
+}

+ 222 - 0
mjava-qiwang/src/main/java/com/malk/qiwang/Controller/CompanyTitleController.java

@@ -0,0 +1,222 @@
+package com.malk.qiwang.Controller;
+
+import com.malk.qiwang.Service.ICompanyTitleService;
+import com.malk.qiwang.entity.CompanyTitle;
+import com.malk.qiwang.mapper.CompanyTitleMapper;
+import com.malk.server.aliwork.YDConf;
+import com.malk.server.aliwork.YDParam;
+import com.malk.server.common.McR;
+import com.malk.service.aliwork.YDClient;
+import com.malk.service.dingtalk.DDClient;
+import com.malk.service.dingtalk.DDClient_Workflow;
+import com.malk.utils.UtilMap;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.checkerframework.checker.units.qual.A;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.util.*;
+
+/**
+ * <p>
+ * 公司抬头库表 前端控制器
+ * </p>
+ *
+ * @author LQY
+ * @since 2026-04-24
+ */
+@RestController
+@RequestMapping("/qw1")
+@Slf4j
+public class CompanyTitleController {
+@Autowired
+private ICompanyTitleService companyTitleService;
+@Autowired
+private YDClient ydClient;
+    @Autowired
+    private DDClient_Workflow ddClientWorkflow ;
+    @Autowired
+    private DDClient ddClient;
+    @Autowired
+    private CompanyTitleMapper companyTitleMapper;
+    @PostMapping("/companyTitle")
+    public McR insertTransfer2(@RequestBody(required = false) Map<String, Object> map) {
+        if (map == null) {
+            map = new HashMap<>();
+            log.info("请求体为空,使用默认参数");
+        }
+        log.info("接收到的参数: {}", map);
+
+        // 获取参数
+        String processCode = "PROC-187CB1F8-5BF0-403A-9D01-94EEF987DD77";
+
+        // 设置时间范围(默认查询最近30天)
+        Long startTime = System.currentTimeMillis() - 30L * 24 * 60 * 60 * 1000;
+        Long endTime = System.currentTimeMillis();
+
+        // 获取钉钉accessToken
+        String accessToken = ddClient.getAccessToken();
+
+        // 递归获取所有审批实例ID
+        List<String> allInstanceIds = ddClientWorkflow.getInstanceIds_all(accessToken, processCode, startTime, endTime, null);
+
+        log.info("共查询到 {} 条审批实例", allInstanceIds.size());
+
+        // 批量处理审批实例
+        int successCount = 0;
+        int failCount = 0;
+
+        // 用于记录处理的税号,避免重复处理同一个税号(可选,根据业务需求)
+        Set<String> processedTaxIds = new HashSet<>();
+
+        for (String instanceId : allInstanceIds) {
+            try {
+                // 获取审批实例详情
+                Map<String, Object> processInstance = ddClientWorkflow.getProcessInstanceId(accessToken, instanceId);
+
+                if (processInstance != null) {
+                    // 提取并保存公司抬头数据
+                    boolean result = saveCompanyTitleFromInstance(processInstance, processedTaxIds);
+                    if (result) {
+                        successCount++;
+                    } else {
+                        failCount++;
+                    }
+                } else {
+                    failCount++;
+                }
+            } catch (Exception e) {
+                log.error("处理审批实例失败: instanceId={}", instanceId, e);
+                failCount++;
+            }
+        }
+
+        log.info("处理完成: 成功={}, 失败={}", successCount, failCount);
+
+        return McR.success();
+    }
+
+    private boolean saveCompanyTitleFromInstance(Map<String, Object> processInstance, Set<String> processedTaxIds) {
+        List<Map<String, Object>> formComponentValues =
+                (List<Map<String, Object>>) processInstance.get("formComponentValues");
+
+        if (formComponentValues == null || formComponentValues.isEmpty()) {
+            log.warn("表单数据为空");
+            return false;
+        }
+
+        String tt = "", sh = "", dz = "", dh = "", yh = "", zh = "", zt = "";
+
+        // 修复:安全获取审批时间,避免类型转换异常
+        Long finishTime = null;
+        Object finishTimeObj = processInstance.get("finishTime");
+        if (finishTimeObj != null) {
+            if (finishTimeObj instanceof Long) {
+                finishTime = (Long) finishTimeObj;
+            } else if (finishTimeObj instanceof String) {
+                try {
+                    finishTime = Long.parseLong((String) finishTimeObj);
+                } catch (NumberFormatException e) {
+                    log.warn("finishTime格式转换失败: {}", finishTimeObj);
+                }
+            } else if (finishTimeObj instanceof Integer) {
+                finishTime = ((Integer) finishTimeObj).longValue();
+            }
+        }
+
+        for (Map<String, Object> formComponentValue : formComponentValues) {
+            String id = String.valueOf(formComponentValue.get("id"));
+            String value = formComponentValue.get("value") != null ?
+                    String.valueOf(formComponentValue.get("value")) : "";
+
+            switch (id) {
+                case "TextField-K2AD4O5B":
+                    tt = value;
+                    break;
+                case "TextField_1JEUFYV5F4E80":
+                    sh = value;
+                    break;
+                case "TextField_EJZY527VJXC0":
+                    dz = value;
+                    break;
+                case "TextField_12YU12T3PZC0":
+                    dh = value;
+                    break;
+                case "TextField_1Z4P9MQFOW3K0":
+                    yh = value;
+                    break;
+                case "TextField_NUL1SS1PJQ80":
+                    zh = value;
+                    break;
+                case "DDSelectField_528A36XIP9C0":
+                    zt = value;
+                    break;
+                default:
+                    break;
+            }
+        }
+
+        if (StringUtils.isBlank(sh)) {
+            log.warn("税号为空,跳过保存");
+            return false;
+        }
+
+        // 可选:避免重复处理同一个税号
+        if (processedTaxIds.contains(sh)) {
+            log.debug("税号 {} 已处理过,跳过", sh);
+            return false;
+        }
+
+        // 查询数据库中该税号的最新记录
+        CompanyTitle existing = companyTitleMapper.selectByTaxId(sh);
+
+        if (existing != null) {
+            // 比较审批时间,判断是否为最新数据
+            if (finishTime != null && existing.getUpdatedAt() != null) {
+                long existingTime = existing.getUpdatedAt().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
+                if (finishTime <= existingTime) {
+                    log.debug("当前审批数据不是最新,跳过更新: taxId={}, 审批时间={}, 数据库更新时间={}",
+                            sh, finishTime, existingTime);
+                    return false;
+                }
+            }
+
+            // 更新
+            existing.setCompanyName(tt);
+            existing.setAddress(dz);
+            existing.setPhone(dh);
+            existing.setBankName(yh);
+            existing.setBankAccount(zh);
+            existing.setStatus("启用".equals(zt) ? (byte) 0 : (byte) 1);
+            existing.setUpdatedAt(LocalDateTime.now());
+
+            companyTitleMapper.updateById(existing);
+            log.info("公司抬头更新成功: taxId={}, 审批时间={}", sh, finishTime);
+        } else {
+            // 新增
+            CompanyTitle companyTitle = new CompanyTitle();
+            companyTitle.setCompanyName(tt);
+            companyTitle.setTaxId(sh);
+            companyTitle.setAddress(dz);
+            companyTitle.setPhone(dh);
+            companyTitle.setBankName(yh);
+            companyTitle.setBankAccount(zh);
+            companyTitle.setStatus("启用".equals(zt) ? (byte) 0 : (byte) 1);
+            companyTitle.setCreatedAt(LocalDateTime.now());
+            companyTitle.setUpdatedAt(LocalDateTime.now());
+
+            companyTitleMapper.insert(companyTitle);
+            log.info("公司抬头新增成功: taxId={}", sh);
+        }
+
+        processedTaxIds.add(sh);
+        return true;
+    }
+}

+ 120 - 0
mjava-qiwang/src/main/java/com/malk/qiwang/Controller/InvoiceLibraryController.java

@@ -0,0 +1,120 @@
+package com.malk.qiwang.Controller;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.malk.qiwang.Service.IInvoiceLibraryService;
+import com.malk.qiwang.Service.QiWangService;
+import com.malk.qiwang.Service.TXYInvoice;
+import com.malk.qiwang.entity.InvoiceLibrary;
+import com.malk.qiwang.mapper.InvoiceLibraryMapper;
+import com.malk.server.common.McR;
+import com.malk.service.aliwork.YDClient;
+import com.malk.service.dingtalk.DDClient;
+import com.malk.service.dingtalk.DDClient_Contacts;
+import com.malk.service.dingtalk.DDClient_Workflow;
+import com.malk.utils.UtilMap;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.stereotype.Controller;
+
+import java.util.Map;
+
+/**
+ * <p>
+ * 发票库表 前端控制器
+ * </p>
+ *
+ * @author LQY
+ * @since 2026-04-27
+ */
+@RestController
+@RequestMapping("/qw2")
+@Slf4j
+public class InvoiceLibraryController {
+    @Autowired
+    private IInvoiceLibraryService invoiceLibrary;
+    @Autowired
+    private DDClient ddClient;
+    @Autowired
+    private YDClient ydClient;
+    @Autowired
+    private TXYInvoice txyInvoice;
+    @Value("${dingtalk.operator}")
+    private String operator;
+    @Value("${dingtalk.downloadPath}")
+    private String downloadPath;
+    @Autowired
+    private DDClient_Workflow ddClientWorkflow ;
+    @Autowired
+    private DDClient_Contacts ddClient_contacts;
+    @Autowired
+    private InvoiceLibraryMapper baseMapper;
+    private static final String url = "http://6de142c3.r39.cpolar.top/qiwang/";
+            //员工报销
+    @PostMapping("/invoiceLibrary")
+    public McR test(@RequestBody Map map) {
+        log.info("map:{}", map);
+        invoiceLibrary.invoiceLibrary(map);
+       return McR.success();
+    }
+    //付款申请
+    @PostMapping("/invoiceLibrary1")
+    public McR invoiceLibrary1(@RequestBody Map map) {
+        log.info("map:{}", map);
+        invoiceLibrary.invoiceLibrary1(map);
+        return McR.success();
+    }
+
+    //合作商付款申请
+    @PostMapping("/invoiceLibrary2")
+    public McR invoiceLibrary2(@RequestBody Map map) {
+        log.info("map:{}", map);
+        invoiceLibrary.invoiceLibrary2(map);
+        return McR.success();
+    }
+
+    //合作商报销申请
+    @PostMapping("/invoiceLibrary3")
+    public McR invoiceLibrary3(@RequestBody Map map) {
+        log.info("map:{}", map);
+        invoiceLibrary.invoiceLibrary3(map);
+        return McR.success();
+    }
+
+    //出差费用申请
+    @PostMapping("/invoiceLibrary5")
+    public McR invoiceLibrary5(@RequestBody Map map) {
+        log.info("map:{}", map);
+        invoiceLibrary.invoiceLibrary5(map);
+        return McR.success();
+    }
+
+    //付款申请(一对多)
+    @PostMapping("/invoiceLibrary6")
+    public McR invoiceLibrary6(@RequestBody Map map) {
+        log.info("map:{}", map);
+        invoiceLibrary.invoiceLibrary6(map);
+        return McR.success();
+    }
+    @PostMapping("/invoiceLibrarys")
+    public McR test2(@RequestBody Map map) {
+        log.info("map:{}", map);
+        invoiceLibrary.invoiceLibrarys(map);
+        return McR.success();
+    }
+    @PostMapping("/updateOaStatusByOaId")
+    public McR test1(@RequestBody Map map) {
+        log.info("map:{}", map);
+        invoiceLibrary.updateOaStatusByOaId(map);
+        return McR.success();
+    }
+    @PostMapping("/deleteAll")
+    public McR deleteAllByOaId(@RequestParam("oaId") String oaId) {
+
+        log.info("map:{}", oaId);
+        invoiceLibrary.deleteAllByOaId(oaId);
+       return McR.success();
+    }
+}

Разлика између датотеке није приказан због своје велике величине
+ 907 - 0
mjava-qiwang/src/main/java/com/malk/qiwang/Controller/QiWangController.java


+ 18 - 0
mjava-qiwang/src/main/java/com/malk/qiwang/MjavaQiwangApplication.java

@@ -0,0 +1,18 @@
+package com.malk.qiwang;
+
+import org.mybatis.spring.annotation.MapperScan;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.scheduling.annotation.EnableScheduling;
+
+
+@SpringBootApplication(scanBasePackages = {"com.malk"})
+@MapperScan("com.malk.qiwang.mapper")
+@EnableScheduling
+public class MjavaQiwangApplication {
+
+    public static void main(String[] args) {
+        SpringApplication.run(MjavaQiwangApplication.class, args);
+    }
+
+}

+ 19 - 0
mjava-qiwang/src/main/java/com/malk/qiwang/Service/ICompanyTitleService.java

@@ -0,0 +1,19 @@
+package com.malk.qiwang.Service;
+
+import com.malk.qiwang.entity.CompanyTitle;
+import com.baomidou.mybatisplus.extension.service.IService;
+
+import java.util.Map;
+
+/**
+ * <p>
+ * 公司抬头库表 服务类
+ * </p>
+ *
+ * @author LQY
+ * @since 2026-04-24
+ */
+public interface ICompanyTitleService extends IService<CompanyTitle> {
+
+    void insertCompanyTitle(Map map);
+}

+ 36 - 0
mjava-qiwang/src/main/java/com/malk/qiwang/Service/IInvoiceLibraryService.java

@@ -0,0 +1,36 @@
+package com.malk.qiwang.Service;
+
+import com.malk.qiwang.entity.InvoiceLibrary;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.malk.server.common.McR;
+
+import java.util.Map;
+
+/**
+ * <p>
+ * 发票库表 服务类
+ * </p>
+ *
+ * @author LQY
+ * @since 2026-04-27
+ */
+public interface IInvoiceLibraryService extends IService<InvoiceLibrary> {
+
+    McR invoiceLibrary(Map map);
+
+    void updateOaStatusByOaId(Map map);
+
+    McR invoiceLibrarys(Map map);
+
+    McR deleteAllByOaId(String oaId);
+
+    McR invoiceLibrary1(Map map);
+
+    McR invoiceLibrary2(Map map);
+
+    McR invoiceLibrary3(Map map);
+
+    McR invoiceLibrary5(Map map);
+
+    McR invoiceLibrary6(Map map);
+}

+ 7 - 0
mjava-qiwang/src/main/java/com/malk/qiwang/Service/McProjectService.java

@@ -0,0 +1,7 @@
+package com.malk.qiwang.Service;
+
+public interface McProjectService {
+
+    void init();
+
+}

+ 9 - 0
mjava-qiwang/src/main/java/com/malk/qiwang/Service/QiWangService.java

@@ -0,0 +1,9 @@
+package com.malk.qiwang.Service;
+
+import java.util.Map;
+
+public interface QiWangService {
+
+
+    void invoiceLibrary(Map map);
+}

+ 39 - 0
mjava-qiwang/src/main/java/com/malk/qiwang/Service/TXYInvoice.java

@@ -0,0 +1,39 @@
+package com.malk.qiwang.Service;
+
+import com.tencentcloudapi.common.exception.TencentCloudSDKException;
+
+import java.util.Map;
+
+public interface TXYInvoice {
+
+    /**
+     * 混贴票据识别 [将弃用, 直接返回识别数据, ppExt: 官方已停止更新, PDF多张异常]
+     *
+     * @param image 下载图片经Base64编码后不超过 7M。图片下载时间不超过 3 秒
+     */
+    @Deprecated
+    Map doMixedInvoiceOCR(String image) throws TencentCloudSDKException;
+
+    /**
+     * 通用票据识别(高级版)[返回结构化数据] ppExt: 新版本官方返回数据已结构化, 以官方为准
+     *
+     * @param image 下载图片经Base64编码后不超过 8M。图片下载时间不超过 3 秒
+     */
+    Map doRecognizeGeneralInvoice(String image) throws TencentCloudSDKException;
+
+    /**
+     * 发票验真[新版]
+     *
+     * @param invoiceKind         ppExt: 全电票, 新版本识别接口, 返回名称为: 电子发票(普通发票) 不包含全电标识, 发类型为: 全电发票. 注意取值
+     * @param invoiceCode         票代码(10或12 位),全电发票为空
+     * @param checkCode           校验码后 6 位,增值税普通发票、增值税电子普通发票、增值税普通发票(卷式)、增值税电子普通发票(通行费)时必填;
+     * @param excludingTax/amount 不含税金额,增值税专用发票、增值税电子专用发票、机动车销售统一发票、二手车销售统一发票、区块链发票时必填; 全电发票为价税合计(含税金额)
+     */
+    Map doVatInvoiceVerifyNew(String invoiceKind, String invoiceCode, String invoiceNo, String invoiceDate, String amount, String checkCode, String excludingTax, String tips) throws TencentCloudSDKException;
+
+    /**
+     * 名片识别
+     */
+    Map doBusinessCardOCR(String image) throws TencentCloudSDKException;
+
+}

+ 151 - 0
mjava-qiwang/src/main/java/com/malk/qiwang/Service/impl/CompanyTitleServiceImpl.java

@@ -0,0 +1,151 @@
+package com.malk.qiwang.Service.impl;
+
+import com.malk.qiwang.Service.TXYInvoice;
+import com.malk.qiwang.entity.CompanyTitle;
+import com.malk.qiwang.mapper.CompanyTitleMapper;
+import com.malk.qiwang.Service.ICompanyTitleService;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.malk.server.aliwork.YDConf;
+import com.malk.server.aliwork.YDParam;
+import com.malk.server.dingtalk.DDR_New;
+import com.malk.service.aliwork.YDClient;
+import com.malk.service.dingtalk.DDClient;
+import com.malk.service.dingtalk.DDClient_Contacts;
+import com.malk.service.dingtalk.DDClient_Workflow;
+import com.malk.utils.UtilMap;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * <p>
+ * 公司抬头库表 服务实现类
+ * </p>
+ *
+ * @author LQY
+ * @since 2026-04-24
+ */
+@Service
+@Slf4j
+public class CompanyTitleServiceImpl extends ServiceImpl<CompanyTitleMapper, CompanyTitle> implements ICompanyTitleService {
+    @Autowired
+    private DDClient ddClient;
+    @Autowired
+    private YDClient ydClient;
+    @Autowired
+    private TXYInvoice txyInvoice;
+    @Value("${dingtalk.operator}")
+    private String operator;
+    @Value("${dingtalk.downloadPath}")
+    private String downloadPath;
+    @Autowired
+    private DDClient_Workflow ddClientWorkflow ;
+    @Autowired
+    private DDClient_Contacts ddClient_contacts;
+    private static final String url = "http://20f2fcef.r39.cpolar.top/qiwang/";
+    @Autowired
+    private CompanyTitleMapper companyTitleMapper;  // 或者使用 Mapper
+    @Override
+    public void insertCompanyTitle(Map map) {
+        log.info("map:{}", map);
+        String processInstanceId = UtilMap.getString(map, "processInstanceId");
+        String action = UtilMap.getString(map, "action");
+        String nsh = UtilMap.getString(map, "nsh");
+        // 删除操作
+        if ("del".equals(action)) {
+            // 根据税号删除
+            if (nsh != null && !nsh.isEmpty()) {
+                int result = companyTitleMapper.deleteByTaxId(nsh);
+                if (result > 0) {
+                    log.info("公司抬头删除成功: 税号={}", nsh);
+                } else {
+                    log.warn("公司抬头删除失败: 税号={} 不存在", nsh);
+                }
+            } else {
+                log.warn("删除操作失败: 税号为空");
+            }
+            return; // 删除操作完成后直接返回
+        }
+
+        // 新增操作(原有逻辑)
+        Map processInstance = ddClientWorkflow.getProcessInstanceId(ddClient.getAccessToken(), processInstanceId);
+
+        if (Objects.nonNull(processInstance)) {
+            List<Map> formComponentValues = (List<Map>) processInstance.get("formComponentValues");
+            System.out.println("formComponentValues====" + formComponentValues);
+
+            String tt = "", sh = "", dz = "", dh = "", yh = "", zh = "", zt = "";
+
+            for (Map formComponentValue : formComponentValues) {
+                String id = String.valueOf(formComponentValue.get("id"));
+                String value = formComponentValue.get("value") != null ?
+                        String.valueOf(formComponentValue.get("value")) : "";
+
+                switch (id) {
+                    case "TextField-K2AD4O5B": tt = value; break;
+                    case "TextField_1JEUFYV5F4E80": sh = value; break;
+                    case "TextField_EJZY527VJXC0": dz = value; break;
+                    case "TextField_12YU12T3PZC0": dh = value; break;
+                    case "TextField_1Z4P9MQFOW3K0": yh = value; break;
+                    case "TextField_NUL1SS1PJQ80": zh = value; break;
+                    case "DDSelectField_528A36XIP9C0": zt = value; break;
+                    default: break;
+                }
+            }
+
+            // 检查税号是否已存在(避免重复插入)
+            CompanyTitle existing = companyTitleMapper.selectByTaxId(sh);
+            if (existing != null) {
+                log.warn("公司抬头已存在: 税号={}, 执行更新操作", sh);
+                // 更新操作
+                existing.setCompanyName(tt);
+                existing.setAddress(dz);
+                existing.setPhone(dh);
+                existing.setBankName(yh);
+                existing.setBankAccount(zh);
+                if ("启用".equals(zt)) {
+                    existing.setStatus((byte) 0);
+                } else if ("停用".equals(zt)) {
+                    existing.setStatus((byte) 1);
+                } else {
+                    existing.setStatus((byte) 0);
+                }
+                existing.setUpdatedAt(LocalDateTime.now());
+                companyTitleMapper.updateById(existing);
+                log.info("公司抬头更新成功: companyName={}, taxId={}", tt, sh);
+            } else {
+                // 新增操作
+                CompanyTitle companyTitle = new CompanyTitle();
+                companyTitle.setCompanyName(tt);
+                companyTitle.setTaxId(sh);
+                companyTitle.setAddress(dz);
+                companyTitle.setPhone(dh);
+                companyTitle.setBankName(yh);
+                companyTitle.setBankAccount(zh);
+
+                // 处理状态
+                if ("启用".equals(zt)) {
+                    companyTitle.setStatus((byte) 0);
+                } else if ("停用".equals(zt)) {
+                    companyTitle.setStatus((byte) 1);
+                } else {
+                    companyTitle.setStatus((byte) 0);
+                }
+
+                companyTitle.setCreatedAt(LocalDateTime.now());
+                companyTitle.setUpdatedAt(LocalDateTime.now());
+
+                // 保存
+                companyTitleMapper.insert(companyTitle);
+                log.info("公司抬头新增成功: companyName={}, taxId={}", tt, sh);
+            }
+        }
+    }
+}

Разлика између датотеке није приказан због своје велике величине
+ 2652 - 0
mjava-qiwang/src/main/java/com/malk/qiwang/Service/impl/InvoiceLibraryServiceImpl.java


+ 29 - 0
mjava-qiwang/src/main/java/com/malk/qiwang/Service/impl/McProjectServiceImpl.java

@@ -0,0 +1,29 @@
+package com.malk.qiwang.Service.impl;
+
+import com.malk.core.McProject;
+
+import com.malk.qiwang.Service.McProjectService;
+import com.malk.service.aliwork.YDClient;
+import lombok.extern.slf4j.Slf4j;
+import org.slf4j.MDC;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Slf4j
+@Service
+public class McProjectServiceImpl implements McProjectService {
+
+    @Autowired
+    private YDClient ydClient;
+
+    @Override
+    public void init() {
+        MDC.put("MDC_KEY_PID","1002");
+        McProject.addDd("1003",new String[] {"dinglh2civttmfufkrha","5Zwobht8IX48Q-y9a3E21cY1oswzCycmgxVFTzNZ8IP62SdItL6EzhmFbCik7wH_"});//卡倍亿一期
+//        McProject.addYida("1003",new String[] {"APP_CRSI2VXGJE74J60OEFPD","C1A66XB1JOAMVXWCE3H28B05EU7T2F3AM4XXLN3"});//特充
+//        McProject.addYida("1009",new String[] {"APP_S5U4NUUK2T4RF8UR17EN","JL666BD1QGXNTZQ5DXEI97Q1USK92I1G6160MTCF"});//卡倍亿二期-分层审核
+//        McProject.addYida("1019",new String[] {"APP_UT77LJBEO7ZH3N5MX1TJ","ND966TC1C69P0KL9EE47C6WBVCBH29IMLRZ1M6E"});//卡倍亿二期-产量分析
+//        McProject.addYida("1020",new String[] {"APP_TROTJSBUI5IMEJYKXJU1","VF666N91369PUM829XYCL8LD9A433TH9GSZ1M8A"});//卡倍亿二期-OCR识别
+        log.info("项目加载完毕:1002");
+    }
+}

+ 17 - 0
mjava-qiwang/src/main/java/com/malk/qiwang/Service/impl/QiWangServiceImpl.java

@@ -0,0 +1,17 @@
+package com.malk.qiwang.Service.impl;
+
+import com.malk.qiwang.Service.QiWangService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.util.Map;
+
+@Slf4j
+@Service
+public class QiWangServiceImpl implements QiWangService {
+
+    @Override
+    public void invoiceLibrary(Map map) {
+
+    }
+}

+ 165 - 0
mjava-qiwang/src/main/java/com/malk/qiwang/Service/impl/TXYImplInvoice.java

@@ -0,0 +1,165 @@
+package com.malk.qiwang.Service.impl;
+
+import com.alibaba.fastjson.JSON;
+import com.malk.qiwang.Service.TXYInvoice;
+import com.malk.qiwang.tencent.TXYConf;
+import com.malk.server.common.McException;
+import com.malk.utils.UtilMap;
+import com.malk.utils.UtilNumber;
+import com.tencentcloudapi.ckafka.v20190819.models.DescribeInstancesRequest;
+import com.tencentcloudapi.common.Credential;
+import com.tencentcloudapi.common.exception.TencentCloudSDKException;
+import com.tencentcloudapi.common.profile.ClientProfile;
+import com.tencentcloudapi.common.profile.HttpProfile;
+import com.tencentcloudapi.cvm.v20170312.CvmClient;
+import com.tencentcloudapi.cvm.v20170312.models.DescribeInstancesResponse;
+import com.tencentcloudapi.ocr.v20181119.OcrClient;
+import com.tencentcloudapi.ocr.v20181119.models.*;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.Map;
+
+@Slf4j
+@Service
+public class TXYImplInvoice implements TXYInvoice {
+
+    @Autowired
+    private TXYConf txyConf;
+
+    // 创建请求描述
+    private ClientProfile doRequest(String endPoint) {
+
+        HttpProfile httpProfile = new HttpProfile();
+        httpProfile.setEndpoint(endPoint);
+        ClientProfile clientProfile = new ClientProfile();
+        clientProfile.setHttpProfile(httpProfile);
+        return clientProfile;
+    }
+
+    /**
+     * 混贴票据识别
+     *
+     * @apiNote https://cloud.tencent.com/document/product/866/37835
+     */
+    @Override
+    public Map doMixedInvoiceOCR(String image) throws TencentCloudSDKException {
+
+        Credential cred = new Credential(txyConf.getSecretId(), txyConf.getSecretKey());
+        ClientProfile clientProfile = doRequest("ocr.tencentcloudapi.com");
+        OcrClient client = new OcrClient(cred, txyConf.getRegion(), clientProfile);
+
+        MixedInvoiceOCRRequest req = new MixedInvoiceOCRRequest();
+        if (image.startsWith("http")) {
+            req.setImageUrl(image);
+        } else {
+            req.setImageBase64(image);
+        }
+        req.setReturnMultiplePage(true); // PDF多页识别, 仅支持返回文件前30页
+        //log.debug("请求参数, {}", JSON.toJSONString(req));
+        MixedInvoiceOCRResponse resp = client.MixedInvoiceOCR(req);
+        String result = MixedInvoiceOCRResponse.toJsonString(resp);
+        log.debug("请求响应, {}", result);
+        return (Map) JSON.parse(result);
+    }
+
+    /**
+     * 通用票据识别(高级版)
+     *
+     * @apiNote https://cloud.tencent.com/document/api/866/90802
+     */
+    @Override
+    public Map doRecognizeGeneralInvoice(String image) throws TencentCloudSDKException {
+        Credential cred = new Credential(txyConf.getSecretId(), txyConf.getSecretKey());
+        ClientProfile clientProfile = doRequest("ocr.tencentcloudapi.com");
+        OcrClient client = new OcrClient(cred, txyConf.getRegion(), clientProfile);
+
+        RecognizeGeneralInvoiceRequest req = new RecognizeGeneralInvoiceRequest();
+        if (image.startsWith("http")) {
+            req.setImageUrl(image);
+        } else {
+            req.setImageBase64(image);
+        }
+        req.setEnableMultiplePage(true); // PDF多页识别, 仅支持返回文件前30页
+        req.setEnableCutImage(false); // 返回切割图片base64
+        RecognizeGeneralInvoiceResponse resp = client.RecognizeGeneralInvoice(req);
+        String result = MixedInvoiceOCRResponse.toJsonString(resp);
+        log.debug("请求响应, {}", result);
+        return (Map) JSON.parse(result);
+    }
+
+    /**
+     * 发票验真[新版]
+     *
+     * @apiNote https://cloud.tencent.com/document/product/866/73674
+     */
+    @Override
+    public Map doVatInvoiceVerifyNew(String invoiceKind, String invoiceCode, String invoiceNo, String invoiceDate, String amount, String checkCode, String excludingTax, String tips) throws TencentCloudSDKException {
+        Credential cred = new Credential(txyConf.getSecretId(), txyConf.getSecretKey());
+        ClientProfile clientProfile = doRequest("ocr.tencentcloudapi.com");
+        OcrClient client = new OcrClient(cred, txyConf.getRegion(), clientProfile);
+
+        // 一些特殊发票校验码只有5位, 兼容
+        if (StringUtils.isNotBlank(checkCode) && checkCode.length() > 6) {
+            checkCode = checkCode.substring(checkCode.length() - 6);
+        }
+
+        VatInvoiceVerifyNewRequest req = new VatInvoiceVerifyNewRequest();
+        req.setInvoiceNo(invoiceNo);
+        req.setInvoiceDate(invoiceDate);
+        req.setInvoiceCode(invoiceCode);
+        req.setCheckCode(checkCode);
+        // ppExt: 全电票, 新版本识别接口, 返回名称为: 电子发票(普通发票) 不包含全电标识, 发类型为: 全电发票. 注意取值
+        if (invoiceKind.contains("全电")) {
+            // 全电票, 需要价税合计且无发票代码与校验码
+            req.setCheckCode(null);
+            req.setInvoiceCode(null);
+            req.setAmount(amount);
+        } else {
+            req.setAmount(null);
+            req.setAmount(excludingTax);
+        }
+        log.debug("发票验真, {}, {}", invoiceKind, JSON.toJSONString(req));
+        VatInvoiceVerifyNewResponse resp = client.VatInvoiceVerifyNew(req);
+        String result = VatInvoiceVerifyNewResponse.toJsonString(resp);
+        Map rsp = (Map) JSON.parse(result);
+
+        // 因全电票取值, 取值价税合计, 单独校验下金额
+        Map invoice = (Map) rsp.get("Invoice");
+        if (StringUtils.isBlank(tips)) {
+            tips = "发票有疑问";
+        }
+        log.debug("请求响应, {}", result);
+        McException.assertAccessException(!UtilNumber.equalBigDecimal(UtilMap.getString(invoice, "AmountWithTax"), amount), tips + ", 价税合计金额不匹配!");
+        // ppExt: 增值税卷票: 票面无税率, 税额. 但接口验证返回或本质上发票是有税率, 税额. 因此取消后置判断
+        McException.assertAccessException(!invoiceKind.contains("卷票") && !UtilNumber.equalBigDecimal(UtilMap.getString(invoice, "AmountWithoutTax"), excludingTax), tips + ", 不含税金额不匹配!");
+        return rsp;
+    }
+
+    /**
+     * 名片识别
+     *
+     * @apiNote https://console.cloud.tencent.com/api/explorer?Product=ocr&Version=2018-11-19&Action=BusinessCardOCR
+     */
+    @Override
+    public Map doBusinessCardOCR(String image) throws TencentCloudSDKException {
+        Credential cred = new Credential(txyConf.getSecretId(), txyConf.getSecretKey());
+        ClientProfile clientProfile = doRequest("ocr.tencentcloudapi.com");
+        OcrClient client = new OcrClient(cred, txyConf.getRegion(), clientProfile);
+
+        BusinessCardOCRRequest req = new BusinessCardOCRRequest();
+        if (image.startsWith("http")) {
+            req.setImageUrl(image);
+        } else {
+            req.setImageBase64(image);
+        }
+        req.setConfig(JSON.toJSONString(UtilMap.map("RetImageType", "PROPROCESS")));
+        //log.debug("请求参数, {}", JSON.toJSONString(req));
+        BusinessCardOCRResponse resp = client.BusinessCardOCR(req);
+        String result = BusinessCardOCRResponse.toJsonString(resp);
+        log.debug("请求响应, {}", result);
+        return (Map) JSON.parse(result);
+    }
+}

+ 19 - 0
mjava-qiwang/src/main/java/com/malk/qiwang/config/StartupRunner.java

@@ -0,0 +1,19 @@
+package com.malk.qiwang.config;
+
+
+import com.malk.qiwang.Service.McProjectService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.ApplicationArguments;
+import org.springframework.boot.ApplicationRunner;
+import org.springframework.stereotype.Component;
+
+@Component
+public class StartupRunner implements ApplicationRunner {
+
+    @Autowired
+    private McProjectService mcProjectService;
+    @Override
+    public void run(ApplicationArguments args) throws Exception {
+        mcProjectService.init();
+    }
+}

+ 76 - 0
mjava-qiwang/src/main/java/com/malk/qiwang/entity/CompanyTitle.java

@@ -0,0 +1,76 @@
+package com.malk.qiwang.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import java.io.Serializable;
+import java.time.LocalDateTime;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * <p>
+ * 公司抬头库表
+ * </p>
+ *
+ * @author LQY
+ * @since 2026-04-24
+ */
+@Getter
+@Setter
+  @TableName("company_title")
+public class CompanyTitle implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+      /**
+     * 主键ID,自增
+     */
+        @TableId(value = "id", type = IdType.AUTO)
+      private Integer id;
+
+      /**
+     * 公司抬头/名称
+     */
+      private String companyName;
+
+      /**
+     * 税号/统一社会信用代码
+     */
+      private String taxId;
+
+      /**
+     * 注册地址
+     */
+      private String address;
+
+      /**
+     * 注册电话
+     */
+      private String phone;
+
+      /**
+     * 开户银行
+     */
+      private String bankName;
+
+      /**
+     * 银行账号
+     */
+      private String bankAccount;
+
+      /**
+     * 抬头是否启用: 0: 启用, 1: 停用
+     */
+      private Byte status;
+
+      /**
+     * 创建时间
+     */
+      private LocalDateTime createdAt;
+
+      /**
+     * 更新时间
+     */
+      private LocalDateTime updatedAt;
+}

+ 128 - 0
mjava-qiwang/src/main/java/com/malk/qiwang/entity/InvoiceLibrary.java

@@ -0,0 +1,128 @@
+package com.malk.qiwang.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * <p>
+ * 发票库表
+ * </p>
+ *
+ * @author LQY
+ * @since 2026-04-27
+ */
+@Getter
+@Setter
+@TableName("invoice_library")
+public class InvoiceLibrary implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 自增主键
+     */
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    /**
+     * OA流水号
+     */
+    private String oaId;
+
+    /**
+     * 发票代码
+     */
+    private String invoiceCode;
+
+    /**
+     * 发票号码
+     */
+    private String invoiceNumber;
+
+    /**
+     * 发票类型
+     */
+    private String invoiceType;
+
+    /**
+     * 开票日期
+     */
+    private LocalDate invoiceDate;
+
+    /**
+     * 不含税金额
+     */
+    private BigDecimal amount;
+
+    /**
+     * 税额
+     */
+    private BigDecimal taxAmount;
+
+    /**
+     * 含税金额
+     */
+    private BigDecimal totalAmount;
+
+    /**
+     * 购买方名称
+     */
+    private String buyerName;
+
+    /**
+     * 购买方税号
+     */
+    private String buyerTaxId;
+
+    /**
+     * 销售方名称
+     */
+    private String sellerName;
+
+    /**
+     * 销售方税号
+     */
+    private String sellerTaxId;
+
+    /**
+     * 状态: 0: 流程中 1: 已通过
+     */
+    private String oaStatus;
+
+    /**
+     * 是否已经生成凭证 0: 否 1: 是
+     */
+    private String invoiceStatus;
+
+    /**
+     * 创建时间
+     */
+    private LocalDateTime createdAt;
+
+    /**
+     * 更新时间
+     */
+    private LocalDateTime updatedAt;
+
+    /**
+     * 科目
+     */
+    private String accountTitle;
+
+    /**
+     * 部门
+     */
+    private String dep;
+
+    /**
+     * 报销金额
+     */
+    private BigDecimal payAmount;
+}

+ 46 - 0
mjava-qiwang/src/main/java/com/malk/qiwang/mapper/CompanyTitleMapper.java

@@ -0,0 +1,46 @@
+package com.malk.qiwang.mapper;
+
+import com.malk.qiwang.entity.CompanyTitle;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Delete;
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Select;
+import org.apache.ibatis.annotations.Update;
+
+/**
+ * <p>
+ * 公司抬头库表 Mapper 接口
+ * </p>
+ *
+ * @author LQY
+ * @since 2026-04-24
+ */
+public interface CompanyTitleMapper extends BaseMapper<CompanyTitle> {
+    /**
+     * 根据税号删除
+     *
+     * @param taxId 税号
+     * @return 删除条数
+     */
+    @Delete("DELETE FROM company_title WHERE tax_id = #{taxId}")
+    int deleteByTaxId(@Param("taxId") String taxId);
+
+    /**
+     * 根据税号查询
+     *
+     * @param taxId 税号
+     * @return 公司抬头信息
+     */
+//    @Select("SELECT * FROM company_title WHERE tax_id = #{taxId}")
+    @Select("SELECT * FROM company_title WHERE company_name = #{companyName}")
+    CompanyTitle selectByTaxId(@Param("companyName") String companyName);
+
+    /**
+     * 软删除(根据税号更新删除标记)
+     *
+     * @param taxId 税号
+     * @return 更新条数
+     */
+    @Update("UPDATE company_title SET deleted_at = NOW() WHERE tax_id = #{taxId}")
+    int softDeleteByTaxId(@Param("taxId") String taxId);
+}

+ 26 - 0
mjava-qiwang/src/main/java/com/malk/qiwang/mapper/InvoiceLibraryMapper.java

@@ -0,0 +1,26 @@
+package com.malk.qiwang.mapper;
+
+import com.malk.qiwang.entity.CompanyTitle;
+import com.malk.qiwang.entity.InvoiceLibrary;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Select;
+
+/**
+ * <p>
+ * 发票库表 Mapper 接口
+ * </p>
+ *
+ * @author LQY
+ * @since 2026-04-27
+ */
+public interface InvoiceLibraryMapper extends BaseMapper<InvoiceLibrary> {
+
+      /**
+     * 根据发票号查询发票记录
+     * @param invoiceNumber 发票号码
+     * @return 发票记录
+     */
+    @Select("SELECT * FROM invoice_library WHERE invoice_number = #{invoiceNumber} LIMIT 1")
+    InvoiceLibrary selectByInvoiceNumber(@Param("invoiceNumber") String invoiceNumber);
+}

+ 5 - 0
mjava-qiwang/src/main/java/com/malk/qiwang/mapper/xml/CompanyTitleMapper.xml

@@ -0,0 +1,5 @@
+<?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.malk.qiwang.mapper.CompanyTitleMapper">
+
+</mapper>

+ 5 - 0
mjava-qiwang/src/main/java/com/malk/qiwang/mapper/xml/InvoiceLibraryMapper.xml

@@ -0,0 +1,5 @@
+<?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.malk.qiwang.mapper.InvoiceLibraryMapper">
+
+</mapper>

+ 220 - 0
mjava-qiwang/src/main/java/com/malk/qiwang/model/McInvoiceDto.java

@@ -0,0 +1,220 @@
+package com.malk.qiwang.model;
+
+import cn.hutool.core.util.ObjectUtil;
+import com.malk.base.BaseDto;
+import com.malk.server.common.McException;
+import com.malk.server.common.McREnum;
+import com.malk.utils.UtilMap;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.apache.commons.lang3.StringUtils;
+
+import java.math.BigDecimal;
+import java.util.Map;
+
+/**
+ * ppExt: 新版本官方返回数据已结构化, 以官方为准
+ *
+ * @apiNote https://cloud.tencent.com/document/api/866/33527#AirTransport 数据结构
+ */
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+public class McInvoiceDto extends BaseDto {
+
+
+    /**************** 发票 ****************/
+
+    /**
+     * 发票名称 [类型全称]
+     */
+    private String name;
+
+    /**
+     * 发票用途 [供应商、报销]
+     */
+    private String type;
+
+    // 兼容: 不为空
+    public String getName() {
+        if (StringUtils.isBlank(name)) {
+            return kindName;
+        }
+        return name;
+    }
+
+    /**
+     * 发票类型 [类型简称]
+     * -
+     * ppExt: 全电票, 新版本识别接口, 返回名称为: 电子发票(普通发票) 不包含全电标识, 发类型为: 全电发票. 注意取值
+     */
+    private String kindName;
+
+    /**
+     * 发票类型 [编码]
+     */
+    private int kind;
+
+    /**
+     * 发票代码
+     */
+    private String code;
+
+    /**
+     * 发票号码
+     */
+    private String serial;
+
+    /**
+     * 开票日期 [yyyy-MM-dd]
+     */
+    private String date;
+
+    /**
+     * 校验码
+     */
+    private String checkCode;
+
+    /**
+     * 价税合计
+     */
+    private BigDecimal amount;
+
+    /**
+     * 不含税金额
+     */
+    private BigDecimal excludingTax;
+
+    /// 没有税额时, 税额可能没传递 [因此前提是不含税为空],不含税金额等于价税合计
+    public BigDecimal getExcludingTax() {
+        if (ObjectUtil.isNull((excludingTax)) && (ObjectUtil.isNull(tax) || BigDecimal.ZERO.compareTo(tax) == 0)) {
+            return amount;
+        }
+        return excludingTax;
+    }
+
+    /**
+     * 不含税金额
+     */
+    private BigDecimal tax;
+
+    /// 取值为null, 合计会异常
+    public BigDecimal getTax() {
+        if (ObjectUtil.isNull(tax)) {
+            return BigDecimal.ZERO;
+        }
+        return tax;
+    }
+
+    /**
+     * 购买方名称
+     */
+    private String buyerName;
+
+    /**
+     * 购买方税号
+     */
+    private String buyerTaxId;
+
+    /**
+     * 销售方名称
+     */
+    private String sellerName;
+
+    /**
+     * 销售方税号
+     */
+    private String sellerTaxId;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+    /**************** 交通 ****************/
+
+    /**
+     * 乘客姓名
+     */
+    private String passengerName;
+
+    /**
+     * 座位类型
+     */
+    private String seatType;
+
+    /**
+     * 出发地
+     */
+    private String departurePort;
+
+    /**
+     * 到达地
+     */
+    private String arrivePort;
+
+    /**
+     * 出发时间 [yyyy-MM-dd HH:mm]
+     */
+    private String departureTime;
+
+    /**
+     * 车次编号/航班号
+     */
+    private String trainNo;
+
+    /**
+     * 保险费
+     */
+    private BigDecimal insuranceCosts;
+
+    /**
+     * 燃油附加费
+     */
+    private BigDecimal fuelCosts;
+
+    /**
+     * 民航发展基金
+     */
+    private BigDecimal constructionCosts;
+
+    /**************** 格式化 ****************/
+
+    /**
+     * 实例Map, 服务宜搭组件映射 [明细组件获取表头, 通过名称转换字段]
+     *
+     * @implSpec const headers = this.$("tableField_liv5f4d2").props.children[0].props.children.map(({ props: { label, fieldId } }) => ({ label, compId: fieldId }))
+     */
+    public static Map formatDtoLabelAndProp() {
+
+        Map data = UtilMap.map("发票名称, 发票类型, 发票代码, 发票号码, 开票日期, 校验码, 价税合计, 不含税金额, 税额", "name, kindName, code, serial, date, checkCode, amount, excludingTax, tax");
+        data.putAll(UtilMap.map("购买方名称, 购买方税号, 销售方名称, 销售方税号", "buyerName, buyerTaxId, sellerName, sellerTaxId"));
+        data.putAll(UtilMap.map("乘客姓名, 座位类型, 出发地, 到达地, 出发时间, 车次编号/航班号, 保险费, 燃油附加费, 民航发展基金", "passengerName, seatType, departurePort, arrivePort, departureTime, trainNo, insuranceCosts, fuelCosts, constructionCosts"));
+        return data;
+    }
+
+    /**
+     * 格式化返回
+     */
+    public static Map formatResponse(Object result) {
+//        return UtilMap.map("result, dto", result, formatDtoLabelAndProp());
+        return UtilMap.map("result", result);
+    }
+
+    /**************** 返回值 ****************/
+
+    // 成功状态标记
+    private final static String SUC_CODE = "OK";
+
+    /**
+     * 断言错误信息
+     */
+    public static void assertSuccess(Map result, String kind) {
+        String code = UtilMap.getString(result, "Code");
+        McException.assertException(!SUC_CODE.equals(code), McREnum.VENDOR_ERROR.getCode(), kind, "tencent");
+    }
+}
+

+ 68 - 0
mjava-qiwang/src/main/java/com/malk/qiwang/model/McInvoiceKind.java

@@ -0,0 +1,68 @@
+package com.malk.qiwang.model;
+
+import lombok.Getter;
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.Arrays;
+import java.util.Optional;
+
+/**
+ * 定义返回值和对应状态的信息 [旧版本]
+ * ppExt: 新版本官方返回数据已结构化, 以官方为准
+ */
+@Deprecated
+@Slf4j
+public enum McInvoiceKind {
+
+    PP("增值税普通发票", 1),
+    ZP("增值税专用发票", 2),
+    DP("增值税电子普通发票", 3),
+    DZ("增值税电子专用发票", 4),
+    QP("全电普通发票", 5),
+    QZ("全电专用发票", 6),
+
+    HC("火车票", 11),
+    HCDZ("火车票", 16),
+    JP("机票行程单", 12),
+    CZC("出租车发票", 13),
+    DE("定额发票", 14),
+    GLGQ("过路过桥费发票", 15),
+
+    FS("非税发票", 21),
+    OT("其它特殊发票", 22),
+
+    UN("未知类型", -1);
+
+    @Getter
+    private String desc;
+    @Getter
+    private int code;
+
+    /**
+     * 根据code查找
+     */
+    public final static String getKindName(int code) {
+        Optional optional = Arrays.stream(McInvoiceKind.values()).filter(item -> item.code == code).findAny();
+        if (optional.isPresent()) {
+            return ((McInvoiceKind) optional.get()).desc;
+        }
+        return UN.getDesc();
+    }
+
+    /**
+     * 根据name查找
+     */
+    public final static int getKindCode(String name) {
+        Optional optional = Arrays.stream(McInvoiceKind.values()).filter(item -> item.desc.equals(name)).findAny();
+        //log.info("发票类型, {}", Arrays.stream(McInvoiceKind.values()).map(item -> item.desc).collect(Collectors.toList()));
+        if (optional.isPresent()) {
+            return ((McInvoiceKind) optional.get()).code;
+        }
+        return UN.getCode();
+    }
+
+    McInvoiceKind(String name, int code) {
+        this.desc = name;
+        this.code = code;
+    }
+}

+ 40 - 0
mjava-qiwang/src/main/java/com/malk/qiwang/tencent/TXYConf.java

@@ -0,0 +1,40 @@
+package com.malk.qiwang.tencent;
+
+import com.malk.utils.UtilMap;
+import lombok.Data;
+import org.springframework.stereotype.Component;
+
+import java.util.Map;
+
+/**
+ * 读取配置文件参考FilePah
+ */
+@Data
+@Component
+//@ConfigurationProperties(prefix = "tencent")
+public class TXYConf {
+
+//    private String APPID="1412895585";
+    private String APPID="1425374127";
+
+//    private String SecretId="AKIDDF3cw3wb3QZUBeuxnTwBCzYu1tWv624t";
+
+//    private String SecretKey="d1CAvRkyxhCbliRwEIB2yugSRNHGSdeb";
+
+    private String SecretId="AKIDZ9WjjU2NUNYAaw87tRmsmUzHLE6Leqsh";
+
+    private String SecretKey="f8NQ8h2CAvTnuTowqX2JNEsKYZXIp9F8";
+
+    private String Region="ap-guangzhou";
+
+    /**
+     * 票据类型
+     * -
+     * 中央非税未返回税号官方说明: 非税发票理论是没有税号的,图片中那个属于信用代码 [两个版本接口均为返回]
+     * ppExt: 新版本官方返回数据已结构化, 以官方为准
+     */
+    @Deprecated
+    public static final Map<String, String> TYPE_INVOICE = UtilMap.map("-1, 0, 1, 2, 3, 5, 8, 9, 10, 11, 12, 13, 15, 16",
+            "未知类型, 出租车发票, 定额发票, 火车票, 增值税发票, 机票行程单, 通用机打发票, 汽车票, 轮船票, 增值税发票(卷票) , 购车发票, 过路过桥费发票, 非税发票, 全电发票");
+
+}

Разлика између датотеке није приказан због своје велике величине
+ 113 - 0
mjava-qiwang/src/main/java/com/malk/qiwang/tencent/TXYConfNh.java


+ 267 - 0
mjava-qiwang/src/main/java/com/malk/qiwang/utils/HTTPHelper.java

@@ -0,0 +1,267 @@
+package com.malk.qiwang.utils;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.*;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.UnknownHostException;
+import java.text.MessageFormat;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map.Entry;
+
+public class HTTPHelper {
+    // slf4j日志记录器
+    private static final Logger LOG = LoggerFactory.getLogger(HTTPHelper.class);
+
+    /***
+     * 向指定URL发送GET方法的请求
+     *
+     * @param apiUrl
+     * @param data
+     * @param projectId
+     * @param signature
+     * @param encoding
+     * @return
+     * @throws Exception
+     */
+    public static String sendGet(String apiUrl, HashMap<String, Object> param,
+                                 LinkedHashMap<String, String> headers, String encoding) throws Exception {
+        // 获得响应内容
+        String http_RespContent = null;
+        HttpURLConnection httpURLConnection = null;
+        int http_StatusCode = 0;
+        String http_RespMessage = null;
+        try {
+            // 实际请求完整Url
+            StringBuffer fullUrl = new StringBuffer();
+            if (null != param) {
+                if (0 != param.size()) {
+                    StringBuffer params = new StringBuffer();
+                    for (Entry<String, Object> entry : param.entrySet()) {
+                        params.append(entry.getKey());
+                        params.append("=");
+                        params.append(entry.getValue().toString());
+                        params.append("&");
+                    }
+                    if (params.length() > 0) {
+                        params.deleteCharAt(params.lastIndexOf("&"));
+                    }
+                    fullUrl.append(apiUrl).append((params.length() > 0) ? "?" + params.toString() : "");
+                } else {
+                    fullUrl.append(apiUrl);
+                }
+            } else {
+                fullUrl.append(apiUrl);
+            }
+
+            LOG.info(">>>> 实际请求Url: " + fullUrl.toString());
+
+            // 建立连接
+            URL url = new URL(fullUrl.toString());
+            httpURLConnection = (HttpURLConnection) url.openConnection();
+            // 需要输出
+            httpURLConnection.setDoOutput(true);
+            // 需要输入
+            httpURLConnection.setDoInput(true);
+            // 不允许缓存
+            httpURLConnection.setUseCaches(false);
+            // HTTP请求方式
+            httpURLConnection.setRequestMethod("GET");
+            // 设置Headers
+            if (null != headers) {
+                for (String key : headers.keySet()) {
+                    httpURLConnection.setRequestProperty(key, headers.get(key));
+                }
+            }
+            // 连接会话
+            httpURLConnection.connect();
+            // 获得响应状态(HTTP状态码)
+            http_StatusCode = httpURLConnection.getResponseCode();
+            // 获得响应消息(HTTP状态码描述)
+            http_RespMessage = httpURLConnection.getResponseMessage();
+            // 获得响应内容
+            if (HttpURLConnection.HTTP_OK == http_StatusCode) {
+                // 返回响应结果
+                http_RespContent = getResponseContent(httpURLConnection);
+            } else {
+                // 返回非200状态时响应结果
+                http_RespContent = getErrorResponseContent(httpURLConnection);
+                String msg =
+                        MessageFormat.format("请求失败: Http状态码 = {0} , {1}", http_StatusCode, http_RespMessage);
+                LOG.info(msg);
+            }
+        } catch (UnknownHostException e) {
+            String message = MessageFormat.format("网络请求时发生异常: {0}", e.getMessage());
+            Exception ex = new Exception(message);
+            ex.initCause(e);
+            throw ex;
+        } catch (MalformedURLException e) {
+            String message = MessageFormat.format("格式错误的URL: {0}", e.getMessage());
+            Exception ex = new Exception(message);
+            ex.initCause(e);
+            throw ex;
+        } catch (IOException e) {
+            String message = MessageFormat.format("网络请求时发生异常: {0}", e.getMessage());
+            Exception ex = new Exception(message);
+            ex.initCause(e);
+            throw ex;
+        } catch (Exception e) {
+            String message = MessageFormat.format("网络请求时发生异常: {0}", e.getMessage());
+            Exception ex = new Exception(message);
+            ex.initCause(e);
+            throw ex;
+        } finally {
+            if (null != httpURLConnection) {
+                httpURLConnection.disconnect();
+            }
+        }
+        return http_RespContent;
+    }
+
+    /***
+     * 向指定URL发送POST方法的请求
+     *
+     * @param apiUrl
+     * @param data
+     * @param projectId
+     * @param signature
+     * @param encoding
+     * @return
+     * @throws Exception
+     */
+    public static String sendPOST(String apiUrl, String data, LinkedHashMap<String, String> headers,
+                                  String encoding) throws Exception {
+        // 获得响应内容
+        String http_RespContent = null;
+        HttpURLConnection httpURLConnection = null;
+        int http_StatusCode = 0;
+        String http_RespMessage = null;
+        try {
+            // 建立连接
+            URL url = new URL(apiUrl);
+            httpURLConnection = (HttpURLConnection) url.openConnection();
+            // 需要输出
+            httpURLConnection.setDoOutput(true);
+            // 需要输入
+            httpURLConnection.setDoInput(true);
+            // 不允许缓存
+            httpURLConnection.setUseCaches(false);
+            // HTTP请求方式
+            httpURLConnection.setRequestMethod("POST");
+            // 设置Headers
+            if (null != headers) {
+                for (String key : headers.keySet()) {
+                    httpURLConnection.setRequestProperty(key, headers.get(key));
+                }
+            }
+            // 连接会话
+            httpURLConnection.connect();
+            // 建立输入流,向指向的URL传入参数
+            DataOutputStream dos = new DataOutputStream(httpURLConnection.getOutputStream());
+            // 设置请求参数
+            dos.write(data.getBytes(encoding));
+            dos.flush();
+            dos.close();
+            // 获得响应状态(HTTP状态码)
+            http_StatusCode = httpURLConnection.getResponseCode();
+            // 获得响应消息(HTTP状态码描述)
+            http_RespMessage = httpURLConnection.getResponseMessage();
+            // 获得响应内容
+            if (HttpURLConnection.HTTP_OK == http_StatusCode) {
+                // 返回响应结果
+                http_RespContent = getResponseContent(httpURLConnection);
+            } else {
+                // 返回非200状态时响应结果
+                http_RespContent = getErrorResponseContent(httpURLConnection);
+                String msg =
+                        MessageFormat.format("请求失败: Http状态码 = {0} , {1}", http_StatusCode, http_RespMessage);
+                LOG.info(msg);
+            }
+        } catch (UnknownHostException e) {
+            String message = MessageFormat.format("网络请求时发生异常: {0}", e.getMessage());
+            Exception ex = new Exception(message);
+            ex.initCause(e);
+            throw ex;
+        } catch (MalformedURLException e) {
+            String message = MessageFormat.format("格式错误的URL: {0}", e.getMessage());
+            Exception ex = new Exception(message);
+            ex.initCause(e);
+            throw ex;
+        } catch (IOException e) {
+            String message = MessageFormat.format("网络请求时发生异常: {0}", e.getMessage());
+            Exception ex = new Exception(message);
+            ex.initCause(e);
+            throw ex;
+        } catch (Exception e) {
+            String message = MessageFormat.format("网络请求时发生异常: {0}", e.getMessage());
+            Exception ex = new Exception(message);
+            ex.initCause(e);
+            throw ex;
+        } finally {
+            if (null != httpURLConnection) {
+                httpURLConnection.disconnect();
+            }
+        }
+        return http_RespContent;
+    }
+
+    /***
+     * 读取HttpResponse响应内容
+     *
+     * @param httpURLConnection
+     * @return
+     * @throws UnsupportedEncodingException
+     * @throws IOException
+     */
+    private static String getResponseContent(HttpURLConnection httpURLConnection)
+            throws UnsupportedEncodingException, IOException {
+        StringBuffer contentBuffer = null;
+        BufferedReader responseReader = null;
+        try {
+            contentBuffer = new StringBuffer();
+            String readLine = null;
+            responseReader =
+                    new BufferedReader(new InputStreamReader(httpURLConnection.getInputStream(), "UTF-8"));
+            while ((readLine = responseReader.readLine()) != null) {
+                contentBuffer.append(readLine);
+            }
+        } finally {
+            if (null != responseReader) {
+                responseReader.close();
+            }
+        }
+        return contentBuffer.toString();
+    }
+
+    /***
+     * 读取HttpResponse响应内容
+     *
+     * @param httpURLConnection
+     * @return
+     * @throws UnsupportedEncodingException
+     * @throws IOException
+     */
+    private static String getErrorResponseContent(HttpURLConnection httpURLConnection)
+            throws UnsupportedEncodingException, IOException {
+        StringBuffer contentBuffer = null;
+        BufferedReader responseReader = null;
+        try {
+            contentBuffer = new StringBuffer();
+            String readLine = null;
+            responseReader =
+                    new BufferedReader(new InputStreamReader(httpURLConnection.getErrorStream(), "UTF-8"));
+            while ((readLine = responseReader.readLine()) != null) {
+                contentBuffer.append(readLine);
+            }
+        } finally {
+            if (null != responseReader) {
+                responseReader.close();
+            }
+        }
+        return contentBuffer.toString();
+    }
+}

+ 84 - 0
mjava-qiwang/src/main/java/com/malk/qiwang/utils/MPGenerator.java

@@ -0,0 +1,84 @@
+package com.malk.qiwang.utils;
+
+import com.baomidou.mybatisplus.generator.FastAutoGenerator;
+import com.baomidou.mybatisplus.generator.config.rules.DbColumnType;
+import com.baomidou.mybatisplus.generator.engine.VelocityTemplateEngine;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+import java.sql.Types;
+
+public class MPGenerator {
+    public static void main(String[] args) {
+        //可以直接去官方文档上查看
+        FastAutoGenerator
+                .create("jdbc:mysql://localhost:3306/invoice_db?useSSL=false&serverTimezone=Asia/Shanghai",
+                "root", "123456")
+                .globalConfig(builder -> {
+                    builder.author("LQY")
+                            .disableOpenDir()
+                            .outputDir(System.getProperty("user.dir")+
+                                    "/mjava-qiwang/src/main/java");
+                })
+                .dataSourceConfig(builder ->
+                        builder.typeConvertHandler((globalConfig, typeRegistry, metaInfo) -> {
+                            int typeCode = metaInfo.getJdbcType().TYPE_CODE;
+                            if (typeCode == Types.SMALLINT) {
+                                return DbColumnType.INTEGER;
+                            }
+                            return typeRegistry.getColumnType(metaInfo);
+                        }))
+                .packageConfig(builder -> {
+                    builder.parent("com.malk.qiwang")
+                            .controller("Controller")
+                            .service("Service")
+                            .serviceImpl("Service.impl")
+                            .mapper("mapper")
+                            .entity("entity");
+                })
+                .strategyConfig(builder -> {
+                    builder.addInclude("invoice_library")
+                            .entityBuilder().enableLombok();
+                })
+                .templateEngine(new VelocityTemplateEngine())
+                .execute();
+    }
+//    public static void main(String[] args) {
+//        String url = "jdbc:sqlserver://113.45.187.194:1433;"+"databaseName=dingding;"+"encrypt=false;"+"trustServerCertificate=true;";
+//
+//        try (Connection conn = DriverManager.getConnection(url, "hgdd", "hgdd@2025")) {
+//            System.out.println("✅ 连接 SQL Server 成功!");
+//        } catch (Exception e) {
+//            e.printStackTrace();
+//        }
+//    }
+
+//    public static void main(String[] args) {
+//        // MySQL 本地连接配置
+//        String url = "jdbc:mysql://localhost:3306/invoice_db?useSSL=false&characterEncoding=utf8&serverTimezone=Asia/Shanghai";
+//        String username = "root";
+//        String password = "123456";
+//
+//        // 加载 MySQL 驱动(JDBC 4.0 后可以省略,但保留更稳妥)
+//        try {
+//            Class.forName("com.mysql.cj.jdbc.Driver");
+//        } catch (ClassNotFoundException e) {
+//            System.out.println("❌ MySQL JDBC 驱动未找到,请检查依赖");
+//            e.printStackTrace();
+//            return;
+//        }
+//
+//        // 使用 try-with-resources 自动关闭连接
+//        try (Connection conn = DriverManager.getConnection(url, username, password)) {
+//            System.out.println("✅ 连接本地 MySQL 数据库成功!");
+//            System.out.println("数据库: invoice_db");
+//            System.out.println("MySQL 版本: " + conn.getMetaData().getDatabaseProductVersion());
+//        } catch (SQLException e) {
+//            System.out.println("❌ 连接 MySQL 数据库失败!");
+//            System.err.println("错误代码: " + e.getErrorCode());
+//            System.err.println("错误信息: " + e.getMessage());
+//            e.printStackTrace();
+//        }
+//    }
+}

+ 60 - 0
mjava-qiwang/src/main/resources/application-dev.yml

@@ -0,0 +1,60 @@
+
+server:
+  port: 9092
+  servlet:
+    context-path: /qiwang
+
+enable:
+  scheduling: true
+logging:
+  config: classpath:logback-spring.xml
+  path: /home/server/logs/qiwang
+  level:
+    com.malk.*: debug
+
+# dingtalk
+dingtalk:
+  agentId: 4362063085
+  appKey: dinglh2civttmfufkrha
+#  appKey: dingknkn9abneqetgc8a
+#  appSecret: 0JnyzqsxqnVrSwP3SiQvZrMGbk0PyUDcl58o1BS2Bro0lVxUYbi_XZ805IJIHUE8
+  appSecret: 5Zwobht8IX48Q-y9a3E21cY1oswzCycmgxVFTzNZ8IP62SdItL6EzhmFbCik7wH_
+  operator: 16441447913471369 #刘志
+  operatorUnionId: 99iiLnPefWJRBnaze9vB2fQiEiE #刘志
+  spaceId: 660951480
+  downloadPath: C:\\Users\\EDY\\Desktop\\项目\\琦王\\files\\
+#  agentId: 2691784047
+#  appKey: dinghbynhnd2dbgypmsa
+#  appSecret: Kl5Xw8x0TlEIlvcJuUkYZD18UTTShJmfdKrAIpY8oX-Q_tazyUKA28nQh7dG5-mq
+  corpId: dingdf7ab7df12214b0335c2f4657eb6378f
+  aesKey: yNR53hywSt4zJjjHUAcabqupuPJKWzVDbkTx12Q6Ctw
+  token: xpJRw0yOIo
+spring:
+  datasource:
+    url: jdbc:mysql://localhost:3306/invoice_db?useSSL=false&characterEncoding=utf8&serverTimezone=Asia/Shanghai
+    username: root
+    password: 123456
+    driver-class-name: com.mysql.cj.jdbc.Driver
+
+mybatis-plus:
+  configuration:
+    #开启驼峰命名自动映射
+    map-underscore-to-camel-case: true
+    #开启日志打印
+    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
+  type-aliases-package: com.malk.qiwang.entity
+  #扫描mapper文件
+  mapper-locations: classpath:mapper/*.xml
+
+#aliwork:
+#  appType: APP_V25MK3XKODKB02UVTGSH
+#  systemToken: IQC66GC1L5TW5HKF6SX645RC4AXG2CFGQOMCMOQ4
+aliwork:
+  appType:
+  systemToken:
+#aliwork:
+#  appType: APP_G1MXVGRR5L6OTZDEME0M
+#  systemToken: PTA66DA1S07X9DRA9EQZ96UUWJTQ2GHXLL5DMY42
+
+
+

+ 43 - 0
mjava-qiwang/src/main/resources/application-prod.yml

@@ -0,0 +1,43 @@
+
+server:
+  port: 9092
+  servlet:
+    context-path: /qiwang
+
+enable:
+  scheduling: true
+logging:
+  config: classpath:logback-spring.xml
+  path: /home/server/logs/qiwang
+  level:
+    com.malk.*: info
+
+# dingtalk
+dingtalk:
+  agentId: 4362063085
+  appKey: dingknkn9abneqetgc8a
+  appSecret: 0JnyzqsxqnVrSwP3SiQvZrMGbk0PyUDcl58o1BS2Bro0lVxUYbi_XZ805IJIHUE8
+  operator: "16441447913471369" #刘志
+  operatorUnionId: 99iiLnPefWJRBnaze9vB2fQiEiE #刘志
+  spaceId: 660951480
+  downloadPath: C:\\Users\\EDY\\Desktop\\项目\\琦王\\files\\
+#  agentId: 2691784047
+#  appKey: dinghbynhnd2dbgypmsa
+#  appSecret: Kl5Xw8x0TlEIlvcJuUkYZD18UTTShJmfdKrAIpY8oX-Q_tazyUKA28nQh7dG5-mq
+  corpId: dingdf7ab7df12214b0335c2f4657eb6378f
+  aesKey: oEBv2fnBAcBSlCZpsYKi5THmQBFaALQIzsFRbenX66n
+  token: h8cSLzNtQ1qTeQuBjY5OpDW
+
+
+#aliwork:
+#  appType: APP_V25MK3XKODKB02UVTGSH
+#  systemToken: IQC66GC1L5TW5HKF6SX645RC4AXG2CFGQOMCMOQ4
+aliwork:
+  appType: APP_RPH7R3LF3SMXLRDY1ZJW
+  systemToken: 7M866K91D4LVACB4EADAZ5UJG7IN3OGA33WAMNT
+#aliwork:
+#  appType: APP_G1MXVGRR5L6OTZDEME0M
+#  systemToken: PTA66DA1S07X9DRA9EQZ96UUWJTQ2GHXLL5DMY42
+
+
+

+ 15 - 0
mjava-qiwang/src/main/resources/application.yml

@@ -0,0 +1,15 @@
+spring:
+  profiles:
+    active: dev
+  servlet:
+    multipart:
+      max-file-size: 100MB
+      max-request-size: 100MB
+  http:
+    enabled: false
+
+  #  configuration:
+  #    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
+  global-config:
+    db-config:
+      id-type: auto

+ 61 - 0
mjava-qiwang/src/main/resources/logback-spring.xml

@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration debug="false" scan="false" scanPeriod="60 seconds">
+    <springProperty scope="context" name="LOG_HOME" source="logging.path" defaultValue="/home/server/log/"/>
+    <property name="FileNamePattern" value="${LOG_HOME}%d{yyyyMM}/%d{dd}"/>
+
+    <!-- 定义控制台输出 -->
+    <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
+        <layout class="ch.qos.logback.classic.PatternLayout">
+            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} - [%thread] - %-5level - %logger{50} - %msg%n</pattern>
+        </layout>
+    </appender>
+
+    <appender name="appLogAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <!-- 指定日志文件的名称 -->
+        <!--<file>${FileNamePattern}/info.log</file>-->
+
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <fileNamePattern>${FileNamePattern}/info-%i.log</fileNamePattern>
+            <MaxHistory>30</MaxHistory>
+            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
+                <MaxFileSize>30MB</MaxFileSize>
+            </timeBasedFileNamingAndTriggeringPolicy>
+        </rollingPolicy>
+
+        <layout class="ch.qos.logback.classic.PatternLayout">
+            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [ %thread ] - [ %-5level ] [ %logger{50} : %line ] - %msg%n</pattern>
+        </layout>
+    </appender>
+
+    <appender name="SIFT" class="ch.qos.logback.classic.sift.SiftingAppender">
+        <discriminator>
+            <Key>processid</Key>
+            <DefaultValue>sys</DefaultValue>
+        </discriminator>
+        <sift>
+            <appender name="FILE-${processid}"
+                      class="ch.qos.logback.core.rolling.RollingFileAppender">
+                <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+                    <FileNamePattern>
+                        ${FileNamePattern}/${processid}.log
+                    </FileNamePattern>
+                </rollingPolicy>
+                <layout class="ch.qos.logback.classic.PatternLayout">
+                    <Pattern>
+                        %d{yyyyMMdd:HH:mm:ss.SSS} [%thread] %-5level %msg%n
+                    </Pattern>
+                </layout>
+            </appender>
+        </sift>
+    </appender>
+
+
+    <!-- 日志输出级别 -->
+    <logger name="org.springframework" level="debug"  additivity="false"/>
+    <logger name="com.zitoo.connecter" level="debug"/>
+    <root level="INFO">
+        <appender-ref ref="stdout"/>
+        <appender-ref ref="appLogAppender"/>
+        <appender-ref ref="SIFT"/>
+    </root>
+</configuration>

+ 13 - 0
mjava-qiwang/src/test/java/com/malk/qiwang/MjavaQiwangApplicationTests.java

@@ -0,0 +1,13 @@
+package com.malk.qiwang;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+
+@SpringBootTest(classes = MjavaQiwangApplication.class)
+class MjavaQiwangApplicationTests {
+
+    @Test
+    void contextLoads() {
+    }
+
+}