package org.dromara.crm.controller; import cn.dev33.satoken.annotation.SaCheckPermission; import jakarta.servlet.http.HttpServletResponse; import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotNull; import lombok.RequiredArgsConstructor; import org.dromara.common.core.domain.R; import org.dromara.common.core.validate.AddGroup; import org.dromara.common.core.validate.EditGroup; import org.dromara.common.excel.utils.ExcelUtil; import org.dromara.common.idempotent.annotation.RepeatSubmit; import org.dromara.common.log.annotation.Log; import org.dromara.common.log.enums.BusinessType; import org.dromara.common.mybatis.core.page.PageQuery; import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.common.web.core.BaseController; import org.dromara.crm.domain.bo.CrmQuoteBo; import org.dromara.crm.domain.vo.CrmQuoteVo; import org.dromara.crm.service.ICrmQuoteService; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import java.util.List; import java.util.Map; /** * 报价单管理Controller * * @author 系统管理员 * @date 2025-01-03 */ @Validated @RequiredArgsConstructor @RestController @RequestMapping("/crm/quote") public class CrmQuoteController extends BaseController { private final ICrmQuoteService quoteService; /** * 查询报价单列表(分页) */ // @SaCheckPermission("crm:quote:list") @GetMapping("/list") public TableDataInfo list(CrmQuoteBo bo, PageQuery pageQuery) { return quoteService.queryPageList(bo, pageQuery); } /** * 查询报价单详情 */ // @SaCheckPermission("crm:quote:query") @GetMapping("/{id}") public R getInfo(@NotNull(message = "主键不能为空") @PathVariable Long id) { return R.ok(quoteService.queryById(id)); } /** * 新增报价单 */ // @SaCheckPermission("crm:quote:add") @Log(title = "报价单管理", businessType = BusinessType.INSERT) @RepeatSubmit() @PostMapping() public R add(@Validated(AddGroup.class) @RequestBody CrmQuoteBo bo) { boolean success = quoteService.insertByBo(bo); if (success) { return R.ok("新增成功", bo.getId()); } return R.fail("新增失败"); } /** * 修改报价单 */ // @SaCheckPermission("crm:quote:edit") @Log(title = "报价单管理", businessType = BusinessType.UPDATE) @RepeatSubmit() @PutMapping() public R edit(@Validated(EditGroup.class) @RequestBody CrmQuoteBo bo) { return toAjax(quoteService.updateByBo(bo)); } /** * 删除报价单 */ // @SaCheckPermission("crm:quote:remove") @Log(title = "报价单管理", businessType = BusinessType.DELETE) @DeleteMapping("/{ids}") public R remove(@NotEmpty(message = "主键不能为空") @PathVariable Long[] ids) { return toAjax(quoteService.deleteWithValidByIds(List.of(ids), true)); } /** * 提交报价单 */ // @SaCheckPermission("crm:quote:submit") @Log(title = "提交报价单", businessType = BusinessType.UPDATE) @RepeatSubmit() @PutMapping("/submit") public R submit(@RequestBody Map params) { // 验证ID参数 Object idObj = params.get("id"); if (idObj == null) { return R.fail("报价单ID不能为空"); } Long id = Long.valueOf(idObj.toString()); // 安全转换approverIds,支持Integer、Long、String等类型 @SuppressWarnings("unchecked") List approverIdsRaw = (List) params.get("approverIds"); List approverIds = null; if (approverIdsRaw != null) { approverIds = approverIdsRaw.stream() .map(obj -> { if (obj instanceof Number) { return ((Number) obj).longValue(); } else { return Long.valueOf(obj.toString()); } }) .collect(java.util.stream.Collectors.toList()); } String remark = (String) params.get("remark"); return toAjax(quoteService.submitQuote(id, approverIds, remark)); } /** * 保存报价单模板内容 */ // @SaCheckPermission("crm:quote:edit") @Log(title = "保存报价单模板", businessType = BusinessType.UPDATE) @RepeatSubmit() @PostMapping("/saveTemplate") public R saveTemplate(@RequestBody Map params) { Object idObj = params.get("id"); if (idObj == null) { return R.fail("报价单ID不能为空"); } Long id = Long.valueOf(idObj.toString()); String templateContent = (String) params.get("templateContent"); return toAjax(quoteService.saveTemplate(id, templateContent)); } /** * 获取报价单模板内容 */ // @SaCheckPermission("crm:quote:query") @GetMapping("/getTemplate/{id}") public R getTemplate(@NotNull(message = "主键不能为空") @PathVariable Long id) { String templateContent = quoteService.getTemplate(id); // 使用 R.ok(msg, data) 确保数据在 data 字段中 return R.ok("获取模板成功", templateContent); } /** * 生成PDF */ // @SaCheckPermission("crm:quote:edit") @Log(title = "生成PDF", businessType = BusinessType.UPDATE) @PostMapping("/generatePdf") public R generatePdf(@RequestBody Map params) { Object idObj = params.get("id"); if (idObj == null) { return R.fail("报价单ID不能为空"); } Long id = Long.valueOf(idObj.toString()); String pdfUrl = quoteService.generatePdf(id); return R.ok(pdfUrl); } /** * 生成分享链接 */ // @SaCheckPermission("crm:quote:share") @Log(title = "生成分享链接", businessType = BusinessType.UPDATE) @PostMapping("/generateShare") public R generateShare(@RequestBody Map params) { Object idObj = params.get("id"); if (idObj == null) { return R.fail("报价单ID不能为空"); } Long id = Long.valueOf(idObj.toString()); Integer validityDays = params.get("validityDays") != null ? Integer.valueOf(params.get("validityDays").toString()) : 7; String password = (String) params.get("password"); String shareLink = quoteService.generateShare(id, validityDays, password); return R.ok(shareLink); } /** * 通过分享码查看报价单 */ @GetMapping("/share/{shareCode}") public R viewByShareCode(@PathVariable String shareCode, @RequestParam(required = false) String password, jakarta.servlet.http.HttpServletRequest request) { String ipAddress = getClientIp(request); String userAgent = request.getHeader("User-Agent"); return R.ok(quoteService.viewByShareCode(shareCode, password, ipAddress, userAgent)); } /** * 获取客户端IP地址 */ private String getClientIp(jakarta.servlet.http.HttpServletRequest request) { String ip = request.getHeader("X-Forwarded-For"); if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("Proxy-Client-IP"); } if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("WL-Proxy-Client-IP"); } if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) { ip = request.getRemoteAddr(); } return ip; } /** * 接受报价单 */ // @SaCheckPermission("crm:quote:edit") @Log(title = "接受报价单", businessType = BusinessType.UPDATE) @PutMapping("/accept") public R accept(@RequestBody Map params) { Object idObj = params.get("id"); if (idObj == null) { return R.fail("报价单ID不能为空"); } Long id = Long.valueOf(idObj.toString()); String remark = (String) params.get("remark"); return toAjax(quoteService.acceptQuote(id, remark)); } /** * 拒绝报价单 */ // @SaCheckPermission("crm:quote:edit") @Log(title = "拒绝报价单", businessType = BusinessType.UPDATE) @PutMapping("/reject") public R reject(@RequestBody Map params) { Object idObj = params.get("id"); if (idObj == null) { return R.fail("报价单ID不能为空"); } Long id = Long.valueOf(idObj.toString()); String rejectReason = (String) params.get("rejectReason"); String remark = (String) params.get("remark"); return toAjax(quoteService.rejectQuote(id, rejectReason, remark)); } /** * 转为合同 */ // @SaCheckPermission("crm:quote:toContract") @Log(title = "转为合同", businessType = BusinessType.INSERT) @PostMapping("/toContract") public R toContract(@RequestBody Map params) { Object idObj = params.get("id"); if (idObj == null) { return R.fail("报价单ID不能为空"); } Long id = Long.valueOf(idObj.toString()); String contractNo = (String) params.get("contractNo"); String contractTitle = (String) params.get("contractTitle"); // 可选参数 java.time.LocalDate signDate = params.get("signDate") != null ? java.time.LocalDate.parse(params.get("signDate").toString()) : null; java.time.LocalDate startDate = params.get("startDate") != null ? java.time.LocalDate.parse(params.get("startDate").toString()) : null; java.time.LocalDate endDate = params.get("endDate") != null ? java.time.LocalDate.parse(params.get("endDate").toString()) : null; Long contractId = quoteService.toContract(id, contractNo, contractTitle, signDate, startDate, endDate); return R.ok(contractId); } /** * 创建新版本 */ // @SaCheckPermission("crm:quote:add") @Log(title = "创建新版本", businessType = BusinessType.INSERT) @PostMapping("/newVersion") public R newVersion(@RequestBody Map params) { Object idObj = params.get("id"); if (idObj == null) { return R.fail("报价单ID不能为空"); } Long id = Long.valueOf(idObj.toString()); String version = (String) params.get("version"); String remark = (String) params.get("remark"); Long newId = quoteService.createNewVersion(id, version, remark); return R.ok(newId); } /** * 查询版本历史 */ // @SaCheckPermission("crm:quote:query") @GetMapping("/versions") public R> queryVersions(@RequestParam String quoteNo) { return R.ok(quoteService.queryVersions(quoteNo)); } /** * 导出报价单列表 */ // @SaCheckPermission("crm:quote:export") @Log(title = "报价单管理", businessType = BusinessType.EXPORT) @PostMapping("/export") public void export(CrmQuoteBo bo, HttpServletResponse response) { List list = quoteService.queryList(bo); ExcelUtil.exportExcel(list, "报价单数据", CrmQuoteVo.class, response); } /** * 报价单统计概览 */ // @SaCheckPermission("crm:quote:list") @GetMapping("/statistics/overview") public R> statisticsOverview(@RequestParam(required = false) Long ownerUserId, @RequestParam(required = false) String beginTime, @RequestParam(required = false) String endTime) { return R.ok(quoteService.statisticsOverview(ownerUserId, beginTime, endTime)); } /** * 报价单状态分布 */ // @SaCheckPermission("crm:quote:list") @GetMapping("/statistics/status") public R>> statisticsStatus() { return R.ok(quoteService.statisticsStatus()); } /** * 报价单趋势分析 */ // @SaCheckPermission("crm:quote:list") @GetMapping("/statistics/trend") public R>> statisticsTrend(@RequestParam(defaultValue = "month") String type, @RequestParam(required = false) String beginTime, @RequestParam(required = false) String endTime) { return R.ok(quoteService.statisticsTrend(type, beginTime, endTime)); } /** * 撤回审批 */ // @SaCheckPermission("crm:quote:edit") @Log(title = "撤回审批", businessType = BusinessType.UPDATE) @PutMapping("/withdrawApproval") public R withdrawApproval(@RequestBody Map params) { Long id = Long.valueOf(params.get("id").toString()); String remark = (String) params.get("remark"); return toAjax(quoteService.withdrawApproval(id, remark)); } /** * 作废报价单 */ // @SaCheckPermission("crm:quote:invalid") @Log(title = "作废报价单", businessType = BusinessType.UPDATE) @PutMapping("/invalid") public R invalid(@RequestBody Map params) { Object idObj = params.get("id"); if (idObj == null) { return R.fail("报价单ID不能为空"); } Long id = Long.valueOf(idObj.toString()); String remark = (String) params.get("remark"); return toAjax(quoteService.invalidQuote(id, remark)); } }