Forráskód Böngészése

欧诺——新增提成表

xmy 2 hete%!(EXTRA string=óta)
szülő
commit
227c3ad186

+ 308 - 1
mjava-ounuo/src/main/java/com/malk/tuosi/schedule/ScheduleTask.java

@@ -24,9 +24,11 @@ import org.apache.catalina.User;
 import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.jdbc.core.JdbcTemplate;
 import org.springframework.web.bind.annotation.*;
 
 import java.text.SimpleDateFormat;
+import java.sql.Timestamp;
 import java.time.*;
 import java.time.format.DateTimeFormatter;
 import java.time.format.DateTimeParseException;
@@ -65,6 +67,8 @@ public class ScheduleTask {
     @Autowired
     private CommissionTableMapper commissionTableMapper;
 
+    @Autowired
+    private JdbcTemplate jdbcTemplate;
 
     /*每天凌晨定时同步任务状态*/
     @SneakyThrows
@@ -1874,7 +1878,310 @@ public class ScheduleTask {
         return McR.success();
     }
 
-    /*todo:人员获取部门*/
+
+    /**
+     * todo: commission_detail  业务提成表
+     */
+
+    /*【说明】
+    * commissionDetailFull 不会自动执行。
+
+  它只在以下情况手工调用:
+
+  - 首次初始化异常,需要重新全量导入
+  - 发现 commission_detail 数据缺失、错误或与 Teambition 不一致
+  - 调整了筛选条件、字段映射后,需要重建全量数据
+
+  正常情况下,首次调用 /commissionDetail 后,后续只依赖每天 05:10 的增量同步。
+    * */
+    @SneakyThrows
+    @PostMapping("/commissionDetailFull")
+    public McR commissionDetailFull() {
+        final String projectId = "6878b323386fac7ab9dbe5e9";
+        final String baseCondition = "tagId != 68f5840f90efa8d7498bd5e9 " +
+                "AND tagId != 68f58407a89ee3bbe639f85c " +
+                "AND cf:691c29c1f8cbae320a1c8e75 != 无 " +
+                "AND cf:691c29c1f8cbae320a1c8e75 != 扩版 " +
+                "AND created >= 2026-01-01 ";
+
+        List<String> conditions = Arrays.asList(
+                baseCondition + "AND scenarioId = 6878b39a27ae5f3cac355fb9 " +
+                        "AND tfsId = 6878b384d3c6329fa3728bc9",
+                baseCondition + "AND scenarioId = 6a7c2663e9cc163c31e5cd62 " +
+                        "AND tfsId = 6a7c003127a2b19abbba302e"
+        );
+
+        Map<String, String> header = getCommonHeader();
+        Set<String> taskIds = new LinkedHashSet<>();
+        for (String condition : conditions) {
+            collectCommissionDetailTaskIds(projectId, condition, header, taskIds);
+        }
+
+        List<Object[]> rows = new ArrayList<>();
+        for (String taskId : taskIds) {
+            JSONObject task = queryCommissionDetailTask(taskId, header);
+            if (task == null) {
+                continue;
+            }
+
+            JSONArray customFields = task.getJSONArray("customfields");
+            String designer = getCommissionDetailFieldValue(customFields, "6a8e4ce280d14a316ce57b0e");
+            String customerShortName = getCommissionDetailFieldValue(customFields, "687df3d4d7dc27d64d57201e");
+            String designFee = getCommissionDetailFieldValue(customFields, "6a798e303023b086a6a38f47");
+            String designCoefficient = getCommissionDetailFieldValue(customFields, "691aafe69819400cd5497bbf");
+            String designLevel = getCommissionDetailFieldValue(customFields, "691c29c1f8cbae320a1c8e75");
+            String businessPerson = getCommissionDetailFieldValue(customFields, "688191972ab6c4f412ea2bb9");
+            String businessConfirmed = getBusinessConfirmationStatus(taskId, header);
+
+            rows.add(new Object[]{
+                    taskId,
+                    task.getString("content"),
+                    designer,
+                    customerShortName,
+                    designFee,
+                    designCoefficient,
+                    designLevel,
+                    businessPerson,
+                    businessConfirmed
+            });
+        }
+
+        //  API
+        jdbcTemplate.update("DELETE FROM commission_detail");
+        if (!rows.isEmpty()) {
+            jdbcTemplate.batchUpdate(
+                    "INSERT INTO commission_detail " +
+                            "(task_id, task_name, xm, khjc, jg, xs, dj, ywmc, ywsfqy) " +
+                            "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",
+                    rows
+            );
+        }
+
+        log.info("新增同步个数: {} ", rows.size());
+        return McR.success(rows.size());
+    }
+
+    @Scheduled(cron = "0 10 5 * * ?")
+    @SneakyThrows
+    @PostMapping("/commissionDetail")
+    public McR commissionDetail() {
+        LocalDateTime lastSuccessAt = getCommissionDetailLastSuccessAt();
+        if (lastSuccessAt == null) {
+            McR response = commissionDetailFull();
+            saveCommissionDetailLastSuccessAt(LocalDateTime.now());
+            return response;
+        }
+
+        String updatedFrom = DateTimeFormatter.ISO_INSTANT.format(
+                lastSuccessAt.minusMinutes(5).atZone(ZoneId.of("Asia/Shanghai")).toInstant()
+        );
+        String condition = "updated >= " + updatedFrom + " AND (scenarioId = 6878b39a27ae5f3cac355fb9 " +
+                "OR scenarioId = 6a7c2663e9cc163c31e5cd62)";
+
+        Set<String> taskIds = new LinkedHashSet<>();
+        collectCommissionDetailTaskIds("6878b323386fac7ab9dbe5e9", condition, getCommonHeader(), taskIds);
+
+        int written = 0;
+        int removed = 0;
+        Map<String, String> header = getCommonHeader();
+        for (String taskId : taskIds) {
+            JSONObject task = queryCommissionDetailTask(taskId, header);
+            if (task == null || !isCommissionDetailEligible(task)) {
+                removed += jdbcTemplate.update("DELETE FROM commission_detail WHERE task_id = ?", taskId);
+                continue;
+            }
+
+            upsertCommissionDetailRow(buildCommissionDetailRow(task, header));
+            written++;
+        }
+
+        saveCommissionDetailLastSuccessAt(LocalDateTime.now());
+        log.info("commission_detail incremental sync completed, written={}, removed={}", written, removed);
+        return McR.success();
+    }
+
+    private LocalDateTime getCommissionDetailLastSuccessAt() {
+        List<Timestamp> timestamps = jdbcTemplate.query(
+                "SELECT last_success_at FROM commission_detail_sync_state WHERE sync_name = ?",
+                (resultSet, rowNum) -> resultSet.getTimestamp(1),
+                "commission_detail"
+        );
+        return timestamps.isEmpty() ? null : timestamps.get(0).toLocalDateTime();
+    }
+
+    private void saveCommissionDetailLastSuccessAt(LocalDateTime successAt) {
+        int updated = jdbcTemplate.update(
+                "UPDATE commission_detail_sync_state SET last_success_at = ? WHERE sync_name = ?",
+                Timestamp.valueOf(successAt),
+                "commission_detail"
+        );
+        if (updated == 0) {
+            jdbcTemplate.update(
+                    "INSERT INTO commission_detail_sync_state (sync_name, last_success_at) VALUES (?, ?)",
+                    "commission_detail",
+                    Timestamp.valueOf(successAt)
+            );
+        }
+    }
+
+    private boolean isCommissionDetailEligible(JSONObject task) {
+        String scenarioId = task.getString("sfcId");
+        String taskStatusId = task.getString("tfsId");
+        boolean designCompleted = "6878b39a27ae5f3cac355fb9".equals(scenarioId)
+                && "6878b384d3c6329fa3728bc9".equals(taskStatusId);
+        boolean seriesCompleted = "6a7c2663e9cc163c31e5cd62".equals(scenarioId)
+                && "6a7c003127a2b19abbba302e".equals(taskStatusId);
+        if (!designCompleted && !seriesCompleted) {
+            return false;
+        }
+
+        String created = task.getString("created");
+        if (StringUtils.isBlank(created) || created.compareTo("2026-01-01") < 0) {
+            return false;
+        }
+
+        JSONArray tagIds = task.getJSONArray("tagIds");
+        if (tagIds != null && (tagIds.contains("68f5840f90efa8d7498bd5e9")
+                || tagIds.contains("68f58407a89ee3bbe639f85c"))) {
+            return false;
+        }
+
+        String designLevel = getCommissionDetailFieldValue(
+                task.getJSONArray("customfields"),
+                "691c29c1f8cbae320a1c8e75"
+        );
+        return !"\u65e0".equals(designLevel) && !"\u6269\u7248".equals(designLevel);
+    }
+
+    private Object[] buildCommissionDetailRow(JSONObject task, Map<String, String> header) {
+        JSONArray customFields = task.getJSONArray("customfields");
+        String taskId = task.getString("id");
+        return new Object[]{
+                taskId,
+                task.getString("content"),
+                getCommissionDetailFieldValue(customFields, "6a8e4ce280d14a316ce57b0e"),
+                getCommissionDetailFieldValue(customFields, "687df3d4d7dc27d64d57201e"),
+                getCommissionDetailFieldValue(customFields, "6a798e303023b086a6a38f47"),
+                getCommissionDetailFieldValue(customFields, "691aafe69819400cd5497bbf"),
+                getCommissionDetailFieldValue(customFields, "691c29c1f8cbae320a1c8e75"),
+                getCommissionDetailFieldValue(customFields, "688191972ab6c4f412ea2bb9"),
+                getBusinessConfirmationStatus(taskId, header)
+        };
+    }
+
+    private void upsertCommissionDetailRow(Object[] row) {
+        int updated = jdbcTemplate.update(
+                "UPDATE commission_detail SET task_name = ?, xm = ?, khjc = ?, jg = ?, xs = ?, " +
+                        "dj = ?, ywmc = ?, ywsfqy = ? WHERE task_id = ?",
+                row[1], row[2], row[3], row[4], row[5], row[6], row[7], row[8], row[0]
+        );
+        if (updated == 0) {
+            jdbcTemplate.update(
+                    "INSERT INTO commission_detail " +
+                            "(task_id, task_name, xm, khjc, jg, xs, dj, ywmc, ywsfqy) " +
+                            "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",
+                    row
+            );
+        }
+    }
+
+    private void collectCommissionDetailTaskIds(String projectId, String condition,
+                                                Map<String, String> header, Set<String> taskIds) throws Exception {
+        Map<String, Object> countParam = new HashMap<>();
+        countParam.put("q", condition);
+        String countResponse = UtilHttp.doGet(
+                "https://open.teambition.com/api/v3/project/" + projectId + "/task/count",
+                header,
+                countParam
+        );
+        Matcher matcher = Pattern.compile("\\\"result\\\":(\\d+)").matcher(countResponse);
+        int total = matcher.find() ? Integer.parseInt(matcher.group(1)) : 0;
+        String pageToken = "";
+
+        for (int page = 0; page < (total + 99) / 100; page++) {
+            Map<String, Object> queryParam = new HashMap<>();
+            queryParam.put("pageSize", 100);
+            queryParam.put("q", condition);
+            if (StringUtils.isNotBlank(pageToken)) {
+                queryParam.put("pageToken", pageToken);
+            }
+
+            String queryResponse = UtilHttp.doGet(
+                    "https://open.teambition.com/api/v3/project/" + projectId + "/task/query",
+                    header,
+                    queryParam
+            );
+            taskIds.addAll(extractIdsFromJson(queryResponse));
+            JsonNode nextPageToken = new ObjectMapper().readTree(queryResponse).get("nextPageToken");
+            pageToken = nextPageToken == null ? "" : nextPageToken.asText();
+            if (StringUtils.isBlank(pageToken)) {
+                break;
+            }
+        }
+    }
+
+    private JSONObject queryCommissionDetailTask(String taskId, Map<String, String> header) {
+        try {
+            Map<String, Object> param = new HashMap<>();
+            param.put("taskId", taskId);
+            String response = UtilHttp.doGet("https://open.teambition.com/api/v3/task/query", header, param);
+            JSONArray result = JSON.parseObject(response).getJSONArray("result");
+            return result == null || result.isEmpty() ? null : result.getJSONObject(0);
+        } catch (Exception e) {
+            log.error("taskId={}", taskId, e);
+            return null;
+        }
+    }
+
+    private String getCommissionDetailFieldValue(JSONArray customFields, String fieldId) {
+        if (customFields == null) {
+            return "";
+        }
+        List<String> values = new ArrayList<>();
+        for (int i = 0; i < customFields.size(); i++) {
+            JSONObject field = customFields.getJSONObject(i);
+            if (!fieldId.equals(field.getString("cfId"))) {
+                continue;
+            }
+            JSONArray valueArray = field.getJSONArray("value");
+            if (valueArray == null) {
+                continue;
+            }
+            for (int j = 0; j < valueArray.size(); j++) {
+                String value = valueArray.getJSONObject(j).getString("title");
+                if (StringUtils.isNotBlank(value)) {
+                    values.add(value);
+                }
+            }
+        }
+        return String.join(",", values);
+    }
+
+    private String getBusinessConfirmationStatus(String taskId, Map<String, String> header) {
+        try {
+            String response = UtilHttp.doGet(
+                    "https://open.teambition.com/api/v3/task/" + taskId + "/node/list",
+                    header,
+                    new HashMap<>()
+            );
+            JSONArray nodes = JSON.parseObject(response).getJSONArray("result");
+            if (nodes == null) {
+                return "\u672a\u786e\u8ba4";
+            }
+            for (int i = 0; i < nodes.size(); i++) {
+                JSONObject node = nodes.getJSONObject(i);
+                String nodeTfsId = node.getString("tfsId");
+                if ("6a4b18e857406a1184ebad21".equals(nodeTfsId)
+                        || "6a7c003127a2b19abbba302d".equals(nodeTfsId)) {
+                    return "finish".equalsIgnoreCase(node.getString("status")) ? "\u5df2\u786e\u8ba4" : "\u672a\u786e\u8ba4";
+                }
+            }
+        } catch (Exception e) {
+            log.error("taskId={}", taskId, e);
+        }
+        return "\u672a\u786e\u8ba4";
+    }
+
     @SneakyThrows
     @PostMapping("/UserGetDeprt")
     String UserGetDeprt(@RequestParam String User){

+ 1 - 1
mjava-ounuo/src/main/resources/application.yml

@@ -1,3 +1,3 @@
 spring:
   profiles:
-    active: prod
+    active: dev