package com.malk.controller; import com.malk.service.workhours.WorkHoursCalcService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import java.time.LocalDate; import java.util.LinkedHashMap; import java.util.Map; /** * 应填报工时临时接口(验证用) */ @Slf4j @RestController @RequestMapping("/workhours") public class WorkHoursController { @Autowired private WorkHoursCalcService workHoursCalcService; /** * 批量删除全部应填报工时数据 * GET /workhours/delete-all */ @GetMapping("/delete-all") public Map deleteAll() { Map result = new LinkedHashMap<>(); try { long start = System.currentTimeMillis(); workHoursCalcService.deleteAllRequiredHours(); long cost = System.currentTimeMillis() - start; result.put("success", true); result.put("message", "应填报工时数据已清空"); result.put("costMs", cost); } catch (Exception e) { log.error("删除失败", e); result.put("success", false); result.put("message", e.getMessage()); } return result; } /** * 单员工单日验证写入(清空后做一条验证,确认归属公司等字段正确带入) * GET /workhours/sync-one?userId=xxx&date=2026-04-22 */ @GetMapping("/sync-one") public Map syncOne(@RequestParam String userId, @RequestParam String date) { Map result = new LinkedHashMap<>(); try { long start = System.currentTimeMillis(); LocalDate workDay = LocalDate.parse(date); Map data = workHoursCalcService.syncOneEmployeeOneDay(userId, workDay); long cost = System.currentTimeMillis() - start; Object ok = data.get("success"); result.put("success", Boolean.TRUE.equals(ok)); result.put("message", Boolean.TRUE.equals(ok) ? "单条验证写入完成" : "单条验证写入失败"); result.put("data", data); result.put("costMs", cost); } catch (Exception e) { log.error("单条验证失败", e); result.put("success", false); result.put("message", e.getMessage()); } return result; } /** * 小批量同步:前 N 个内部员工 × 当月工作日(默认 count=5) * GET /workhours/sync-batch?count=5&month=2026-04 */ @GetMapping("/sync-batch") public Map syncBatch(@RequestParam(defaultValue = "5") int count, @RequestParam(required = false) String month) { Map result = new LinkedHashMap<>(); try { LocalDate targetMonth = null; if (month != null && !month.isEmpty()) { targetMonth = LocalDate.parse(month + "-01"); } long start = System.currentTimeMillis(); Map stats = workHoursCalcService.syncBatchEmployees(count, targetMonth); long cost = System.currentTimeMillis() - start; result.put("success", true); result.put("message", "小批量同步完成"); result.put("stats", stats); result.put("costMs", cost); } catch (Exception e) { log.error("小批量同步失败", e); result.put("success", false); result.put("message", e.getMessage()); } return result; } /** * 回填存量应填报工时的「是否cf员工」字段(textField_mpp7a2k7),值取自人员档案 * GET /workhours/backfill-cf (实际更新) * GET /workhours/backfill-cf?dryRun=true(仅扫描预览,不更新) */ @GetMapping("/backfill-cf") public Map backfillCf(@RequestParam(defaultValue = "false") boolean dryRun) { Map result = new LinkedHashMap<>(); try { long start = System.currentTimeMillis(); Map stats = workHoursCalcService.backfillCfEmployee(dryRun); long cost = System.currentTimeMillis() - start; result.put("success", true); result.put("message", dryRun ? "cf字段回填预览完成(未更新)" : "cf字段回填完成"); result.put("stats", stats); result.put("costMs", cost); } catch (Exception e) { log.error("cf字段回填失败", e); result.put("success", false); result.put("message", e.getMessage()); } return result; } /** * 回填存量应填报工时的「归属公司」+「属性」两字段, 值取自人员档案 * ppExt: 一次性接口, 不设定时; 主流程 upsertDailyHours 已带最新两字段, 后续变动自动同步, 老记录靠本接口一次对齐 * GET /workhours/backfill-company-attr (实际更新) * GET /workhours/backfill-company-attr?dryRun=true(仅扫描预览, 不更新) */ @GetMapping("/backfill-company-attr") public Map backfillCompanyAttr(@RequestParam(defaultValue = "false") boolean dryRun) { Map result = new LinkedHashMap<>(); try { long start = System.currentTimeMillis(); Map stats = workHoursCalcService.backfillCompanyAndAttribute(dryRun); long cost = System.currentTimeMillis() - start; result.put("success", true); result.put("message", dryRun ? "公司+属性回填预览完成(未更新)" : "公司+属性回填完成"); result.put("stats", stats); result.put("costMs", cost); } catch (Exception e) { log.error("公司+属性回填失败", e); result.put("success", false); result.put("message", e.getMessage()); } return result; } /** * 清理同一员工同一日期的重复应填报工时(一次性接口,默认仅预览) * GET /workhours/cleanup-duplicates (仅预览) * GET /workhours/cleanup-duplicates?dryRun=false(实际删除) */ @GetMapping("/cleanup-duplicates") public Map cleanupDuplicates(@RequestParam(defaultValue = "true") boolean dryRun) { Map result = new LinkedHashMap<>(); try { long start = System.currentTimeMillis(); Map stats = workHoursCalcService.cleanupDuplicateHours(dryRun); long cost = System.currentTimeMillis() - start; result.put("success", true); result.put("message", dryRun ? "重复工时清理预览完成(未删除)" : "重复工时清理完成"); result.put("stats", stats); result.put("costMs", cost); } catch (Exception e) { log.error("重复工时清理失败", e); result.put("success", false); result.put("message", e.getMessage()); } return result; } /** * 清理已离职员工「离职日之后」的历史应报工时(一次性接口) * GET /workhours/cleanup-after-offline (实际删除) * GET /workhours/cleanup-after-offline?dryRun=true(仅统计不删除) */ @GetMapping("/cleanup-after-offline") public Map cleanupAfterOffline(@RequestParam(defaultValue = "false") boolean dryRun) { Map result = new LinkedHashMap<>(); try { long start = System.currentTimeMillis(); Map stats = workHoursCalcService.cleanupAfterOffline(dryRun); long cost = System.currentTimeMillis() - start; result.put("success", true); result.put("message", dryRun ? "离职后工时清理预览完成(未删除)" : "离职后工时清理完成"); result.put("stats", stats); result.put("costMs", cost); } catch (Exception e) { log.error("离职后工时清理失败", e); result.put("success", false); result.put("message", e.getMessage()); } return result; } /** * 清理已写入的"未来"应报工时 (workDay > cutoff, cutoff 缺省 today) * GET /workhours/cleanup-future (实际删除, cutoff=today) * GET /workhours/cleanup-future?dryRun=true (仅预览) * GET /workhours/cleanup-future?cutoff=2026-07-15 (指定保留截止日) */ @GetMapping("/cleanup-future") public Map cleanupFuture(@RequestParam(required = false) String cutoff, @RequestParam(defaultValue = "false") boolean dryRun) { Map result = new LinkedHashMap<>(); try { LocalDate cutoffDate = (cutoff == null || cutoff.isEmpty()) ? null : LocalDate.parse(cutoff); long start = System.currentTimeMillis(); Map stats = workHoursCalcService.cleanupFutureHours(cutoffDate, dryRun); long cost = System.currentTimeMillis() - start; result.put("success", true); result.put("message", dryRun ? "未来工时清理预览完成(未删除)" : "未来工时清理完成"); result.put("stats", stats); result.put("costMs", cost); } catch (Exception e) { log.error("未来工时清理失败", e); result.put("success", false); result.put("message", e.getMessage()); } return result; } /** * 项目变更审批增量同步(手动触发;定时器 ProjectChangeSyncTimer 定期跑) * GET /workhours/sync-project-changes?daysBack=7 * 只更新变更时间未来的应报工时 Manager, 变更前记录一律不动 */ @GetMapping("/sync-project-changes") public Map syncProjectChanges(@RequestParam(defaultValue = "7") int daysBack) { Map result = new LinkedHashMap<>(); try { long start = System.currentTimeMillis(); Map stats = workHoursCalcService.syncProjectChanges(daysBack); long cost = System.currentTimeMillis() - start; result.put("success", true); result.put("message", "项目变更增量同步完成"); result.put("stats", stats); result.put("costMs", cost); } catch (Exception e) { log.error("项目变更同步失败", e); result.put("success", false); result.put("message", e.getMessage()); } return result; } /** * 全量同步当月应填报工时 * GET /workhours/sync?month=2026-04 */ @GetMapping("/sync") public Map sync(@RequestParam(required = false) String month) { Map result = new LinkedHashMap<>(); try { LocalDate targetMonth = null; if (month != null && !month.isEmpty()) { targetMonth = LocalDate.parse(month + "-01"); } long start = System.currentTimeMillis(); Map stats = workHoursCalcService.calculateAndSyncMonthlyHours(targetMonth); long cost = System.currentTimeMillis() - start; result.put("success", true); result.put("message", "全量同步完成"); result.put("stats", stats); result.put("costMs", cost); } catch (Exception e) { log.error("同步失败", e); result.put("success", false); result.put("message", e.getMessage()); } return result; } }