Selaa lähdekoodia

顺丰会议室

malk 1 päivä sitten
vanhempi
commit
ce31331f30
48 muutettua tiedostoa jossa 1070 lisäystä ja 225 poistoa
  1. 4 1
      mjava-hake/src/main/java/com/malk/hake/schedule/HKScheduleTask.java
  2. 6 6
      mjava-hake/src/main/resources/application-dev.yml
  3. 1 1
      mjava-suodisi/pom.xml
  4. 0 0
      mjava-harrison/src/main/java/com/malk/suodisi/Boot.java
  5. 0 0
      mjava-harrison/src/main/java/com/malk/suodisi/controller/DDController.java
  6. 94 0
      mjava-harrison/src/main/java/com/malk/suodisi/controller/HLController.java
  7. 0 0
      mjava-harrison/src/main/java/com/malk/suodisi/controller/SDSController.java
  8. 0 0
      mjava-harrison/src/main/java/com/malk/suodisi/controller/XYController.java
  9. 0 0
      mjava-harrison/src/main/java/com/malk/suodisi/entity/XyTodo.java
  10. 0 0
      mjava-harrison/src/main/java/com/malk/suodisi/server/XYR.java
  11. 24 8
      mjava-suodisi/src/main/resources/application-dev.yml
  12. 39 0
      mjava-harrison/src/main/resources/application-prod.yml
  13. BIN
      mjava-harrison/src/main/resources/templates/FLOURISH 销售发票.xlsx
  14. BIN
      mjava-harrison/src/main/resources/templates/HARRISON 海运-物流服务.xlsx
  15. BIN
      mjava-harrison/src/main/resources/templates/HARRISON 贸易-吨度.xlsx
  16. BIN
      mjava-harrison/src/main/resources/templates/HARRISON 贸易-湿吨.xlsx
  17. BIN
      mjava-harrison/src/main/resources/templates/HARRISON 贸易-磅价.xlsx
  18. BIN
      mjava-harrison/src/main/resources/templates/HARRISON 通用-加工费.xlsx
  19. BIN
      mjava-suodisi/src/main/resources/templates/HL2025 invoice list.xls
  20. BIN
      mjava-harrison/src/main/resources/templates/VIC 贸易-吨度.xlsx
  21. BIN
      mjava-harrison/src/main/resources/templates/VIC 贸易-干吨价.xlsx
  22. BIN
      mjava-harrison/src/main/resources/templates/VIC 贸易-磅价.xlsx
  23. BIN
      mjava-harrison/src/main/resources/templates/VIC 通用-联检费.xlsx
  24. BIN
      mjava-harrison/src/main/resources/templates/VIC 通用模板.xlsx
  25. BIN
      mjava-harrison/src/main/resources/templates/VLINK 海运-拖车费.xlsx
  26. BIN
      mjava-harrison/src/main/resources/templates/VLINK 贸易-佣金.xlsx
  27. BIN
      mjava-harrison/src/main/resources/templates/VLINK 贸易-加工费.xlsx
  28. BIN
      mjava-harrison/src/main/resources/templates/VLINK 贸易-尾矿.xlsx
  29. BIN
      mjava-harrison/src/main/resources/templates/VLINK 贸易-干吨价.xlsx
  30. BIN
      mjava-harrison/src/main/resources/templates/VLINK 贸易-湿吨.xlsx
  31. BIN
      mjava-harrison/src/main/resources/templates/VLINK 通用模板.xlsx
  32. BIN
      mjava-harrison/src/main/resources/templates/ZEEK 物流-拖车费.xlsx
  33. 0 0
      mjava-harrison/src/test/resources/server.sh
  34. 117 117
      mjava-mcli/src/main/java/com/malk/mcli/controller/DocController.java
  35. 54 0
      mjava-shunfeng/pom.xml
  36. 30 0
      mjava-shunfeng/src/main/java/com/malk/shunfeng/Boot.java
  37. 111 0
      mjava-shunfeng/src/main/java/com/malk/shunfeng/controller/ZoomController.java
  38. 46 0
      mjava-shunfeng/src/main/java/com/malk/shunfeng/server/ZoomConf.java
  39. 71 0
      mjava-shunfeng/src/main/java/com/malk/shunfeng/server/ZoomR.java
  40. 59 0
      mjava-shunfeng/src/main/java/com/malk/shunfeng/service/ZoomClient.java
  41. 179 0
      mjava-shunfeng/src/main/java/com/malk/shunfeng/service/impl/ZoomClientImpl.java
  42. 48 0
      mjava-shunfeng/src/main/resources/application-dev.yml
  43. 48 0
      mjava-shunfeng/src/main/resources/application-prod.yml
  44. 134 0
      mjava-shunfeng/src/main/resources/logback-spring.xml
  45. 0 52
      mjava-suodisi/src/main/java/com/malk/suodisi/controller/HLController.java
  46. 0 38
      mjava-suodisi/src/main/resources/application-prod.yml
  47. 3 1
      mjava/src/main/java/com/malk/utils/UtilMap.java
  48. 2 1
      pom.xml

+ 4 - 1
mjava-hake/src/main/java/com/malk/hake/schedule/HKScheduleTask.java

@@ -24,7 +24,7 @@ public class HKScheduleTask {
     private HKClient hkClient;
 
     /**
-     * 同步通讯录: 早上6:00  中午11:30    下午16:00    晚上19:30
+     * 同步通讯录: 早上6:00, 下午16:00
      */
     @Scheduled(cron = "0 0 6,16 * * ? ")
     public void timer_1() {
@@ -36,6 +36,9 @@ public class HKScheduleTask {
         }
     }
 
+    /**
+     * 同步通讯录: 中午11:30, 下午19:30
+     */
     @Scheduled(cron = "0 30 11,19 * * ? ")
     public void timer_2() {
         try {

+ 6 - 6
mjava-hake/src/main/resources/application-dev.yml

@@ -36,12 +36,12 @@ logging:
 
 # dingtalk
 dingtalk:
-  agentId: 2757249888
-  appKey: ding3zk0vhfifznseopg
-  appSecret: Eso_p9HrVrJClEqcwbYuuOeDbS5Lb0e8Qq_HWtJm4_GYR38E-O5UEvakWpxXAvqq
-  corpId: dinge61fe69900ea236b35c2f4657eb6378f
-  aesKey:
-  token:
+  agentId: 4134974045
+  appKey: ding18nioxnonf0o6u6i
+  appSecret: 7zTtX_GkfOypY3KaKR6EIjxwsHJh5ILjQHqSncNWQybVnEuDpJaxDMmSJs9ViMhd
+  corpId: dingb68b6b0ff271114ff2c783f7214b6d69
+  aesKey: c5f7VXmUQf4sIK3d9mcgyerWwwVEwrt94NorS15eK08
+  token: n5vu40R5UKHEcQTui
   operator: ""   # OA管理员账号
 
 # aliwork-上海

+ 1 - 1
mjava-suodisi/pom.xml

@@ -9,7 +9,7 @@
     </parent>
     <modelVersion>4.0.0</modelVersion>
 
-    <artifactId>mjava-suodisi</artifactId>
+    <artifactId>mjava-harrison</artifactId>
     <description>索迪斯 poc 动态审批条件程序</description>
 
     <properties>

mjava-suodisi/src/main/java/com/malk/suodisi/Boot.java → mjava-harrison/src/main/java/com/malk/suodisi/Boot.java


mjava-suodisi/src/main/java/com/malk/suodisi/controller/DDController.java → mjava-harrison/src/main/java/com/malk/suodisi/controller/DDController.java


+ 94 - 0
mjava-harrison/src/main/java/com/malk/suodisi/controller/HLController.java

@@ -0,0 +1,94 @@
+package com.malk.suodisi.controller;
+
+import com.alibaba.excel.EasyExcel;
+import com.alibaba.excel.ExcelWriter;
+import com.alibaba.excel.write.metadata.WriteSheet;
+import com.alibaba.fastjson.JSON;
+import com.malk.server.aliwork.YDConf;
+import com.malk.server.aliwork.YDParam;
+import com.malk.server.common.McException;
+import com.malk.server.common.McR;
+import com.malk.server.dingtalk.DDConf;
+import com.malk.service.aliwork.YDClient;
+import com.malk.suodisi.server.XYR;
+import com.malk.utils.*;
+import lombok.SneakyThrows;
+import lombok.extern.slf4j.Slf4j;
+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.web.bind.annotation.RestController;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.InputStream;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * 错误抛出与拦截详见 CatchException
+ */
+@Slf4j
+@RestController
+@RequestMapping("/harrison")
+public class HLController {
+
+    @Autowired
+    private YDClient ydClient;
+
+    @Autowired
+    private DDConf ddConf;
+
+    @PostMapping("/invoice")
+    McR HL_invoice(@RequestBody Map data, HttpServletResponse response, HttpServletRequest request) {
+
+//        data.putAll(UtilMap.map("type, abbr, formId", "销售发票, FLOURISH, 4070f44f-72da-4beb-b4b4-64d65d70c271"));
+        log.info("invoice, {}", data);
+
+        McException.assertParamException_Null(data, "formId, abbr, type");
+
+        String formId = UtilMap.getString(data, "formId");
+        Map formData = ydClient.queryData(YDParam.builder()
+                .formInstId(formId)
+                .build(), YDConf.FORM_QUERY.retrieve_id).getFormData();
+
+        /// 字段: 销售方, 购买方, 发票号, 合同号, 付款条款, 船名, 提单号, 原厂地(国家), 装货港, 卸货港, 包装方式, 贸易方式, 提单日期, 品名
+        Map printData = UtilMap.map("seller, buyer, invoice, contract, payment, vessel, lading, origin, ladP, disP, packing, trade, bDate, commodity",
+                "selectField_mgvg350c, selectField_mgvh3io0, textField_l6q5sysr, selectField_mkdpx3e7, selectField_l5rljms7, selectField_migsdo11, textField_migsdo16, textField_migsdo17, selectField_mkdpx3e8, selectField_mkdpx3e9, textareaField_migsdo1a, textField_migsdo1b, dateField_migsdo1e, selectField_l6mztuvx",
+                formData);
+        printData.put("date", UtilDateTime.formatDate(new Date(UtilMap.getLong(formData, "dateField_mgvqvr4l")))); // 开票日期
+        String title = UtilMap.getString(formData, "selectField_mgvqvr4j").equals("最终发票") ? "FINAL COMMERCIAL INVOICE" : "PROVISIONAL COMMERCIAL INVOICE";
+        printData.put("title", title); // 发票类型
+        String[] sAddrs = UtilMap.getString(formData, "textareaField_migsdo0w").split("\\|");
+        for (int i = 0; i < sAddrs.length; i++) {
+            printData.put("sAddr" + i, sAddrs[i]); // 销售方-地址
+        }
+        String[] bAddrs = UtilMap.getString(formData, "textareaField_migsdo0x").split("\\|");
+        for (int i = 0; i < bAddrs.length; i++) {
+            printData.put("bAddr" + i, bAddrs[i]); // 购买方-地址
+        }
+        List<Map> details =  UtilMap.getList(formData, "tableField_mgvqvr53");
+        List<Map> dataList = details.stream().map(item-> {
+            // 字段: 业务-业务类型, 吨数-结算吨数, 单价-发票金额(原币), 总价-发票合计(原币)
+            Map row = UtilMap.map("part, num, price, amount, rate",
+                    "selectField_mgugk66v, numberField_mgvqvr58, numberField_l5ryl6qe, numberField_l5m487p0, numberField_mgugk673",
+                    item);
+            row.put("quantity", UtilMap.getFloat(item, "numberField_mgvqvr58") + "MT");
+            row.put("exchange", UtilNumber.formatPrecisionString(UtilMap.getFloat(item, "numberField_l5ryl6qe") * UtilMap.getFloat(item, "numberField_mgugk673")));
+            row.put("empty", ""); // ppExt: 添加了子表, 若模板未用到会异常, 添加标注
+            return row;
+        }).collect(Collectors.toList());
+        printData.put("oTotal", UtilMap.getFloat(formData, "numberField_mhek85oq")); // 发票金额合计-原币
+        log.info("printData, {}", printData);
+
+        String type = UtilMap.getString(data, "type"); // 模板类型
+        String abbr = UtilMap.getString(data, "abbr"); // 公司简称
+        String fileName = abbr + " " + title;
+        UtilExcel.exportMapAndListByTemplate(response, printData, dataList, Map.class, fileName, abbr + " " + type + ".xlsx");
+        return  McR.success();
+    }
+
+}
+
+

mjava-suodisi/src/main/java/com/malk/suodisi/controller/SDSController.java → mjava-harrison/src/main/java/com/malk/suodisi/controller/SDSController.java


mjava-suodisi/src/main/java/com/malk/suodisi/controller/XYController.java → mjava-harrison/src/main/java/com/malk/suodisi/controller/XYController.java


mjava-suodisi/src/main/java/com/malk/suodisi/entity/XyTodo.java → mjava-harrison/src/main/java/com/malk/suodisi/entity/XyTodo.java


mjava-suodisi/src/main/java/com/malk/suodisi/server/XYR.java → mjava-harrison/src/main/java/com/malk/suodisi/server/XYR.java


+ 24 - 8
mjava-suodisi/src/main/resources/application-dev.yml

@@ -55,20 +55,36 @@ logging:
 #  systemToken: IC766WA11EW8BMOPBEZZMBA4MPUQ214JDL7FLC9
 
 
-## 厦门象屿 - 股份门户
+### 厦门象屿 - 股份门户
+## dingtalk
+#dingtalk:
+#  agentId: 4091040619
+#  appKey: ding7mjaj6fjhuzyij8k
+#  appSecret: j9L0F3yzN4gpdMDpuCxW0dVSOwFHEfmrPUMtNXFu5IWvQDu0qGdfZ9-R3SO_LBa5
+#  corpId: dingb68b6b0ff271114ff2c783f7214b6d69
+#  aesKey: n7HjQmK8EKFWgEZyU13FQtbIeFcKueRsh82DhaMWvmu
+#  token: JjrmLlIUrBCBFhGEnX3xaK7vXobGwd
+#  operator: ""   # 林壹[开头需要转一下字符串], OA管理员账号
+#
+## aliwork
+#aliwork:
+#  appType: APP_YH7W0E5637YUBU5UJ837
+#  systemToken: IC766WA11EW8BMOPBEZZMBA4MPUQ214JDL7FLC9
+
+## harrison - 发票模板
 # dingtalk
 dingtalk:
   agentId: 4091040619
-  appKey: ding7mjaj6fjhuzyij8k
-  appSecret: j9L0F3yzN4gpdMDpuCxW0dVSOwFHEfmrPUMtNXFu5IWvQDu0qGdfZ9-R3SO_LBa5
-  corpId: dingb68b6b0ff271114ff2c783f7214b6d69
-  aesKey: n7HjQmK8EKFWgEZyU13FQtbIeFcKueRsh82DhaMWvmu
-  token: JjrmLlIUrBCBFhGEnX3xaK7vXobGwd
+  appKey: dinglu5ea0vlsmy2ys0q
+  appSecret: dev5GRm6pTC9XJbcu_sEyFpm8hatYl9nNkMZ4ILV72omQZZYyC83fv_hB4J-tnyd
+  corpId: ding97f04d85a46b5149a39a90f97fcb1e09
+  aesKey:
+  token:
   operator: ""   # 林壹[开头需要转一下字符串], OA管理员账号
 
 # aliwork
 aliwork:
-  appType: APP_YH7W0E5637YUBU5UJ837
-  systemToken: IC766WA11EW8BMOPBEZZMBA4MPUQ214JDL7FLC9
+  appType: APP_CPWWGT5FEIH6EYXJZLDR
+  systemToken: P9966091SLBZXORGEO00WCMYKK2A3GWWNUBGM26H
 
 

+ 39 - 0
mjava-harrison/src/main/resources/application-prod.yml

@@ -0,0 +1,39 @@
+# 环境配置
+server:
+  port: 9099
+  servlet:
+    context-path: /api
+
+# condition
+spel:
+  scheduling: false       # 定时任务是否执行
+  multiSource: false      # 是否多数据源配置
+
+spring:
+  # database
+  datasource:
+    hikari:
+      connection-init-sql: SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci           # SqlServer, Oracle 无需设置类型
+    driver-class-name: com.mysql.cj.jdbc.Driver
+    username: root
+    password: mu123
+    url: jdbc:mysql://127.0.0.1:3306/mjava?serverTimezone=Asia/Shanghai&useUnicode=yes&characterEncoding=UTF-8&useSSL=true
+  jpa:
+    database: MYSQL
+    database-platform: org.hibernate.dialect.MySQL57Dialect
+
+## harrison - 发票模板
+# dingtalk
+dingtalk:
+  agentId: 4091040619
+  appKey: dinglu5ea0vlsmy2ys0q
+  appSecret: dev5GRm6pTC9XJbcu_sEyFpm8hatYl9nNkMZ4ILV72omQZZYyC83fv_hB4J-tnyd
+  corpId: ding97f04d85a46b5149a39a90f97fcb1e09
+  aesKey:
+  token:
+  operator: ""   # 林壹[开头需要转一下字符串], OA管理员账号
+
+# aliwork
+aliwork:
+  appType: APP_CPWWGT5FEIH6EYXJZLDR
+  systemToken: P9966091SLBZXORGEO00WCMYKK2A3GWWNUBGM26H

BIN
mjava-harrison/src/main/resources/templates/FLOURISH 销售发票.xlsx


BIN
mjava-harrison/src/main/resources/templates/HARRISON 海运-物流服务.xlsx


BIN
mjava-harrison/src/main/resources/templates/HARRISON 贸易-吨度.xlsx


BIN
mjava-harrison/src/main/resources/templates/HARRISON 贸易-湿吨.xlsx


BIN
mjava-harrison/src/main/resources/templates/HARRISON 贸易-磅价.xlsx


BIN
mjava-harrison/src/main/resources/templates/HARRISON 通用-加工费.xlsx


BIN
mjava-suodisi/src/main/resources/templates/HL2025 invoice list.xls


BIN
mjava-harrison/src/main/resources/templates/VIC 贸易-吨度.xlsx


BIN
mjava-harrison/src/main/resources/templates/VIC 贸易-干吨价.xlsx


BIN
mjava-harrison/src/main/resources/templates/VIC 贸易-磅价.xlsx


BIN
mjava-harrison/src/main/resources/templates/VIC 通用-联检费.xlsx


BIN
mjava-harrison/src/main/resources/templates/VIC 通用模板.xlsx


BIN
mjava-harrison/src/main/resources/templates/VLINK 海运-拖车费.xlsx


BIN
mjava-harrison/src/main/resources/templates/VLINK 贸易-佣金.xlsx


BIN
mjava-harrison/src/main/resources/templates/VLINK 贸易-加工费.xlsx


BIN
mjava-harrison/src/main/resources/templates/VLINK 贸易-尾矿.xlsx


BIN
mjava-harrison/src/main/resources/templates/VLINK 贸易-干吨价.xlsx


BIN
mjava-harrison/src/main/resources/templates/VLINK 贸易-湿吨.xlsx


BIN
mjava-harrison/src/main/resources/templates/VLINK 通用模板.xlsx


BIN
mjava-harrison/src/main/resources/templates/ZEEK 物流-拖车费.xlsx


mjava-suodisi/src/test/resources/server.sh → mjava-harrison/src/test/resources/server.sh


+ 117 - 117
mjava-mcli/src/main/java/com/malk/mcli/controller/DocController.java

@@ -1,120 +1,120 @@
-package com.malk.mcli.controller;
-
-import com.malk.server.common.FilePath;
-import com.malk.server.common.McR;
-import com.spire.doc.Document;
-import com.spire.pdf.FileFormat;
-import com.spire.pdf.PdfDocument;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
-
-/**
- * 错误抛出与拦截详见CatchException
- */
-@Slf4j
-@RestController
-@RequestMapping("/doc")
-public class DocController {
-
-
-    @Autowired
-    private FilePath filePath;
-
-    @PostMapping("doc")
-    McR doc() {
-
-        //创建Document实例,加载第一个Word示例文档
-        com.spire.doc.Document doc1 = new com.spire.doc.Document();
-//        doc1.loadFromFile(filePath.getPath().getFile() + "工作确认单.docx");
-        doc1.loadFromFile(filePath.getPath().getFile() + "杉泰-太医管家产品购销合同(实体卡)V2.0.docx");
-
-        //创建Document实例,加载第二个Word示例文档
-        com.spire.doc.Document doc2 = new Document();
-//        doc2.loadFromFile(filePath.getPath().getFile() + "工作确认单(1).docx");
-        doc2.loadFromFile(filePath.getPath().getFile() + "杉泰-太医管家产品购销合同(实体卡)V2.0 2.docx");
-//        doc2.loadFromFile(filePath.getPath().getFile() + "杉泰-太医管家产品购销合同(实体卡)V2.0 2.pdf", FileFormat.PDF);
-
-        //比较两个示例文档的内容差异
-//        doc1.compare(doc2, "Host");
-        doc1.compare(doc2, "宁静");
-
-        //保存结果文档
-        doc1.saveToFile(filePath.getPath().getFile() + "diff-衫泰.docx");
-
-        return McR.success();
-    }
-
-    @PostMapping("doc2")
-    McR doc2() {
-
-        //加载PDF
-        PdfDocument pdf = new PdfDocument();
-        pdf.loadFromFile(filePath.getPath().getFile() + "杉泰-太医管家产品购销合同(实体卡)V2.0 2.pdf");
-
-        //保存为Word格式
-        pdf.saveToFile(filePath.getPath().getFile() + "杉泰-太医管家产品购销合同(实体卡)V2.0 pdf.docx", FileFormat.DOCX);
-
-        com.spire.doc.Document doc1 = new com.spire.doc.Document();
-        doc1.loadFromFile(filePath.getPath().getFile() + "杉泰-太医管家产品购销合同(实体卡)V2.0.docx");
-
-        //创建Document实例,加载第二个Word示例文档
-        com.spire.doc.Document doc2 = new Document();
-        doc2.loadFromFile(filePath.getPath().getFile() + "杉泰-太医管家产品购销合同(实体卡)V2.0 pdf.docx");
-
-        // 比对位置错乱
-        //比较两个示例文档的内容差异
-//        doc1.compare(doc2, "Host");
-        doc1.compare(doc2, "宁静");
+//package com.malk.mcli.controller;
 //
-//        //保存结果文档
-        doc1.saveToFile(filePath.getPath().getFile() + "diff-衫泰-pdf.docx");
-
-
-//        Comparer comparer = new Comparer(filePath.getPath().getFile() + "杉泰-太医管家产品购销合同(实体卡)V2.0.docx");
-//        try {
-//            comparer.add(filePath.getPath().getFile() + "杉泰-太医管家产品购销合同(实体卡)V2.0 2.docx");
-//            comparer.compare("grou-衫泰.docx");
-//        } finally {
-//            comparer.dispose();
-//        }
-        return McR.success();
-    }
-
-    @PostMapping("doc3")
-    McR doc3() {
-
-
-        // 乱码
-//        com.spire.doc.Document doc = new com.spire.doc.Document();
-//        doc.loadFromFile(filePath.getPath().getFile() + "杉泰-太医管家产品购销合同(实体卡)V2.0.docx");
-//        doc.saveToFile(filePath.getPath().getFile() + "WordToPDF.pdf", com.spire.doc.FileFormat.PDF);
-
-        com.spire.doc.Document doc1 = new com.spire.doc.Document();
-        doc1.loadFromFile(filePath.getPath().getFile() + "杉泰-太医管家产品购销合同(实体卡)V2.0.pdf", com.spire.doc.FileFormat.PDF);
-
-        //创建Document实例,加载第二个Word示例文档
-        com.spire.doc.Document doc2 = new Document();
-        doc2.loadFromFile(filePath.getPath().getFile() + "杉泰-太医管家产品购销合同(实体卡)V2.0 2.pdf", com.spire.doc.FileFormat.PDF);
-
-        //比较两个示例文档的内容差异
-//        doc1.compare(doc2, "Host");
-        doc1.compare(doc2, "宁静");
+//import com.malk.server.common.FilePath;
+//import com.malk.server.common.McR;
+//import com.spire.doc.Document;
+//import com.spire.pdf.FileFormat;
+//import com.spire.pdf.PdfDocument;
+//import lombok.extern.slf4j.Slf4j;
+//import org.springframework.beans.factory.annotation.Autowired;
+//import org.springframework.web.bind.annotation.PostMapping;
+//import org.springframework.web.bind.annotation.RequestMapping;
+//import org.springframework.web.bind.annotation.RestController;
+//
+///**
+// * 错误抛出与拦截详见CatchException
+// */
+//@Slf4j
+//@RestController
+//@RequestMapping("/doc")
+//public class DocController {
+//
+//
+//    @Autowired
+//    private FilePath filePath;
+//
+//    @PostMapping("doc")
+//    McR doc() {
+//
+//        //创建Document实例,加载第一个Word示例文档
+//        com.spire.doc.Document doc1 = new com.spire.doc.Document();
+////        doc1.loadFromFile(filePath.getPath().getFile() + "工作确认单.docx");
+//        doc1.loadFromFile(filePath.getPath().getFile() + "杉泰-太医管家产品购销合同(实体卡)V2.0.docx");
+//
+//        //创建Document实例,加载第二个Word示例文档
+//        com.spire.doc.Document doc2 = new Document();
+////        doc2.loadFromFile(filePath.getPath().getFile() + "工作确认单(1).docx");
+//        doc2.loadFromFile(filePath.getPath().getFile() + "杉泰-太医管家产品购销合同(实体卡)V2.0 2.docx");
+////        doc2.loadFromFile(filePath.getPath().getFile() + "杉泰-太医管家产品购销合同(实体卡)V2.0 2.pdf", FileFormat.PDF);
+//
+//        //比较两个示例文档的内容差异
+////        doc1.compare(doc2, "Host");
+//        doc1.compare(doc2, "宁静");
 //
 //        //保存结果文档
-        doc1.saveToFile(filePath.getPath().getFile() + "diff-衫泰-pdf-pdf.pdf");
-
-
-//        Comparer comparer = new Comparer(filePath.getPath().getFile() + "杉泰-太医管家产品购销合同(实体卡)V2.0.docx");
-//        try {
-//            comparer.add(filePath.getPath().getFile() + "杉泰-太医管家产品购销合同(实体卡)V2.0 2.docx");
-//            comparer.compare("grou-衫泰.docx");
-//        } finally {
-//            comparer.dispose();
-//        }
-        return McR.success();
-    }
-}
-
+//        doc1.saveToFile(filePath.getPath().getFile() + "diff-衫泰.docx");
+//
+//        return McR.success();
+//    }
+//
+//    @PostMapping("doc2")
+//    McR doc2() {
+//
+//        //加载PDF
+//        PdfDocument pdf = new PdfDocument();
+//        pdf.loadFromFile(filePath.getPath().getFile() + "杉泰-太医管家产品购销合同(实体卡)V2.0 2.pdf");
+//
+//        //保存为Word格式
+//        pdf.saveToFile(filePath.getPath().getFile() + "杉泰-太医管家产品购销合同(实体卡)V2.0 pdf.docx", FileFormat.DOCX);
+//
+//        com.spire.doc.Document doc1 = new com.spire.doc.Document();
+//        doc1.loadFromFile(filePath.getPath().getFile() + "杉泰-太医管家产品购销合同(实体卡)V2.0.docx");
+//
+//        //创建Document实例,加载第二个Word示例文档
+//        com.spire.doc.Document doc2 = new Document();
+//        doc2.loadFromFile(filePath.getPath().getFile() + "杉泰-太医管家产品购销合同(实体卡)V2.0 pdf.docx");
+//
+//        // 比对位置错乱
+//        //比较两个示例文档的内容差异
+////        doc1.compare(doc2, "Host");
+//        doc1.compare(doc2, "宁静");
+////
+////        //保存结果文档
+//        doc1.saveToFile(filePath.getPath().getFile() + "diff-衫泰-pdf.docx");
+//
+//
+////        Comparer comparer = new Comparer(filePath.getPath().getFile() + "杉泰-太医管家产品购销合同(实体卡)V2.0.docx");
+////        try {
+////            comparer.add(filePath.getPath().getFile() + "杉泰-太医管家产品购销合同(实体卡)V2.0 2.docx");
+////            comparer.compare("grou-衫泰.docx");
+////        } finally {
+////            comparer.dispose();
+////        }
+//        return McR.success();
+//    }
+//
+//    @PostMapping("doc3")
+//    McR doc3() {
+//
+//
+//        // 乱码
+////        com.spire.doc.Document doc = new com.spire.doc.Document();
+////        doc.loadFromFile(filePath.getPath().getFile() + "杉泰-太医管家产品购销合同(实体卡)V2.0.docx");
+////        doc.saveToFile(filePath.getPath().getFile() + "WordToPDF.pdf", com.spire.doc.FileFormat.PDF);
+//
+//        com.spire.doc.Document doc1 = new com.spire.doc.Document();
+//        doc1.loadFromFile(filePath.getPath().getFile() + "杉泰-太医管家产品购销合同(实体卡)V2.0.pdf", com.spire.doc.FileFormat.PDF);
+//
+//        //创建Document实例,加载第二个Word示例文档
+//        com.spire.doc.Document doc2 = new Document();
+//        doc2.loadFromFile(filePath.getPath().getFile() + "杉泰-太医管家产品购销合同(实体卡)V2.0 2.pdf", com.spire.doc.FileFormat.PDF);
+//
+//        //比较两个示例文档的内容差异
+////        doc1.compare(doc2, "Host");
+//        doc1.compare(doc2, "宁静");
+////
+////        //保存结果文档
+//        doc1.saveToFile(filePath.getPath().getFile() + "diff-衫泰-pdf-pdf.pdf");
+//
+//
+////        Comparer comparer = new Comparer(filePath.getPath().getFile() + "杉泰-太医管家产品购销合同(实体卡)V2.0.docx");
+////        try {
+////            comparer.add(filePath.getPath().getFile() + "杉泰-太医管家产品购销合同(实体卡)V2.0 2.docx");
+////            comparer.compare("grou-衫泰.docx");
+////        } finally {
+////            comparer.dispose();
+////        }
+//        return McR.success();
+//    }
+//}
+//

+ 54 - 0
mjava-shunfeng/pom.xml

@@ -0,0 +1,54 @@
+<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>java-mcli</artifactId>
+        <groupId>com.malk</groupId>
+        <version>1.0-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>mjava-shunfeng</artifactId>
+    <description>丰声 - Zoom会议集成服务</description>
+
+    <properties>
+        <maven.compiler.source>8</maven.compiler.source>
+        <maven.compiler.target>8</maven.compiler.target>
+    </properties>
+
+    <dependencies>
+        <!-- 核心模块-->
+        <dependency>
+            <groupId>com.malk</groupId>
+            <artifactId>mjava</artifactId>
+            <version>${mjava.version}</version>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+                <version>2.1.1.RELEASE</version>
+                <configuration>
+                    <includeSystemScope>true</includeSystemScope>
+                    <!-- 如果没有该配置,devtools不会生效: 打包时关闭 -->
+                    <fork>false</fork>
+                    <!-- 避免中文乱码 -->
+                    <jvmArguments>-Dfile.encoding=UTF-8</jvmArguments>
+                </configuration>
+                <!-- 允许生成可运行jar -->
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>repackage</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+        <finalName>${project.artifactId}</finalName>
+    </build>
+</project>

+ 30 - 0
mjava-shunfeng/src/main/java/com/malk/shunfeng/Boot.java

@@ -0,0 +1,30 @@
+package com.malk.shunfeng;
+
+import com.querydsl.jpa.impl.JPAQueryFactory;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.annotation.Bean;
+import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
+
+import javax.persistence.EntityManager;
+
+/**
+ * 丰声项目启动类
+ * - Zoom会议集成服务
+ */
+@EnableJpaAuditing
+@SpringBootApplication(scanBasePackages = {"com.malk"})
+public class Boot {
+
+    public static void main(String... args) {
+        SpringApplication.run(Boot.class, args);
+    }
+
+    /**
+     * 让Spring管理JPAQueryFactory
+     */
+    @Bean
+    public JPAQueryFactory jpaQueryFactory(EntityManager entityManager) {
+        return new JPAQueryFactory(entityManager);
+    }
+}

+ 111 - 0
mjava-shunfeng/src/main/java/com/malk/shunfeng/controller/ZoomController.java

@@ -0,0 +1,111 @@
+package com.malk.shunfeng.controller;
+
+import com.malk.server.common.McR;
+import com.malk.shunfeng.service.ZoomClient;
+import com.malk.utils.UtilMap;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.Map;
+
+/**
+ * Zoom会议控制器
+ * - prd 提供Zoom会议创建、更新、删除、查询接口
+ */
+@Slf4j
+@RestController
+@RequestMapping("/zoom")
+public class ZoomController {
+
+    @Autowired
+    private ZoomClient zoomClient;
+
+    /**
+     * 创建Zoom会议
+     * - prd 请求参数参考Zoom API文档
+     * @param params 会议参数
+     *  - topic: 会议主题 (必填)
+     *  - type: 会议类型 1-即时会议, 2-预约会议 (默认2)
+     *  - start_time: 开始时间 ISO 8601格式 (type=2时必填)
+     *  - duration: 会议时长(分钟) (默认60)
+     *  - timezone: 时区 (默认Asia/Shanghai)
+     *  - password: 会议密码 (可选)
+     *  - settings: 会议设置 (可选)
+     *    - host_video: 主持人视频 (默认true)
+     *    - participant_video: 参会者视频 (默认true)
+     *    - join_before_host: 允许在主持人之前加入 (默认false)
+     *    - mute_upon_entry: 入会时静音 (默认true)
+     */
+    @PostMapping("/meeting/create")
+    public McR createMeeting(@RequestBody Map<String, Object> params) {
+        log.info("创建Zoom会议请求: {}", params);
+
+        String topic = UtilMap.getString(params, "topic");
+        // fixme 使用 get_default 处理默认值
+        Integer type = (Integer) UtilMap.get_default(params, "type", 2);
+        String startTime = UtilMap.getString(params, "start_time");
+        Integer duration = (Integer) UtilMap.get_default(params, "duration", 60);
+        String timezone = UtilMap.getString_default(params, "timezone", "Asia/Shanghai");
+        String password = UtilMap.getString(params, "password");
+        Map<String, Object> settings = UtilMap.getMap(params, "settings");
+
+        // fixme 默认会议设置,settings为空map时也需要设置默认值
+        if (settings == null || settings.isEmpty()) {
+            settings = UtilMap.map(
+                    "host_video", true,
+                    "participant_video", true,
+                    "join_before_host", false,
+                    "mute_upon_entry", true
+            );
+        }
+
+        Map<String, Object> result = zoomClient.createMeeting(topic, type, startTime, duration, timezone, password, settings);
+        return McR.success(result);
+    }
+
+    /**
+     * 更新Zoom会议
+     * @param meetingId 会议ID
+     * @param params 更新参数 (同创建参数)
+     */
+    @PatchMapping("/meeting/{meetingId}")
+    public McR updateMeeting(@PathVariable Long meetingId, @RequestBody Map<String, Object> params) {
+        log.info("更新Zoom会议请求: meetingId={}, params={}", meetingId, params);
+
+        Map<String, Object> result = zoomClient.updateMeeting(meetingId, params);
+        return McR.success(result);
+    }
+
+    /**
+     * 删除Zoom会议
+     * @param meetingId 会议ID
+     */
+    @DeleteMapping("/meeting/{meetingId}")
+    public McR deleteMeeting(@PathVariable Long meetingId) {
+        log.info("删除Zoom会议请求: meetingId={}", meetingId);
+
+        zoomClient.deleteMeeting(meetingId);
+        return McR.success();
+    }
+
+    /**
+     * 查询Zoom会议详情
+     * @param meetingId 会议ID
+     */
+    @GetMapping("/meeting/{meetingId}")
+    public McR getMeeting(@PathVariable Long meetingId) {
+        log.info("查询Zoom会议请求: meetingId={}", meetingId);
+
+        Map<String, Object> result = zoomClient.getMeeting(meetingId);
+        return McR.success(result);
+    }
+
+    /**
+     * 测试接口
+     */
+    @GetMapping("/test")
+    public McR test() {
+        return McR.success("Zoom service is running");
+    }
+}

+ 46 - 0
mjava-shunfeng/src/main/java/com/malk/shunfeng/server/ZoomConf.java

@@ -0,0 +1,46 @@
+package com.malk.shunfeng.server;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+/**
+ * Zoom API 配置类
+ * - prd Zoom使用OAuth 2.0认证
+ */
+@Data
+@Component
+@ConfigurationProperties(prefix = "zoom")
+public class ZoomConf {
+
+    /**
+     * Zoom API基础URL
+     */
+    private String baseUrl = "https://api.zoom.us/v2";
+
+    /**
+     * OAuth 授权URL
+     */
+    private String oauthUrl = "https://zoom.us/oauth/token";
+
+    /**
+     * Zoom Account ID (Server-to-Server OAuth)
+     */
+    private String accountId;
+
+    /**
+     * Zoom Client ID
+     */
+    private String clientId;
+
+    /**
+     * Zoom Client Secret
+     */
+    private String clientSecret;
+
+    /**
+     * 默认会议主持人用户ID
+     * - fixme 可以是邮箱或userId,需根据实际配置
+     */
+    private String defaultUserId;
+}

+ 71 - 0
mjava-shunfeng/src/main/java/com/malk/shunfeng/server/ZoomR.java

@@ -0,0 +1,71 @@
+package com.malk.shunfeng.server;
+
+import com.malk.server.common.McException;
+import com.malk.server.common.VenR;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * Zoom API 返回值结构
+ * - prd Zoom API错误时返回 code 和 message 字段
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class ZoomR extends VenR {
+
+    /**
+     * 会议ID
+     */
+    private Long id;
+
+    /**
+     * 会议主题
+     */
+    private String topic;
+
+    /**
+     * 加入会议链接
+     */
+    private String join_url;
+
+    /**
+     * 主持人启动链接
+     */
+    private String start_url;
+
+    /**
+     * 会议密码
+     */
+    private String password;
+
+    /**
+     * 开始时间
+     */
+    private String start_time;
+
+    /**
+     * 会议时长(分钟)
+     */
+    private Integer duration;
+
+    /**
+     * 时区
+     */
+    private String timezone;
+
+    /**
+     * 错误码 (错误时返回)
+     */
+    private Integer code;
+
+    /**
+     * 错误信息 (错误时返回)
+     */
+    private String message;
+
+    @Override
+    public void assertSuccess() {
+        // fixme Zoom API 错误时返回 code 字段
+        McException.assertException(code != null && code != 0, String.valueOf(code), message);
+    }
+}

+ 59 - 0
mjava-shunfeng/src/main/java/com/malk/shunfeng/service/ZoomClient.java

@@ -0,0 +1,59 @@
+package com.malk.shunfeng.service;
+
+import java.util.Map;
+
+/**
+ * Zoom会议客户端接口
+ * - prd 封装Zoom会议API操作
+ */
+public interface ZoomClient {
+
+    /**
+     * 获取OAuth Access Token
+     * - fixme 使用Server-to-Server OAuth方式获取Token
+     * @return access_token
+     */
+    String getAccessToken();
+
+    /**
+     * 创建Zoom会议
+     * @param userId 用户ID (邮箱或me)
+     * @param topic 会议主题
+     * @param type 会议类型: 1-即时会议, 2-预约会议
+     * @param startTime 开始时间 (ISO 8601格式: 2024-07-01T10:00:00Z)
+     * @param duration 会议时长(分钟)
+     * @param timezone 时区
+     * @param password 会议密码
+     * @param settings 会议设置
+     * @return 会议信息
+     */
+    Map<String, Object> createMeeting(String userId, String topic, Integer type, String startTime,
+                                       Integer duration, String timezone, String password, Map<String, Object> settings);
+
+    /**
+     * 创建Zoom会议 (使用默认用户)
+     */
+    Map<String, Object> createMeeting(String topic, Integer type, String startTime,
+                                       Integer duration, String timezone, String password, Map<String, Object> settings);
+
+    /**
+     * 更新Zoom会议
+     * @param meetingId 会议ID
+     * @param params 更新参数
+     * @return 更新结果
+     */
+    Map<String, Object> updateMeeting(Long meetingId, Map<String, Object> params);
+
+    /**
+     * 删除Zoom会议
+     * @param meetingId 会议ID
+     */
+    void deleteMeeting(Long meetingId);
+
+    /**
+     * 查询Zoom会议详情
+     * @param meetingId 会议ID
+     * @return 会议信息
+     */
+    Map<String, Object> getMeeting(Long meetingId);
+}

+ 179 - 0
mjava-shunfeng/src/main/java/com/malk/shunfeng/service/impl/ZoomClientImpl.java

@@ -0,0 +1,179 @@
+package com.malk.shunfeng.service.impl;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.malk.shunfeng.server.ZoomConf;
+import com.malk.shunfeng.server.ZoomR;
+import com.malk.shunfeng.service.ZoomClient;
+import com.malk.utils.UtilHttp;
+import com.malk.utils.UtilMap;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Base64;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Zoom会议客户端实现
+ * - prd 使用Server-to-Server OAuth认证方式
+ * - fixme Token有效期为1小时,生产环境建议缓存Token
+ */
+@Slf4j
+@Service
+public class ZoomClientImpl implements ZoomClient {
+
+    @Autowired
+    private ZoomConf zoomConf;
+
+    /**
+     * 缓存Token及过期时间
+     */
+    private String cachedToken;
+    private long tokenExpireTime;
+
+    /**
+     * 获取OAuth Access Token
+     * - fixme 使用Server-to-Server OAuth (Account Credentials) 方式
+     */
+    @Override
+    public String getAccessToken() {
+        // ppExt Token缓存,避免频繁请求
+        if (cachedToken != null && System.currentTimeMillis() < tokenExpireTime) {
+            return cachedToken;
+        }
+
+        // 构建Basic Auth: Base64(client_id:client_secret)
+        String credentials = zoomConf.getClientId() + ":" + zoomConf.getClientSecret();
+        String basicAuth = Base64.getEncoder().encodeToString(credentials.getBytes(StandardCharsets.UTF_8));
+
+        Map<String, String> header = new HashMap<>();
+        header.put("Authorization", "Basic " + basicAuth);
+        header.put("Content-Type", "application/x-www-form-urlencoded");
+
+        // Server-to-Server OAuth 请求参数 (form-urlencoded方式)
+        Map<String, Object> formParams = new HashMap<>();
+        formParams.put("grant_type", "account_credentials");
+        formParams.put("account_id", zoomConf.getAccountId());
+
+        // fixme 使用form方式提交,body传空Map避免方法重载歧义
+        String rsp = UtilHttp.doPost(zoomConf.getOauthUrl(), header, null, (Map) null, formParams);
+        JSONObject json = JSON.parseObject(rsp);
+
+        log.debug("Zoom OAuth响应: {}", rsp);
+
+        cachedToken = json.getString("access_token");
+        // fixme Token有效期通常为3600秒,提前5分钟刷新
+        int expiresIn = json.getIntValue("expires_in");
+        tokenExpireTime = System.currentTimeMillis() + (expiresIn - 300) * 1000L;
+
+        return cachedToken;
+    }
+
+    /**
+     * 构建请求头
+     */
+    private Map<String, String> buildHeader() {
+        Map<String, String> header = new HashMap<>();
+        header.put("Authorization", "Bearer " + getAccessToken());
+        header.put("Content-Type", "application/json");
+        return header;
+    }
+
+    /**
+     * 创建Zoom会议
+     * - prd POST /v2/users/{userId}/meetings
+     */
+    @Override
+    public Map<String, Object> createMeeting(String userId, String topic, Integer type, String startTime,
+                                              Integer duration, String timezone, String password, Map<String, Object> settings) {
+        String url = zoomConf.getBaseUrl() + "/users/" + userId + "/meetings";
+
+        Map<String, Object> body = new HashMap<>();
+        body.put("topic", topic);
+        body.put("type", type);
+        body.put("start_time", startTime);
+        body.put("duration", duration);
+        body.put("timezone", timezone);
+        if (password != null) {
+            body.put("password", password);
+        }
+        if (settings != null) {
+            body.put("settings", settings);
+        }
+
+        ZoomR r = (ZoomR) UtilHttp.doPost(url, buildHeader(), null, body, ZoomR.class);
+        log.info("创建Zoom会议, topic={}, response={}", topic, JSON.toJSONString(r));
+
+        return UtilMap.map(
+                "id", r.getId(),
+                "topic", r.getTopic(),
+                "join_url", r.getJoin_url(),
+                "start_url", r.getStart_url(),
+                "password", r.getPassword(),
+                "start_time", r.getStart_time(),
+                "duration", r.getDuration()
+        );
+    }
+
+    /**
+     * 创建Zoom会议 (使用默认用户)
+     */
+    @Override
+    public Map<String, Object> createMeeting(String topic, Integer type, String startTime,
+                                              Integer duration, String timezone, String password, Map<String, Object> settings) {
+        return createMeeting(zoomConf.getDefaultUserId(), topic, type, startTime, duration, timezone, password, settings);
+    }
+
+    /**
+     * 更新Zoom会议
+     * - prd PATCH /v2/meetings/{meetingId}
+     */
+    @Override
+    public Map<String, Object> updateMeeting(Long meetingId, Map<String, Object> params) {
+        String url = zoomConf.getBaseUrl() + "/meetings/" + meetingId;
+
+        String rsp = UtilHttp.doPatch(url, buildHeader(), null, params);
+        log.info("更新Zoom会议, meetingId={}, response={}", meetingId, rsp);
+
+        // fixme 更新成功返回204 No Content,返回查询结果
+        return getMeeting(meetingId);
+    }
+
+    /**
+     * 删除Zoom会议
+     * - prd DELETE /v2/meetings/{meetingId}
+     */
+    @Override
+    public void deleteMeeting(Long meetingId) {
+        String url = zoomConf.getBaseUrl() + "/meetings/" + meetingId;
+
+        String rsp = UtilHttp.doDelete(url, buildHeader(), null, (Map) null);
+        log.info("删除Zoom会议, meetingId={}, response={}", meetingId, rsp);
+    }
+
+    /**
+     * 查询Zoom会议详情
+     * - prd GET /v2/meetings/{meetingId}
+     */
+    @Override
+    public Map<String, Object> getMeeting(Long meetingId) {
+        String url = zoomConf.getBaseUrl() + "/meetings/" + meetingId;
+
+        ZoomR r = (ZoomR) UtilHttp.doGet(url, buildHeader(), null, ZoomR.class);
+        log.info("查询Zoom会议, meetingId={}, response={}", meetingId, JSON.toJSONString(r));
+
+        return UtilMap.map(
+                "id", r.getId(),
+                "topic", r.getTopic(),
+                "join_url", r.getJoin_url(),
+                "start_url", r.getStart_url(),
+                "password", r.getPassword(),
+                "start_time", r.getStart_time(),
+                "duration", r.getDuration(),
+                "timezone", r.getTimezone()
+        );
+    }
+}

+ 48 - 0
mjava-shunfeng/src/main/resources/application-dev.yml

@@ -0,0 +1,48 @@
+# 环境配置
+server:
+  port: 9010
+  servlet:
+    context-path: /api
+
+# condition
+spel:
+  scheduling: false        # 定时任务是否执行
+  multiSource: false       # 是否多数据源配置
+
+spring:
+  # database
+  datasource:
+    hikari:
+      connection-init-sql: SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci
+    driver-class-name: com.mysql.cj.jdbc.Driver
+    username: root
+    password: mu123
+    url: jdbc:mysql://127.0.0.1:3306/mjava?serverTimezone=Asia/Shanghai&useUnicode=yes&characterEncoding=UTF-8&useSSL=true
+  jpa:
+    hibernate:
+      ddl-auto: none      # JPA对表没有任何操作
+    show-sql: true
+    database: MYSQL
+    database-platform: org.hibernate.dialect.MySQL57Dialect
+
+# filepath
+file:
+  path:
+    file: /Users/malk/server/_Tool/var/mjava/tmp/file/
+    image: /Users/malk/server/_Tool/var/mjava/tmp/image/
+    tmp: /Users/malk/server/_Tool/var/mjava/tmp/
+  source:
+    fonts: /Users/malk/server/_Tool/fonts/simsun.ttc
+logging:
+  file:
+    path: /Users/malk/server/_Tool/var/mjava/log
+
+# Zoom API配置
+# fixme 需要在 https://marketplace.zoom.us/ 创建 Server-to-Server OAuth App 获取以下配置
+zoom:
+  baseUrl: https://api.zoom.us/v2
+  oauthUrl: https://zoom.us/oauth/token
+  accountId: YOUR_ACCOUNT_ID        # Zoom账号ID
+  clientId: YOUR_CLIENT_ID          # OAuth Client ID
+  clientSecret: YOUR_CLIENT_SECRET  # OAuth Client Secret
+  defaultUserId: me                 # 默认主持人用户ID (可以是邮箱或 "me")

+ 48 - 0
mjava-shunfeng/src/main/resources/application-prod.yml

@@ -0,0 +1,48 @@
+# 环境配置
+server:
+  port: 9010
+  servlet:
+    context-path: /api
+
+# condition
+spel:
+  scheduling: false        # 定时任务是否执行
+  multiSource: false       # 是否多数据源配置
+
+spring:
+  # database
+  datasource:
+    hikari:
+      connection-init-sql: SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci
+    driver-class-name: com.mysql.cj.jdbc.Driver
+    username: ${DB_USERNAME:root}
+    password: ${DB_PASSWORD:password}
+    url: jdbc:mysql://${DB_HOST:127.0.0.1}:${DB_PORT:3306}/${DB_NAME:mjava}?serverTimezone=Asia/Shanghai&useUnicode=yes&characterEncoding=UTF-8&useSSL=true
+  jpa:
+    hibernate:
+      ddl-auto: none      # JPA对表没有任何操作
+    show-sql: false
+    database: MYSQL
+    database-platform: org.hibernate.dialect.MySQL57Dialect
+
+# filepath
+file:
+  path:
+    file: /var/mjava/tmp/file/
+    image: /var/mjava/tmp/image/
+    tmp: /var/mjava/tmp/
+  source:
+    fonts: /var/mjava/fonts/simsun.ttc
+logging:
+  file:
+    path: /var/mjava/log
+
+# Zoom API配置
+# prd 生产环境通过环境变量注入敏感配置
+zoom:
+  baseUrl: https://api.zoom.us/v2
+  oauthUrl: https://zoom.us/oauth/token
+  accountId: ${ZOOM_ACCOUNT_ID}
+  clientId: ${ZOOM_CLIENT_ID}
+  clientSecret: ${ZOOM_CLIENT_SECRET}
+  defaultUserId: ${ZOOM_DEFAULT_USER_ID:me}

+ 134 - 0
mjava-shunfeng/src/main/resources/logback-spring.xml

@@ -0,0 +1,134 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration>
+    <!-- 日志存放路径 -->
+    <springProperty scope="context" name="log.path" source="logging.file.path" defaultValue="./logs"/>
+    <!-- 日志输出格式 -->
+    <property name="log.pattern" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level - [%method,%line] - %msg%n"/>
+
+    <!-- 控制台输出 -->
+    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
+        <encoder>
+            <pattern>${log.pattern}</pattern>
+        </encoder>
+    </appender>
+
+    <!-- 错误日志输出 -->
+    <appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <file>${log.path}/error.log</file>
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <fileNamePattern>${log.path}/%d{yyyy-MM-dd}/error-%i.log.gz</fileNamePattern>
+            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
+                <maxFileSize>20MB</maxFileSize>
+            </timeBasedFileNamingAndTriggeringPolicy>
+            <maxHistory>60</maxHistory>
+        </rollingPolicy>
+        <encoder>
+            <pattern>${log.pattern}</pattern>
+        </encoder>
+        <filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <level>ERROR</level>
+            <onMatch>ACCEPT</onMatch>
+            <onMismatch>DENY</onMismatch>
+        </filter>
+    </appender>
+
+    <!-- 警告日志输出 -->
+    <appender name="WARN_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <file>${log.path}/warn.log</file>
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <fileNamePattern>${log.path}/%d{yyyy-MM-dd}/warn-%i.log.gz</fileNamePattern>
+            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
+                <maxFileSize>20MB</maxFileSize>
+            </timeBasedFileNamingAndTriggeringPolicy>
+            <maxHistory>60</maxHistory>
+        </rollingPolicy>
+        <encoder>
+            <pattern>${log.pattern}</pattern>
+        </encoder>
+        <filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <level>WARN</level>
+            <onMatch>ACCEPT</onMatch>
+            <onMismatch>DENY</onMismatch>
+        </filter>
+    </appender>
+
+    <!-- 记录日志输出 -->
+    <appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <file>${log.path}/info.log</file>
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <fileNamePattern>${log.path}/%d{yyyy-MM-dd}/info-%i.log.gz</fileNamePattern>
+            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
+                <maxFileSize>20MB</maxFileSize>
+            </timeBasedFileNamingAndTriggeringPolicy>
+            <maxHistory>60</maxHistory>
+        </rollingPolicy>
+        <encoder>
+            <pattern>${log.pattern}</pattern>
+        </encoder>
+        <filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <level>INFO</level>
+            <onMatch>ACCEPT</onMatch>
+            <onMismatch>DENY</onMismatch>
+        </filter>
+    </appender>
+
+    <!-- 调试日志输出 -->
+    <appender name="DEBUG_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <file>${log.path}/debug.log</file>
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <fileNamePattern>${log.path}/%d{yyyy-MM-dd}/debug-%i.log.gz</fileNamePattern>
+            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
+                <maxFileSize>20MB</maxFileSize>
+            </timeBasedFileNamingAndTriggeringPolicy>
+            <maxHistory>30</maxHistory>
+        </rollingPolicy>
+        <encoder>
+            <pattern>${log.pattern}</pattern>
+        </encoder>
+        <filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <level>DEBUG</level>
+            <onMatch>ACCEPT</onMatch>
+            <onMismatch>DENY</onMismatch>
+        </filter>
+    </appender>
+
+    <!-- Spring日志级别控制  -->
+    <logger name="org.springframework" level="warn"/>
+
+    <!-- hikari 日志级别 -->
+    <Logger name="com.zaxxer.hikari" level="info"></Logger>
+
+    <!-- Hibernate 日志级别 -->
+    <logger name="org.hibernate.type.descriptor.sql.BasicBinder" level="DEBUG"/>
+    <logger name="org.hibernate.type.descriptor.sql.BasicExtractor" level="DEBUG"/>
+    <logger name="org.hibernate.SQL" level="DEBUG"/>
+    <logger name="org.hibernate.engine.QueryParameters" level="DEBUG"/>
+    <logger name="org.hibernate.engine.query.HQLQueryPlan" level="DEBUG"/>
+
+    <!-- 开发环境: 打印控制台 -->
+    <springProfile name="dev">
+        <root level="info">
+            <appender-ref ref="CONSOLE"/>
+        </root>
+    </springProfile>
+
+    <!-- 测试环境:输出文件 -->
+    <springProfile name="test">
+        <root level="info">
+            <appender-ref ref="DEBUG_FILE"/>
+            <appender-ref ref="INFO_FILE"/>
+            <appender-ref ref="WARN_FILE"/>
+            <appender-ref ref="ERROR_FILE"/>
+        </root>
+    </springProfile>
+
+    <!-- 生产环境: 输出文件 -->
+    <springProfile name="prod">
+        <root level="info">
+            <appender-ref ref="DEBUG_FILE"/>
+            <appender-ref ref="INFO_FILE"/>
+            <appender-ref ref="WARN_FILE"/>
+            <appender-ref ref="ERROR_FILE"/>
+        </root>
+    </springProfile>
+</configuration>

+ 0 - 52
mjava-suodisi/src/main/java/com/malk/suodisi/controller/HLController.java

@@ -1,52 +0,0 @@
-package com.malk.suodisi.controller;
-
-import com.alibaba.fastjson.JSON;
-import com.malk.server.aliwork.YDConf;
-import com.malk.server.aliwork.YDParam;
-import com.malk.server.common.McException;
-import com.malk.server.common.McR;
-import com.malk.server.dingtalk.DDConf;
-import com.malk.service.aliwork.YDClient;
-import com.malk.suodisi.server.XYR;
-import com.malk.utils.*;
-import lombok.extern.slf4j.Slf4j;
-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.web.bind.annotation.RestController;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import java.util.*;
-
-/**
- * 错误抛出与拦截详见 CatchException
- */
-@Slf4j
-@RestController
-@RequestMapping("/hl")
-public class HLController {
-
-    @Autowired
-    private YDClient ydClient;
-
-    @Autowired
-    private DDConf ddConf;
-
-    /**
-     * 动态审批人
-     */
-    @PostMapping("/invoice/hl")
-    McR HL_invoice(@RequestBody Map data, HttpServletResponse response, HttpServletRequest request) {
-
-        Map dataMain = new HashMap();
-        List<Map> dataList = new ArrayList();
-        String fileName = "HL invoice";
-
-        UtilExcel.exportMapAndListByTemplate(response, dataMain, dataList, Map.class, fileName, "HL2025 invoice list.xls");
-        return  McR.success();
-    }
-}
-
-

+ 0 - 38
mjava-suodisi/src/main/resources/application-prod.yml

@@ -1,38 +0,0 @@
-# 环境配置
-server:
-  port: 9010
-  servlet:
-    context-path: /api/suodisi
-
-# condition
-spel:
-  scheduling: true        # 定时任务是否执行
-  multiSource: false      # 是否多数据源配置
-
-spring:
-  # database
-  datasource:
-    hikari:
-      connection-init-sql: SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci           # SqlServer, Oracle 无需设置类型
-    driver-class-name: com.mysql.cj.jdbc.Driver
-    username: root
-    password: cp-root@2022++
-    url: jdbc:mysql://47.97.181.40:3306/mjava?serverTimezone=Asia/Shanghai&useUnicode=yes&characterEncoding=UTF-8&useSSL=true
-  jpa:
-    database: MYSQL
-    database-platform: org.hibernate.dialect.MySQL57Dialect
-
-# dingtalk
-dingtalk:
-  agentId: 1963716187
-  appKey: ding8qyulwwmad6j7k6c
-  appSecret: e_hRHuubw-Xi0OuPJOYdXSElVzOC0IPgMrHQTBuAM9BqW-DFnrcsSyBHi7Me3xSv
-  corpId: ding321c72787fffc78b35c2f4657eb6378f
-  aesKey:
-  token:
-  operator: "095358016629044412"   # 牧语[开头需要转一下字符串], OA管理员账号
-
-# aliwork
-aliwork:
-  appType: APP_YH7W0E5637YUBU5UJ837
-  systemToken: IC766WA11EW8BMOPBEZZMBA4MPUQ214JDL7FLC9

+ 3 - 1
mjava/src/main/java/com/malk/utils/UtilMap.java

@@ -77,7 +77,9 @@ public abstract class UtilMap {
         }
         Map data = new HashMap();
         Arrays.stream(cprops).forEach(UtilMc.consumerWithIndex((item, index) -> {
-            data.put(sprops[index], map.get(item));
+            if (map.get(item) != null) {
+                data.put(sprops[index], map.get(item));
+            }
         }));
         return data;
     }

+ 2 - 1
pom.xml

@@ -13,7 +13,7 @@
         <module>mjava-hangshi</module>
         <module>mjava-guyuan</module>
         <module>mjava-mcli</module>
-        <module>mjava-suodisi</module>
+        <module>mjava-harrison</module>
         <module>mjava-cloudpure</module>
         <module>mjava-zhuogao</module>
         <module>mjava-fengkaili</module>
@@ -32,6 +32,7 @@
         <module>mjava-aiwei</module>
         <module>mjava-lemeng</module>
         <module>mjava-ruisi</module>
+        <module>mjava-shunfeng</module>
     </modules>
     <packaging>pom</packaging>