CrmQuoteController.java 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383
  1. package org.dromara.crm.controller;
  2. import cn.dev33.satoken.annotation.SaCheckPermission;
  3. import jakarta.servlet.http.HttpServletResponse;
  4. import jakarta.validation.constraints.NotEmpty;
  5. import jakarta.validation.constraints.NotNull;
  6. import lombok.RequiredArgsConstructor;
  7. import org.dromara.common.core.domain.R;
  8. import org.dromara.common.core.validate.AddGroup;
  9. import org.dromara.common.core.validate.EditGroup;
  10. import org.dromara.common.excel.utils.ExcelUtil;
  11. import org.dromara.common.idempotent.annotation.RepeatSubmit;
  12. import org.dromara.common.log.annotation.Log;
  13. import org.dromara.common.log.enums.BusinessType;
  14. import org.dromara.common.mybatis.core.page.PageQuery;
  15. import org.dromara.common.mybatis.core.page.TableDataInfo;
  16. import org.dromara.common.web.core.BaseController;
  17. import org.dromara.crm.domain.bo.CrmQuoteBo;
  18. import org.dromara.crm.domain.vo.CrmQuoteVo;
  19. import org.dromara.crm.service.ICrmQuoteService;
  20. import org.springframework.validation.annotation.Validated;
  21. import org.springframework.web.bind.annotation.*;
  22. import java.util.List;
  23. import java.util.Map;
  24. /**
  25. * 报价单管理Controller
  26. *
  27. * @author 系统管理员
  28. * @date 2025-01-03
  29. */
  30. @Validated
  31. @RequiredArgsConstructor
  32. @RestController
  33. @RequestMapping("/crm/quote")
  34. public class CrmQuoteController extends BaseController {
  35. private final ICrmQuoteService quoteService;
  36. /**
  37. * 查询报价单列表(分页)
  38. */
  39. // @SaCheckPermission("crm:quote:list")
  40. @GetMapping("/list")
  41. public TableDataInfo<CrmQuoteVo> list(CrmQuoteBo bo, PageQuery pageQuery) {
  42. return quoteService.queryPageList(bo, pageQuery);
  43. }
  44. /**
  45. * 查询报价单详情
  46. */
  47. // @SaCheckPermission("crm:quote:query")
  48. @GetMapping("/{id}")
  49. public R<CrmQuoteVo> getInfo(@NotNull(message = "主键不能为空") @PathVariable Long id) {
  50. return R.ok(quoteService.queryById(id));
  51. }
  52. /**
  53. * 新增报价单
  54. */
  55. // @SaCheckPermission("crm:quote:add")
  56. @Log(title = "报价单管理", businessType = BusinessType.INSERT)
  57. @RepeatSubmit()
  58. @PostMapping()
  59. public R<Long> add(@Validated(AddGroup.class) @RequestBody CrmQuoteBo bo) {
  60. boolean success = quoteService.insertByBo(bo);
  61. if (success) {
  62. return R.ok("新增成功", bo.getId());
  63. }
  64. return R.fail("新增失败");
  65. }
  66. /**
  67. * 修改报价单
  68. */
  69. // @SaCheckPermission("crm:quote:edit")
  70. @Log(title = "报价单管理", businessType = BusinessType.UPDATE)
  71. @RepeatSubmit()
  72. @PutMapping()
  73. public R<Void> edit(@Validated(EditGroup.class) @RequestBody CrmQuoteBo bo) {
  74. return toAjax(quoteService.updateByBo(bo));
  75. }
  76. /**
  77. * 删除报价单
  78. */
  79. // @SaCheckPermission("crm:quote:remove")
  80. @Log(title = "报价单管理", businessType = BusinessType.DELETE)
  81. @DeleteMapping("/{ids}")
  82. public R<Void> remove(@NotEmpty(message = "主键不能为空") @PathVariable Long[] ids) {
  83. return toAjax(quoteService.deleteWithValidByIds(List.of(ids), true));
  84. }
  85. /**
  86. * 提交报价单
  87. */
  88. // @SaCheckPermission("crm:quote:submit")
  89. @Log(title = "提交报价单", businessType = BusinessType.UPDATE)
  90. @RepeatSubmit()
  91. @PutMapping("/submit")
  92. public R<Void> submit(@RequestBody Map<String, Object> params) {
  93. // 验证ID参数
  94. Object idObj = params.get("id");
  95. if (idObj == null) {
  96. return R.fail("报价单ID不能为空");
  97. }
  98. Long id = Long.valueOf(idObj.toString());
  99. // 安全转换approverIds,支持Integer、Long、String等类型
  100. @SuppressWarnings("unchecked")
  101. List<Object> approverIdsRaw = (List<Object>) params.get("approverIds");
  102. List<Long> approverIds = null;
  103. if (approverIdsRaw != null) {
  104. approverIds = approverIdsRaw.stream()
  105. .map(obj -> {
  106. if (obj instanceof Number) {
  107. return ((Number) obj).longValue();
  108. } else {
  109. return Long.valueOf(obj.toString());
  110. }
  111. })
  112. .collect(java.util.stream.Collectors.toList());
  113. }
  114. String remark = (String) params.get("remark");
  115. return toAjax(quoteService.submitQuote(id, approverIds, remark));
  116. }
  117. /**
  118. * 保存报价单模板内容
  119. */
  120. // @SaCheckPermission("crm:quote:edit")
  121. @Log(title = "保存报价单模板", businessType = BusinessType.UPDATE)
  122. @RepeatSubmit()
  123. @PostMapping("/saveTemplate")
  124. public R<Void> saveTemplate(@RequestBody Map<String, Object> params) {
  125. Object idObj = params.get("id");
  126. if (idObj == null) {
  127. return R.fail("报价单ID不能为空");
  128. }
  129. Long id = Long.valueOf(idObj.toString());
  130. String templateContent = (String) params.get("templateContent");
  131. return toAjax(quoteService.saveTemplate(id, templateContent));
  132. }
  133. /**
  134. * 获取报价单模板内容
  135. */
  136. // @SaCheckPermission("crm:quote:query")
  137. @GetMapping("/getTemplate/{id}")
  138. public R<String> getTemplate(@NotNull(message = "主键不能为空") @PathVariable Long id) {
  139. String templateContent = quoteService.getTemplate(id);
  140. // 使用 R.ok(msg, data) 确保数据在 data 字段中
  141. return R.ok("获取模板成功", templateContent);
  142. }
  143. /**
  144. * 生成PDF
  145. */
  146. // @SaCheckPermission("crm:quote:edit")
  147. @Log(title = "生成PDF", businessType = BusinessType.UPDATE)
  148. @PostMapping("/generatePdf")
  149. public R<String> generatePdf(@RequestBody Map<String, Object> params) {
  150. Object idObj = params.get("id");
  151. if (idObj == null) {
  152. return R.fail("报价单ID不能为空");
  153. }
  154. Long id = Long.valueOf(idObj.toString());
  155. String pdfUrl = quoteService.generatePdf(id);
  156. return R.ok(pdfUrl);
  157. }
  158. /**
  159. * 生成分享链接
  160. */
  161. // @SaCheckPermission("crm:quote:share")
  162. @Log(title = "生成分享链接", businessType = BusinessType.UPDATE)
  163. @PostMapping("/generateShare")
  164. public R<String> generateShare(@RequestBody Map<String, Object> params) {
  165. Object idObj = params.get("id");
  166. if (idObj == null) {
  167. return R.fail("报价单ID不能为空");
  168. }
  169. Long id = Long.valueOf(idObj.toString());
  170. Integer validityDays = params.get("validityDays") != null
  171. ? Integer.valueOf(params.get("validityDays").toString())
  172. : 7;
  173. String password = (String) params.get("password");
  174. String shareLink = quoteService.generateShare(id, validityDays, password);
  175. return R.ok(shareLink);
  176. }
  177. /**
  178. * 通过分享码查看报价单
  179. */
  180. @GetMapping("/share/{shareCode}")
  181. public R<CrmQuoteVo> viewByShareCode(@PathVariable String shareCode,
  182. @RequestParam(required = false) String password,
  183. jakarta.servlet.http.HttpServletRequest request) {
  184. String ipAddress = getClientIp(request);
  185. String userAgent = request.getHeader("User-Agent");
  186. return R.ok(quoteService.viewByShareCode(shareCode, password, ipAddress, userAgent));
  187. }
  188. /**
  189. * 获取客户端IP地址
  190. */
  191. private String getClientIp(jakarta.servlet.http.HttpServletRequest request) {
  192. String ip = request.getHeader("X-Forwarded-For");
  193. if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
  194. ip = request.getHeader("Proxy-Client-IP");
  195. }
  196. if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
  197. ip = request.getHeader("WL-Proxy-Client-IP");
  198. }
  199. if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
  200. ip = request.getRemoteAddr();
  201. }
  202. return ip;
  203. }
  204. /**
  205. * 接受报价单
  206. */
  207. // @SaCheckPermission("crm:quote:edit")
  208. @Log(title = "接受报价单", businessType = BusinessType.UPDATE)
  209. @PutMapping("/accept")
  210. public R<Void> accept(@RequestBody Map<String, Object> params) {
  211. Object idObj = params.get("id");
  212. if (idObj == null) {
  213. return R.fail("报价单ID不能为空");
  214. }
  215. Long id = Long.valueOf(idObj.toString());
  216. String remark = (String) params.get("remark");
  217. return toAjax(quoteService.acceptQuote(id, remark));
  218. }
  219. /**
  220. * 拒绝报价单
  221. */
  222. // @SaCheckPermission("crm:quote:edit")
  223. @Log(title = "拒绝报价单", businessType = BusinessType.UPDATE)
  224. @PutMapping("/reject")
  225. public R<Void> reject(@RequestBody Map<String, Object> params) {
  226. Object idObj = params.get("id");
  227. if (idObj == null) {
  228. return R.fail("报价单ID不能为空");
  229. }
  230. Long id = Long.valueOf(idObj.toString());
  231. String rejectReason = (String) params.get("rejectReason");
  232. String remark = (String) params.get("remark");
  233. return toAjax(quoteService.rejectQuote(id, rejectReason, remark));
  234. }
  235. /**
  236. * 转为合同
  237. */
  238. // @SaCheckPermission("crm:quote:toContract")
  239. @Log(title = "转为合同", businessType = BusinessType.INSERT)
  240. @PostMapping("/toContract")
  241. public R<Long> toContract(@RequestBody Map<String, Object> params) {
  242. Object idObj = params.get("id");
  243. if (idObj == null) {
  244. return R.fail("报价单ID不能为空");
  245. }
  246. Long id = Long.valueOf(idObj.toString());
  247. String contractNo = (String) params.get("contractNo");
  248. String contractTitle = (String) params.get("contractTitle");
  249. // 可选参数
  250. java.time.LocalDate signDate = params.get("signDate") != null
  251. ? java.time.LocalDate.parse(params.get("signDate").toString())
  252. : null;
  253. java.time.LocalDate startDate = params.get("startDate") != null
  254. ? java.time.LocalDate.parse(params.get("startDate").toString())
  255. : null;
  256. java.time.LocalDate endDate = params.get("endDate") != null
  257. ? java.time.LocalDate.parse(params.get("endDate").toString())
  258. : null;
  259. Long contractId = quoteService.toContract(id, contractNo, contractTitle, signDate, startDate, endDate);
  260. return R.ok(contractId);
  261. }
  262. /**
  263. * 创建新版本
  264. */
  265. // @SaCheckPermission("crm:quote:add")
  266. @Log(title = "创建新版本", businessType = BusinessType.INSERT)
  267. @PostMapping("/newVersion")
  268. public R<Long> newVersion(@RequestBody Map<String, Object> params) {
  269. Object idObj = params.get("id");
  270. if (idObj == null) {
  271. return R.fail("报价单ID不能为空");
  272. }
  273. Long id = Long.valueOf(idObj.toString());
  274. String version = (String) params.get("version");
  275. String remark = (String) params.get("remark");
  276. Long newId = quoteService.createNewVersion(id, version, remark);
  277. return R.ok(newId);
  278. }
  279. /**
  280. * 查询版本历史
  281. */
  282. // @SaCheckPermission("crm:quote:query")
  283. @GetMapping("/versions")
  284. public R<List<CrmQuoteVo>> queryVersions(@RequestParam String quoteNo) {
  285. return R.ok(quoteService.queryVersions(quoteNo));
  286. }
  287. /**
  288. * 导出报价单列表
  289. */
  290. // @SaCheckPermission("crm:quote:export")
  291. @Log(title = "报价单管理", businessType = BusinessType.EXPORT)
  292. @PostMapping("/export")
  293. public void export(CrmQuoteBo bo, HttpServletResponse response) {
  294. List<CrmQuoteVo> list = quoteService.queryList(bo);
  295. ExcelUtil.exportExcel(list, "报价单数据", CrmQuoteVo.class, response);
  296. }
  297. /**
  298. * 报价单统计概览
  299. */
  300. // @SaCheckPermission("crm:quote:list")
  301. @GetMapping("/statistics/overview")
  302. public R<Map<String, Object>> statisticsOverview(@RequestParam(required = false) Long ownerUserId,
  303. @RequestParam(required = false) String beginTime,
  304. @RequestParam(required = false) String endTime) {
  305. return R.ok(quoteService.statisticsOverview(ownerUserId, beginTime, endTime));
  306. }
  307. /**
  308. * 报价单状态分布
  309. */
  310. // @SaCheckPermission("crm:quote:list")
  311. @GetMapping("/statistics/status")
  312. public R<List<Map<String, Object>>> statisticsStatus() {
  313. return R.ok(quoteService.statisticsStatus());
  314. }
  315. /**
  316. * 报价单趋势分析
  317. */
  318. // @SaCheckPermission("crm:quote:list")
  319. @GetMapping("/statistics/trend")
  320. public R<List<Map<String, Object>>> statisticsTrend(@RequestParam(defaultValue = "month") String type,
  321. @RequestParam(required = false) String beginTime,
  322. @RequestParam(required = false) String endTime) {
  323. return R.ok(quoteService.statisticsTrend(type, beginTime, endTime));
  324. }
  325. /**
  326. * 撤回审批
  327. */
  328. // @SaCheckPermission("crm:quote:edit")
  329. @Log(title = "撤回审批", businessType = BusinessType.UPDATE)
  330. @PutMapping("/withdrawApproval")
  331. public R<Void> withdrawApproval(@RequestBody Map<String, Object> params) {
  332. Long id = Long.valueOf(params.get("id").toString());
  333. String remark = (String) params.get("remark");
  334. return toAjax(quoteService.withdrawApproval(id, remark));
  335. }
  336. /**
  337. * 作废报价单
  338. */
  339. // @SaCheckPermission("crm:quote:invalid")
  340. @Log(title = "作废报价单", businessType = BusinessType.UPDATE)
  341. @PutMapping("/invalid")
  342. public R<Void> invalid(@RequestBody Map<String, Object> params) {
  343. Object idObj = params.get("id");
  344. if (idObj == null) {
  345. return R.fail("报价单ID不能为空");
  346. }
  347. Long id = Long.valueOf(idObj.toString());
  348. String remark = (String) params.get("remark");
  349. return toAjax(quoteService.invalidQuote(id, remark));
  350. }
  351. }