diff --git a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/controller/CrmGiftApplyController.java b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/controller/CrmGiftApplyController.java new file mode 100644 index 00000000..4c050207 --- /dev/null +++ b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/controller/CrmGiftApplyController.java @@ -0,0 +1,144 @@ +package org.dromara.oa.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.oa.crm.domain.bo.CrmGiftApplyBo; +import org.dromara.oa.crm.domain.vo.CrmGiftApplyVo; +import org.dromara.oa.crm.service.ICrmGiftApplyService; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 礼品申请 + * 前端访问路由地址为:/oa/crm/crmGiftApply + * + * @author Yinq + * @date 2025-12-19 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/crm/crmGiftApply") +public class CrmGiftApplyController extends BaseController { + + private final ICrmGiftApplyService crmGiftApplyService; + + /** + * 查询礼品申请列表 + */ + @SaCheckPermission("oa/crm:crmGiftApply:list") + @GetMapping("/list") + public TableDataInfo list(CrmGiftApplyBo bo, PageQuery pageQuery) { + return crmGiftApplyService.queryPageList(bo, pageQuery); + } + + /** + * 导出礼品申请列表 + */ + @SaCheckPermission("oa/crm:crmGiftApply:export") + @Log(title = "礼品申请", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(CrmGiftApplyBo bo, HttpServletResponse response) { + List list = crmGiftApplyService.queryList(bo); + ExcelUtil.exportExcel(list, "礼品申请", CrmGiftApplyVo.class, response); + } + + /** + * 获取礼品申请详细信息 + * + * @param giftApplyId 主键 + */ + @SaCheckPermission("oa/crm:crmGiftApply:query") + @GetMapping("/{giftApplyId}") + public R getInfo(@NotNull(message = "主键不能为空") + @PathVariable("giftApplyId") Long giftApplyId) { + return R.ok(crmGiftApplyService.queryById(giftApplyId)); + } + + /** + * 新增礼品申请 + */ + @SaCheckPermission("oa/crm:crmGiftApply:add") + @Log(title = "礼品申请", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + public R add(@Validated(AddGroup.class) @RequestBody CrmGiftApplyBo bo) { + return toAjax(crmGiftApplyService.insertByBo(bo)); + } + + /** + * 修改礼品申请 + */ + @SaCheckPermission("oa/crm:crmGiftApply:edit") + @Log(title = "礼品申请", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + public R edit(@Validated(EditGroup.class) @RequestBody CrmGiftApplyBo bo) { + return toAjax(crmGiftApplyService.updateByBo(bo)); + } + + /** + * 删除礼品申请 + * + * @param giftApplyIds 主键串 + */ + @SaCheckPermission("oa/crm:crmGiftApply:remove") + @Log(title = "礼品申请", businessType = BusinessType.DELETE) + @DeleteMapping("/{giftApplyIds}") + public R remove(@NotEmpty(message = "主键不能为空") + @PathVariable("giftApplyIds") Long[] giftApplyIds) { + return toAjax(crmGiftApplyService.deleteWithValidByIds(List.of(giftApplyIds), true)); + } + + /** + * 提交礼品申请并发起审批流程 + */ + @SaCheckPermission("oa/crm:crmGiftApply:add") + @Log(title = "礼品申请", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping("/submitAndFlowStart") + public R giftApplySubmitAndFlowStart(@RequestBody CrmGiftApplyBo bo) { + return R.ok(crmGiftApplyService.giftApplySubmitAndFlowStart(bo)); + } + + /** + * 下拉框查询礼品申请列表 + */ + @GetMapping("/getCrmGiftApplyList") + public R> getCrmGiftApplyList(CrmGiftApplyBo bo) { + List list = crmGiftApplyService.queryList(bo); + return R.ok(list); + } + + /** + * 批量发放礼品 + * 业务流程: + * 1. 校验申请状态必须为"已审批"(3) + * 2. 遍历申请明细,批量创建发放记录 + * 3. 自动扣减库存 + * 4. 更新申请单的领用日期 + */ + @SaCheckPermission("oa/crm:crmGiftApply:issue") + @Log(title = "礼品发放", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PostMapping("/batchIssue") + public R batchIssue(@RequestBody CrmGiftApplyBo bo) { + return toAjax(crmGiftApplyService.batchIssueGifts(bo)); + } + +} diff --git a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/controller/CrmGiftApplyDetailController.java b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/controller/CrmGiftApplyDetailController.java new file mode 100644 index 00000000..1897bc62 --- /dev/null +++ b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/controller/CrmGiftApplyDetailController.java @@ -0,0 +1,117 @@ +package org.dromara.oa.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.oa.crm.domain.bo.CrmGiftApplyDetailBo; +import org.dromara.oa.crm.domain.vo.CrmGiftApplyDetailVo; +import org.dromara.oa.crm.service.ICrmGiftApplyDetailService; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 礼品申请明细 + * 前端访问路由地址为:/oa/crm/crmGiftApplyDetail + * + * @author Yinq + * @date 2025-12-19 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/crm/crmGiftApplyDetail") +public class CrmGiftApplyDetailController extends BaseController { + + private final ICrmGiftApplyDetailService crmGiftApplyDetailService; + + /** + * 查询礼品申请明细列表 + */ + @SaCheckPermission("oa/crm:crmGiftApplyDetail:list") + @GetMapping("/list") + public TableDataInfo list(CrmGiftApplyDetailBo bo, PageQuery pageQuery) { + return crmGiftApplyDetailService.queryPageList(bo, pageQuery); + } + + /** + * 导出礼品申请明细列表 + */ + @SaCheckPermission("oa/crm:crmGiftApplyDetail:export") + @Log(title = "礼品申请明细", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(CrmGiftApplyDetailBo bo, HttpServletResponse response) { + List list = crmGiftApplyDetailService.queryList(bo); + ExcelUtil.exportExcel(list, "礼品申请明细", CrmGiftApplyDetailVo.class, response); + } + + /** + * 获取礼品申请明细详细信息 + * + * @param detailId 主键 + */ + @SaCheckPermission("oa/crm:crmGiftApplyDetail:query") + @GetMapping("/{detailId}") + public R getInfo(@NotNull(message = "主键不能为空") + @PathVariable("detailId") Long detailId) { + return R.ok(crmGiftApplyDetailService.queryById(detailId)); + } + + /** + * 新增礼品申请明细 + */ + @SaCheckPermission("oa/crm:crmGiftApplyDetail:add") + @Log(title = "礼品申请明细", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + public R add(@Validated(AddGroup.class) @RequestBody CrmGiftApplyDetailBo bo) { + return toAjax(crmGiftApplyDetailService.insertByBo(bo)); + } + + /** + * 修改礼品申请明细 + */ + @SaCheckPermission("oa/crm:crmGiftApplyDetail:edit") + @Log(title = "礼品申请明细", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + public R edit(@Validated(EditGroup.class) @RequestBody CrmGiftApplyDetailBo bo) { + return toAjax(crmGiftApplyDetailService.updateByBo(bo)); + } + + /** + * 删除礼品申请明细 + * + * @param detailIds 主键串 + */ + @SaCheckPermission("oa/crm:crmGiftApplyDetail:remove") + @Log(title = "礼品申请明细", businessType = BusinessType.DELETE) + @DeleteMapping("/{detailIds}") + public R remove(@NotEmpty(message = "主键不能为空") + @PathVariable("detailIds") Long[] detailIds) { + return toAjax(crmGiftApplyDetailService.deleteWithValidByIds(List.of(detailIds), true)); + } + + /** + * 下拉框查询礼品申请明细列表 + */ + @GetMapping("/getCrmGiftApplyDetailList") + public R> getCrmGiftApplyDetailList(CrmGiftApplyDetailBo bo) { + List list = crmGiftApplyDetailService.queryList(bo); + return R.ok(list); + } + +} diff --git a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/controller/CrmGiftInfoController.java b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/controller/CrmGiftInfoController.java new file mode 100644 index 00000000..23402d92 --- /dev/null +++ b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/controller/CrmGiftInfoController.java @@ -0,0 +1,117 @@ +package org.dromara.oa.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.oa.crm.domain.bo.CrmGiftInfoBo; +import org.dromara.oa.crm.domain.vo.CrmGiftInfoVo; +import org.dromara.oa.crm.service.ICrmGiftInfoService; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 礼品信息 + * 前端访问路由地址为:/oa/crm/crmGiftInfo + * + * @author Yinq + * @date 2025-12-19 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/crm/crmGiftInfo") +public class CrmGiftInfoController extends BaseController { + + private final ICrmGiftInfoService crmGiftInfoService; + + /** + * 查询礼品信息列表 + */ + @SaCheckPermission("oa/crm:crmGiftInfo:list") + @GetMapping("/list") + public TableDataInfo list(CrmGiftInfoBo bo, PageQuery pageQuery) { + return crmGiftInfoService.queryPageList(bo, pageQuery); + } + + /** + * 导出礼品信息列表 + */ + @SaCheckPermission("oa/crm:crmGiftInfo:export") + @Log(title = "礼品信息", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(CrmGiftInfoBo bo, HttpServletResponse response) { + List list = crmGiftInfoService.queryList(bo); + ExcelUtil.exportExcel(list, "礼品信息", CrmGiftInfoVo.class, response); + } + + /** + * 获取礼品信息详细信息 + * + * @param giftId 主键 + */ + @SaCheckPermission("oa/crm:crmGiftInfo:query") + @GetMapping("/{giftId}") + public R getInfo(@NotNull(message = "主键不能为空") + @PathVariable("giftId") Long giftId) { + return R.ok(crmGiftInfoService.queryById(giftId)); + } + + /** + * 新增礼品信息 + */ + @SaCheckPermission("oa/crm:crmGiftInfo:add") + @Log(title = "礼品信息", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + public R add(@Validated(AddGroup.class) @RequestBody CrmGiftInfoBo bo) { + return toAjax(crmGiftInfoService.insertByBo(bo)); + } + + /** + * 修改礼品信息 + */ + @SaCheckPermission("oa/crm:crmGiftInfo:edit") + @Log(title = "礼品信息", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + public R edit(@Validated(EditGroup.class) @RequestBody CrmGiftInfoBo bo) { + return toAjax(crmGiftInfoService.updateByBo(bo)); + } + + /** + * 删除礼品信息 + * + * @param giftIds 主键串 + */ + @SaCheckPermission("oa/crm:crmGiftInfo:remove") + @Log(title = "礼品信息", businessType = BusinessType.DELETE) + @DeleteMapping("/{giftIds}") + public R remove(@NotEmpty(message = "主键不能为空") + @PathVariable("giftIds") Long[] giftIds) { + return toAjax(crmGiftInfoService.deleteWithValidByIds(List.of(giftIds), true)); + } + + /** + * 下拉框查询礼品信息列表 + */ + @GetMapping("/getCrmGiftInfoList") + public R> getCrmGiftInfoList(CrmGiftInfoBo bo) { + List list = crmGiftInfoService.queryList(bo); + return R.ok(list); + } + +} diff --git a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/controller/CrmGiftIssueRecordController.java b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/controller/CrmGiftIssueRecordController.java new file mode 100644 index 00000000..7ac1ce9e --- /dev/null +++ b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/controller/CrmGiftIssueRecordController.java @@ -0,0 +1,117 @@ +package org.dromara.oa.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.oa.crm.domain.bo.CrmGiftIssueRecordBo; +import org.dromara.oa.crm.domain.vo.CrmGiftIssueRecordVo; +import org.dromara.oa.crm.service.ICrmGiftIssueRecordService; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 礼品发放记录 + * 前端访问路由地址为:/oa/crm/crmGiftIssueRecord + * + * @author Yinq + * @date 2025-12-19 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/crm/crmGiftIssueRecord") +public class CrmGiftIssueRecordController extends BaseController { + + private final ICrmGiftIssueRecordService crmGiftIssueRecordService; + + /** + * 查询礼品发放记录列表 + */ + @SaCheckPermission("oa/crm:crmGiftIssueRecord:list") + @GetMapping("/list") + public TableDataInfo list(CrmGiftIssueRecordBo bo, PageQuery pageQuery) { + return crmGiftIssueRecordService.queryPageList(bo, pageQuery); + } + + /** + * 导出礼品发放记录列表 + */ + @SaCheckPermission("oa/crm:crmGiftIssueRecord:export") + @Log(title = "礼品发放记录", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(CrmGiftIssueRecordBo bo, HttpServletResponse response) { + List list = crmGiftIssueRecordService.queryList(bo); + ExcelUtil.exportExcel(list, "礼品发放记录", CrmGiftIssueRecordVo.class, response); + } + + /** + * 获取礼品发放记录详细信息 + * + * @param issueRecordId 主键 + */ + @SaCheckPermission("oa/crm:crmGiftIssueRecord:query") + @GetMapping("/{issueRecordId}") + public R getInfo(@NotNull(message = "主键不能为空") + @PathVariable("issueRecordId") Long issueRecordId) { + return R.ok(crmGiftIssueRecordService.queryById(issueRecordId)); + } + + /** + * 新增礼品发放记录 + */ + @SaCheckPermission("oa/crm:crmGiftIssueRecord:add") + @Log(title = "礼品发放记录", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + public R add(@Validated(AddGroup.class) @RequestBody CrmGiftIssueRecordBo bo) { + return toAjax(crmGiftIssueRecordService.insertByBo(bo)); + } + + /** + * 修改礼品发放记录 + */ + @SaCheckPermission("oa/crm:crmGiftIssueRecord:edit") + @Log(title = "礼品发放记录", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + public R edit(@Validated(EditGroup.class) @RequestBody CrmGiftIssueRecordBo bo) { + return toAjax(crmGiftIssueRecordService.updateByBo(bo)); + } + + /** + * 删除礼品发放记录 + * + * @param issueRecordIds 主键串 + */ + @SaCheckPermission("oa/crm:crmGiftIssueRecord:remove") + @Log(title = "礼品发放记录", businessType = BusinessType.DELETE) + @DeleteMapping("/{issueRecordIds}") + public R remove(@NotEmpty(message = "主键不能为空") + @PathVariable("issueRecordIds") Long[] issueRecordIds) { + return toAjax(crmGiftIssueRecordService.deleteWithValidByIds(List.of(issueRecordIds), true)); + } + + /** + * 下拉框查询礼品发放记录列表 + */ + @GetMapping("/getCrmGiftIssueRecordList") + public R> getCrmGiftIssueRecordList(CrmGiftIssueRecordBo bo) { + List list = crmGiftIssueRecordService.queryList(bo); + return R.ok(list); + } + +} diff --git a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/domain/CrmGiftApply.java b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/domain/CrmGiftApply.java new file mode 100644 index 00000000..7cac682f --- /dev/null +++ b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/domain/CrmGiftApply.java @@ -0,0 +1,132 @@ +package org.dromara.oa.crm.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.tenant.core.TenantEntity; + +import java.io.Serial; +import java.math.BigDecimal; +import java.util.Date; + +/** + * 礼品申请对象 crm_gift_apply + * + * @author Yinq + * @date 2025-12-19 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("crm_gift_apply") +public class CrmGiftApply extends TenantEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 申请单ID + */ + @TableId(value = "gift_apply_id", type = IdType.ASSIGN_ID) + private Long giftApplyId; + + /** + * 礼品申请编号 + */ + private String applicationCode; + + /** + * 申请人ID + */ + private Long applicantId; + + /** + * 申请人姓名 + */ + private String applicantName; + + /** + * 申请人所属部门ID + */ + private Long applicantDeptId; + + /** + * 申请人所属部门名称 + */ + private String applicantDeptName; + + /** + * 申请日期 + */ + private Date applicationDate; + + /** + * 客户ID + */ + private Long customerUnitId; + + /** + * 客户名称 + */ + private String customerUnitName; + + /** + * 客人姓名 + */ + private String guestName; + + /** + * 人数 + */ + private Integer peopleCount; + + /** + * 申请事由 + */ + private String applicationReason; + + /** + * 领用人ID + */ + private Long recipientId; + + /** + * 领用人姓名 + */ + private String recipientName; + + /** + * 领用日期 + */ + private Date recipientDate; + + /** + * 申请总金额 + */ + private BigDecimal totalAmount; + + /** + * 礼品申请状态(1暂存 2审批中 3已审批 4作废) + */ + private String applicationStatus; + + /** + * 流程状态 + */ + private String flowStatus; + + /** + * 备注 + */ + private String remark; + + /** + * 删除标志(0代表存在 1代表删除) + */ + @TableLogic + private String delFlag; + + +} diff --git a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/domain/CrmGiftApplyDetail.java b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/domain/CrmGiftApplyDetail.java new file mode 100644 index 00000000..5eae1fa9 --- /dev/null +++ b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/domain/CrmGiftApplyDetail.java @@ -0,0 +1,96 @@ +package org.dromara.oa.crm.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.tenant.core.TenantEntity; + +import java.io.Serial; +import java.math.BigDecimal; + +/** + * 礼品申请明细对象 crm_gift_apply_detail + * + * @author Yinq + * @date 2025-12-19 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("crm_gift_apply_detail") +public class CrmGiftApplyDetail extends TenantEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 礼品申请明细ID + */ + @TableId(value = "detail_id", type = IdType.AUTO) + private Long detailId; + + /** + * 申请单ID + */ + private Long applicationId; + + /** + * 礼品ID + */ + private Long giftId; + + /** + * 礼品编码 + */ + private String giftCode; + + /** + * 礼品名称 + */ + private String giftName; + + /** + * 规格型号 + */ + private String specification; + + /** + * 单价(含税) + */ + private BigDecimal unitPrice; + + /** + * 库存数量 + */ + private Integer inventoryQuantity; + + /** + * 申请数量 + */ + private Integer applicationQuantity; + + /** + * 礼品金额 + */ + private BigDecimal giftAmount; + + /** + * 排序号 + */ + private Integer sortOrder; + + /** + * 备注 + */ + private String remark; + + /** + * 删除标志(0代表存在 1代表删除) + */ + @TableLogic + private String delFlag; + + +} diff --git a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/domain/CrmGiftInfo.java b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/domain/CrmGiftInfo.java new file mode 100644 index 00000000..678ccbbd --- /dev/null +++ b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/domain/CrmGiftInfo.java @@ -0,0 +1,76 @@ +package org.dromara.oa.crm.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.tenant.core.TenantEntity; + +import java.io.Serial; +import java.math.BigDecimal; + +/** + * 礼品信息对象 crm_gift_info + * + * @author Yinq + * @date 2025-12-19 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("crm_gift_info") +public class CrmGiftInfo extends TenantEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 礼品ID + */ + @TableId(value = "gift_id", type = IdType.AUTO) + private Long giftId; + + /** + * 礼品编码 + */ + private String giftCode; + + /** + * 礼品名称 + */ + private String giftName; + + /** + * 规格型号 + */ + private String specification; + + /** + * 采购单价(含税) + */ + private BigDecimal unitPrice; + + /** + * 单位ID + */ + private Long unitId; + + /** + * 库存数量 + */ + private Integer inventoryQuantity; + + /** + * 备注 + */ + private String remark; + + /** + * 删除标志(0代表存在 1代表删除) + */ + @TableLogic + private String delFlag; + + +} diff --git a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/domain/CrmGiftIssueRecord.java b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/domain/CrmGiftIssueRecord.java new file mode 100644 index 00000000..4b8cfd75 --- /dev/null +++ b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/domain/CrmGiftIssueRecord.java @@ -0,0 +1,131 @@ +package org.dromara.oa.crm.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.tenant.core.TenantEntity; + +import java.io.Serial; +import java.util.Date; + +/** + * 礼品发放记录对象 crm_gift_issue_record + * + * @author Yinq + * @date 2025-12-19 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("crm_gift_issue_record") +public class CrmGiftIssueRecord extends TenantEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 发放记录ID + */ + @TableId(value = "issue_record_id", type = IdType.AUTO) + private Long issueRecordId; + + /** + * 申请单ID + */ + private Long applicationId; + + /** + * 申请单编号 + */ + private String applicationCode; + + /** + * 申请明细ID + */ + private Long detailId; + + /** + * 礼品ID + */ + private Long giftId; + + /** + * 礼品编码 + */ + private String giftCode; + + /** + * 礼品名称 + */ + private String giftName; + + /** + * 规格型号 + */ + private String specification; + + /** + * 发放数量 + */ + private Integer issueQuantity; + + /** + * 发放时间 + */ + private Date issueDate; + + /** + * 领取人ID + */ + private Long recipientId; + + /** + * 领取人姓名 + */ + private String recipientName; + + /** + * 发放人ID + */ + private Long issueBy; + + /** + * 发放人姓名 + */ + private String issueByName; + + /** + * 发放部门ID + */ + private Long issueDeptId; + + /** + * 发放部门名称 + */ + private String issueDeptName; + + /** + * 发放前库存数量 + */ + private Integer beforeInventory; + + /** + * 发放后库存数量 + */ + private Integer afterInventory; + + /** + * 备注 + */ + private String remark; + + /** + * 删除标志(0代表存在 1代表删除) + */ + @TableLogic + private String delFlag; + + +} diff --git a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/domain/bo/CrmGiftApplyBo.java b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/domain/bo/CrmGiftApplyBo.java new file mode 100644 index 00000000..710ff622 --- /dev/null +++ b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/domain/bo/CrmGiftApplyBo.java @@ -0,0 +1,176 @@ +package org.dromara.oa.crm.domain.bo; + +import cn.hutool.core.util.ObjectUtil; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; +import org.dromara.oa.crm.domain.CrmGiftApply; +import org.dromara.oa.crm.domain.CrmGiftApplyDetail; +import org.dromara.workflow.api.domain.RemoteFlowInstanceBizExt; + +import java.math.BigDecimal; +import java.util.*; + +/** + * 礼品申请业务对象 crm_gift_apply + * + * @author Yinq + * @date 2025-12-19 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = CrmGiftApply.class, reverseConvertGenerate = false) +public class CrmGiftApplyBo extends BaseEntity { + + /** + * 申请单ID + */ + @NotNull(message = "申请单ID不能为空", groups = { EditGroup.class }) + private Long giftApplyId; + + /** + * 礼品申请编号 + */ + @NotBlank(message = "礼品申请编号不能为空", groups = { AddGroup.class, EditGroup.class }) + private String applicationCode; + + /** + * 申请人ID + */ + @NotNull(message = "申请人ID不能为空", groups = { AddGroup.class, EditGroup.class }) + private Long applicantId; + + /** + * 申请人姓名 + */ + private String applicantName; + + /** + * 申请人所属部门ID + */ + @NotNull(message = "申请人所属部门ID不能为空", groups = { AddGroup.class, EditGroup.class }) + private Long applicantDeptId; + + /** + * 申请人所属部门名称 + */ + private String applicantDeptName; + + /** + * 申请日期 + */ + @NotNull(message = "申请日期不能为空", groups = { AddGroup.class, EditGroup.class }) + private Date applicationDate; + + /** + * 客户ID + */ + private Long customerUnitId; + + /** + * 客户名称 + */ + private String customerUnitName; + + /** + * 客人姓名 + */ + private String guestName; + + /** + * 人数 + */ + private Integer peopleCount; + + /** + * 申请事由 + */ + private String applicationReason; + + /** + * 领用人ID + */ + private Long recipientId; + + /** + * 领用人姓名 + */ + private String recipientName; + + /** + * 领用日期 + */ + private Date recipientDate; + + /** + * 申请总金额 + */ + private BigDecimal totalAmount; + + /** + * 礼品申请状态(1暂存 2审批中 3已审批 4作废) + */ + private String applicationStatus; + + /** + * 流程状态 + */ + private String flowStatus; + + /** + * 备注 + */ + private String remark; + + // ==================== 流程相关字段 ==================== + + /** + * 流程定义编码 + */ + private String flowCode; + + /** + * 办理人(可不填 用于覆盖当前节点办理人) + */ + private String handler; + + /** + * 流程变量,前端会提交一个元素{'entity': {业务详情数据对象}} + */ + private Map variables; + + /** + * 流程业务扩展信息 + */ + private RemoteFlowInstanceBizExt bizExt; + + public Map getVariables() { + if (variables == null) { + return new HashMap<>(16); + } + variables.entrySet().removeIf(entry -> Objects.isNull(entry.getValue())); + return variables; + } + + public RemoteFlowInstanceBizExt getBizExt() { + if (ObjectUtil.isNull(bizExt)) { + bizExt = new RemoteFlowInstanceBizExt(); + } + return bizExt; + } + + + + // ==================== 子表数据 ==================== + + /** + * 礼品申请明细列表 + */ + private List detailList; + +} diff --git a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/domain/bo/CrmGiftApplyDetailBo.java b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/domain/bo/CrmGiftApplyDetailBo.java new file mode 100644 index 00000000..79a028b8 --- /dev/null +++ b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/domain/bo/CrmGiftApplyDetailBo.java @@ -0,0 +1,90 @@ +package org.dromara.oa.crm.domain.bo; + +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; +import org.dromara.oa.crm.domain.CrmGiftApplyDetail; + +import java.math.BigDecimal; + +/** + * 礼品申请明细业务对象 crm_gift_apply_detail + * + * @author Yinq + * @date 2025-12-19 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = CrmGiftApplyDetail.class, reverseConvertGenerate = false) +public class CrmGiftApplyDetailBo extends BaseEntity { + + /** + * 礼品申请明细ID + */ + @NotNull(message = "礼品申请明细ID不能为空", groups = { EditGroup.class }) + private Long detailId; + + /** + * 申请单ID + */ + @NotNull(message = "申请单ID不能为空", groups = { AddGroup.class, EditGroup.class }) + private Long applicationId; + + /** + * 礼品ID + */ + @NotNull(message = "礼品ID不能为空", groups = { AddGroup.class, EditGroup.class }) + private Long giftId; + + /** + * 礼品编码 + */ + private String giftCode; + + /** + * 礼品名称 + */ + private String giftName; + + /** + * 规格型号 + */ + private String specification; + + /** + * 单价(含税) + */ + private BigDecimal unitPrice; + + /** + * 库存数量 + */ + private Integer inventoryQuantity; + + /** + * 申请数量 + */ + @NotNull(message = "申请数量不能为空", groups = { AddGroup.class, EditGroup.class }) + private Integer applicationQuantity; + + /** + * 礼品金额 + */ + private BigDecimal giftAmount; + + /** + * 排序号 + */ + private Integer sortOrder; + + /** + * 备注 + */ + private String remark; + + +} diff --git a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/domain/bo/CrmGiftInfoBo.java b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/domain/bo/CrmGiftInfoBo.java new file mode 100644 index 00000000..4670fd75 --- /dev/null +++ b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/domain/bo/CrmGiftInfoBo.java @@ -0,0 +1,69 @@ +package org.dromara.oa.crm.domain.bo; + +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; +import org.dromara.oa.crm.domain.CrmGiftInfo; + +import java.math.BigDecimal; + +/** + * 礼品信息业务对象 crm_gift_info + * + * @author Yinq + * @date 2025-12-19 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = CrmGiftInfo.class, reverseConvertGenerate = false) +public class CrmGiftInfoBo extends BaseEntity { + + /** + * 礼品ID + */ + @NotNull(message = "礼品ID不能为空", groups = { EditGroup.class }) + private Long giftId; + + /** + * 礼品编码 + */ + private String giftCode; + + /** + * 礼品名称 + */ + @NotBlank(message = "礼品名称不能为空", groups = { AddGroup.class, EditGroup.class }) + private String giftName; + + /** + * 规格型号 + */ + private String specification; + + /** + * 采购单价(含税) + */ + private BigDecimal unitPrice; + + /** + * 单位ID + */ + private Long unitId; + + /** + * 库存数量 + */ + private Integer inventoryQuantity; + + /** + * 备注 + */ + private String remark; + + +} diff --git a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/domain/bo/CrmGiftIssueRecordBo.java b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/domain/bo/CrmGiftIssueRecordBo.java new file mode 100644 index 00000000..eb6c7fb0 --- /dev/null +++ b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/domain/bo/CrmGiftIssueRecordBo.java @@ -0,0 +1,128 @@ +package org.dromara.oa.crm.domain.bo; + +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; +import org.dromara.oa.crm.domain.CrmGiftIssueRecord; + +import java.util.Date; + +/** + * 礼品发放记录业务对象 crm_gift_issue_record + * + * @author Yinq + * @date 2025-12-19 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = CrmGiftIssueRecord.class, reverseConvertGenerate = false) +public class CrmGiftIssueRecordBo extends BaseEntity { + + /** + * 发放记录ID + */ + @NotNull(message = "发放记录ID不能为空", groups = { EditGroup.class }) + private Long issueRecordId; + + /** + * 申请单ID + */ + @NotNull(message = "申请单ID不能为空", groups = { AddGroup.class, EditGroup.class }) + private Long applicationId; + + /** + * 申请单编号 + */ + private String applicationCode; + + /** + * 申请明细ID + */ + @NotNull(message = "申请明细ID不能为空", groups = { AddGroup.class, EditGroup.class }) + private Long detailId; + + /** + * 礼品ID + */ + @NotNull(message = "礼品ID不能为空", groups = { AddGroup.class, EditGroup.class }) + private Long giftId; + + /** + * 礼品编码 + */ + private String giftCode; + + /** + * 礼品名称 + */ + private String giftName; + + /** + * 规格型号 + */ + private String specification; + + /** + * 发放数量 + */ + @NotNull(message = "发放数量不能为空", groups = { AddGroup.class, EditGroup.class }) + private Integer issueQuantity; + + /** + * 发放时间 + */ + @NotNull(message = "发放时间不能为空", groups = { AddGroup.class, EditGroup.class }) + private Date issueDate; + + /** + * 领取人ID + */ + @NotNull(message = "领取人ID不能为空", groups = { AddGroup.class, EditGroup.class }) + private Long recipientId; + + /** + * 领取人姓名 + */ + private String recipientName; + + /** + * 发放人ID + */ + private Long issueBy; + + /** + * 发放人姓名 + */ + private String issueByName; + + /** + * 发放部门ID + */ + private Long issueDeptId; + + /** + * 发放部门名称 + */ + private String issueDeptName; + + /** + * 发放前库存数量 + */ + private Integer beforeInventory; + + /** + * 发放后库存数量 + */ + private Integer afterInventory; + + /** + * 备注 + */ + private String remark; + + +} diff --git a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/domain/vo/CrmGiftApplyDetailVo.java b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/domain/vo/CrmGiftApplyDetailVo.java new file mode 100644 index 00000000..00a58a1d --- /dev/null +++ b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/domain/vo/CrmGiftApplyDetailVo.java @@ -0,0 +1,102 @@ +package org.dromara.oa.crm.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import org.dromara.oa.crm.domain.CrmGiftApplyDetail; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; + + + +/** + * 礼品申请明细视图对象 crm_gift_apply_detail + * + * @author Yinq + * @date 2025-12-19 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = CrmGiftApplyDetail.class) +public class CrmGiftApplyDetailVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 礼品申请明细ID + */ + @ExcelProperty(value = "礼品申请明细ID") + private Long detailId; + + /** + * 申请单ID + */ + @ExcelProperty(value = "申请单ID") + private Long applicationId; + + /** + * 礼品ID + */ + @ExcelProperty(value = "礼品ID") + private Long giftId; + + /** + * 礼品编码 + */ + @ExcelProperty(value = "礼品编码") + private String giftCode; + + /** + * 礼品名称 + */ + @ExcelProperty(value = "礼品名称") + private String giftName; + + /** + * 规格型号 + */ + @ExcelProperty(value = "规格型号") + private String specification; + + /** + * 单价(含税) + */ + @ExcelProperty(value = "单价(含税)") + private BigDecimal unitPrice; + + /** + * 库存数量 + */ + @ExcelProperty(value = "库存数量") + private Integer inventoryQuantity; + + /** + * 申请数量 + */ + @ExcelProperty(value = "申请数量") + private Integer applicationQuantity; + + /** + * 礼品金额 + */ + @ExcelProperty(value = "礼品金额") + private BigDecimal giftAmount; + + /** + * 排序号 + */ + @ExcelProperty(value = "排序号") + private Integer sortOrder; + + /** + * 备注 + */ + @ExcelProperty(value = "备注") + private String remark; + + +} diff --git a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/domain/vo/CrmGiftApplyVo.java b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/domain/vo/CrmGiftApplyVo.java new file mode 100644 index 00000000..29575906 --- /dev/null +++ b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/domain/vo/CrmGiftApplyVo.java @@ -0,0 +1,153 @@ +package org.dromara.oa.crm.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import org.dromara.oa.crm.domain.CrmGiftApply; +import org.dromara.oa.crm.domain.CrmGiftApplyDetail; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; +import java.util.List; + + + +/** + * 礼品申请视图对象 crm_gift_apply + * + * @author Yinq + * @date 2025-12-19 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = CrmGiftApply.class) +public class CrmGiftApplyVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 申请单ID + */ + @ExcelProperty(value = "申请单ID") + private Long giftApplyId; + + /** + * 礼品申请编号 + */ + @ExcelProperty(value = "礼品申请编号") + private String applicationCode; + + /** + * 申请人ID + */ + @ExcelProperty(value = "申请人ID") + private Long applicantId; + + /** + * 申请人姓名 + */ + @ExcelProperty(value = "申请人姓名") + private String applicantName; + + /** + * 申请人所属部门ID + */ + @ExcelProperty(value = "申请人所属部门ID") + private Long applicantDeptId; + + /** + * 申请人所属部门名称 + */ + @ExcelProperty(value = "申请人所属部门名称") + private String applicantDeptName; + + /** + * 申请日期 + */ + @ExcelProperty(value = "申请日期") + private Date applicationDate; + + /** + * 客户ID + */ + @ExcelProperty(value = "客户ID") + private Long customerUnitId; + + /** + * 客户名称 + */ + @ExcelProperty(value = "客户名称") + private String customerUnitName; + + /** + * 客人姓名 + */ + @ExcelProperty(value = "客人姓名") + private String guestName; + + /** + * 人数 + */ + @ExcelProperty(value = "人数") + private Integer peopleCount; + + /** + * 申请事由 + */ + @ExcelProperty(value = "申请事由") + private String applicationReason; + + /** + * 领用人ID + */ + @ExcelProperty(value = "领用人ID") + private Long recipientId; + + /** + * 领用人姓名 + */ + @ExcelProperty(value = "领用人姓名") + private String recipientName; + + /** + * 领用日期 + */ + @ExcelProperty(value = "领用日期") + private Date recipientDate; + + /** + * 申请总金额 + */ + @ExcelProperty(value = "申请总金额") + private BigDecimal totalAmount; + + /** + * 礼品申请状态(1暂存 2审批中 3已审批 4作废) + */ + @ExcelProperty(value = "礼品申请状态(1暂存 2审批中 3已审批 4作废)") + private String applicationStatus; + + /** + * 流程状态 + */ + @ExcelProperty(value = "流程状态") + private String flowStatus; + + /** + * 备注 + */ + @ExcelProperty(value = "备注") + private String remark; + + // ==================== 子表数据 ==================== + + /** + * 礼品申请明细列表 + */ + private List detailList; + +} diff --git a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/domain/vo/CrmGiftInfoVo.java b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/domain/vo/CrmGiftInfoVo.java new file mode 100644 index 00000000..2a01606d --- /dev/null +++ b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/domain/vo/CrmGiftInfoVo.java @@ -0,0 +1,78 @@ +package org.dromara.oa.crm.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import org.dromara.oa.crm.domain.CrmGiftInfo; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; + + + +/** + * 礼品信息视图对象 crm_gift_info + * + * @author Yinq + * @date 2025-12-19 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = CrmGiftInfo.class) +public class CrmGiftInfoVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 礼品ID + */ + @ExcelProperty(value = "礼品ID") + private Long giftId; + + /** + * 礼品编码 + */ + @ExcelProperty(value = "礼品编码") + private String giftCode; + + /** + * 礼品名称 + */ + @ExcelProperty(value = "礼品名称") + private String giftName; + + /** + * 规格型号 + */ + @ExcelProperty(value = "规格型号") + private String specification; + + /** + * 采购单价(含税) + */ + @ExcelProperty(value = "采购单价(含税)") + private BigDecimal unitPrice; + + /** + * 单位ID + */ + @ExcelProperty(value = "单位ID") + private Long unitId; + + /** + * 库存数量 + */ + @ExcelProperty(value = "库存数量") + private Integer inventoryQuantity; + + /** + * 备注 + */ + @ExcelProperty(value = "备注") + private String remark; + + +} diff --git a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/domain/vo/CrmGiftIssueRecordVo.java b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/domain/vo/CrmGiftIssueRecordVo.java new file mode 100644 index 00000000..30fa2051 --- /dev/null +++ b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/domain/vo/CrmGiftIssueRecordVo.java @@ -0,0 +1,144 @@ +package org.dromara.oa.crm.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import org.dromara.oa.crm.domain.CrmGiftIssueRecord; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + + +/** + * 礼品发放记录视图对象 crm_gift_issue_record + * + * @author Yinq + * @date 2025-12-19 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = CrmGiftIssueRecord.class) +public class CrmGiftIssueRecordVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 发放记录ID + */ + @ExcelProperty(value = "发放记录ID") + private Long issueRecordId; + + /** + * 申请单ID + */ + @ExcelProperty(value = "申请单ID") + private Long applicationId; + + /** + * 申请单编号 + */ + @ExcelProperty(value = "申请单编号") + private String applicationCode; + + /** + * 申请明细ID + */ + @ExcelProperty(value = "申请明细ID") + private Long detailId; + + /** + * 礼品ID + */ + @ExcelProperty(value = "礼品ID") + private Long giftId; + + /** + * 礼品编码 + */ + @ExcelProperty(value = "礼品编码") + private String giftCode; + + /** + * 礼品名称 + */ + @ExcelProperty(value = "礼品名称") + private String giftName; + + /** + * 规格型号 + */ + @ExcelProperty(value = "规格型号") + private String specification; + + /** + * 发放数量 + */ + @ExcelProperty(value = "发放数量") + private Integer issueQuantity; + + /** + * 发放时间 + */ + @ExcelProperty(value = "发放时间") + private Date issueDate; + + /** + * 领取人ID + */ + @ExcelProperty(value = "领取人ID") + private Long recipientId; + + /** + * 领取人姓名 + */ + @ExcelProperty(value = "领取人姓名") + private String recipientName; + + /** + * 发放人ID + */ + @ExcelProperty(value = "发放人ID") + private Long issueBy; + + /** + * 发放人姓名 + */ + @ExcelProperty(value = "发放人姓名") + private String issueByName; + + /** + * 发放部门ID + */ + @ExcelProperty(value = "发放部门ID") + private Long issueDeptId; + + /** + * 发放部门名称 + */ + @ExcelProperty(value = "发放部门名称") + private String issueDeptName; + + /** + * 发放前库存数量 + */ + @ExcelProperty(value = "发放前库存数量") + private Integer beforeInventory; + + /** + * 发放后库存数量 + */ + @ExcelProperty(value = "发放后库存数量") + private Integer afterInventory; + + /** + * 备注 + */ + @ExcelProperty(value = "备注") + private String remark; + + +} diff --git a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/domain/vo/GiftPendingQuantityVO.java b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/domain/vo/GiftPendingQuantityVO.java new file mode 100644 index 00000000..1244d280 --- /dev/null +++ b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/domain/vo/GiftPendingQuantityVO.java @@ -0,0 +1,29 @@ +package org.dromara.oa.crm.domain.vo; + +import lombok.Data; + +/** + * 待审批中礼品数量 VO + * 用于库存校验时查询所有审批中申请的礼品数量汇总 + * + * @author Yinq + * @date 2025-01-28 + */ +@Data +public class GiftPendingQuantityVO { + + /** + * 礼品ID + */ + private Long giftId; + + /** + * 礼品名称 + */ + private String giftName; + + /** + * 待审批中的数量(所有审批中申请的该礼品数量总和) + */ + private Integer pendingQuantity; +} diff --git a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/mapper/CrmGiftApplyDetailMapper.java b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/mapper/CrmGiftApplyDetailMapper.java new file mode 100644 index 00000000..eee0c568 --- /dev/null +++ b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/mapper/CrmGiftApplyDetailMapper.java @@ -0,0 +1,114 @@ +package org.dromara.oa.crm.mapper; + +import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.baomidou.mybatisplus.core.toolkit.Constants; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.github.yulichang.wrapper.MPJLambdaWrapper; +import org.apache.ibatis.annotations.Param; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; +import org.dromara.oa.crm.domain.CrmGiftApplyDetail; +import org.dromara.oa.crm.domain.vo.CrmGiftApplyDetailVo; + +import java.util.Collection; +import java.util.List; + +/** + * 礼品申请明细Mapper接口 + * + * @author Yinq + * @date 2025-12-19 + */ +public interface CrmGiftApplyDetailMapper extends BaseMapperPlus { + + /** + * 查询礼品申请明细列表 + * + * @param page 分页 + * @param queryWrapper 条件 + * @return 礼品申请明细集合 + */ + public Page selectCustomCrmGiftApplyDetailVoList(@Param("page") Page page, @Param(Constants.WRAPPER) MPJLambdaWrapper queryWrapper); + + /** + * 查询礼品申请明细列表 + * + * @param queryWrapper 条件 + * @return 礼品申请明细集合 + */ + public List selectCustomCrmGiftApplyDetailVoList(@Param(Constants.WRAPPER) MPJLambdaWrapper queryWrapper); + + /** + * 根据ID查询礼品申请明细详情 + * + * @param detailId 主键ID + * @return 礼品申请明细对象 + */ + CrmGiftApplyDetailVo selectCustomCrmGiftApplyDetailVoById(@Param("detailId") Long detailId); + + /** + * 根据ID列表批量查询礼品申请明细 + * + * @param ids ID集合 + * @return 礼品申请明细集合 + */ + List selectCustomCrmGiftApplyDetailVoByIds(@Param("ids") Collection ids); + + /** + * 统计礼品申请明细记录数 + * + * @param queryWrapper 查询条件 + * @return 记录总数 + */ + Long countCustomCrmGiftApplyDetail(@Param(Constants.WRAPPER) Wrapper queryWrapper); + + /** + * 分页查询礼品申请明细(自定义条件) + * + * @param page 分页对象 + * @param queryWrapper 查询条件 + * @return 分页结果 + */ + Page selectCustomCrmGiftApplyDetailVoPage(@Param("page") Page page, @Param(Constants.WRAPPER) Wrapper queryWrapper); + + /** + * 批量插入礼品申请明细 + * + * @param list 礼品申请明细对象集合 + * @return 影响行数 + */ + int batchInsertCrmGiftApplyDetail(@Param("list") List list); + + /** + * 批量更新礼品申请明细 + * + * @param list 礼品申请明细对象集合 + * @return 影响行数 + */ + int batchUpdateCrmGiftApplyDetail(@Param("list") List list); + + /** + * 根据自定义条件删除礼品申请明细 + * + * @param queryWrapper 删除条件 + * @return 影响行数 + */ + int deleteCustomCrmGiftApplyDetail(@Param(Constants.WRAPPER) Wrapper queryWrapper); + + /** + * 根据ID列表批量删除礼品申请明细 + * + * @param ids ID集合 + * @return 影响行数 + */ + int deleteCustomCrmGiftApplyDetailByIds(@Param("ids") Collection ids); + + /** + * 检查礼品申请明细是否存在 + * + * @param queryWrapper 查询条件 + * @return 是否存在 + */ + Boolean existsCrmGiftApplyDetail(@Param(Constants.WRAPPER) Wrapper queryWrapper); + + +} diff --git a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/mapper/CrmGiftApplyMapper.java b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/mapper/CrmGiftApplyMapper.java new file mode 100644 index 00000000..d1dd7dae --- /dev/null +++ b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/mapper/CrmGiftApplyMapper.java @@ -0,0 +1,128 @@ +package org.dromara.oa.crm.mapper; + +import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.baomidou.mybatisplus.core.toolkit.Constants; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.github.yulichang.wrapper.MPJLambdaWrapper; +import org.apache.ibatis.annotations.Param; +import org.dromara.common.mybatis.annotation.DataColumn; +import org.dromara.common.mybatis.annotation.DataPermission; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; +import org.dromara.oa.crm.domain.CrmGiftApply; +import org.dromara.oa.crm.domain.vo.CrmGiftApplyVo; +import org.dromara.oa.crm.domain.vo.GiftPendingQuantityVO; + +import java.util.Collection; +import java.util.List; + +/** + * 礼品申请Mapper接口 + * + * @author Yinq + * @date 2025-12-19 + */ +public interface CrmGiftApplyMapper extends BaseMapperPlus { + + /** + * 查询礼品申请列表 + * + * @param page 分页 + * @param queryWrapper 条件 + * @return 礼品申请集合 + */ + @DataPermission({ + @DataColumn(key = "deptName", value = "t.create_dept"), + @DataColumn(key = "userName", value = "t.create_by") + }) + Page selectCustomCrmGiftApplyVoList(@Param("page") Page page, @Param(Constants.WRAPPER) MPJLambdaWrapper queryWrapper); + + /** + * 查询礼品申请列表 + * + * @param queryWrapper 条件 + * @return 礼品申请集合 + */ + public List selectCustomCrmGiftApplyVoList(@Param(Constants.WRAPPER) MPJLambdaWrapper queryWrapper); + + /** + * 根据ID查询礼品申请详情 + * + * @param giftApplyId 主键ID + * @return 礼品申请对象 + */ + CrmGiftApplyVo selectCustomCrmGiftApplyVoById(@Param("giftApplyId") Long giftApplyId); + + /** + * 根据ID列表批量查询礼品申请 + * + * @param ids ID集合 + * @return 礼品申请集合 + */ + List selectCustomCrmGiftApplyVoByIds(@Param("ids") Collection ids); + + /** + * 统计礼品申请记录数 + * + * @param queryWrapper 查询条件 + * @return 记录总数 + */ + Long countCustomCrmGiftApply(@Param(Constants.WRAPPER) Wrapper queryWrapper); + + /** + * 分页查询礼品申请(自定义条件) + * + * @param page 分页对象 + * @param queryWrapper 查询条件 + * @return 分页结果 + */ + Page selectCustomCrmGiftApplyVoPage(@Param("page") Page page, @Param(Constants.WRAPPER) Wrapper queryWrapper); + + /** + * 批量插入礼品申请 + * + * @param list 礼品申请对象集合 + * @return 影响行数 + */ + int batchInsertCrmGiftApply(@Param("list") List list); + + /** + * 批量更新礼品申请 + * + * @param list 礼品申请对象集合 + * @return 影响行数 + */ + int batchUpdateCrmGiftApply(@Param("list") List list); + + /** + * 根据自定义条件删除礼品申请 + * + * @param queryWrapper 删除条件 + * @return 影响行数 + */ + int deleteCustomCrmGiftApply(@Param(Constants.WRAPPER) Wrapper queryWrapper); + + /** + * 根据ID列表批量删除礼品申请 + * + * @param ids ID集合 + * @return 影响行数 + */ + int deleteCustomCrmGiftApplyByIds(@Param("ids") Collection ids); + + /** + * 检查礼品申请是否存在 + * + * @param queryWrapper 查询条件 + * @return 是否存在 + */ + Boolean existsCrmGiftApply(@Param(Constants.WRAPPER) Wrapper queryWrapper); + + /** + * ⭐ 查询待审批中相同礼品的数量汇总 + * 用于增强库存校验:提交时考虑所有审批中申请的库存占用 + * + * @return 待审批礼品数量列表 + */ + List selectPendingGiftQuantity(); + +} diff --git a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/mapper/CrmGiftInfoMapper.java b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/mapper/CrmGiftInfoMapper.java new file mode 100644 index 00000000..c5b43165 --- /dev/null +++ b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/mapper/CrmGiftInfoMapper.java @@ -0,0 +1,123 @@ +package org.dromara.oa.crm.mapper; + +import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.baomidou.mybatisplus.core.toolkit.Constants; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.github.yulichang.wrapper.MPJLambdaWrapper; +import org.apache.ibatis.annotations.Param; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; +import org.dromara.oa.crm.domain.CrmGiftInfo; +import org.dromara.oa.crm.domain.vo.CrmGiftInfoVo; + +import java.util.Collection; +import java.util.List; + +/** + * 礼品信息Mapper接口 + * + * @author Yinq + * @date 2025-12-19 + */ +public interface CrmGiftInfoMapper extends BaseMapperPlus { + + /** + * 查询礼品信息列表 + * + * @param page 分页 + * @param queryWrapper 条件 + * @return 礼品信息集合 + */ + public Page selectCustomCrmGiftInfoVoList(@Param("page") Page page, @Param(Constants.WRAPPER) MPJLambdaWrapper queryWrapper); + + /** + * 查询礼品信息列表 + * + * @param queryWrapper 条件 + * @return 礼品信息集合 + */ + public List selectCustomCrmGiftInfoVoList(@Param(Constants.WRAPPER) MPJLambdaWrapper queryWrapper); + + /** + * 根据ID查询礼品信息详情 + * + * @param giftId 主键ID + * @return 礼品信息对象 + */ + CrmGiftInfoVo selectCustomCrmGiftInfoVoById(@Param("giftId") Long giftId); + + /** + * 根据ID列表批量查询礼品信息 + * + * @param ids ID集合 + * @return 礼品信息集合 + */ + List selectCustomCrmGiftInfoVoByIds(@Param("ids") Collection ids); + + /** + * 统计礼品信息记录数 + * + * @param queryWrapper 查询条件 + * @return 记录总数 + */ + Long countCustomCrmGiftInfo(@Param(Constants.WRAPPER) Wrapper queryWrapper); + + /** + * 分页查询礼品信息(自定义条件) + * + * @param page 分页对象 + * @param queryWrapper 查询条件 + * @return 分页结果 + */ + Page selectCustomCrmGiftInfoVoPage(@Param("page") Page page, @Param(Constants.WRAPPER) Wrapper queryWrapper); + + /** + * 批量插入礼品信息 + * + * @param list 礼品信息对象集合 + * @return 影响行数 + */ + int batchInsertCrmGiftInfo(@Param("list") List list); + + /** + * 批量更新礼品信息 + * + * @param list 礼品信息对象集合 + * @return 影响行数 + */ + int batchUpdateCrmGiftInfo(@Param("list") List list); + + /** + * 根据自定义条件删除礼品信息 + * + * @param queryWrapper 删除条件 + * @return 影响行数 + */ + int deleteCustomCrmGiftInfo(@Param(Constants.WRAPPER) Wrapper queryWrapper); + + /** + * 根据ID列表批量删除礼品信息 + * + * @param ids ID集合 + * @return 影响行数 + */ + int deleteCustomCrmGiftInfoByIds(@Param("ids") Collection ids); + + /** + * 检查礼品信息是否存在 + * + * @param queryWrapper 查询条件 + * @return 是否存在 + */ + Boolean existsCrmGiftInfo(@Param(Constants.WRAPPER) Wrapper queryWrapper); + + /** + * 原子扣减库存(并发安全) + * 只有当库存数量 >= 扣减数量时才执行更新,防止并发超发 + * + * @param giftId 礼品ID + * @param deductQuantity 扣减数量 + * @return 影响行数(0表示库存不足或礼品不存在,1表示扣减成功) + */ + int deductInventory(@Param("giftId") Long giftId, @Param("deductQuantity") Integer deductQuantity); + +} diff --git a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/mapper/CrmGiftIssueRecordMapper.java b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/mapper/CrmGiftIssueRecordMapper.java new file mode 100644 index 00000000..8159aa6b --- /dev/null +++ b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/mapper/CrmGiftIssueRecordMapper.java @@ -0,0 +1,114 @@ +package org.dromara.oa.crm.mapper; + +import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.baomidou.mybatisplus.core.toolkit.Constants; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.github.yulichang.wrapper.MPJLambdaWrapper; +import org.apache.ibatis.annotations.Param; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; +import org.dromara.oa.crm.domain.CrmGiftIssueRecord; +import org.dromara.oa.crm.domain.vo.CrmGiftIssueRecordVo; + +import java.util.Collection; +import java.util.List; + +/** + * 礼品发放记录Mapper接口 + * + * @author Yinq + * @date 2025-12-19 + */ +public interface CrmGiftIssueRecordMapper extends BaseMapperPlus { + + /** + * 查询礼品发放记录列表 + * + * @param page 分页 + * @param queryWrapper 条件 + * @return 礼品发放记录集合 + */ + public Page selectCustomCrmGiftIssueRecordVoList(@Param("page") Page page, @Param(Constants.WRAPPER) MPJLambdaWrapper queryWrapper); + + /** + * 查询礼品发放记录列表 + * + * @param queryWrapper 条件 + * @return 礼品发放记录集合 + */ + public List selectCustomCrmGiftIssueRecordVoList(@Param(Constants.WRAPPER) MPJLambdaWrapper queryWrapper); + + /** + * 根据ID查询礼品发放记录详情 + * + * @param issueRecordId 主键ID + * @return 礼品发放记录对象 + */ + CrmGiftIssueRecordVo selectCustomCrmGiftIssueRecordVoById(@Param("issueRecordId") Long issueRecordId); + + /** + * 根据ID列表批量查询礼品发放记录 + * + * @param ids ID集合 + * @return 礼品发放记录集合 + */ + List selectCustomCrmGiftIssueRecordVoByIds(@Param("ids") Collection ids); + + /** + * 统计礼品发放记录记录数 + * + * @param queryWrapper 查询条件 + * @return 记录总数 + */ + Long countCustomCrmGiftIssueRecord(@Param(Constants.WRAPPER) Wrapper queryWrapper); + + /** + * 分页查询礼品发放记录(自定义条件) + * + * @param page 分页对象 + * @param queryWrapper 查询条件 + * @return 分页结果 + */ + Page selectCustomCrmGiftIssueRecordVoPage(@Param("page") Page page, @Param(Constants.WRAPPER) Wrapper queryWrapper); + + /** + * 批量插入礼品发放记录 + * + * @param list 礼品发放记录对象集合 + * @return 影响行数 + */ + int batchInsertCrmGiftIssueRecord(@Param("list") List list); + + /** + * 批量更新礼品发放记录 + * + * @param list 礼品发放记录对象集合 + * @return 影响行数 + */ + int batchUpdateCrmGiftIssueRecord(@Param("list") List list); + + /** + * 根据自定义条件删除礼品发放记录 + * + * @param queryWrapper 删除条件 + * @return 影响行数 + */ + int deleteCustomCrmGiftIssueRecord(@Param(Constants.WRAPPER) Wrapper queryWrapper); + + /** + * 根据ID列表批量删除礼品发放记录 + * + * @param ids ID集合 + * @return 影响行数 + */ + int deleteCustomCrmGiftIssueRecordByIds(@Param("ids") Collection ids); + + /** + * 检查礼品发放记录是否存在 + * + * @param queryWrapper 查询条件 + * @return 是否存在 + */ + Boolean existsCrmGiftIssueRecord(@Param(Constants.WRAPPER) Wrapper queryWrapper); + + +} diff --git a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/service/ICrmGiftApplyDetailService.java b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/service/ICrmGiftApplyDetailService.java new file mode 100644 index 00000000..db145f89 --- /dev/null +++ b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/service/ICrmGiftApplyDetailService.java @@ -0,0 +1,68 @@ +package org.dromara.oa.crm.service; + +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.oa.crm.domain.bo.CrmGiftApplyDetailBo; +import org.dromara.oa.crm.domain.vo.CrmGiftApplyDetailVo; + +import java.util.Collection; +import java.util.List; + +/** + * 礼品申请明细Service接口 + * + * @author Yinq + * @date 2025-12-19 + */ +public interface ICrmGiftApplyDetailService { + + /** + * 查询礼品申请明细 + * + * @param detailId 主键 + * @return 礼品申请明细 + */ + CrmGiftApplyDetailVo queryById(Long detailId); + + /** + * 分页查询礼品申请明细列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 礼品申请明细分页列表 + */ + TableDataInfo queryPageList(CrmGiftApplyDetailBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的礼品申请明细列表 + * + * @param bo 查询条件 + * @return 礼品申请明细列表 + */ + List queryList(CrmGiftApplyDetailBo bo); + + /** + * 新增礼品申请明细 + * + * @param bo 礼品申请明细 + * @return 是否新增成功 + */ + Boolean insertByBo(CrmGiftApplyDetailBo bo); + + /** + * 修改礼品申请明细 + * + * @param bo 礼品申请明细 + * @return 是否修改成功 + */ + Boolean updateByBo(CrmGiftApplyDetailBo bo); + + /** + * 校验并批量删除礼品申请明细信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/service/ICrmGiftApplyService.java b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/service/ICrmGiftApplyService.java new file mode 100644 index 00000000..59a88fbe --- /dev/null +++ b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/service/ICrmGiftApplyService.java @@ -0,0 +1,89 @@ +package org.dromara.oa.crm.service; + +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.oa.crm.domain.bo.CrmGiftApplyBo; +import org.dromara.oa.crm.domain.vo.CrmGiftApplyVo; + +import java.util.Collection; +import java.util.List; + +/** + * 礼品申请Service接口 + * + * @author Yinq + * @date 2025-12-19 + */ +public interface ICrmGiftApplyService { + + /** + * 查询礼品申请 + * + * @param giftApplyId 主键 + * @return 礼品申请 + */ + CrmGiftApplyVo queryById(Long giftApplyId); + + /** + * 分页查询礼品申请列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 礼品申请分页列表 + */ + TableDataInfo queryPageList(CrmGiftApplyBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的礼品申请列表 + * + * @param bo 查询条件 + * @return 礼品申请列表 + */ + List queryList(CrmGiftApplyBo bo); + + /** + * 新增礼品申请 + * + * @param bo 礼品申请 + * @return 是否新增成功 + */ + Boolean insertByBo(CrmGiftApplyBo bo); + + /** + * 修改礼品申请 + * + * @param bo 礼品申请 + * @return 是否修改成功 + */ + Boolean updateByBo(CrmGiftApplyBo bo); + + /** + * 校验并批量删除礼品申请信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); + + /** + * 提交礼品申请并发起审批流程 + * + * @param bo 礼品申请(含流程信息和明细列表) + * @return 礼品申请VO + */ + CrmGiftApplyVo giftApplySubmitAndFlowStart(CrmGiftApplyBo bo); + + /** + * 批量发放礼品 + * 业务流程: + * 1. 校验申请状态必须为"已审批"(3) + * 2. 遍历申请明细,批量创建发放记录 + * 3. 自动扣减库存 + * 4. 更新申请单的领用日期 + * + * @param bo 礼品申请(包含申请ID、明细列表、领用人信息) + * @return 是否发放成功 + */ + Boolean batchIssueGifts(CrmGiftApplyBo bo); +} diff --git a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/service/ICrmGiftInfoService.java b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/service/ICrmGiftInfoService.java new file mode 100644 index 00000000..37c881d8 --- /dev/null +++ b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/service/ICrmGiftInfoService.java @@ -0,0 +1,68 @@ +package org.dromara.oa.crm.service; + +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.oa.crm.domain.bo.CrmGiftInfoBo; +import org.dromara.oa.crm.domain.vo.CrmGiftInfoVo; + +import java.util.Collection; +import java.util.List; + +/** + * 礼品信息Service接口 + * + * @author Yinq + * @date 2025-12-19 + */ +public interface ICrmGiftInfoService { + + /** + * 查询礼品信息 + * + * @param giftId 主键 + * @return 礼品信息 + */ + CrmGiftInfoVo queryById(Long giftId); + + /** + * 分页查询礼品信息列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 礼品信息分页列表 + */ + TableDataInfo queryPageList(CrmGiftInfoBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的礼品信息列表 + * + * @param bo 查询条件 + * @return 礼品信息列表 + */ + List queryList(CrmGiftInfoBo bo); + + /** + * 新增礼品信息 + * + * @param bo 礼品信息 + * @return 是否新增成功 + */ + Boolean insertByBo(CrmGiftInfoBo bo); + + /** + * 修改礼品信息 + * + * @param bo 礼品信息 + * @return 是否修改成功 + */ + Boolean updateByBo(CrmGiftInfoBo bo); + + /** + * 校验并批量删除礼品信息信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/service/ICrmGiftIssueRecordService.java b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/service/ICrmGiftIssueRecordService.java new file mode 100644 index 00000000..2ad88f3b --- /dev/null +++ b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/service/ICrmGiftIssueRecordService.java @@ -0,0 +1,68 @@ +package org.dromara.oa.crm.service; + +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.oa.crm.domain.bo.CrmGiftIssueRecordBo; +import org.dromara.oa.crm.domain.vo.CrmGiftIssueRecordVo; + +import java.util.Collection; +import java.util.List; + +/** + * 礼品发放记录Service接口 + * + * @author Yinq + * @date 2025-12-19 + */ +public interface ICrmGiftIssueRecordService { + + /** + * 查询礼品发放记录 + * + * @param issueRecordId 主键 + * @return 礼品发放记录 + */ + CrmGiftIssueRecordVo queryById(Long issueRecordId); + + /** + * 分页查询礼品发放记录列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 礼品发放记录分页列表 + */ + TableDataInfo queryPageList(CrmGiftIssueRecordBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的礼品发放记录列表 + * + * @param bo 查询条件 + * @return 礼品发放记录列表 + */ + List queryList(CrmGiftIssueRecordBo bo); + + /** + * 新增礼品发放记录 + * + * @param bo 礼品发放记录 + * @return 是否新增成功 + */ + Boolean insertByBo(CrmGiftIssueRecordBo bo); + + /** + * 修改礼品发放记录 + * + * @param bo 礼品发放记录 + * @return 是否修改成功 + */ + Boolean updateByBo(CrmGiftIssueRecordBo bo); + + /** + * 校验并批量删除礼品发放记录信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/service/impl/CrmGiftApplyDetailServiceImpl.java b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/service/impl/CrmGiftApplyDetailServiceImpl.java new file mode 100644 index 00000000..d9e3959a --- /dev/null +++ b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/service/impl/CrmGiftApplyDetailServiceImpl.java @@ -0,0 +1,141 @@ +package org.dromara.oa.crm.service.impl; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.github.yulichang.toolkit.JoinWrappers; +import com.github.yulichang.wrapper.MPJLambdaWrapper; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.oa.crm.domain.CrmGiftApplyDetail; +import org.dromara.oa.crm.domain.bo.CrmGiftApplyDetailBo; +import org.dromara.oa.crm.domain.vo.CrmGiftApplyDetailVo; +import org.dromara.oa.crm.mapper.CrmGiftApplyDetailMapper; +import org.dromara.oa.crm.service.ICrmGiftApplyDetailService; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 礼品申请明细Service业务层处理 + * + * @author Yinq + * @date 2025-12-19 + */ +@RequiredArgsConstructor +@Service +public class CrmGiftApplyDetailServiceImpl implements ICrmGiftApplyDetailService { + + private final CrmGiftApplyDetailMapper baseMapper; + + /** + * 查询礼品申请明细 + * + * @param detailId 主键 + * @return 礼品申请明细 + */ + @Override + public CrmGiftApplyDetailVo queryById(Long detailId){ + return baseMapper.selectVoById(detailId); + } + + /** + * 分页查询礼品申请明细列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 礼品申请明细分页列表 + */ + @Override + public TableDataInfo queryPageList(CrmGiftApplyDetailBo bo, PageQuery pageQuery) { + MPJLambdaWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的礼品申请明细列表 + * + * @param bo 查询条件 + * @return 礼品申请明细列表 + */ + @Override + public List queryList(CrmGiftApplyDetailBo bo) { + MPJLambdaWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private MPJLambdaWrapper buildQueryWrapper(CrmGiftApplyDetailBo bo) { + Map params = bo.getParams(); + MPJLambdaWrapper lqw = JoinWrappers.lambda(CrmGiftApplyDetail.class) + .selectAll(CrmGiftApplyDetail.class) + .eq(CrmGiftApplyDetail::getDelFlag, "0") + .eq(bo.getApplicationId() != null, CrmGiftApplyDetail::getApplicationId, bo.getApplicationId()) + .eq(bo.getGiftId() != null, CrmGiftApplyDetail::getGiftId, bo.getGiftId()) + .eq(StringUtils.isNotBlank(bo.getGiftCode()), CrmGiftApplyDetail::getGiftCode, bo.getGiftCode()) + .like(StringUtils.isNotBlank(bo.getGiftName()), CrmGiftApplyDetail::getGiftName, bo.getGiftName()) + .eq(StringUtils.isNotBlank(bo.getSpecification()), CrmGiftApplyDetail::getSpecification, bo.getSpecification()) + .eq(bo.getUnitPrice() != null, CrmGiftApplyDetail::getUnitPrice, bo.getUnitPrice()) + .eq(bo.getInventoryQuantity() != null, CrmGiftApplyDetail::getInventoryQuantity, bo.getInventoryQuantity()) + .eq(bo.getApplicationQuantity() != null, CrmGiftApplyDetail::getApplicationQuantity, bo.getApplicationQuantity()) + .eq(bo.getGiftAmount() != null, CrmGiftApplyDetail::getGiftAmount, bo.getGiftAmount()) + .eq(bo.getSortOrder() != null, CrmGiftApplyDetail::getSortOrder, bo.getSortOrder()) +; + return lqw; + } + + /** + * 新增礼品申请明细 + * + * @param bo 礼品申请明细 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(CrmGiftApplyDetailBo bo) { + CrmGiftApplyDetail add = MapstructUtils.convert(bo, CrmGiftApplyDetail.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setDetailId(add.getDetailId()); + } + return flag; + } + + /** + * 修改礼品申请明细 + * + * @param bo 礼品申请明细 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(CrmGiftApplyDetailBo bo) { + CrmGiftApplyDetail update = MapstructUtils.convert(bo, CrmGiftApplyDetail.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(CrmGiftApplyDetail entity){ + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除礼品申请明细信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if(isValid){ + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/service/impl/CrmGiftApplyServiceImpl.java b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/service/impl/CrmGiftApplyServiceImpl.java new file mode 100644 index 00000000..bf6b00d3 --- /dev/null +++ b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/service/impl/CrmGiftApplyServiceImpl.java @@ -0,0 +1,638 @@ +package org.dromara.oa.crm.service.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.convert.Convert; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.github.yulichang.toolkit.JoinWrappers; +import com.github.yulichang.wrapper.MPJLambdaWrapper; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.dubbo.config.annotation.DubboReference; +import org.apache.seata.spring.annotation.GlobalTransactional; +import org.dromara.common.core.enums.BusinessStatusEnum; +import org.dromara.common.core.enums.OAStatusEnum; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.satoken.utils.LoginHelper; +import org.dromara.common.tenant.helper.TenantHelper; +import org.dromara.oa.crm.domain.CrmGiftApply; +import org.dromara.oa.crm.domain.CrmGiftApplyDetail; +import org.dromara.oa.crm.domain.CrmGiftInfo; +import org.dromara.oa.crm.domain.CrmGiftIssueRecord; +import org.dromara.oa.crm.domain.bo.CrmGiftApplyBo; +import org.dromara.oa.crm.domain.vo.CrmGiftApplyVo; +import org.dromara.oa.crm.domain.vo.GiftPendingQuantityVO; +import org.dromara.oa.crm.mapper.CrmGiftApplyDetailMapper; +import org.dromara.oa.crm.mapper.CrmGiftApplyMapper; +import org.dromara.oa.crm.mapper.CrmGiftInfoMapper; +import org.dromara.oa.crm.mapper.CrmGiftIssueRecordMapper; +import org.dromara.oa.crm.service.ICrmGiftApplyService; +import org.dromara.system.api.RemoteCodeRuleService; +import org.dromara.workflow.api.RemoteWorkflowService; +import org.dromara.workflow.api.domain.RemoteStartProcess; +import org.dromara.workflow.api.event.ProcessEvent; +import org.springframework.context.event.EventListener; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * 礼品申请Service业务层处理 + * + * @author Yinq + * @date 2025-12-19 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class CrmGiftApplyServiceImpl implements ICrmGiftApplyService { + + private final CrmGiftApplyMapper baseMapper; + private final CrmGiftApplyDetailMapper detailMapper; + private final CrmGiftIssueRecordMapper issueRecordMapper; + private final CrmGiftInfoMapper giftInfoMapper; + + /** 礼品申请编码规则 */ + private static final String GIFT_APPLY_CODE_RULE = "1019"; + + @DubboReference(timeout = 30000) + private RemoteWorkflowService remoteWorkflowService; + + @DubboReference + private RemoteCodeRuleService remoteCodeRuleService; + + /** + * 查询礼品申请(含明细列表) + * + * @param giftApplyId 主键 + * @return 礼品申请 + */ + @Override + public CrmGiftApplyVo queryById(Long giftApplyId){ + CrmGiftApplyVo vo = baseMapper.selectVoById(giftApplyId); + if (vo != null) { + // 查询明细列表 + List detailList = detailMapper.selectList( + Wrappers.lambdaQuery(CrmGiftApplyDetail.class) + .eq(CrmGiftApplyDetail::getApplicationId, giftApplyId) + .orderByAsc(CrmGiftApplyDetail::getSortOrder) + ); + vo.setDetailList(detailList); + } + return vo; + } + + /** + * 分页查询礼品申请列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 礼品申请分页列表 + */ + @Override + public TableDataInfo queryPageList(CrmGiftApplyBo bo, PageQuery pageQuery) { + MPJLambdaWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的礼品申请列表 + * + * @param bo 查询条件 + * @return 礼品申请列表 + */ + @Override + public List queryList(CrmGiftApplyBo bo) { + MPJLambdaWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private MPJLambdaWrapper buildQueryWrapper(CrmGiftApplyBo bo) { + Map params = bo.getParams(); + MPJLambdaWrapper lqw = JoinWrappers.lambda(CrmGiftApply.class) + .selectAll(CrmGiftApply.class) + .eq(CrmGiftApply::getDelFlag, "0") + .eq(StringUtils.isNotBlank(bo.getApplicationCode()), CrmGiftApply::getApplicationCode, bo.getApplicationCode()) + .eq(bo.getApplicantId() != null, CrmGiftApply::getApplicantId, bo.getApplicantId()) + .like(StringUtils.isNotBlank(bo.getApplicantName()), CrmGiftApply::getApplicantName, bo.getApplicantName()) + .eq(bo.getApplicantDeptId() != null, CrmGiftApply::getApplicantDeptId, bo.getApplicantDeptId()) + .like(StringUtils.isNotBlank(bo.getApplicantDeptName()), CrmGiftApply::getApplicantDeptName, bo.getApplicantDeptName()) + .eq(bo.getApplicationDate() != null, CrmGiftApply::getApplicationDate, bo.getApplicationDate()) + .eq(bo.getCustomerUnitId() != null, CrmGiftApply::getCustomerUnitId, bo.getCustomerUnitId()) + .like(StringUtils.isNotBlank(bo.getCustomerUnitName()), CrmGiftApply::getCustomerUnitName, bo.getCustomerUnitName()) + .like(StringUtils.isNotBlank(bo.getGuestName()), CrmGiftApply::getGuestName, bo.getGuestName()) + .eq(bo.getPeopleCount() != null, CrmGiftApply::getPeopleCount, bo.getPeopleCount()) + .eq(StringUtils.isNotBlank(bo.getApplicationReason()), CrmGiftApply::getApplicationReason, bo.getApplicationReason()) + .eq(bo.getRecipientId() != null, CrmGiftApply::getRecipientId, bo.getRecipientId()) + .like(StringUtils.isNotBlank(bo.getRecipientName()), CrmGiftApply::getRecipientName, bo.getRecipientName()) + .eq(bo.getRecipientDate() != null, CrmGiftApply::getRecipientDate, bo.getRecipientDate()) + .eq(bo.getTotalAmount() != null, CrmGiftApply::getTotalAmount, bo.getTotalAmount()) + .eq(StringUtils.isNotBlank(bo.getApplicationStatus()), CrmGiftApply::getApplicationStatus, bo.getApplicationStatus()) + .eq(StringUtils.isNotBlank(bo.getFlowStatus()), CrmGiftApply::getFlowStatus, bo.getFlowStatus()) + .orderByDesc(CrmGiftApply::getCreateTime); + return lqw; + } + + /** + * 新增礼品申请(含明细) + * + * @param bo 礼品申请 + * @return 是否新增成功 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean insertByBo(CrmGiftApplyBo bo) { + CrmGiftApply add = MapstructUtils.convert(bo, CrmGiftApply.class); + // P0-3: 新增时自动生成编号 + if (StringUtils.isBlank(add.getApplicationCode())) { + add.setApplicationCode(remoteCodeRuleService.selectCodeRuleCode(GIFT_APPLY_CODE_RULE)); + } + // 新增时设置默认状态为草稿 + if (StringUtils.isBlank(add.getApplicationStatus())) { + add.setApplicationStatus(OAStatusEnum.DRAFT.getStatus()); + } + if (StringUtils.isBlank(add.getFlowStatus())) { + add.setFlowStatus(BusinessStatusEnum.DRAFT.getStatus()); + } + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setGiftApplyId(add.getGiftApplyId()); + bo.setApplicationCode(add.getApplicationCode()); // 回填生成的编号 + // 保存明细列表 + if (CollUtil.isNotEmpty(bo.getDetailList())) { + saveDetailList(add.getGiftApplyId(), bo.getDetailList()); + } + } + return flag; + } + + /** + * 修改礼品申请(含明细) + * 只有草稿状态才能修改 + * + * @param bo 礼品申请 + * @return 是否修改成功 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean updateByBo(CrmGiftApplyBo bo) { + // 校验仅草稿状态可修改 + CrmGiftApply existing = baseMapper.selectById(bo.getGiftApplyId()); + if (existing == null) { + throw new ServiceException("礼品申请不存在"); + } + if (!OAStatusEnum.DRAFT.getStatus().equals(existing.getApplicationStatus())) { + throw new ServiceException("只有草稿状态的礼品申请才能修改"); + } + CrmGiftApply update = MapstructUtils.convert(bo, CrmGiftApply.class); + // 编号不允许修改 + update.setApplicationCode(existing.getApplicationCode()); + validEntityBeforeSave(update); + boolean flag = baseMapper.updateById(update) > 0; + if (flag && bo.getDetailList() != null) { + // 更新明细列表 + saveDetailList(bo.getGiftApplyId(), bo.getDetailList()); + } + return flag; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(CrmGiftApply entity){ + if (StringUtils.isBlank(entity.getApplicationCode())) { + throw new ServiceException("礼品申请编号不能为空"); + } + // 编号唯一性校验 + Boolean exists = baseMapper.exists( + Wrappers.lambdaQuery(CrmGiftApply.class) + .eq(CrmGiftApply::getDelFlag, "0") + .eq(CrmGiftApply::getApplicationCode, entity.getApplicationCode()) + .ne(entity.getGiftApplyId() != null, CrmGiftApply::getGiftApplyId, entity.getGiftApplyId()) + ); + if (Boolean.TRUE.equals(exists)) { + throw new ServiceException("礼品申请编号已存在,请重新生成"); + } + } + + /** + * 校验并批量删除礼品申请信息(含明细) + * 主子表删除必须在同一事务中同时删除,保证数据一致性 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + // 校验只能删除草稿状态的礼品申请 + for (Long id : ids) { + CrmGiftApply apply = baseMapper.selectById(id); + if (apply != null && !OAStatusEnum.DRAFT.getStatus().equals(apply.getApplicationStatus())) { + throw new ServiceException("只能删除草稿状态的礼品申请"); + } + } + } + // 先删除子表明细 + for (Long id : ids) { + detailMapper.delete(Wrappers.lambdaQuery(CrmGiftApplyDetail.class) + .eq(CrmGiftApplyDetail::getApplicationId, id)); + } + // 再删除主表 + return baseMapper.deleteByIds(ids) > 0; + } + + /** + * 提交礼品申请并发起审批流程 + * 流程编码:CRMGIFT (CRM Gift Apply) + * + * @param bo 礼品申请(含流程信息和明细列表) + * @return 礼品申请VO + */ + @Override + @GlobalTransactional(rollbackFor = Exception.class) + public CrmGiftApplyVo giftApplySubmitAndFlowStart(CrmGiftApplyBo bo) { + // 1. 基本校验 + if (CollUtil.isEmpty(bo.getDetailList())) { + throw new ServiceException("礼品申请明细不能为空"); + } + + // P1-6: 校验金额一致性 + validAmountConsistency(bo); + + // ⭐ P1-7: 增强库存校验(考虑待审批中的库存占用) + validInventoryWithPending(bo); + + // 2. 设置状态为审批中 + bo.setApplicationStatus(OAStatusEnum.APPROVING.getStatus()); + bo.setFlowStatus(BusinessStatusEnum.WAITING.getStatus()); + + // 3. 保存或更新(调用 insertByBo/updateByBo) + if (StringUtils.isNull(bo.getGiftApplyId())) { + this.insertByBo(bo); + } else { + // 更新:校验仅草稿状态可提交 + CrmGiftApply existing = baseMapper.selectById(bo.getGiftApplyId()); + if (existing == null) { + throw new ServiceException("礼品申请不存在"); + } +// if (!OAStatusEnum.DRAFT.getStatus().equals(existing.getApplicationStatus())) { +// throw new ServiceException("只有草稿状态的礼品申请才能提交"); +// } + this.updateByBo(bo); + } + + // 4. 后端发起需要忽略权限 + bo.getVariables().put("ignore", true); + + // 5. 发起审批流程(直接使用前端传递的 variables 和 bizExt) + RemoteStartProcess startProcess = new RemoteStartProcess(); + startProcess.setBusinessId(bo.getGiftApplyId().toString()); + startProcess.setFlowCode(bo.getFlowCode()); + startProcess.setVariables(bo.getVariables()); + startProcess.setBizExt(bo.getBizExt()); + bo.getBizExt().setBusinessId(startProcess.getBusinessId()); + boolean flagOne = remoteWorkflowService.startCompleteTask(startProcess); + if (!flagOne) { + throw new ServiceException("流程发起异常"); + } + + return queryById(bo.getGiftApplyId()); + } + + /** + * P1-6: 校验金额一致性 + */ + private void validAmountConsistency(CrmGiftApplyBo bo) { + if (CollUtil.isEmpty(bo.getDetailList())) { + return; + } + + java.math.BigDecimal calculatedTotal = java.math.BigDecimal.ZERO; + for (CrmGiftApplyDetail detail : bo.getDetailList()) { + if (detail.getUnitPrice() != null && detail.getApplicationQuantity() != null) { + java.math.BigDecimal amount = detail.getUnitPrice().multiply( + new java.math.BigDecimal(detail.getApplicationQuantity())); + calculatedTotal = calculatedTotal.add(amount); + } + } + + if (bo.getTotalAmount() == null || bo.getTotalAmount().compareTo(calculatedTotal) != 0) { + throw new ServiceException("申请总金额与明细金额不一致,请重新计算"); + } + } + + /** + * ⭐ P1-7: 增强库存校验(考虑待审批中的库存占用) + * 校验公式:当前库存 >= 当前申请数量 + 待审批中相同礼品数量 + * + * @param bo 礼品申请 + * @throws ServiceException 库存不足时抛出异常 + */ + private void validInventoryWithPending(CrmGiftApplyBo bo) { + // 1. 查询待审批中相同礼品的数量汇总 + List pendingList = baseMapper.selectPendingGiftQuantity(); + + // 转换为 Map: gift_id -> pending_quantity + Map pendingMap = pendingList.stream() + .collect(Collectors.toMap( + GiftPendingQuantityVO::getGiftId, + GiftPendingQuantityVO::getPendingQuantity, + (v1, v2) -> v1 + v2 // 相同 gift_id 累加 + )); + + // 2. 遍历当前申请明细,逐个校验 + List insufficientGifts = new ArrayList<>(); + + for (CrmGiftApplyDetail detail : bo.getDetailList()) { + // 查询礼品信息 + CrmGiftInfo giftInfo = giftInfoMapper.selectById(detail.getGiftId()); + if (giftInfo == null) { + throw new ServiceException("礼品不存在:" + detail.getGiftName()); + } + + // 获取当前库存 + int currentInventory = giftInfo.getInventoryQuantity() == null ? 0 : giftInfo.getInventoryQuantity(); + + // 获取待审批中相同礼品数量 + Integer pendingQuantity = pendingMap.getOrDefault(detail.getGiftId(), 0); + + // 当前申请数量 + Integer currentQuantity = detail.getApplicationQuantity(); + + // ⭐ 校验公式:当前库存 >= 当前申请数量 + 待审批中数量 + if (currentInventory < currentQuantity + pendingQuantity) { + insufficientGifts.add(String.format( + "【%s】当前库存 %d 个,待审批中 %d 个,本次申请 %d 个,合计需要 %d 个,缺口 %d 个", + detail.getGiftName(), + currentInventory, + pendingQuantity, + currentQuantity, + currentQuantity + pendingQuantity, + (currentQuantity + pendingQuantity) - currentInventory + )); + } + } + + // 3. 如果有库存不足的礼品,抛出异常 + if (!insufficientGifts.isEmpty()) { + throw new ServiceException("库存不足,无法提交审批:\n" + String.join("\n", insufficientGifts)); + } + } + + /** + * 保存礼品申请明细列表 + * 先删除旧明细,再批量插入新明细 + */ + @Transactional( rollbackFor = Exception.class) + public void saveDetailList(Long applicationId, List detailList) { + // 删除旧明细 + detailMapper.delete(Wrappers.lambdaQuery(CrmGiftApplyDetail.class) + .eq(CrmGiftApplyDetail::getApplicationId, applicationId)); + + // 批量插入新明细 + if (CollUtil.isNotEmpty(detailList)) { + for (int i = 0; i < detailList.size(); i++) { + CrmGiftApplyDetail detail = detailList.get(i); + detail.setApplicationId(applicationId); + detail.setDetailId(null); // 清空ID,确保新增 + if (detail.getSortOrder() == null) { + detail.setSortOrder(i + 1); + } + } + detailMapper.insertBatch(detailList); + } + } + + /** + * 流程监听器:礼品申请流程状态变化时更新业务状态 + * ⭐ 修改:审批通过时自动发放 + * 流程编码:CRMGIFT (CRM Gift Apply) + */ + @EventListener(condition = "#processEvent.flowCode == 'CRMGIFT'") + public void processHandler(ProcessEvent processEvent) { + TenantHelper.dynamic(processEvent.getTenantId(), () -> { + log.info("礼品申请流程监听器执行:{}", processEvent.toString()); + CrmGiftApply giftApply = baseMapper.selectById(Convert.toLong(processEvent.getBusinessId())); + if (giftApply == null) { + log.warn("礼品申请不存在,businessId={}", processEvent.getBusinessId()); + return; + } + + String oldStatus = giftApply.getFlowStatus(); + giftApply.setFlowStatus(processEvent.getStatus()); + + // 审批中 + if (Objects.equals(processEvent.getStatus(), BusinessStatusEnum.WAITING.getStatus())) { + giftApply.setApplicationStatus(OAStatusEnum.APPROVING.getStatus()); + } + // ⭐ 审批通过:自动发放 + else if (Objects.equals(processEvent.getStatus(), BusinessStatusEnum.FINISH.getStatus())) { + // 幂等性检查:避免重复发放(通过 recipient_date 判断) + if (giftApply.getRecipientDate() == null) { + log.info("礼品申请审批通过,自动发放:giftApplyId={}", giftApply.getGiftApplyId()); + autoIssueGifts(giftApply); + } else { + log.info("礼品申请已发放,跳过:giftApplyId={}", giftApply.getGiftApplyId()); + } + giftApply.setApplicationStatus(OAStatusEnum.COMPLETED.getStatus()); + } + // 驳回/撤销:回到草稿 + else if (Objects.equals(processEvent.getStatus(), BusinessStatusEnum.BACK.getStatus()) + || Objects.equals(processEvent.getStatus(), BusinessStatusEnum.CANCEL.getStatus())) { + giftApply.setApplicationStatus(OAStatusEnum.DRAFT.getStatus()); + } + // 作废/终止 + else if (Objects.equals(processEvent.getStatus(), BusinessStatusEnum.INVALID.getStatus()) + || Objects.equals(processEvent.getStatus(), BusinessStatusEnum.TERMINATION.getStatus())) { + giftApply.setApplicationStatus(OAStatusEnum.INVALID.getStatus()); + } + + baseMapper.updateById(giftApply); + }); + } + + /** + * ⭐ 自动发放礼品(审批通过时调用) + * 遍历明细 → 原子扣减库存 → 创建发放记录 → 更新申请单 recipient_date + * + * @param giftApply 礼品申请 + */ + @Transactional(rollbackFor = Exception.class) + public void autoIssueGifts(CrmGiftApply giftApply) { + // 1. 查询申请明细 + List detailList = detailMapper.selectList( + Wrappers.lambdaQuery(CrmGiftApplyDetail.class) + .eq(CrmGiftApplyDetail::getApplicationId, giftApply.getGiftApplyId()) + ); + if (CollUtil.isEmpty(detailList)) { + throw new ServiceException("礼品申请明细不存在"); + } + + // 2. 遍历明细,批量发放 + for (CrmGiftApplyDetail detail : detailList) { + // 2.1 查询礼品信息 + CrmGiftInfo giftInfo = giftInfoMapper.selectById(detail.getGiftId()); + if (giftInfo == null) { + throw new ServiceException("礼品不存在:" + detail.getGiftName()); + } + + // 2.2 校验库存 + Integer currentInventory = giftInfo.getInventoryQuantity(); + if (currentInventory == null || currentInventory < detail.getApplicationQuantity()) { + throw new ServiceException("礼品[" + detail.getGiftName() + "]库存不足,当前库存:" + + (currentInventory == null ? 0 : currentInventory) + + ",需要:" + detail.getApplicationQuantity()); + } + + // 2.3 原子扣减库存(使用 WHERE inventory_quantity >= #{quantity} 避免并发超发) + int affected = giftInfoMapper.deductInventory(detail.getGiftId(), detail.getApplicationQuantity()); + if (affected == 0) { + throw new ServiceException("礼品[" + detail.getGiftName() + "]库存扣减失败,请重试"); + } + + // 2.4 创建发放记录 + CrmGiftIssueRecord record = new CrmGiftIssueRecord(); + record.setApplicationId(giftApply.getGiftApplyId()); + record.setApplicationCode(giftApply.getApplicationCode()); + record.setDetailId(detail.getDetailId()); + record.setGiftId(detail.getGiftId()); + record.setGiftCode(detail.getGiftCode()); + record.setGiftName(detail.getGiftName()); + record.setSpecification(detail.getSpecification()); + record.setIssueQuantity(detail.getApplicationQuantity()); + record.setIssueDate(new Date()); + record.setRecipientId(giftApply.getRecipientId()); + record.setRecipientName(giftApply.getRecipientName()); + // 发放人使用申请人信息(自动发放) + record.setIssueBy(giftApply.getApplicantId()); + record.setIssueByName(giftApply.getApplicantName()); + record.setIssueDeptId(giftApply.getApplicantDeptId()); + record.setIssueDeptName(giftApply.getApplicantDeptName()); + record.setBeforeInventory(currentInventory); + record.setAfterInventory(currentInventory - detail.getApplicationQuantity()); + issueRecordMapper.insert(record); + + log.info("礼品自动发放成功:giftId={}, giftName={}, quantity={}, before={}, after={}", + detail.getGiftId(), detail.getGiftName(), detail.getApplicationQuantity(), + currentInventory, currentInventory - detail.getApplicationQuantity()); + } + + // 3. 更新申请单的领用日期(作为已发放标志) + CrmGiftApply update = new CrmGiftApply(); + update.setGiftApplyId(giftApply.getGiftApplyId()); + update.setRecipientDate(new Date()); + baseMapper.updateById(update); + + log.info("礼品申请自动发放完成:giftApplyId={}, detailCount={}", giftApply.getGiftApplyId(), detailList.size()); + } + + /** + * 批量发放礼品 + * 业务流程: + * 1. 校验申请状态必须为"已审批"(3) + * 2. P0-5: 校验是否已发放(幂等性) + * 3. P0-5: 校验申请数量 > 0 + * 4. 遍历申请明细,批量创建发放记录 + * 5. 自动扣减库存 + * 6. 更新申请单的领用日期(作为已发放标志) + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean batchIssueGifts(CrmGiftApplyBo bo) { + // 1. 校验申请状态 + CrmGiftApply apply = baseMapper.selectById(bo.getGiftApplyId()); + if (apply == null) { + throw new ServiceException("礼品申请不存在"); + } + if (!OAStatusEnum.COMPLETED.getStatus().equals(apply.getApplicationStatus())) { + throw new ServiceException("只能发放已审批通过的礼品申请"); + } + + // P0-5: 校验是否已发放(幂等性) + if (apply.getRecipientDate() != null) { + throw new ServiceException("该申请已发放,不能重复发放"); + } + + // 2. 查询申请明细 + List detailList = detailMapper.selectList( + Wrappers.lambdaQuery(CrmGiftApplyDetail.class) + .eq(CrmGiftApplyDetail::getApplicationId, bo.getGiftApplyId()) + ); + if (CollUtil.isEmpty(detailList)) { + throw new ServiceException("礼品申请明细不存在"); + } + + // P0-5: 校验申请数量 > 0 + for (CrmGiftApplyDetail detail : detailList) { + if (detail.getApplicationQuantity() == null || detail.getApplicationQuantity() <= 0) { + throw new ServiceException("礼品[" + detail.getGiftName() + "]申请数量必须大于0"); + } + } + + // 3. 遍历明细,批量发放 + for (CrmGiftApplyDetail detail : detailList) { + // 查询礼品信息 + CrmGiftInfo giftInfo = giftInfoMapper.selectById(detail.getGiftId()); + if (giftInfo == null) { + throw new ServiceException("礼品不存在:" + detail.getGiftName()); + } + + // 校验库存 + Integer currentInventory = giftInfo.getInventoryQuantity(); + if (currentInventory == null || currentInventory < detail.getApplicationQuantity()) { + throw new ServiceException("礼品[" + detail.getGiftName() + "]库存不足,当前库存:" + (currentInventory == null ? 0 : currentInventory) + ",需要:" + detail.getApplicationQuantity()); + } + + // 原子扣减库存 + int affected = giftInfoMapper.deductInventory(detail.getGiftId(), detail.getApplicationQuantity()); + if (affected == 0) { + throw new ServiceException("礼品[" + detail.getGiftName() + "]库存扣减失败,请重试"); + } + + // 创建发放记录 + CrmGiftIssueRecord record = new CrmGiftIssueRecord(); + record.setApplicationId(bo.getGiftApplyId()); + record.setApplicationCode(apply.getApplicationCode()); + record.setDetailId(detail.getDetailId()); + record.setGiftId(detail.getGiftId()); + record.setGiftCode(detail.getGiftCode()); + record.setGiftName(detail.getGiftName()); + record.setSpecification(detail.getSpecification()); + record.setIssueQuantity(detail.getApplicationQuantity()); + record.setIssueDate(new Date()); + record.setRecipientId(bo.getRecipientId() != null ? bo.getRecipientId() : apply.getRecipientId()); + record.setRecipientName(bo.getRecipientName() != null ? bo.getRecipientName() : apply.getRecipientName()); + record.setIssueBy(LoginHelper.getUserId()); + record.setIssueByName(LoginHelper.getUsername()); + record.setIssueDeptId(LoginHelper.getDeptId()); + // 尝试获取部门名称 + try { + record.setIssueDeptName(LoginHelper.getLoginUser().getDeptName()); + } catch (Exception e) { + // 获取失败时忽略 + } + record.setBeforeInventory(currentInventory); + record.setAfterInventory(currentInventory - detail.getApplicationQuantity()); + issueRecordMapper.insert(record); + } + + // 4. P0-13: 更新申请单的领用日期(作为已发放标志) + CrmGiftApply update = new CrmGiftApply(); + update.setGiftApplyId(bo.getGiftApplyId()); + update.setRecipientDate(new Date()); + baseMapper.updateById(update); + + return true; + } +} diff --git a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/service/impl/CrmGiftInfoServiceImpl.java b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/service/impl/CrmGiftInfoServiceImpl.java new file mode 100644 index 00000000..ba33afd5 --- /dev/null +++ b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/service/impl/CrmGiftInfoServiceImpl.java @@ -0,0 +1,177 @@ +package org.dromara.oa.crm.service.impl; + +import cn.hutool.core.collection.CollUtil; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.github.yulichang.toolkit.JoinWrappers; +import com.github.yulichang.wrapper.MPJLambdaWrapper; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.oa.crm.domain.CrmGiftApplyDetail; +import org.dromara.oa.crm.domain.CrmGiftInfo; +import org.dromara.oa.crm.domain.bo.CrmGiftInfoBo; +import org.dromara.oa.crm.domain.vo.CrmGiftInfoVo; +import org.dromara.oa.crm.mapper.CrmGiftApplyDetailMapper; +import org.dromara.oa.crm.mapper.CrmGiftApplyMapper; +import org.dromara.oa.crm.mapper.CrmGiftInfoMapper; +import org.dromara.oa.crm.service.ICrmGiftInfoService; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 礼品信息Service业务层处理 + * + * @author Yinq + * @date 2025-12-19 + */ +@RequiredArgsConstructor +@Service +public class CrmGiftInfoServiceImpl implements ICrmGiftInfoService { + + private final CrmGiftInfoMapper baseMapper; + private final CrmGiftApplyDetailMapper applyDetailMapper; + private final CrmGiftApplyMapper applyMapper; + + /** + * 查询礼品信息 + * + * @param giftId 主键 + * @return 礼品信息 + */ + @Override + public CrmGiftInfoVo queryById(Long giftId){ + return baseMapper.selectVoById(giftId); + } + + /** + * 分页查询礼品信息列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 礼品信息分页列表 + */ + @Override + public TableDataInfo queryPageList(CrmGiftInfoBo bo, PageQuery pageQuery) { + MPJLambdaWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的礼品信息列表 + * + * @param bo 查询条件 + * @return 礼品信息列表 + */ + @Override + public List queryList(CrmGiftInfoBo bo) { + MPJLambdaWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private MPJLambdaWrapper buildQueryWrapper(CrmGiftInfoBo bo) { + Map params = bo.getParams(); + MPJLambdaWrapper lqw = JoinWrappers.lambda(CrmGiftInfo.class) + .selectAll(CrmGiftInfo.class) + .eq(CrmGiftInfo::getDelFlag, "0") + .eq(StringUtils.isNotBlank(bo.getGiftCode()), CrmGiftInfo::getGiftCode, bo.getGiftCode()) + .like(StringUtils.isNotBlank(bo.getGiftName()), CrmGiftInfo::getGiftName, bo.getGiftName()) + .eq(StringUtils.isNotBlank(bo.getSpecification()), CrmGiftInfo::getSpecification, bo.getSpecification()) + .eq(bo.getUnitPrice() != null, CrmGiftInfo::getUnitPrice, bo.getUnitPrice()) + .eq(bo.getUnitId() != null, CrmGiftInfo::getUnitId, bo.getUnitId()) + .eq(bo.getInventoryQuantity() != null, CrmGiftInfo::getInventoryQuantity, bo.getInventoryQuantity()) + .orderByDesc(CrmGiftInfo::getCreateTime); + return lqw; + } + + /** + * 新增礼品信息 + * + * @param bo 礼品信息 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(CrmGiftInfoBo bo) { + CrmGiftInfo add = MapstructUtils.convert(bo, CrmGiftInfo.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setGiftId(add.getGiftId()); + } + return flag; + } + + /** + * 修改礼品信息 + * + * @param bo 礼品信息 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(CrmGiftInfoBo bo) { + CrmGiftInfo update = MapstructUtils.convert(bo, CrmGiftInfo.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + * 实现礼品编码唯一性校验,以及礼品名称+规格型号组合唯一性校验 + */ + private void validEntityBeforeSave(CrmGiftInfo entity){ + // 礼品编码唯一性校验 + Boolean codeExists = baseMapper.exists( + Wrappers.lambdaQuery(CrmGiftInfo.class) + .eq(CrmGiftInfo::getGiftCode, entity.getGiftCode()) + .eq(CrmGiftInfo::getDelFlag, "0") + .ne(entity.getGiftId() != null, CrmGiftInfo::getGiftId, entity.getGiftId()) + ); + if (Boolean.TRUE.equals(codeExists)) { + throw new ServiceException("礼品编码已存在"); + } + + // 礼品名称+规格型号组合唯一性校验 + Boolean comboExists = baseMapper.exists( + Wrappers.lambdaQuery(CrmGiftInfo.class) + .eq(CrmGiftInfo::getGiftName, entity.getGiftName()) + .eq(StringUtils.isNotBlank(entity.getSpecification()), CrmGiftInfo::getSpecification, entity.getSpecification()) + .eq(CrmGiftInfo::getDelFlag, "0") + .ne(entity.getGiftId() != null, CrmGiftInfo::getGiftId, entity.getGiftId()) + ); + if (Boolean.TRUE.equals(comboExists)) { + throw new ServiceException("相同名称和规格的礼品已存在"); + } + } + + /** + * 校验并批量删除礼品信息信息 + * P1-3: 删除前校验是否有未完成的申请引用该礼品 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + // P1-3: 校验是否有未完成的申请引用该礼品 + for (Long giftId : ids) { + List details = applyDetailMapper.selectList( + Wrappers.lambdaQuery(CrmGiftApplyDetail.class) + .eq(CrmGiftApplyDetail::getGiftId, giftId) + ); + if (CollUtil.isNotEmpty(details)) { + throw new ServiceException("该礼品存在关联的申请记录,无法删除"); + } + } + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/service/impl/CrmGiftIssueRecordServiceImpl.java b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/service/impl/CrmGiftIssueRecordServiceImpl.java new file mode 100644 index 00000000..900993a4 --- /dev/null +++ b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/service/impl/CrmGiftIssueRecordServiceImpl.java @@ -0,0 +1,227 @@ +package org.dromara.oa.crm.service.impl; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.github.yulichang.toolkit.JoinWrappers; +import com.github.yulichang.wrapper.MPJLambdaWrapper; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.satoken.utils.LoginHelper; +import org.dromara.oa.crm.domain.CrmGiftInfo; +import org.dromara.oa.crm.domain.CrmGiftIssueRecord; +import org.dromara.oa.crm.domain.bo.CrmGiftIssueRecordBo; +import org.dromara.oa.crm.domain.vo.CrmGiftIssueRecordVo; +import org.dromara.oa.crm.mapper.CrmGiftInfoMapper; +import org.dromara.oa.crm.mapper.CrmGiftIssueRecordMapper; +import org.dromara.oa.crm.service.ICrmGiftIssueRecordService; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Collection; +import java.util.Date; +import java.util.List; +import java.util.Map; + +/** + * 礼品发放记录Service业务层处理 + * + * @author Yinq + * @date 2025-12-19 + */ +@RequiredArgsConstructor +@Service +public class CrmGiftIssueRecordServiceImpl implements ICrmGiftIssueRecordService { + + private final CrmGiftIssueRecordMapper baseMapper; + private final CrmGiftInfoMapper giftInfoMapper; + + /** + * 查询礼品发放记录 + * + * @param issueRecordId 主键 + * @return 礼品发放记录 + */ + @Override + public CrmGiftIssueRecordVo queryById(Long issueRecordId){ + return baseMapper.selectVoById(issueRecordId); + } + + /** + * 分页查询礼品发放记录列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 礼品发放记录分页列表 + */ + @Override + public TableDataInfo queryPageList(CrmGiftIssueRecordBo bo, PageQuery pageQuery) { + MPJLambdaWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的礼品发放记录列表 + * + * @param bo 查询条件 + * @return 礼品发放记录列表 + */ + @Override + public List queryList(CrmGiftIssueRecordBo bo) { + MPJLambdaWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private MPJLambdaWrapper buildQueryWrapper(CrmGiftIssueRecordBo bo) { + Map params = bo.getParams(); + MPJLambdaWrapper lqw = JoinWrappers.lambda(CrmGiftIssueRecord.class) + .selectAll(CrmGiftIssueRecord.class) + .eq(CrmGiftIssueRecord::getDelFlag, "0") + .eq(bo.getApplicationId() != null, CrmGiftIssueRecord::getApplicationId, bo.getApplicationId()) + .eq(StringUtils.isNotBlank(bo.getApplicationCode()), CrmGiftIssueRecord::getApplicationCode, bo.getApplicationCode()) + .eq(bo.getDetailId() != null, CrmGiftIssueRecord::getDetailId, bo.getDetailId()) + .eq(bo.getGiftId() != null, CrmGiftIssueRecord::getGiftId, bo.getGiftId()) + .eq(StringUtils.isNotBlank(bo.getGiftCode()), CrmGiftIssueRecord::getGiftCode, bo.getGiftCode()) + .like(StringUtils.isNotBlank(bo.getGiftName()), CrmGiftIssueRecord::getGiftName, bo.getGiftName()) + .eq(StringUtils.isNotBlank(bo.getSpecification()), CrmGiftIssueRecord::getSpecification, bo.getSpecification()) + .eq(bo.getIssueQuantity() != null, CrmGiftIssueRecord::getIssueQuantity, bo.getIssueQuantity()) + .eq(bo.getIssueDate() != null, CrmGiftIssueRecord::getIssueDate, bo.getIssueDate()) + .eq(bo.getRecipientId() != null, CrmGiftIssueRecord::getRecipientId, bo.getRecipientId()) + .like(StringUtils.isNotBlank(bo.getRecipientName()), CrmGiftIssueRecord::getRecipientName, bo.getRecipientName()) + .eq(bo.getIssueBy() != null, CrmGiftIssueRecord::getIssueBy, bo.getIssueBy()) + .like(StringUtils.isNotBlank(bo.getIssueByName()), CrmGiftIssueRecord::getIssueByName, bo.getIssueByName()) + .eq(bo.getIssueDeptId() != null, CrmGiftIssueRecord::getIssueDeptId, bo.getIssueDeptId()) + .like(StringUtils.isNotBlank(bo.getIssueDeptName()), CrmGiftIssueRecord::getIssueDeptName, bo.getIssueDeptName()) + .eq(bo.getBeforeInventory() != null, CrmGiftIssueRecord::getBeforeInventory, bo.getBeforeInventory()) + .eq(bo.getAfterInventory() != null, CrmGiftIssueRecord::getAfterInventory, bo.getAfterInventory()) + .orderByDesc(CrmGiftIssueRecord::getCreateTime); + return lqw; + } + + /** + * 新增礼品发放记录(并发安全库存扣减) + * 业务流程: + * 1. 校验必填字段和发放数量 + * 2. 查询礼品信息,记录扣减前库存 + * 3. 原子扣减库存(乐观锁:库存 >= 扣减量) + * 4. 自动填充发放人、发放部门信息 + * 5. 记录扣减前后库存并插入发放记录 + * + * @param bo 礼品发放记录 + * @return 是否新增成功 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean insertByBo(CrmGiftIssueRecordBo bo) { + // 1. 基本校验 + if (bo.getGiftId() == null) { + throw new ServiceException("礼品ID不能为空"); + } + if (bo.getIssueQuantity() == null || bo.getIssueQuantity() <= 0) { + throw new ServiceException("发放数量必须大于0"); + } + + // 2. 查询礼品信息,获取当前库存 + CrmGiftInfo giftInfo = giftInfoMapper.selectById(bo.getGiftId()); + if (giftInfo == null || "1".equals(giftInfo.getDelFlag())) { + throw new ServiceException("礼品不存在或已被删除"); + } + Integer beforeInventory = giftInfo.getInventoryQuantity(); + if (beforeInventory == null || beforeInventory < bo.getIssueQuantity()) { + throw new ServiceException("库存不足,当前库存:" + (beforeInventory == null ? 0 : beforeInventory) + ",需要发放:" + bo.getIssueQuantity()); + } + + // 3. 原子扣减库存(乐观锁:库存 >= 扣减量时才更新) + int affected = giftInfoMapper.deductInventory(bo.getGiftId(), bo.getIssueQuantity()); + if (affected == 0) { + // 扣减失败,说明并发场景下库存已被其他线程扣减,库存不足 + throw new ServiceException("库存扣减失败,可能库存不足或已被其他操作占用,请重试"); + } + + // 4. 自动填充发放人、发放部门信息(从当前登录用户获取) + bo.setIssueBy(LoginHelper.getUserId()); + bo.setIssueByName(LoginHelper.getUsername()); + bo.setIssueDeptId(LoginHelper.getDeptId()); + // 部门名称需要从登录用户信息获取(如果Bo中未设置) + if (StringUtils.isBlank(bo.getIssueDeptName())) { + // 尝试从LoginHelper获取部门名称 + try { + bo.setIssueDeptName(LoginHelper.getLoginUser().getDeptName()); + } catch (Exception e) { + // 获取失败时忽略,后续可考虑通过部门ID查询 + } + } + // 发放日期默认当前时间 + if (bo.getIssueDate() == null) { + bo.setIssueDate(new Date()); + } + + // 5. 填充礼品冗余信息(如果前端未传) + if (StringUtils.isBlank(bo.getGiftCode())) { + bo.setGiftCode(giftInfo.getGiftCode()); + } + if (StringUtils.isBlank(bo.getGiftName())) { + bo.setGiftName(giftInfo.getGiftName()); + } + if (StringUtils.isBlank(bo.getSpecification())) { + bo.setSpecification(giftInfo.getSpecification()); + } + + // 6. 记录扣减前后库存 + bo.setBeforeInventory(beforeInventory); + bo.setAfterInventory(beforeInventory - bo.getIssueQuantity()); + + // 7. 插入发放记录 + CrmGiftIssueRecord add = MapstructUtils.convert(bo, CrmGiftIssueRecord.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setIssueRecordId(add.getIssueRecordId()); + } + return flag; + } + + /** + * 修改礼品发放记录 + * P1-2: 发放记录作为出库台账,仅允许修改备注字段,禁止修改其他业务字段 + * + * @param bo 礼品发放记录 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(CrmGiftIssueRecordBo bo) { + CrmGiftIssueRecord existing = baseMapper.selectById(bo.getIssueRecordId()); + if (existing == null) { + throw new ServiceException("发放记录不存在"); + } + + // 仅允许修改备注字段,其他字段不允许修改 + CrmGiftIssueRecord update = new CrmGiftIssueRecord(); + update.setIssueRecordId(bo.getIssueRecordId()); + update.setRemark(bo.getRemark()); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(CrmGiftIssueRecord entity){ + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除礼品发放记录信息 + * P1-1: 发放记录作为审计台账,禁止删除 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + throw new ServiceException("发放记录作为审计台账,禁止删除"); + } +} diff --git a/ruoyi-modules/ruoyi-oa/src/main/resources/mapper/oa/crm/CrmGiftApplyDetailMapper.xml b/ruoyi-modules/ruoyi-oa/src/main/resources/mapper/oa/crm/CrmGiftApplyDetailMapper.xml new file mode 100644 index 00000000..966cccd0 --- /dev/null +++ b/ruoyi-modules/ruoyi-oa/src/main/resources/mapper/oa/crm/CrmGiftApplyDetailMapper.xml @@ -0,0 +1,233 @@ + + + + + + + + + + + + + + + + + + + + + + + insert into crm_gift_apply_detail( + tenant_id, + + application_id, + + gift_id, + + gift_code, + + gift_name, + + specification, + + unit_price, + + inventory_quantity, + + application_quantity, + + gift_amount, + + sort_order, + + remark, + + del_flag, + + create_dept, + + create_by, + + create_time, + + update_by, + + update_time + + ) + values + + ( + #{item.tenantId}, + + #{item.applicationId}, + + #{item.giftId}, + + #{item.giftCode}, + + #{item.giftName}, + + #{item.specification}, + + #{item.unitPrice}, + + #{item.inventoryQuantity}, + + #{item.applicationQuantity}, + + #{item.giftAmount}, + + #{item.sortOrder}, + + #{item.remark}, + + #{item.delFlag}, + + #{item.createDept}, + + #{item.createBy}, + + #{item.createTime}, + + #{item.updateBy}, + + #{item.updateTime} + + ) + + + + + + + update crm_gift_apply_detail t + + + t.tenant_id = #{item.tenantId}, + + + t.application_id = #{item.applicationId}, + + + t.gift_id = #{item.giftId}, + + + t.gift_code = #{item.giftCode}, + + + t.gift_name = #{item.giftName}, + + + t.specification = #{item.specification}, + + + t.unit_price = #{item.unitPrice}, + + + t.inventory_quantity = #{item.inventoryQuantity}, + + + t.application_quantity = #{item.applicationQuantity}, + + + t.gift_amount = #{item.giftAmount}, + + + t.sort_order = #{item.sortOrder}, + + + t.remark = #{item.remark}, + + + t.del_flag = #{item.delFlag}, + + + t.create_dept = #{item.createDept}, + + + t.create_by = #{item.createBy}, + + + t.create_time = #{item.createTime}, + + + t.update_by = #{item.updateBy}, + + + t.update_time = #{item.updateTime} + + + where t.detail_id = #{item.detailId} + + + + + + delete from crm_gift_apply_detail t + + + AND ${ew.sqlSegment} + + + + + + + delete from crm_gift_apply_detail t + where t.detail_id in + + #{id} + + + + + + + + diff --git a/ruoyi-modules/ruoyi-oa/src/main/resources/mapper/oa/crm/CrmGiftApplyMapper.xml b/ruoyi-modules/ruoyi-oa/src/main/resources/mapper/oa/crm/CrmGiftApplyMapper.xml new file mode 100644 index 00000000..56f04a53 --- /dev/null +++ b/ruoyi-modules/ruoyi-oa/src/main/resources/mapper/oa/crm/CrmGiftApplyMapper.xml @@ -0,0 +1,297 @@ + + + + + + + + + + + + + + + + + + + + + + + insert into crm_gift_apply( + tenant_id, + + application_code, + + applicant_id, + + applicant_name, + + applicant_dept_id, + + applicant_dept_name, + + application_date, + + customer_unit_id, + + customer_unit_name, + + guest_name, + + people_count, + + application_reason, + + recipient_id, + + recipient_name, + + recipient_date, + + total_amount, + + application_status, + + flow_status, + + remark, + + del_flag, + + create_dept, + + create_by, + + create_time, + + update_by, + + update_time + + ) + values + + ( + #{item.tenantId}, + + #{item.applicationCode}, + + #{item.applicantId}, + + #{item.applicantName}, + + #{item.applicantDeptId}, + + #{item.applicantDeptName}, + + #{item.applicationDate}, + + #{item.customerUnitId}, + + #{item.customerUnitName}, + + #{item.guestName}, + + #{item.peopleCount}, + + #{item.applicationReason}, + + #{item.recipientId}, + + #{item.recipientName}, + + #{item.recipientDate}, + + #{item.totalAmount}, + + #{item.applicationStatus}, + + #{item.flowStatus}, + + #{item.remark}, + + #{item.delFlag}, + + #{item.createDept}, + + #{item.createBy}, + + #{item.createTime}, + + #{item.updateBy}, + + #{item.updateTime} + + ) + + + + + + + update crm_gift_apply t + + + t.tenant_id = #{item.tenantId}, + + + t.application_code = #{item.applicationCode}, + + + t.applicant_id = #{item.applicantId}, + + + t.applicant_name = #{item.applicantName}, + + + t.applicant_dept_id = #{item.applicantDeptId}, + + + t.applicant_dept_name = #{item.applicantDeptName}, + + + t.application_date = #{item.applicationDate}, + + + t.customer_unit_id = #{item.customerUnitId}, + + + t.customer_unit_name = #{item.customerUnitName}, + + + t.guest_name = #{item.guestName}, + + + t.people_count = #{item.peopleCount}, + + + t.application_reason = #{item.applicationReason}, + + + t.recipient_id = #{item.recipientId}, + + + t.recipient_name = #{item.recipientName}, + + + t.recipient_date = #{item.recipientDate}, + + + t.total_amount = #{item.totalAmount}, + + + t.application_status = #{item.applicationStatus}, + + + t.flow_status = #{item.flowStatus}, + + + t.remark = #{item.remark}, + + + t.del_flag = #{item.delFlag}, + + + t.create_dept = #{item.createDept}, + + + t.create_by = #{item.createBy}, + + + t.create_time = #{item.createTime}, + + + t.update_by = #{item.updateBy}, + + + t.update_time = #{item.updateTime} + + + where t.gift_apply_id = #{item.giftApplyId} + + + + + + delete from crm_gift_apply t + + + AND ${ew.sqlSegment} + + + + + + + delete from crm_gift_apply t + where t.gift_apply_id in + + #{id} + + + + + + + + + + diff --git a/ruoyi-modules/ruoyi-oa/src/main/resources/mapper/oa/crm/CrmGiftInfoMapper.xml b/ruoyi-modules/ruoyi-oa/src/main/resources/mapper/oa/crm/CrmGiftInfoMapper.xml new file mode 100644 index 00000000..cdd94811 --- /dev/null +++ b/ruoyi-modules/ruoyi-oa/src/main/resources/mapper/oa/crm/CrmGiftInfoMapper.xml @@ -0,0 +1,214 @@ + + + + + + + + + + + + + + + + + + + + + + + insert into crm_gift_info( + tenant_id, + + gift_code, + + gift_name, + + specification, + + unit_price, + + unit_id, + + inventory_quantity, + + remark, + + del_flag, + + create_dept, + + create_by, + + create_time, + + update_by, + + update_time + + ) + values + + ( + #{item.tenantId}, + + #{item.giftCode}, + + #{item.giftName}, + + #{item.specification}, + + #{item.unitPrice}, + + #{item.unitId}, + + #{item.inventoryQuantity}, + + #{item.remark}, + + #{item.delFlag}, + + #{item.createDept}, + + #{item.createBy}, + + #{item.createTime}, + + #{item.updateBy}, + + #{item.updateTime} + + ) + + + + + + + update crm_gift_info t + + + t.tenant_id = #{item.tenantId}, + + + t.gift_code = #{item.giftCode}, + + + t.gift_name = #{item.giftName}, + + + t.specification = #{item.specification}, + + + t.unit_price = #{item.unitPrice}, + + + t.unit_id = #{item.unitId}, + + + t.inventory_quantity = #{item.inventoryQuantity}, + + + t.remark = #{item.remark}, + + + t.del_flag = #{item.delFlag}, + + + t.create_dept = #{item.createDept}, + + + t.create_by = #{item.createBy}, + + + t.create_time = #{item.createTime}, + + + t.update_by = #{item.updateBy}, + + + t.update_time = #{item.updateTime} + + + where t.gift_id = #{item.giftId} + + + + + + delete from crm_gift_info t + + + AND ${ew.sqlSegment} + + + + + + + delete from crm_gift_info t + where t.gift_id in + + #{id} + + + + + + + + + update crm_gift_info + set inventory_quantity = inventory_quantity - #{deductQuantity}, + update_time = now() + where gift_id = #{giftId} + and del_flag = '0' + and inventory_quantity >= #{deductQuantity} + + + diff --git a/ruoyi-modules/ruoyi-oa/src/main/resources/mapper/oa/crm/CrmGiftIssueRecordMapper.xml b/ruoyi-modules/ruoyi-oa/src/main/resources/mapper/oa/crm/CrmGiftIssueRecordMapper.xml new file mode 100644 index 00000000..dc48a5c7 --- /dev/null +++ b/ruoyi-modules/ruoyi-oa/src/main/resources/mapper/oa/crm/CrmGiftIssueRecordMapper.xml @@ -0,0 +1,282 @@ + + + + + + + + + + + + + + + + + + + + + + + insert into crm_gift_issue_record( + tenant_id, + + application_id, + + application_code, + + detail_id, + + gift_id, + + gift_code, + + gift_name, + + specification, + + issue_quantity, + + issue_date, + + recipient_id, + + recipient_name, + + issue_by, + + issue_by_name, + + issue_dept_id, + + issue_dept_name, + + before_inventory, + + after_inventory, + + remark, + + del_flag, + + create_dept, + + create_by, + + create_time, + + update_by, + + update_time + + ) + values + + ( + #{item.tenantId}, + + #{item.applicationId}, + + #{item.applicationCode}, + + #{item.detailId}, + + #{item.giftId}, + + #{item.giftCode}, + + #{item.giftName}, + + #{item.specification}, + + #{item.issueQuantity}, + + #{item.issueDate}, + + #{item.recipientId}, + + #{item.recipientName}, + + #{item.issueBy}, + + #{item.issueByName}, + + #{item.issueDeptId}, + + #{item.issueDeptName}, + + #{item.beforeInventory}, + + #{item.afterInventory}, + + #{item.remark}, + + #{item.delFlag}, + + #{item.createDept}, + + #{item.createBy}, + + #{item.createTime}, + + #{item.updateBy}, + + #{item.updateTime} + + ) + + + + + + + update crm_gift_issue_record t + + + t.tenant_id = #{item.tenantId}, + + + t.application_id = #{item.applicationId}, + + + t.application_code = #{item.applicationCode}, + + + t.detail_id = #{item.detailId}, + + + t.gift_id = #{item.giftId}, + + + t.gift_code = #{item.giftCode}, + + + t.gift_name = #{item.giftName}, + + + t.specification = #{item.specification}, + + + t.issue_quantity = #{item.issueQuantity}, + + + t.issue_date = #{item.issueDate}, + + + t.recipient_id = #{item.recipientId}, + + + t.recipient_name = #{item.recipientName}, + + + t.issue_by = #{item.issueBy}, + + + t.issue_by_name = #{item.issueByName}, + + + t.issue_dept_id = #{item.issueDeptId}, + + + t.issue_dept_name = #{item.issueDeptName}, + + + t.before_inventory = #{item.beforeInventory}, + + + t.after_inventory = #{item.afterInventory}, + + + t.remark = #{item.remark}, + + + t.del_flag = #{item.delFlag}, + + + t.create_dept = #{item.createDept}, + + + t.create_by = #{item.createBy}, + + + t.create_time = #{item.createTime}, + + + t.update_by = #{item.updateBy}, + + + t.update_time = #{item.updateTime} + + + where t.issue_record_id = #{item.issueRecordId} + + + + + + delete from crm_gift_issue_record t + + + AND ${ew.sqlSegment} + + + + + + + delete from crm_gift_issue_record t + where t.issue_record_id in + + #{id} + + + + + + + +