package com.kaidi.oa.web; import com.kaidi.oa.common.ApiException; import com.kaidi.oa.common.ApiResp; import com.kaidi.oa.common.NotFoundException; import com.kaidi.oa.domain.ContractTemplate; import com.kaidi.oa.repository.ContractTemplateRepository; 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; /** * Contract template library in the unified contract center (business chain 3). * contractType is one of 经营合同 / 采购合同 / 成本合同 / 销售合同 / 法务合同; * applicableSubject is one of 总公司 / 分公司 / 集团小公司 / 不限; * status is one of 启用 / 停用. */ @RestController @RequestMapping("/api/oa/contract-templates") public class ContractTemplateController { private final ContractTemplateRepository templateRepo; public ContractTemplateController(ContractTemplateRepository templateRepo) { this.templateRepo = templateRepo; } @GetMapping public ApiResp> list(@RequestParam(required = false) String contractType, @RequestParam(required = false) String status) { List list; if (contractType != null && !contractType.isBlank()) { list = templateRepo.findByContractType(contractType); } else if (status != null && !status.isBlank()) { list = templateRepo.findByStatus(status); } else { list = templateRepo.findAll(); } return ApiResp.ok(list); } @GetMapping("/{id}") public ApiResp get(@PathVariable Long id) { return ApiResp.ok(templateRepo.findById(id) .orElseThrow(() -> new NotFoundException("contract template not found: " + id))); } /** * 取模板正文(供「模板起草」页消费 bodyTemplate,并附必备条款与占位符列表)。 * 之前 bodyTemplate 字段已存却无页面消费(LOGIC_GAP),此端点把模板正文显式端出, * 让起草页能"选模板 → 拉正文 → 填占位符 → 生成草稿"。占位符抽取 {xxx} 形式。 */ @GetMapping("/{id}/body") public ApiResp body(@PathVariable Long id) { ContractTemplate t = templateRepo.findById(id) .orElseThrow(() -> new NotFoundException("contract template not found: " + id)); List placeholders = extractPlaceholders(t.getBodyTemplate()); return ApiResp.ok(new TemplateBody(t.getId(), t.getName(), t.getContractType(), t.getApplicableSubject(), t.getVersion(), t.getBodyTemplate(), t.getRequiredClauses(), placeholders)); } public record TemplateBody(Long id, String name, String contractType, String applicableSubject, String version, String bodyTemplate, String requiredClauses, List placeholders) { } /** 抽取 {占位符} 形式的占位符名(去重保序),供起草页生成填充表单。 */ private static List extractPlaceholders(String body) { List out = new java.util.ArrayList<>(); if (body == null || body.isBlank()) { return out; } java.util.regex.Matcher m = java.util.regex.Pattern.compile("\\{([^{}]{1,40})\\}").matcher(body); while (m.find()) { String name = m.group(1).trim(); if (!name.isEmpty() && !out.contains(name)) { out.add(name); } } return out; } public record CreateContractTemplateRequest( String name, String contractType, String applicableSubject, String bodyTemplate, String requiredClauses, String version, String status, String updatedBy) { } @PostMapping public ApiResp create(@RequestBody CreateContractTemplateRequest req) { if (req.name() == null || req.name().isBlank()) { throw new ApiException(400, "name is required"); } ContractTemplate t = new ContractTemplate(); t.setName(req.name()); t.setContractType(req.contractType()); t.setApplicableSubject(req.applicableSubject()); t.setBodyTemplate(req.bodyTemplate()); t.setRequiredClauses(req.requiredClauses()); t.setVersion(req.version() == null || req.version().isBlank() ? "V1.0" : req.version()); t.setStatus(req.status() == null || req.status().isBlank() ? "启用" : req.status()); t.setUpdatedBy(req.updatedBy()); t.setCreatedAt(Instant.now()); return ApiResp.ok(templateRepo.save(t)); } }