package com.kaidi.oa.web; import com.kaidi.oa.common.Money; import com.kaidi.oa.common.ApiException; import com.kaidi.oa.common.ApiResp; import com.kaidi.oa.common.NotFoundException; import com.kaidi.oa.domain.Budget; import com.kaidi.oa.repository.BudgetRepository; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; 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.RequestParam; import org.springframework.web.bind.annotation.RestController; import java.time.Instant; import java.util.List; /** * Unified budget / cost-center for budgeting and execution monitoring * (budget vs. actual cost). element (cost element) is one of * 人工费 / 材料费 / 机械费 / 差旅费 / 管理费 / 外协费 / 分包费; period is one of * 年度 / 季度 / 月度; status is one of 执行中 / 超支 / 已结. Creating a budget whose * actualAmount exceeds budgetAmount automatically sets status to 超支. */ @RestController @RequestMapping("/api/oa/budgets") public class BudgetController { private final BudgetRepository budgetRepo; public BudgetController(BudgetRepository budgetRepo) { this.budgetRepo = budgetRepo; } @GetMapping public ApiResp> list(@RequestParam(required = false) Long projectId, @RequestParam(required = false) String element, @RequestParam(required = false) String status) { List list; if (projectId != null) { list = budgetRepo.findByProjectId(projectId); } else if (element != null && !element.isBlank()) { list = budgetRepo.findByElement(element); } else if (status != null && !status.isBlank()) { list = budgetRepo.findByStatus(status); } else { list = budgetRepo.findAll(); } return ApiResp.ok(list); } @GetMapping("/{id}") public ApiResp get(@PathVariable Long id) { return ApiResp.ok(budgetRepo.findById(id) .orElseThrow(() -> new NotFoundException("budget not found: " + id))); } public record CreateBudgetRequest( String name, Long projectId, String companySubject, String element, String year, String period, Double budgetAmount, Double actualAmount, String owner, String status) { } @PostMapping public ApiResp create(@RequestBody CreateBudgetRequest req) { if (req.name() == null || req.name().isBlank()) { throw new ApiException(400, "name is required"); } double budgetAmount = req.budgetAmount() == null ? 0 : req.budgetAmount(); double actualAmount = req.actualAmount() == null ? 0 : req.actualAmount(); Budget b = new Budget(); b.setName(req.name()); b.setProjectId(req.projectId()); b.setCompanySubject(req.companySubject()); b.setElement(req.element()); b.setYear(req.year()); b.setPeriod(req.period()); b.setBudgetAmount(Money.of(budgetAmount)); b.setActualAmount(Money.of(actualAmount)); b.setOwner(req.owner()); if (Money.gt(b.getActualAmount(), b.getBudgetAmount())) { b.setStatus("超支"); } else { b.setStatus(req.status() == null || req.status().isBlank() ? "执行中" : req.status()); } b.setCreatedAt(Instant.now()); return ApiResp.ok(budgetRepo.save(b)); } }