feat(oa/crm): 新增礼品管理模块

- 创建礼品申请实体类CrmGiftApply及其业务对象CrmGiftApplyBo
- 创建礼品申请明细实体类CrmGiftApplyDetail及其业务对象CrmGiftApplyDetailBo
- 实现礼品申请控制器CrmGiftApplyController提供完整的CRUD操作
- 实现礼品申请明细控制器CrmGiftApplyDetailController提供明细管理功能
- 添加礼品申请及明细的数据访问层映射器和实现服务类
- 集成工作流程相关字段支持礼品申请审批流程
- 实现礼品申请批量发放功能和库存扣减逻辑
- 配置MyBatis映射文件支持数据持久化操作
dev
zangch@mesnac.com 2 months ago
parent c7507ccaf6
commit 82d2ad7545

@ -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<CrmGiftApplyVo> 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<CrmGiftApplyVo> list = crmGiftApplyService.queryList(bo);
ExcelUtil.exportExcel(list, "礼品申请", CrmGiftApplyVo.class, response);
}
/**
*
*
* @param giftApplyId
*/
@SaCheckPermission("oa/crm:crmGiftApply:query")
@GetMapping("/{giftApplyId}")
public R<CrmGiftApplyVo> 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<Void> 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<Void> 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<Void> 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<CrmGiftApplyVo> giftApplySubmitAndFlowStart(@RequestBody CrmGiftApplyBo bo) {
return R.ok(crmGiftApplyService.giftApplySubmitAndFlowStart(bo));
}
/**
*
*/
@GetMapping("/getCrmGiftApplyList")
public R<List<CrmGiftApplyVo>> getCrmGiftApplyList(CrmGiftApplyBo bo) {
List<CrmGiftApplyVo> 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<Void> batchIssue(@RequestBody CrmGiftApplyBo bo) {
return toAjax(crmGiftApplyService.batchIssueGifts(bo));
}
}

@ -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<CrmGiftApplyDetailVo> 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<CrmGiftApplyDetailVo> list = crmGiftApplyDetailService.queryList(bo);
ExcelUtil.exportExcel(list, "礼品申请明细", CrmGiftApplyDetailVo.class, response);
}
/**
*
*
* @param detailId
*/
@SaCheckPermission("oa/crm:crmGiftApplyDetail:query")
@GetMapping("/{detailId}")
public R<CrmGiftApplyDetailVo> 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<Void> 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<Void> 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<Void> remove(@NotEmpty(message = "主键不能为空")
@PathVariable("detailIds") Long[] detailIds) {
return toAjax(crmGiftApplyDetailService.deleteWithValidByIds(List.of(detailIds), true));
}
/**
*
*/
@GetMapping("/getCrmGiftApplyDetailList")
public R<List<CrmGiftApplyDetailVo>> getCrmGiftApplyDetailList(CrmGiftApplyDetailBo bo) {
List<CrmGiftApplyDetailVo> list = crmGiftApplyDetailService.queryList(bo);
return R.ok(list);
}
}

@ -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<CrmGiftInfoVo> 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<CrmGiftInfoVo> list = crmGiftInfoService.queryList(bo);
ExcelUtil.exportExcel(list, "礼品信息", CrmGiftInfoVo.class, response);
}
/**
*
*
* @param giftId
*/
@SaCheckPermission("oa/crm:crmGiftInfo:query")
@GetMapping("/{giftId}")
public R<CrmGiftInfoVo> 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<Void> 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<Void> 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<Void> remove(@NotEmpty(message = "主键不能为空")
@PathVariable("giftIds") Long[] giftIds) {
return toAjax(crmGiftInfoService.deleteWithValidByIds(List.of(giftIds), true));
}
/**
*
*/
@GetMapping("/getCrmGiftInfoList")
public R<List<CrmGiftInfoVo>> getCrmGiftInfoList(CrmGiftInfoBo bo) {
List<CrmGiftInfoVo> list = crmGiftInfoService.queryList(bo);
return R.ok(list);
}
}

@ -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<CrmGiftIssueRecordVo> 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<CrmGiftIssueRecordVo> list = crmGiftIssueRecordService.queryList(bo);
ExcelUtil.exportExcel(list, "礼品发放记录", CrmGiftIssueRecordVo.class, response);
}
/**
*
*
* @param issueRecordId
*/
@SaCheckPermission("oa/crm:crmGiftIssueRecord:query")
@GetMapping("/{issueRecordId}")
public R<CrmGiftIssueRecordVo> 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<Void> 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<Void> 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<Void> remove(@NotEmpty(message = "主键不能为空")
@PathVariable("issueRecordIds") Long[] issueRecordIds) {
return toAjax(crmGiftIssueRecordService.deleteWithValidByIds(List.of(issueRecordIds), true));
}
/**
*
*/
@GetMapping("/getCrmGiftIssueRecordList")
public R<List<CrmGiftIssueRecordVo>> getCrmGiftIssueRecordList(CrmGiftIssueRecordBo bo) {
List<CrmGiftIssueRecordVo> list = crmGiftIssueRecordService.queryList(bo);
return R.ok(list);
}
}

@ -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;
}

@ -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;
}

@ -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;
}

@ -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;
}

@ -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<String, Object> variables;
/**
*
*/
private RemoteFlowInstanceBizExt bizExt;
public Map<String, Object> 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<CrmGiftApplyDetail> detailList;
}

@ -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;
}

@ -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;
}

@ -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;
}

@ -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;
}

@ -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<CrmGiftApplyDetail> detailList;
}

@ -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;
}

@ -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;
}

@ -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;
}

@ -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<CrmGiftApplyDetail, CrmGiftApplyDetailVo> {
/**
*
*
* @param page
* @param queryWrapper
* @return
*/
public Page<CrmGiftApplyDetailVo> selectCustomCrmGiftApplyDetailVoList(@Param("page") Page<CrmGiftApplyDetailVo> page, @Param(Constants.WRAPPER) MPJLambdaWrapper<CrmGiftApplyDetail> queryWrapper);
/**
*
*
* @param queryWrapper
* @return
*/
public List<CrmGiftApplyDetailVo> selectCustomCrmGiftApplyDetailVoList(@Param(Constants.WRAPPER) MPJLambdaWrapper<CrmGiftApplyDetail> queryWrapper);
/**
* ID
*
* @param detailId ID
* @return
*/
CrmGiftApplyDetailVo selectCustomCrmGiftApplyDetailVoById(@Param("detailId") Long detailId);
/**
* ID
*
* @param ids ID
* @return
*/
List<CrmGiftApplyDetailVo> selectCustomCrmGiftApplyDetailVoByIds(@Param("ids") Collection<Long> ids);
/**
*
*
* @param queryWrapper
* @return
*/
Long countCustomCrmGiftApplyDetail(@Param(Constants.WRAPPER) Wrapper<CrmGiftApplyDetail> queryWrapper);
/**
*
*
* @param page
* @param queryWrapper
* @return
*/
Page<CrmGiftApplyDetailVo> selectCustomCrmGiftApplyDetailVoPage(@Param("page") Page<CrmGiftApplyDetailVo> page, @Param(Constants.WRAPPER) Wrapper<CrmGiftApplyDetail> queryWrapper);
/**
*
*
* @param list
* @return
*/
int batchInsertCrmGiftApplyDetail(@Param("list") List<CrmGiftApplyDetail> list);
/**
*
*
* @param list
* @return
*/
int batchUpdateCrmGiftApplyDetail(@Param("list") List<CrmGiftApplyDetail> list);
/**
*
*
* @param queryWrapper
* @return
*/
int deleteCustomCrmGiftApplyDetail(@Param(Constants.WRAPPER) Wrapper<CrmGiftApplyDetail> queryWrapper);
/**
* ID
*
* @param ids ID
* @return
*/
int deleteCustomCrmGiftApplyDetailByIds(@Param("ids") Collection<Long> ids);
/**
*
*
* @param queryWrapper
* @return
*/
Boolean existsCrmGiftApplyDetail(@Param(Constants.WRAPPER) Wrapper<CrmGiftApplyDetail> queryWrapper);
}

@ -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<CrmGiftApply, CrmGiftApplyVo> {
/**
*
*
* @param page
* @param queryWrapper
* @return
*/
@DataPermission({
@DataColumn(key = "deptName", value = "t.create_dept"),
@DataColumn(key = "userName", value = "t.create_by")
})
Page<CrmGiftApplyVo> selectCustomCrmGiftApplyVoList(@Param("page") Page<CrmGiftApplyVo> page, @Param(Constants.WRAPPER) MPJLambdaWrapper<CrmGiftApply> queryWrapper);
/**
*
*
* @param queryWrapper
* @return
*/
public List<CrmGiftApplyVo> selectCustomCrmGiftApplyVoList(@Param(Constants.WRAPPER) MPJLambdaWrapper<CrmGiftApply> queryWrapper);
/**
* ID
*
* @param giftApplyId ID
* @return
*/
CrmGiftApplyVo selectCustomCrmGiftApplyVoById(@Param("giftApplyId") Long giftApplyId);
/**
* ID
*
* @param ids ID
* @return
*/
List<CrmGiftApplyVo> selectCustomCrmGiftApplyVoByIds(@Param("ids") Collection<Long> ids);
/**
*
*
* @param queryWrapper
* @return
*/
Long countCustomCrmGiftApply(@Param(Constants.WRAPPER) Wrapper<CrmGiftApply> queryWrapper);
/**
*
*
* @param page
* @param queryWrapper
* @return
*/
Page<CrmGiftApplyVo> selectCustomCrmGiftApplyVoPage(@Param("page") Page<CrmGiftApplyVo> page, @Param(Constants.WRAPPER) Wrapper<CrmGiftApply> queryWrapper);
/**
*
*
* @param list
* @return
*/
int batchInsertCrmGiftApply(@Param("list") List<CrmGiftApply> list);
/**
*
*
* @param list
* @return
*/
int batchUpdateCrmGiftApply(@Param("list") List<CrmGiftApply> list);
/**
*
*
* @param queryWrapper
* @return
*/
int deleteCustomCrmGiftApply(@Param(Constants.WRAPPER) Wrapper<CrmGiftApply> queryWrapper);
/**
* ID
*
* @param ids ID
* @return
*/
int deleteCustomCrmGiftApplyByIds(@Param("ids") Collection<Long> ids);
/**
*
*
* @param queryWrapper
* @return
*/
Boolean existsCrmGiftApply(@Param(Constants.WRAPPER) Wrapper<CrmGiftApply> queryWrapper);
/**
*
*
*
* @return
*/
List<GiftPendingQuantityVO> selectPendingGiftQuantity();
}

@ -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<CrmGiftInfo, CrmGiftInfoVo> {
/**
*
*
* @param page
* @param queryWrapper
* @return
*/
public Page<CrmGiftInfoVo> selectCustomCrmGiftInfoVoList(@Param("page") Page<CrmGiftInfoVo> page, @Param(Constants.WRAPPER) MPJLambdaWrapper<CrmGiftInfo> queryWrapper);
/**
*
*
* @param queryWrapper
* @return
*/
public List<CrmGiftInfoVo> selectCustomCrmGiftInfoVoList(@Param(Constants.WRAPPER) MPJLambdaWrapper<CrmGiftInfo> queryWrapper);
/**
* ID
*
* @param giftId ID
* @return
*/
CrmGiftInfoVo selectCustomCrmGiftInfoVoById(@Param("giftId") Long giftId);
/**
* ID
*
* @param ids ID
* @return
*/
List<CrmGiftInfoVo> selectCustomCrmGiftInfoVoByIds(@Param("ids") Collection<Long> ids);
/**
*
*
* @param queryWrapper
* @return
*/
Long countCustomCrmGiftInfo(@Param(Constants.WRAPPER) Wrapper<CrmGiftInfo> queryWrapper);
/**
*
*
* @param page
* @param queryWrapper
* @return
*/
Page<CrmGiftInfoVo> selectCustomCrmGiftInfoVoPage(@Param("page") Page<CrmGiftInfoVo> page, @Param(Constants.WRAPPER) Wrapper<CrmGiftInfo> queryWrapper);
/**
*
*
* @param list
* @return
*/
int batchInsertCrmGiftInfo(@Param("list") List<CrmGiftInfo> list);
/**
*
*
* @param list
* @return
*/
int batchUpdateCrmGiftInfo(@Param("list") List<CrmGiftInfo> list);
/**
*
*
* @param queryWrapper
* @return
*/
int deleteCustomCrmGiftInfo(@Param(Constants.WRAPPER) Wrapper<CrmGiftInfo> queryWrapper);
/**
* ID
*
* @param ids ID
* @return
*/
int deleteCustomCrmGiftInfoByIds(@Param("ids") Collection<Long> ids);
/**
*
*
* @param queryWrapper
* @return
*/
Boolean existsCrmGiftInfo(@Param(Constants.WRAPPER) Wrapper<CrmGiftInfo> queryWrapper);
/**
*
* >=
*
* @param giftId ID
* @param deductQuantity
* @return 01
*/
int deductInventory(@Param("giftId") Long giftId, @Param("deductQuantity") Integer deductQuantity);
}

@ -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<CrmGiftIssueRecord, CrmGiftIssueRecordVo> {
/**
*
*
* @param page
* @param queryWrapper
* @return
*/
public Page<CrmGiftIssueRecordVo> selectCustomCrmGiftIssueRecordVoList(@Param("page") Page<CrmGiftIssueRecordVo> page, @Param(Constants.WRAPPER) MPJLambdaWrapper<CrmGiftIssueRecord> queryWrapper);
/**
*
*
* @param queryWrapper
* @return
*/
public List<CrmGiftIssueRecordVo> selectCustomCrmGiftIssueRecordVoList(@Param(Constants.WRAPPER) MPJLambdaWrapper<CrmGiftIssueRecord> queryWrapper);
/**
* ID
*
* @param issueRecordId ID
* @return
*/
CrmGiftIssueRecordVo selectCustomCrmGiftIssueRecordVoById(@Param("issueRecordId") Long issueRecordId);
/**
* ID
*
* @param ids ID
* @return
*/
List<CrmGiftIssueRecordVo> selectCustomCrmGiftIssueRecordVoByIds(@Param("ids") Collection<Long> ids);
/**
*
*
* @param queryWrapper
* @return
*/
Long countCustomCrmGiftIssueRecord(@Param(Constants.WRAPPER) Wrapper<CrmGiftIssueRecord> queryWrapper);
/**
*
*
* @param page
* @param queryWrapper
* @return
*/
Page<CrmGiftIssueRecordVo> selectCustomCrmGiftIssueRecordVoPage(@Param("page") Page<CrmGiftIssueRecordVo> page, @Param(Constants.WRAPPER) Wrapper<CrmGiftIssueRecord> queryWrapper);
/**
*
*
* @param list
* @return
*/
int batchInsertCrmGiftIssueRecord(@Param("list") List<CrmGiftIssueRecord> list);
/**
*
*
* @param list
* @return
*/
int batchUpdateCrmGiftIssueRecord(@Param("list") List<CrmGiftIssueRecord> list);
/**
*
*
* @param queryWrapper
* @return
*/
int deleteCustomCrmGiftIssueRecord(@Param(Constants.WRAPPER) Wrapper<CrmGiftIssueRecord> queryWrapper);
/**
* ID
*
* @param ids ID
* @return
*/
int deleteCustomCrmGiftIssueRecordByIds(@Param("ids") Collection<Long> ids);
/**
*
*
* @param queryWrapper
* @return
*/
Boolean existsCrmGiftIssueRecord(@Param(Constants.WRAPPER) Wrapper<CrmGiftIssueRecord> queryWrapper);
}

@ -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<CrmGiftApplyDetailVo> queryPageList(CrmGiftApplyDetailBo bo, PageQuery pageQuery);
/**
*
*
* @param bo
* @return
*/
List<CrmGiftApplyDetailVo> 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<Long> ids, Boolean isValid);
}

@ -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<CrmGiftApplyVo> queryPageList(CrmGiftApplyBo bo, PageQuery pageQuery);
/**
*
*
* @param bo
* @return
*/
List<CrmGiftApplyVo> 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<Long> ids, Boolean isValid);
/**
*
*
* @param bo
* @return VO
*/
CrmGiftApplyVo giftApplySubmitAndFlowStart(CrmGiftApplyBo bo);
/**
*
*
* 1. "已审批"(3)
* 2.
* 3.
* 4.
*
* @param bo ID
* @return
*/
Boolean batchIssueGifts(CrmGiftApplyBo bo);
}

@ -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<CrmGiftInfoVo> queryPageList(CrmGiftInfoBo bo, PageQuery pageQuery);
/**
*
*
* @param bo
* @return
*/
List<CrmGiftInfoVo> 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<Long> ids, Boolean isValid);
}

@ -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<CrmGiftIssueRecordVo> queryPageList(CrmGiftIssueRecordBo bo, PageQuery pageQuery);
/**
*
*
* @param bo
* @return
*/
List<CrmGiftIssueRecordVo> 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<Long> ids, Boolean isValid);
}

@ -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<CrmGiftApplyDetailVo> queryPageList(CrmGiftApplyDetailBo bo, PageQuery pageQuery) {
MPJLambdaWrapper<CrmGiftApplyDetail> lqw = buildQueryWrapper(bo);
Page<CrmGiftApplyDetailVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
return TableDataInfo.build(result);
}
/**
*
*
* @param bo
* @return
*/
@Override
public List<CrmGiftApplyDetailVo> queryList(CrmGiftApplyDetailBo bo) {
MPJLambdaWrapper<CrmGiftApplyDetail> lqw = buildQueryWrapper(bo);
return baseMapper.selectVoList(lqw);
}
private MPJLambdaWrapper<CrmGiftApplyDetail> buildQueryWrapper(CrmGiftApplyDetailBo bo) {
Map<String, Object> params = bo.getParams();
MPJLambdaWrapper<CrmGiftApplyDetail> 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<Long> ids, Boolean isValid) {
if(isValid){
//TODO 做一些业务上的校验,判断是否需要校验
}
return baseMapper.deleteByIds(ids) > 0;
}
}

@ -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<CrmGiftApplyDetail> 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<CrmGiftApplyVo> queryPageList(CrmGiftApplyBo bo, PageQuery pageQuery) {
MPJLambdaWrapper<CrmGiftApply> lqw = buildQueryWrapper(bo);
Page<CrmGiftApplyVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
return TableDataInfo.build(result);
}
/**
*
*
* @param bo
* @return
*/
@Override
public List<CrmGiftApplyVo> queryList(CrmGiftApplyBo bo) {
MPJLambdaWrapper<CrmGiftApply> lqw = buildQueryWrapper(bo);
return baseMapper.selectVoList(lqw);
}
private MPJLambdaWrapper<CrmGiftApply> buildQueryWrapper(CrmGiftApplyBo bo) {
Map<String, Object> params = bo.getParams();
MPJLambdaWrapper<CrmGiftApply> 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<Long> 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<GiftPendingQuantityVO> pendingList = baseMapper.selectPendingGiftQuantity();
// 转换为 Map: gift_id -> pending_quantity
Map<Long, Integer> pendingMap = pendingList.stream()
.collect(Collectors.toMap(
GiftPendingQuantityVO::getGiftId,
GiftPendingQuantityVO::getPendingQuantity,
(v1, v2) -> v1 + v2 // 相同 gift_id 累加
));
// 2. 遍历当前申请明细,逐个校验
List<String> 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<CrmGiftApplyDetail> 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<CrmGiftApplyDetail> 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<CrmGiftApplyDetail> 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;
}
}

@ -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<CrmGiftInfoVo> queryPageList(CrmGiftInfoBo bo, PageQuery pageQuery) {
MPJLambdaWrapper<CrmGiftInfo> lqw = buildQueryWrapper(bo);
Page<CrmGiftInfoVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
return TableDataInfo.build(result);
}
/**
*
*
* @param bo
* @return
*/
@Override
public List<CrmGiftInfoVo> queryList(CrmGiftInfoBo bo) {
MPJLambdaWrapper<CrmGiftInfo> lqw = buildQueryWrapper(bo);
return baseMapper.selectVoList(lqw);
}
private MPJLambdaWrapper<CrmGiftInfo> buildQueryWrapper(CrmGiftInfoBo bo) {
Map<String, Object> params = bo.getParams();
MPJLambdaWrapper<CrmGiftInfo> 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<Long> ids, Boolean isValid) {
if (isValid) {
// P1-3: 校验是否有未完成的申请引用该礼品
for (Long giftId : ids) {
List<CrmGiftApplyDetail> details = applyDetailMapper.selectList(
Wrappers.lambdaQuery(CrmGiftApplyDetail.class)
.eq(CrmGiftApplyDetail::getGiftId, giftId)
);
if (CollUtil.isNotEmpty(details)) {
throw new ServiceException("该礼品存在关联的申请记录,无法删除");
}
}
}
return baseMapper.deleteByIds(ids) > 0;
}
}

@ -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<CrmGiftIssueRecordVo> queryPageList(CrmGiftIssueRecordBo bo, PageQuery pageQuery) {
MPJLambdaWrapper<CrmGiftIssueRecord> lqw = buildQueryWrapper(bo);
Page<CrmGiftIssueRecordVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
return TableDataInfo.build(result);
}
/**
*
*
* @param bo
* @return
*/
@Override
public List<CrmGiftIssueRecordVo> queryList(CrmGiftIssueRecordBo bo) {
MPJLambdaWrapper<CrmGiftIssueRecord> lqw = buildQueryWrapper(bo);
return baseMapper.selectVoList(lqw);
}
private MPJLambdaWrapper<CrmGiftIssueRecord> buildQueryWrapper(CrmGiftIssueRecordBo bo) {
Map<String, Object> params = bo.getParams();
MPJLambdaWrapper<CrmGiftIssueRecord> 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<Long> ids, Boolean isValid) {
throw new ServiceException("发放记录作为审计台账,禁止删除");
}
}

@ -0,0 +1,233 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.dromara.oa.crm.mapper.CrmGiftApplyDetailMapper">
<resultMap type="org.dromara.oa.crm.domain.vo.CrmGiftApplyDetailVo" id="CrmGiftApplyDetailResult">
</resultMap>
<select id="selectCustomCrmGiftApplyDetailVoList" resultMap="CrmGiftApplyDetailResult">
select t.detail_id, t.tenant_id, t.application_id, t.gift_id, t.gift_code, t.gift_name, t.specification, t.unit_price, t.inventory_quantity, t.application_quantity, t.gift_amount, t.sort_order, t.remark, t.del_flag, t.create_dept, t.create_by, t.create_time, t.update_by, t.update_time from crm_gift_apply_detail t
<where>
<if test="ew != null and ew.sqlSegment != null and ew.sqlSegment != ''">
AND ${ew.sqlSegment}
</if>
</where>
</select>
<!-- 根据ID查询详情 -->
<select id="selectCustomCrmGiftApplyDetailVoById" resultMap="CrmGiftApplyDetailResult">
select t.detail_id, t.tenant_id, t.application_id, t.gift_id, t.gift_code, t.gift_name, t.specification, t.unit_price, t.inventory_quantity, t.application_quantity, t.gift_amount, t.sort_order, t.remark, t.del_flag, t.create_dept, t.create_by, t.create_time, t.update_by, t.update_time
from crm_gift_apply_detail t
where t.detail_id = #{detailId}
</select>
<!-- 批量查询 - 根据ID列表 -->
<select id="selectCustomCrmGiftApplyDetailVoByIds" resultMap="CrmGiftApplyDetailResult">
select t.detail_id, t.tenant_id, t.application_id, t.gift_id, t.gift_code, t.gift_name, t.specification, t.unit_price, t.inventory_quantity, t.application_quantity, t.gift_amount, t.sort_order, t.remark, t.del_flag, t.create_dept, t.create_by, t.create_time, t.update_by, t.update_time
from crm_gift_apply_detail t
where t.detail_id in
<foreach collection="ids" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</select>
<!-- 统计查询 -->
<select id="countCustomCrmGiftApplyDetail" resultType="java.lang.Long">
select count(1) from crm_gift_apply_detail t
<where>
<if test="ew != null and ew.sqlSegment != null and ew.sqlSegment != ''">
AND ${ew.sqlSegment}
</if>
</where>
</select>
<!-- 分页查询(带自定义条件) -->
<select id="selectCustomCrmGiftApplyDetailVoPage" resultMap="CrmGiftApplyDetailResult">
select t.detail_id, t.tenant_id, t.application_id, t.gift_id, t.gift_code, t.gift_name, t.specification, t.unit_price, t.inventory_quantity, t.application_quantity, t.gift_amount, t.sort_order, t.remark, t.del_flag, t.create_dept, t.create_by, t.create_time, t.update_by, t.update_time
from crm_gift_apply_detail t
<where>
<if test="ew != null and ew.sqlSegment != null and ew.sqlSegment != ''">
AND ${ew.sqlSegment}
</if>
</where>
</select>
<!-- 批量插入 -->
<insert id="batchInsertCrmGiftApplyDetail">
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
<foreach collection="list" item="item" separator=",">
(
#{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}
)
</foreach>
</insert>
<!-- 批量更新 -->
<update id="batchUpdateCrmGiftApplyDetail">
<foreach collection="list" item="item" separator=";">
update crm_gift_apply_detail t
<set>
<if test="item.tenantId != null and item.tenantId != ''">
t.tenant_id = #{item.tenantId},
</if>
<if test="item.applicationId != null">
t.application_id = #{item.applicationId},
</if>
<if test="item.giftId != null">
t.gift_id = #{item.giftId},
</if>
<if test="item.giftCode != null and item.giftCode != ''">
t.gift_code = #{item.giftCode},
</if>
<if test="item.giftName != null and item.giftName != ''">
t.gift_name = #{item.giftName},
</if>
<if test="item.specification != null and item.specification != ''">
t.specification = #{item.specification},
</if>
<if test="item.unitPrice != null">
t.unit_price = #{item.unitPrice},
</if>
<if test="item.inventoryQuantity != null">
t.inventory_quantity = #{item.inventoryQuantity},
</if>
<if test="item.applicationQuantity != null">
t.application_quantity = #{item.applicationQuantity},
</if>
<if test="item.giftAmount != null">
t.gift_amount = #{item.giftAmount},
</if>
<if test="item.sortOrder != null">
t.sort_order = #{item.sortOrder},
</if>
<if test="item.remark != null and item.remark != ''">
t.remark = #{item.remark},
</if>
<if test="item.delFlag != null and item.delFlag != ''">
t.del_flag = #{item.delFlag},
</if>
<if test="item.createDept != null">
t.create_dept = #{item.createDept},
</if>
<if test="item.createBy != null">
t.create_by = #{item.createBy},
</if>
<if test="item.createTime != null">
t.create_time = #{item.createTime},
</if>
<if test="item.updateBy != null">
t.update_by = #{item.updateBy},
</if>
<if test="item.updateTime != null">
t.update_time = #{item.updateTime}
</if>
</set>
where t.detail_id = #{item.detailId}
</foreach>
</update>
<!-- 根据自定义条件删除 -->
<delete id="deleteCustomCrmGiftApplyDetail">
delete from crm_gift_apply_detail t
<where>
<if test="ew != null and ew.sqlSegment != null and ew.sqlSegment != ''">
AND ${ew.sqlSegment}
</if>
</where>
</delete>
<!-- 根据ID列表批量删除 -->
<delete id="deleteCustomCrmGiftApplyDetailByIds">
delete from crm_gift_apply_detail t
where t.detail_id in
<foreach collection="ids" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</delete>
<!-- 检查是否存在 -->
<select id="existsCrmGiftApplyDetail" resultType="java.lang.Boolean">
select count(1) > 0 from crm_gift_apply_detail t
<where>
<if test="ew != null and ew.sqlSegment != null and ew.sqlSegment != ''">
AND ${ew.sqlSegment}
</if>
</where>
</select>
</mapper>

@ -0,0 +1,297 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.dromara.oa.crm.mapper.CrmGiftApplyMapper">
<resultMap type="org.dromara.oa.crm.domain.vo.CrmGiftApplyVo" id="CrmGiftApplyResult">
</resultMap>
<select id="selectCustomCrmGiftApplyVoList" resultMap="CrmGiftApplyResult">
select t.gift_apply_id, t.tenant_id, t.application_code, t.applicant_id, t.applicant_name, t.applicant_dept_id, t.applicant_dept_name, t.application_date, t.customer_unit_id, t.customer_unit_name, t.guest_name, t.people_count, t.application_reason, t.recipient_id, t.recipient_name, t.recipient_date, t.total_amount, t.application_status, t.flow_status, t.remark, t.del_flag, t.create_dept, t.create_by, t.create_time, t.update_by, t.update_time from crm_gift_apply t
<where>
<if test="ew != null and ew.sqlSegment != null and ew.sqlSegment != ''">
AND ${ew.sqlSegment}
</if>
</where>
</select>
<!-- 根据ID查询详情 -->
<select id="selectCustomCrmGiftApplyVoById" resultMap="CrmGiftApplyResult">
select t.gift_apply_id, t.tenant_id, t.application_code, t.applicant_id, t.applicant_name, t.applicant_dept_id, t.applicant_dept_name, t.application_date, t.customer_unit_id, t.customer_unit_name, t.guest_name, t.people_count, t.application_reason, t.recipient_id, t.recipient_name, t.recipient_date, t.total_amount, t.application_status, t.flow_status, t.remark, t.del_flag, t.create_dept, t.create_by, t.create_time, t.update_by, t.update_time
from crm_gift_apply t
where t.gift_apply_id = #{giftApplyId}
</select>
<!-- 批量查询 - 根据ID列表 -->
<select id="selectCustomCrmGiftApplyVoByIds" resultMap="CrmGiftApplyResult">
select t.gift_apply_id, t.tenant_id, t.application_code, t.applicant_id, t.applicant_name, t.applicant_dept_id, t.applicant_dept_name, t.application_date, t.customer_unit_id, t.customer_unit_name, t.guest_name, t.people_count, t.application_reason, t.recipient_id, t.recipient_name, t.recipient_date, t.total_amount, t.application_status, t.flow_status, t.remark, t.del_flag, t.create_dept, t.create_by, t.create_time, t.update_by, t.update_time
from crm_gift_apply t
where t.gift_apply_id in
<foreach collection="ids" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</select>
<!-- 统计查询 -->
<select id="countCustomCrmGiftApply" resultType="java.lang.Long">
select count(1) from crm_gift_apply t
<where>
<if test="ew != null and ew.sqlSegment != null and ew.sqlSegment != ''">
AND ${ew.sqlSegment}
</if>
</where>
</select>
<!-- 分页查询(带自定义条件) -->
<select id="selectCustomCrmGiftApplyVoPage" resultMap="CrmGiftApplyResult">
select t.gift_apply_id, t.tenant_id, t.application_code, t.applicant_id, t.applicant_name, t.applicant_dept_id, t.applicant_dept_name, t.application_date, t.customer_unit_id, t.customer_unit_name, t.guest_name, t.people_count, t.application_reason, t.recipient_id, t.recipient_name, t.recipient_date, t.total_amount, t.application_status, t.flow_status, t.remark, t.del_flag, t.create_dept, t.create_by, t.create_time, t.update_by, t.update_time
from crm_gift_apply t
<where>
<if test="ew != null and ew.sqlSegment != null and ew.sqlSegment != ''">
AND ${ew.sqlSegment}
</if>
</where>
</select>
<!-- 批量插入 -->
<insert id="batchInsertCrmGiftApply">
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
<foreach collection="list" item="item" separator=",">
(
#{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}
)
</foreach>
</insert>
<!-- 批量更新 -->
<update id="batchUpdateCrmGiftApply">
<foreach collection="list" item="item" separator=";">
update crm_gift_apply t
<set>
<if test="item.tenantId != null and item.tenantId != ''">
t.tenant_id = #{item.tenantId},
</if>
<if test="item.applicationCode != null and item.applicationCode != ''">
t.application_code = #{item.applicationCode},
</if>
<if test="item.applicantId != null">
t.applicant_id = #{item.applicantId},
</if>
<if test="item.applicantName != null and item.applicantName != ''">
t.applicant_name = #{item.applicantName},
</if>
<if test="item.applicantDeptId != null">
t.applicant_dept_id = #{item.applicantDeptId},
</if>
<if test="item.applicantDeptName != null and item.applicantDeptName != ''">
t.applicant_dept_name = #{item.applicantDeptName},
</if>
<if test="item.applicationDate != null">
t.application_date = #{item.applicationDate},
</if>
<if test="item.customerUnitId != null">
t.customer_unit_id = #{item.customerUnitId},
</if>
<if test="item.customerUnitName != null and item.customerUnitName != ''">
t.customer_unit_name = #{item.customerUnitName},
</if>
<if test="item.guestName != null and item.guestName != ''">
t.guest_name = #{item.guestName},
</if>
<if test="item.peopleCount != null">
t.people_count = #{item.peopleCount},
</if>
<if test="item.applicationReason != null and item.applicationReason != ''">
t.application_reason = #{item.applicationReason},
</if>
<if test="item.recipientId != null">
t.recipient_id = #{item.recipientId},
</if>
<if test="item.recipientName != null and item.recipientName != ''">
t.recipient_name = #{item.recipientName},
</if>
<if test="item.recipientDate != null">
t.recipient_date = #{item.recipientDate},
</if>
<if test="item.totalAmount != null">
t.total_amount = #{item.totalAmount},
</if>
<if test="item.applicationStatus != null and item.applicationStatus != ''">
t.application_status = #{item.applicationStatus},
</if>
<if test="item.flowStatus != null and item.flowStatus != ''">
t.flow_status = #{item.flowStatus},
</if>
<if test="item.remark != null and item.remark != ''">
t.remark = #{item.remark},
</if>
<if test="item.delFlag != null and item.delFlag != ''">
t.del_flag = #{item.delFlag},
</if>
<if test="item.createDept != null">
t.create_dept = #{item.createDept},
</if>
<if test="item.createBy != null">
t.create_by = #{item.createBy},
</if>
<if test="item.createTime != null">
t.create_time = #{item.createTime},
</if>
<if test="item.updateBy != null">
t.update_by = #{item.updateBy},
</if>
<if test="item.updateTime != null">
t.update_time = #{item.updateTime}
</if>
</set>
where t.gift_apply_id = #{item.giftApplyId}
</foreach>
</update>
<!-- 根据自定义条件删除 -->
<delete id="deleteCustomCrmGiftApply">
delete from crm_gift_apply t
<where>
<if test="ew != null and ew.sqlSegment != null and ew.sqlSegment != ''">
AND ${ew.sqlSegment}
</if>
</where>
</delete>
<!-- 根据ID列表批量删除 -->
<delete id="deleteCustomCrmGiftApplyByIds">
delete from crm_gift_apply t
where t.gift_apply_id in
<foreach collection="ids" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</delete>
<!-- 检查是否存在 -->
<select id="existsCrmGiftApply" resultType="java.lang.Boolean">
select count(1) > 0 from crm_gift_apply t
<where>
<if test="ew != null and ew.sqlSegment != null and ew.sqlSegment != ''">
AND ${ew.sqlSegment}
</if>
</where>
</select>
<!-- ⭐ 查询待审批中相同礼品的数量汇总(用于增强库存校验) -->
<select id="selectPendingGiftQuantity" resultType="org.dromara.oa.crm.domain.vo.GiftPendingQuantityVO">
SELECT
d.gift_id AS giftId,
i.gift_name AS giftName,
SUM(d.application_quantity) AS pendingQuantity
FROM crm_gift_apply a
INNER JOIN crm_gift_apply_detail d ON a.gift_apply_id = d.application_id
INNER JOIN crm_gift_info i ON d.gift_id = i.gift_id
WHERE a.application_status = '2'
AND a.flow_status = 'waiting'
AND a.del_flag = '0'
AND d.del_flag = '0'
GROUP BY d.gift_id, i.gift_name
</select>
</mapper>

@ -0,0 +1,214 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.dromara.oa.crm.mapper.CrmGiftInfoMapper">
<resultMap type="org.dromara.oa.crm.domain.vo.CrmGiftInfoVo" id="CrmGiftInfoResult">
</resultMap>
<select id="selectCustomCrmGiftInfoVoList" resultMap="CrmGiftInfoResult">
select t.gift_id, t.tenant_id, t.gift_code, t.gift_name, t.specification, t.unit_price, t.unit_id, t.inventory_quantity, t.remark, t.del_flag, t.create_dept, t.create_by, t.create_time, t.update_by, t.update_time from crm_gift_info t
<where>
<if test="ew != null and ew.sqlSegment != null and ew.sqlSegment != ''">
AND ${ew.sqlSegment}
</if>
</where>
</select>
<!-- 根据ID查询详情 -->
<select id="selectCustomCrmGiftInfoVoById" resultMap="CrmGiftInfoResult">
select t.gift_id, t.tenant_id, t.gift_code, t.gift_name, t.specification, t.unit_price, t.unit_id, t.inventory_quantity, t.remark, t.del_flag, t.create_dept, t.create_by, t.create_time, t.update_by, t.update_time
from crm_gift_info t
where t.gift_id = #{giftId}
</select>
<!-- 批量查询 - 根据ID列表 -->
<select id="selectCustomCrmGiftInfoVoByIds" resultMap="CrmGiftInfoResult">
select t.gift_id, t.tenant_id, t.gift_code, t.gift_name, t.specification, t.unit_price, t.unit_id, t.inventory_quantity, t.remark, t.del_flag, t.create_dept, t.create_by, t.create_time, t.update_by, t.update_time
from crm_gift_info t
where t.gift_id in
<foreach collection="ids" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</select>
<!-- 统计查询 -->
<select id="countCustomCrmGiftInfo" resultType="java.lang.Long">
select count(1) from crm_gift_info t
<where>
<if test="ew != null and ew.sqlSegment != null and ew.sqlSegment != ''">
AND ${ew.sqlSegment}
</if>
</where>
</select>
<!-- 分页查询(带自定义条件) -->
<select id="selectCustomCrmGiftInfoVoPage" resultMap="CrmGiftInfoResult">
select t.gift_id, t.tenant_id, t.gift_code, t.gift_name, t.specification, t.unit_price, t.unit_id, t.inventory_quantity, t.remark, t.del_flag, t.create_dept, t.create_by, t.create_time, t.update_by, t.update_time
from crm_gift_info t
<where>
<if test="ew != null and ew.sqlSegment != null and ew.sqlSegment != ''">
AND ${ew.sqlSegment}
</if>
</where>
</select>
<!-- 批量插入 -->
<insert id="batchInsertCrmGiftInfo">
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
<foreach collection="list" item="item" separator=",">
(
#{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}
)
</foreach>
</insert>
<!-- 批量更新 -->
<update id="batchUpdateCrmGiftInfo">
<foreach collection="list" item="item" separator=";">
update crm_gift_info t
<set>
<if test="item.tenantId != null and item.tenantId != ''">
t.tenant_id = #{item.tenantId},
</if>
<if test="item.giftCode != null and item.giftCode != ''">
t.gift_code = #{item.giftCode},
</if>
<if test="item.giftName != null and item.giftName != ''">
t.gift_name = #{item.giftName},
</if>
<if test="item.specification != null and item.specification != ''">
t.specification = #{item.specification},
</if>
<if test="item.unitPrice != null">
t.unit_price = #{item.unitPrice},
</if>
<if test="item.unitId != null">
t.unit_id = #{item.unitId},
</if>
<if test="item.inventoryQuantity != null">
t.inventory_quantity = #{item.inventoryQuantity},
</if>
<if test="item.remark != null and item.remark != ''">
t.remark = #{item.remark},
</if>
<if test="item.delFlag != null and item.delFlag != ''">
t.del_flag = #{item.delFlag},
</if>
<if test="item.createDept != null">
t.create_dept = #{item.createDept},
</if>
<if test="item.createBy != null">
t.create_by = #{item.createBy},
</if>
<if test="item.createTime != null">
t.create_time = #{item.createTime},
</if>
<if test="item.updateBy != null">
t.update_by = #{item.updateBy},
</if>
<if test="item.updateTime != null">
t.update_time = #{item.updateTime}
</if>
</set>
where t.gift_id = #{item.giftId}
</foreach>
</update>
<!-- 根据自定义条件删除 -->
<delete id="deleteCustomCrmGiftInfo">
delete from crm_gift_info t
<where>
<if test="ew != null and ew.sqlSegment != null and ew.sqlSegment != ''">
AND ${ew.sqlSegment}
</if>
</where>
</delete>
<!-- 根据ID列表批量删除 -->
<delete id="deleteCustomCrmGiftInfoByIds">
delete from crm_gift_info t
where t.gift_id in
<foreach collection="ids" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</delete>
<!-- 检查是否存在 -->
<select id="existsCrmGiftInfo" resultType="java.lang.Boolean">
select count(1) > 0 from crm_gift_info t
<where>
<if test="ew != null and ew.sqlSegment != null and ew.sqlSegment != ''">
AND ${ew.sqlSegment}
</if>
</where>
</select>
<!-- 原子扣减库存:库存数量 >= 扣减数量时才执行更新,防止并发超发 -->
<update id="deductInventory">
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}
</update>
</mapper>

@ -0,0 +1,282 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.dromara.oa.crm.mapper.CrmGiftIssueRecordMapper">
<resultMap type="org.dromara.oa.crm.domain.vo.CrmGiftIssueRecordVo" id="CrmGiftIssueRecordResult">
</resultMap>
<select id="selectCustomCrmGiftIssueRecordVoList" resultMap="CrmGiftIssueRecordResult">
select t.issue_record_id, t.tenant_id, t.application_id, t.application_code, t.detail_id, t.gift_id, t.gift_code, t.gift_name, t.specification, t.issue_quantity, t.issue_date, t.recipient_id, t.recipient_name, t.issue_by, t.issue_by_name, t.issue_dept_id, t.issue_dept_name, t.before_inventory, t.after_inventory, t.remark, t.del_flag, t.create_dept, t.create_by, t.create_time, t.update_by, t.update_time from crm_gift_issue_record t
<where>
<if test="ew != null and ew.sqlSegment != null and ew.sqlSegment != ''">
AND ${ew.sqlSegment}
</if>
</where>
</select>
<!-- 根据ID查询详情 -->
<select id="selectCustomCrmGiftIssueRecordVoById" resultMap="CrmGiftIssueRecordResult">
select t.issue_record_id, t.tenant_id, t.application_id, t.application_code, t.detail_id, t.gift_id, t.gift_code, t.gift_name, t.specification, t.issue_quantity, t.issue_date, t.recipient_id, t.recipient_name, t.issue_by, t.issue_by_name, t.issue_dept_id, t.issue_dept_name, t.before_inventory, t.after_inventory, t.remark, t.del_flag, t.create_dept, t.create_by, t.create_time, t.update_by, t.update_time
from crm_gift_issue_record t
where t.issue_record_id = #{issueRecordId}
</select>
<!-- 批量查询 - 根据ID列表 -->
<select id="selectCustomCrmGiftIssueRecordVoByIds" resultMap="CrmGiftIssueRecordResult">
select t.issue_record_id, t.tenant_id, t.application_id, t.application_code, t.detail_id, t.gift_id, t.gift_code, t.gift_name, t.specification, t.issue_quantity, t.issue_date, t.recipient_id, t.recipient_name, t.issue_by, t.issue_by_name, t.issue_dept_id, t.issue_dept_name, t.before_inventory, t.after_inventory, t.remark, t.del_flag, t.create_dept, t.create_by, t.create_time, t.update_by, t.update_time
from crm_gift_issue_record t
where t.issue_record_id in
<foreach collection="ids" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</select>
<!-- 统计查询 -->
<select id="countCustomCrmGiftIssueRecord" resultType="java.lang.Long">
select count(1) from crm_gift_issue_record t
<where>
<if test="ew != null and ew.sqlSegment != null and ew.sqlSegment != ''">
AND ${ew.sqlSegment}
</if>
</where>
</select>
<!-- 分页查询(带自定义条件) -->
<select id="selectCustomCrmGiftIssueRecordVoPage" resultMap="CrmGiftIssueRecordResult">
select t.issue_record_id, t.tenant_id, t.application_id, t.application_code, t.detail_id, t.gift_id, t.gift_code, t.gift_name, t.specification, t.issue_quantity, t.issue_date, t.recipient_id, t.recipient_name, t.issue_by, t.issue_by_name, t.issue_dept_id, t.issue_dept_name, t.before_inventory, t.after_inventory, t.remark, t.del_flag, t.create_dept, t.create_by, t.create_time, t.update_by, t.update_time
from crm_gift_issue_record t
<where>
<if test="ew != null and ew.sqlSegment != null and ew.sqlSegment != ''">
AND ${ew.sqlSegment}
</if>
</where>
</select>
<!-- 批量插入 -->
<insert id="batchInsertCrmGiftIssueRecord">
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
<foreach collection="list" item="item" separator=",">
(
#{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}
)
</foreach>
</insert>
<!-- 批量更新 -->
<update id="batchUpdateCrmGiftIssueRecord">
<foreach collection="list" item="item" separator=";">
update crm_gift_issue_record t
<set>
<if test="item.tenantId != null and item.tenantId != ''">
t.tenant_id = #{item.tenantId},
</if>
<if test="item.applicationId != null">
t.application_id = #{item.applicationId},
</if>
<if test="item.applicationCode != null and item.applicationCode != ''">
t.application_code = #{item.applicationCode},
</if>
<if test="item.detailId != null">
t.detail_id = #{item.detailId},
</if>
<if test="item.giftId != null">
t.gift_id = #{item.giftId},
</if>
<if test="item.giftCode != null and item.giftCode != ''">
t.gift_code = #{item.giftCode},
</if>
<if test="item.giftName != null and item.giftName != ''">
t.gift_name = #{item.giftName},
</if>
<if test="item.specification != null and item.specification != ''">
t.specification = #{item.specification},
</if>
<if test="item.issueQuantity != null">
t.issue_quantity = #{item.issueQuantity},
</if>
<if test="item.issueDate != null">
t.issue_date = #{item.issueDate},
</if>
<if test="item.recipientId != null">
t.recipient_id = #{item.recipientId},
</if>
<if test="item.recipientName != null and item.recipientName != ''">
t.recipient_name = #{item.recipientName},
</if>
<if test="item.issueBy != null">
t.issue_by = #{item.issueBy},
</if>
<if test="item.issueByName != null and item.issueByName != ''">
t.issue_by_name = #{item.issueByName},
</if>
<if test="item.issueDeptId != null">
t.issue_dept_id = #{item.issueDeptId},
</if>
<if test="item.issueDeptName != null and item.issueDeptName != ''">
t.issue_dept_name = #{item.issueDeptName},
</if>
<if test="item.beforeInventory != null">
t.before_inventory = #{item.beforeInventory},
</if>
<if test="item.afterInventory != null">
t.after_inventory = #{item.afterInventory},
</if>
<if test="item.remark != null and item.remark != ''">
t.remark = #{item.remark},
</if>
<if test="item.delFlag != null and item.delFlag != ''">
t.del_flag = #{item.delFlag},
</if>
<if test="item.createDept != null">
t.create_dept = #{item.createDept},
</if>
<if test="item.createBy != null">
t.create_by = #{item.createBy},
</if>
<if test="item.createTime != null">
t.create_time = #{item.createTime},
</if>
<if test="item.updateBy != null">
t.update_by = #{item.updateBy},
</if>
<if test="item.updateTime != null">
t.update_time = #{item.updateTime}
</if>
</set>
where t.issue_record_id = #{item.issueRecordId}
</foreach>
</update>
<!-- 根据自定义条件删除 -->
<delete id="deleteCustomCrmGiftIssueRecord">
delete from crm_gift_issue_record t
<where>
<if test="ew != null and ew.sqlSegment != null and ew.sqlSegment != ''">
AND ${ew.sqlSegment}
</if>
</where>
</delete>
<!-- 根据ID列表批量删除 -->
<delete id="deleteCustomCrmGiftIssueRecordByIds">
delete from crm_gift_issue_record t
where t.issue_record_id in
<foreach collection="ids" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</delete>
<!-- 检查是否存在 -->
<select id="existsCrmGiftIssueRecord" resultType="java.lang.Boolean">
select count(1) > 0 from crm_gift_issue_record t
<where>
<if test="ew != null and ew.sqlSegment != null and ew.sqlSegment != ''">
AND ${ew.sqlSegment}
</if>
</where>
</select>
</mapper>
Loading…
Cancel
Save