已修改14个文件
已添加31个文件
1878 ■■■■■ 文件已修改
src/main/java/com/ruoyi/basic/controller/CustomerController.java 16 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/basic/pojo/Customer.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/basic/service/ICustomerService.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/basic/service/impl/CustomerServiceImpl.java 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/common/config/MyBaseMapper.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/common/utils/file/FileUploadUtils.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/common/vo/FileVo.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/purchase/controller/PurchaseLedgerController.java 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/purchase/mapper/PurchaseLedgerMapper.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/purchase/pojo/PurchaseLedger.java 97 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/purchase/service/IPurchaseLedgerService.java 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/purchase/service/impl/PurchaseLedgerServiceImpl.java 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/controller/InvoiceLedgerController.java 100 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/controller/InvoiceRegistrationController.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/controller/ReceiptPaymentController.java 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/controller/SalesLedgerController.java 39 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/controller/SalesLedgerProductController.java 91 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/dto/InvoiceLedgerDto.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/dto/ReceiptPaymentDto.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/dto/SalesLedgerDto.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/dto/SalesLedgerProductDTO.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/excel/InvoiceLedgerExcelDto.java 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/mapper/InvoiceLedgerFileMapper.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/mapper/InvoiceLedgerMapper.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/mapper/ReceiptPaymentMapper.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/mapper/SalesLedgerMapper.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/mapper/SalesLedgerProductMapper.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/pojo/InvoiceLedger.java 72 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/pojo/InvoiceLedgerFile.java 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/pojo/ReceiptPayment.java 79 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/pojo/SalesLedger.java 20 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/pojo/SalesLedgerProduct.java 87 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/service/ISalesLedgerProductService.java 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/service/ISalesLedgerService.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/service/InvoiceLedgerService.java 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/service/ReceiptPaymentService.java 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/service/impl/InvoiceLedgerServiceImpl.java 171 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/service/impl/InvoiceRegistrationServiceImpl.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/service/impl/ReceiptPaymentServiceImpl.java 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/service/impl/SalesLedgerProductServiceImpl.java 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/service/impl/SalesLedgerServiceImpl.java 189 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application.yml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/sales/InvoiceLedgerMapper.xml 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/sales/ReceiptPaymentMapper.xml 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/sales/SalesLedgerMapper.xml 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/basic/controller/CustomerController.java
@@ -8,7 +8,6 @@
import lombok.AllArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
@@ -91,8 +90,19 @@
     * åˆ é™¤å®¢æˆ·æ¡£æ¡ˆ
     */
    @Log(title = "客户档案", businessType = BusinessType.DELETE)
    @DeleteMapping("/{ids}")
    public AjaxResult remove(@PathVariable Long[] ids) {
    @DeleteMapping("/delCustomer")
    public AjaxResult remove(@RequestBody Long[] ids) {
        if (ids == null || ids.length == 0) {
            return AjaxResult.error("请传入要删除的ID");
        }
        return toAjax(customerService.deleteCustomerByIds(ids));
    }
    /**
     * æŸ¥è¯¢å®¢æˆ·
     */
    @GetMapping("/customerList")
    public List customerList(Customer customer) {
       return  customerService.customerList(customer);
    }
}
src/main/java/com/ruoyi/basic/pojo/Customer.java
@@ -79,4 +79,7 @@
    @TableField(exist = false)
    private Long[] ids;
    @TableField(exist = false)
    private String addressPhone;
}
src/main/java/com/ruoyi/basic/service/ICustomerService.java
@@ -53,4 +53,11 @@
    int deleteCustomerByIds(Long[] ids);
    List<Customer> selectCustomerListByIds(Long[] ids);
    /**
     * æŸ¥è¯¢å®¢æˆ·ä¿¡æ¯
     *
     * @return ç»“æžœ
     */
    List customerList(Customer customer);
}
src/main/java/com/ruoyi/basic/service/impl/CustomerServiceImpl.java
@@ -2,12 +2,14 @@
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.basic.mapper.CustomerMapper;
import com.ruoyi.basic.pojo.Customer;
import com.ruoyi.basic.service.ICustomerService;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.framework.security.LoginUser;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@@ -15,6 +17,8 @@
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
@@ -49,11 +53,17 @@
    @Override
    public List<Customer> selectCustomerList(Customer customer) {
        LambdaQueryWrapper<Customer> queryWrapper = new LambdaQueryWrapper<>();
        if (customer.getCustomerName() != null && !customer.getCustomerName().isEmpty()) {
        if (StringUtils.isNotBlank(customer.getCustomerName())) {
            queryWrapper.eq(Customer::getCustomerName, customer.getCustomerName());
        }
        List<Customer> customerList = customerMapper.selectList(queryWrapper);
        return customerList;
        // ä½¿ç”¨ Stream ä¿®æ”¹æ¯ä¸ª Customer çš„ addressPhone å­—段
        return customerList.stream().peek(c ->
                c.setAddressPhone(c.getCompanyAddress() + "( " + c.getCompanyPhone() + " )")
        ).collect(Collectors.toList());
    }
    /**
@@ -102,4 +112,42 @@
        queryWrapper.in(Customer::getId, Arrays.asList(ids));
        return customerMapper.selectList(queryWrapper);
    }
    @Override
    public List<Map<String, Object>> customerList(Customer customer) {
        LambdaQueryWrapper<Customer> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.select(Customer::getId, Customer::getCustomerName,Customer::getTaxpayerIdentificationNumber);
        // èŽ·å–åŽŸå§‹æŸ¥è¯¢ç»“æžœ
        List<Map<String, Object>> result = customerMapper.selectMaps(queryWrapper);
        // å°†ä¸‹åˆ’线命名转换为驼峰命名
        return result.stream().map(map -> map.entrySet().stream()
                .collect(Collectors.toMap(
                        entry -> underlineToCamel(entry.getKey()),
                        Map.Entry::getValue))
        ).collect(Collectors.toList());
    }
    /**
     * ä¸‹åˆ’线命名转驼峰命名
     */
    private String underlineToCamel(String param) {
        if (param == null || "".equals(param.trim())) {
            return "";
        }
        int len = param.length();
        StringBuilder sb = new StringBuilder(len);
        for (int i = 0; i < len; i++) {
            char c = param.charAt(i);
            if (c == '_') {
                if (++i < len) {
                    sb.append(Character.toUpperCase(param.charAt(i)));
                }
            } else {
                sb.append(Character.toLowerCase(c));
            }
        }
        return sb.toString();
    }
}
src/main/java/com/ruoyi/common/config/MyBaseMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,20 @@
package com.ruoyi.common.config;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import java.util.List;
public interface MyBaseMapper<T> extends BaseMapper<T> {
    /**
     * æ‰¹é‡æ’入(仅插入非空字段)
     * @param list å®žä½“列表
     * @return æ’入成功的记录数
     */
    int insertBatchSomeColumn(List<T> list);
    /**
     * æ‰¹é‡æ›´æ–°ï¼ˆä»…更新非空字段)
     */
    int updateBatchSomeColumn(List<T> list);
}
src/main/java/com/ruoyi/common/utils/file/FileUploadUtils.java
@@ -144,7 +144,8 @@
    {
        int dirLastIndex = RuoYiConfig.getProfile().length() + 1;
        String currentDir = StringUtils.substring(uploadDir, dirLastIndex);
        return Constants.RESOURCE_PREFIX + "/" + currentDir + "/" + fileName;
//        return Constants.RESOURCE_PREFIX + "/" + currentDir + "/" + fileName;
        return currentDir + "/" + fileName;
    }
    /**
src/main/java/com/ruoyi/common/vo/FileVo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,31 @@
package com.ruoyi.common.vo;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.time.LocalDateTime;
@Data
public class FileVo {
    @ApiModelProperty(value = "文件名称")
    private String fileName;
    @ApiModelProperty(value = "文件路径")
    private String filePath;
    @ApiModelProperty(value = "文件大小")
    private int fileSize;
    @ApiModelProperty(value = "创建时间")
    private LocalDateTime createTime;
    @ApiModelProperty(value = "修改时间")
    private LocalDateTime updateTime;
    @ApiModelProperty(value = "创建用户")
    private Integer createUser;
    @ApiModelProperty(value = "修改用户")
    private Integer updateUser;
}
src/main/java/com/ruoyi/purchase/controller/PurchaseLedgerController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,73 @@
package com.ruoyi.purchase.controller;
import javax.servlet.http.HttpServletResponse;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.purchase.pojo.PurchaseLedger;
import com.ruoyi.purchase.service.IPurchaseLedgerService;
import lombok.AllArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.ruoyi.framework.aspectj.lang.annotation.Log;
import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
import com.ruoyi.framework.web.controller.BaseController;
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.framework.web.page.TableDataInfo;
import java.util.List;
/**
 * é‡‡è´­å°è´¦Controller
 *
 * @author ruoyi
 * @date 2025-05-09
 */
@RestController
@RequestMapping("/system/ledger")
@AllArgsConstructor
public class PurchaseLedgerController extends BaseController {
    private IPurchaseLedgerService purchaseLedgerService;
    /**
     * æŸ¥è¯¢é‡‡è´­å°è´¦åˆ—表
     */
    @GetMapping("/list")
    public TableDataInfo list(PurchaseLedger purchaseLedger) {
        startPage();
        List<PurchaseLedger> list = purchaseLedgerService.selectPurchaseLedgerList(purchaseLedger);
        return getDataTable(list);
    }
    /**
     * å¯¼å‡ºé‡‡è´­å°è´¦åˆ—表
     */
    @Log(title = "采购台账", businessType = BusinessType.EXPORT)
    @PostMapping("/export")
    public void export(HttpServletResponse response, PurchaseLedger purchaseLedger) {
        List<PurchaseLedger> list = purchaseLedgerService.selectPurchaseLedgerList(purchaseLedger);
        ExcelUtil<PurchaseLedger> util = new ExcelUtil<PurchaseLedger>(PurchaseLedger.class);
        util.exportExcel(response, list, "【请填写功能名称】数据");
    }
    /**
     * æ–°å¢žä¿®æ”¹é‡‡è´­å°è´¦
     */
    @Log(title = "采购台账", businessType = BusinessType.INSERT)
    @PostMapping ("/addOrEditPurchase")
    public AjaxResult addOrEditPurchase(@RequestBody PurchaseLedger purchaseLedger) {
        return toAjax(purchaseLedgerService.addOrEditPurchase(purchaseLedger));
    }
    /**
     * åˆ é™¤é‡‡è´­å°è´¦
     */
    @Log(title = "采购台账", businessType = BusinessType.DELETE)
    @DeleteMapping("/delPurchase")
    public AjaxResult remove(@RequestBody Long[] ids) {
        return toAjax(purchaseLedgerService.deletePurchaseLedgerByIds(ids));
    }
}
src/main/java/com/ruoyi/purchase/mapper/PurchaseLedgerMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,15 @@
package com.ruoyi.purchase.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ruoyi.purchase.pojo.PurchaseLedger;
/**
 * é‡‡è´­å°è´¦Mapper接口
 *
 * @author ruoyi
 * @date 2025-05-09
 */
public interface PurchaseLedgerMapper extends BaseMapper<PurchaseLedger> {
}
src/main/java/com/ruoyi/purchase/pojo/PurchaseLedger.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,97 @@
package com.ruoyi.purchase.pojo;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.ruoyi.framework.aspectj.lang.annotation.Excel;
import lombok.Data;
/**
 * é‡‡è´­å°è´¦å¯¹è±¡ purchase_ledger
 *
 * @author ruoyi
 * @date 2025-05-09
 */
@TableName("purchase_ledger")
@Data
public class PurchaseLedger {
    private static final long serialVersionUID = 1L;
    /**
     * è‡ªå¢žä¸»é”®ID
     */
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;
    /**
     * é‡‡è´­åˆåŒå·
     */
    @Excel(name = "采购合同号")
    private String purchaseContractNumber;
    /**
     * ä¾›åº”商名称
     */
    @Excel(name = "供应商名称")
    private String supplierName;
    /**
     * å½•入人姓名
     */
    @Excel(name = "录入人姓名")
    private String recorderName;
    /**
     * é”€å”®åˆåŒå·
     */
    @Excel(name = "销售合同号")
    private String salesContractNo;
    /**
     * é¡¹ç›®åç§°
     */
    @Excel(name = "项目名称")
    private String projectName;
    /**
     * å½•入日期
     */
    @JsonFormat(pattern = "yyyy-MM-dd")
    @Excel(name = "录入日期", width = 30, dateFormat = "yyyy-MM-dd")
    private Date entryDate;
    /**
     * å¤‡æ³¨
     */
    @Excel(name = "备注")
    private String remarks;
    /**
     * é™„件材料路径或名称
     */
    @Excel(name = "附件材料路径或名称")
    private String attachmentMaterials;
    /**
     * è®°å½•创建时间
     */
    @JsonFormat(pattern = "yyyy-MM-dd")
    @Excel(name = "记录创建时间", width = 30, dateFormat = "yyyy-MM-dd")
    private Date createdAt;
    /**
     * è®°å½•最后更新时间
     */
    @JsonFormat(pattern = "yyyy-MM-dd")
    @Excel(name = "记录最后更新时间", width = 30, dateFormat = "yyyy-MM-dd")
    private Date updatedAt;
    /**
     * å…³è”销售台账主表主键
     */
    private Long salesLedgerId;
}
src/main/java/com/ruoyi/purchase/service/IPurchaseLedgerService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,23 @@
package com.ruoyi.purchase.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.purchase.pojo.PurchaseLedger;
import java.util.List;
/**
 * é‡‡è´­å°è´¦Service接口
 *
 * @author ruoyi
 * @date 2025-05-09
 */
public interface IPurchaseLedgerService extends IService<PurchaseLedger> {
    List<PurchaseLedger> selectPurchaseLedgerList(PurchaseLedger purchaseLedger);
    int addOrEditPurchase(PurchaseLedger purchaseLedger);
    int deletePurchaseLedgerByIds(Long[] ids);
}
src/main/java/com/ruoyi/purchase/service/impl/PurchaseLedgerServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,44 @@
package com.ruoyi.purchase.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.purchase.mapper.PurchaseLedgerMapper;
import com.ruoyi.purchase.pojo.PurchaseLedger;
import com.ruoyi.purchase.service.IPurchaseLedgerService;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import java.util.Arrays;
import java.util.List;
/**
 * é‡‡è´­å°è´¦Service业务层处理
 *
 * @author ruoyi
 * @date 2025-05-09
 */
@Service
@AllArgsConstructor
public class PurchaseLedgerServiceImpl extends ServiceImpl<PurchaseLedgerMapper, PurchaseLedger> implements IPurchaseLedgerService {
    private PurchaseLedgerMapper purchaseLedgerMapper;
    @Override
    public List<PurchaseLedger> selectPurchaseLedgerList(PurchaseLedger purchaseLedger) {
        return purchaseLedgerMapper.selectList(new LambdaQueryWrapper<>());
    }
    @Override
    public int addOrEditPurchase(PurchaseLedger purchaseLedger) {
        if (purchaseLedger.getId() == null) {
            return purchaseLedgerMapper.insert(purchaseLedger);
        } else {
            return purchaseLedgerMapper.updateById(purchaseLedger);
        }
    }
    @Override
    public int deletePurchaseLedgerByIds(Long[] ids) {
        return purchaseLedgerMapper.deleteBatchIds(Arrays.asList(ids));
    }
}
src/main/java/com/ruoyi/sales/controller/InvoiceLedgerController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,100 @@
package com.ruoyi.sales.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.sales.dto.InvoiceLedgerDto;
import com.ruoyi.sales.service.InvoiceLedgerService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
@RestController
@RequestMapping("/invoiceLedger")
public class InvoiceLedgerController {
    @Autowired
    private InvoiceLedgerService invoiceLedgerService;
    /**
     * å¼€ç¥¨å°è´¦æ–°å¢ž
     * @param invoiceLedgerDto
     * @return
     */
    @PostMapping("/add")
    public AjaxResult invoiceLedgerAdd(@RequestBody InvoiceLedgerDto invoiceLedgerDto) {
        invoiceLedgerService.invoiceLedgerAdd(invoiceLedgerDto);
        return AjaxResult.success();
    }
    /**
     * å¼€ç¥¨å°è´¦åˆ é™¤
     * @param ids
     * @return
     */
    @DeleteMapping("/del")
    public AjaxResult invoiceLedgerDel(@RequestParam List<Integer> ids) {
        invoiceLedgerService.invoiceLedgerDel(ids);
        return AjaxResult.success();
    }
    /**
     * å¼€ç¥¨å°è´¦ä¿®æ”¹
     * @param invoiceLedgerDto
     * @return
     */
    @PostMapping("/update")
    public AjaxResult invoiceLedgerUpdate(@RequestBody InvoiceLedgerDto invoiceLedgerDto) {
        invoiceLedgerService.invoiceLedgerUpdate(invoiceLedgerDto);
        return AjaxResult.success();
    }
    /**
     * å¼€ç¥¨å°è´¦åˆ†é¡µæŸ¥è¯¢
     * @param page
     * @param invoiceLedgerDto
     * @return
     */
    @GetMapping("/page")
    public AjaxResult invoiceLedgerPage(Page page, InvoiceLedgerDto invoiceLedgerDto) {
        return AjaxResult.success(invoiceLedgerService.invoiceLedgerPage(page, invoiceLedgerDto));
    }
    /**
     * å¼€ç¥¨å°è´¦æ–‡ä»¶æŸ¥è¯¢
     * @param invoiceLedgerId
     * @return
     */
    @GetMapping("/fileList")
    public AjaxResult invoiceLedgerFileList(Integer invoiceLedgerId) {
        return AjaxResult.success(invoiceLedgerService.invoiceLedgerFileList(invoiceLedgerId));
    }
    /**
     * å¼€ç¥¨å°è´¦æ–‡ä»¶ä¸Šä¼ 
     * @param file
     * @return
     */
    @PostMapping("/uploadFile")
    public AjaxResult invoiceLedgerUploadFile(MultipartFile file) {
        try {
            return AjaxResult.success(invoiceLedgerService.invoiceLedgerUploadFile(file));
        }catch (Exception e) {
            return AjaxResult.error(e.getMessage());
        }
    }
    /**
     * é™„件下载
     * @param response
     * @param invoiceLedgerDto
     * @return
     */
    @GetMapping("/downloadFile")
    public void invoiceLedgerDownloadFile(HttpServletResponse response, InvoiceLedgerDto invoiceLedgerDto) {
        invoiceLedgerService.invoiceLedgerDownload(response, invoiceLedgerDto);
    }
}
src/main/java/com/ruoyi/sales/controller/InvoiceRegistrationController.java
@@ -34,7 +34,7 @@
     * @return
     */
    @DeleteMapping("/del")
    public AjaxResult invoiceRegistrationDel(List<Integer> ids) {
    public AjaxResult invoiceRegistrationDel(@RequestParam List<Integer> ids) {
        invoiceRegistrationService.invoiceRegistrationDel(ids);
        return AjaxResult.success();
    }
src/main/java/com/ruoyi/sales/controller/ReceiptPaymentController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,63 @@
package com.ruoyi.sales.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.sales.dto.ReceiptPaymentDto;
import com.ruoyi.sales.pojo.ReceiptPayment;
import com.ruoyi.sales.service.ReceiptPaymentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/receiptPayment")
public class ReceiptPaymentController {
    @Autowired
    private ReceiptPaymentService receiptPaymentService;
    /**
     * å›žæ¬¾ç™»è®°æ–°å¢ž
     * @param receiptPayment
     * @return
     */
    @PostMapping("/add")
    public AjaxResult receiptPaymentAdd (@RequestBody ReceiptPayment receiptPayment) {
        receiptPaymentService.receiptPaymentAdd(receiptPayment);
        return AjaxResult.success();
    }
    /**
     * å›žæ¬¾ç™»è®°ä¿®æ”¹
     * @param receiptPayment
     * @return
     */
    @PostMapping("/update")
    public AjaxResult receiptPaymentUpdate (@RequestBody ReceiptPayment receiptPayment) {
        return AjaxResult.success(receiptPaymentService.receiptPaymentUpdate(receiptPayment));
    }
    /**
     * å›žæ¬¾ç™»è®°åˆ é™¤
     * @param ids
     * @return
     */
    @DeleteMapping("/del")
    public AjaxResult receiptPaymentDel (@RequestBody List<Integer> ids) {
        return AjaxResult.success(receiptPaymentService.receiptPaymentDel(ids));
    }
    /**
     * å›žæ¬¾ç™»è®°åˆ†é¡µæŸ¥è¯¢
     * @param page
     * @param receiptPaymentDto
     * @return
     */
    @GetMapping("/listPage")
    public AjaxResult receiptPaymentListPage (Page page, ReceiptPaymentDto receiptPaymentDto) {
        return AjaxResult.success(receiptPaymentService.receiptPaymentListPage(page,receiptPaymentDto));
    }
}
src/main/java/com/ruoyi/sales/controller/SalesLedgerController.java
@@ -4,13 +4,12 @@
import javax.servlet.http.HttpServletResponse;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.sales.dto.SalesLedgerDto;
import com.ruoyi.sales.pojo.SalesLedger;
import com.ruoyi.sales.service.ISalesLedgerService;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
@@ -47,6 +46,17 @@
    }
    /**
     * æŸ¥è¯¢é”€å”®å°è´¦å’Œäº§å“çˆ¶å­åˆ—表
     */
    @GetMapping("/getSalesLedgerWithProducts")
    public TableDataInfo getSalesLedgerWithProducts()
    {
        startPage();
        List<SalesLedgerDto> list = salesLedgerService.getSalesLedgerWithProducts();
        return getDataTable(list);
    }
    /**
     * å¯¼å‡ºé”€å”®å°è´¦åˆ—表
     */
    @Log(title = "销售台账", businessType = BusinessType.EXPORT)
@@ -68,32 +78,25 @@
    }
    /**
     * æ–°å¢žé”€å”®å°è´¦
     * æ–°å¢žä¿®æ”¹é”€å”®å°è´¦
     */
    @Log(title = "销售台账", businessType = BusinessType.INSERT)
    @PostMapping ("/insertSalesLedger")
    public AjaxResult add(@RequestBody SalesLedger salesLedger)
    @PostMapping ("/addOrUpdateSalesLedger")
    public AjaxResult add(@RequestBody SalesLedgerDto salesLedgerDto)
    {
        return toAjax(salesLedgerService.insertSalesLedger(salesLedger));
    }
    /**
     * ä¿®æ”¹é”€å”®å°è´¦
     */
    @Log(title = "销售台账", businessType = BusinessType.UPDATE)
    @PostMapping ("/updateSalesLedger")
    public AjaxResult edit(@RequestBody SalesLedger salesLedger)
    {
        return toAjax(salesLedgerService.updateSalesLedger(salesLedger));
        return toAjax(salesLedgerService.addOrUpdateSalesLedger(salesLedgerDto));
    }
    /**
     * åˆ é™¤é”€å”®å°è´¦
     */
    @Log(title = "销售台账", businessType = BusinessType.DELETE)
    @DeleteMapping("/{ids}")
    public AjaxResult remove(@PathVariable Long[] ids)
    @DeleteMapping("/delLedger")
    public AjaxResult remove(@RequestBody Long[] ids)
    {
        if (ids == null || ids.length == 0) {
            return AjaxResult.error("请传入要删除的ID");
        }
        return toAjax(salesLedgerService.deleteSalesLedgerByIds(ids));
    }
}
src/main/java/com/ruoyi/sales/controller/SalesLedgerProductController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,91 @@
package com.ruoyi.sales.controller;
import javax.servlet.http.HttpServletResponse;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.sales.pojo.SalesLedgerProduct;
import com.ruoyi.sales.service.ISalesLedgerProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.ruoyi.framework.aspectj.lang.annotation.Log;
import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
import com.ruoyi.framework.web.controller.BaseController;
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.framework.web.page.TableDataInfo;
import java.util.List;
/**
 * äº§å“ä¿¡æ¯Controller
 *
 * @author ruoyi
 * @date 2025-05-08
 */
@RestController
@RequestMapping("/sales/product")
public class SalesLedgerProductController extends BaseController
{
    @Autowired
    private ISalesLedgerProductService salesLedgerProductService;
    /**
     * æŸ¥è¯¢äº§å“ä¿¡æ¯åˆ—表
     */
    @GetMapping("/list")
    public TableDataInfo list(SalesLedgerProduct salesLedgerProduct)
    {
        startPage();
        List<SalesLedgerProduct> list = salesLedgerProductService.selectSalesLedgerProductList(salesLedgerProduct);
        return getDataTable(list);
    }
    /**
     * å¯¼å‡ºäº§å“ä¿¡æ¯åˆ—表
     */
    @Log(title = "产品信息", businessType = BusinessType.EXPORT)
    @PostMapping("/export")
    public void export(HttpServletResponse response, SalesLedgerProduct salesLedgerProduct)
    {
        List<SalesLedgerProduct> list = salesLedgerProductService.selectSalesLedgerProductList(salesLedgerProduct);
        ExcelUtil<SalesLedgerProduct> util = new ExcelUtil<SalesLedgerProduct>(SalesLedgerProduct.class);
        util.exportExcel(response, list, "产品信息数据");
    }
    /**
     * èŽ·å–äº§å“ä¿¡æ¯è¯¦ç»†ä¿¡æ¯
     */
    @GetMapping(value = "/{id}")
    public AjaxResult getInfo(@PathVariable("id") Long id)
    {
        return success(salesLedgerProductService.selectSalesLedgerProductById(id));
    }
    /**
     * æ–°å¢žä¿®æ”¹äº§å“ä¿¡æ¯
     */
    @Log(title = "产品信息", businessType = BusinessType.INSERT)
    @PostMapping  ("/addOrUpdateSalesLedgerProduct")
    public AjaxResult add(@RequestBody SalesLedgerProduct salesLedgerProduct)
    {
        return toAjax(salesLedgerProductService.addOrUpdateSalesLedgerProduct(salesLedgerProduct));
    }
    /**
     * åˆ é™¤äº§å“ä¿¡æ¯
     */
    @Log(title = "产品信息", businessType = BusinessType.DELETE)
    @DeleteMapping("/delProduct")
    public AjaxResult remove(@RequestBody Long[] ids)
    {
        if (ids == null || ids.length == 0) {
            return AjaxResult.error("请传入要删除的ID");
        }
        return toAjax(salesLedgerProductService.deleteSalesLedgerProductByIds(ids));
    }
}
src/main/java/com/ruoyi/sales/dto/InvoiceLedgerDto.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,19 @@
package com.ruoyi.sales.dto;
import com.ruoyi.common.vo.FileVo;
import com.ruoyi.sales.pojo.InvoiceLedger;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.List;
@Data
public class InvoiceLedgerDto extends InvoiceLedger {
    private List<FileVo> fileList;
    @ApiModelProperty(value = "查询文本")
    private String searchText;
    @ApiModelProperty(value = "客户名称")
    private String customerName;
}
src/main/java/com/ruoyi/sales/dto/ReceiptPaymentDto.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,19 @@
package com.ruoyi.sales.dto;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.sales.pojo.ReceiptPayment;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Data
public class ReceiptPaymentDto extends ReceiptPayment {
}
src/main/java/com/ruoyi/sales/dto/SalesLedgerDto.java
@@ -1,4 +1,26 @@
package com.ruoyi.sales.dto;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.sales.pojo.SalesLedgerProduct;
import lombok.Data;
import java.util.Date;
import java.util.List;
@Data
public class SalesLedgerDto {
    private Long id;
    private String salesContractNo;
    private String customerContractNo;
    private String projectName;
    @JsonFormat(pattern = "yyyy-MM-dd")
    private Date entryDate;
    private String salesman;
    private Long customerId;
    private String customerName;
    private String entryPerson;
    private String remarks;
    private String attachmentMaterials;
    private Boolean hasChildren = false;
    private List<SalesLedgerProduct> productData;
}
src/main/java/com/ruoyi/sales/dto/SalesLedgerProductDTO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,4 @@
package com.ruoyi.sales.dto;
public class SalesLedgerProductDTO {
}
src/main/java/com/ruoyi/sales/excel/InvoiceLedgerExcelDto.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,42 @@
package com.ruoyi.sales.excel;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.common.vo.FileVo;
import com.ruoyi.framework.aspectj.lang.annotation.Excel;
import com.ruoyi.sales.pojo.InvoiceLedger;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
@Data
public class InvoiceLedgerExcelDto {
    @Excel(name = "销售合同号")
    private String salesContractNo;
    @Excel(name = "客户名称")
    private String customerName;
    @Excel(name = "发票号")
    private String invoiceNo;
    @Excel(name =  "发票金额")
    private BigDecimal invoiceAmount;
    @Excel(name = "税率")
    private BigDecimal taxRate;
    @Excel(name =  "开票人")
    private String invoicePerson;
    @Excel(name =  "开票时间")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime invoiceDate;
}
src/main/java/com/ruoyi/sales/mapper/InvoiceLedgerFileMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,8 @@
package com.ruoyi.sales.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ruoyi.sales.pojo.InvoiceLedger;
import com.ruoyi.sales.pojo.InvoiceLedgerFile;
public interface InvoiceLedgerFileMapper extends BaseMapper<InvoiceLedgerFile> {
}
src/main/java/com/ruoyi/sales/mapper/InvoiceLedgerMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,29 @@
package com.ruoyi.sales.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.sales.dto.InvoiceLedgerDto;
import com.ruoyi.sales.pojo.InvoiceLedger;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface InvoiceLedgerMapper extends BaseMapper<InvoiceLedger> {
    /**
     * å¼€ç¥¨å°è´¦åˆ†é¡µæŸ¥è¯¢
     * @param page
     * @param invoiceLedgerDto
     * @return
     */
    IPage<InvoiceLedgerDto> invoiceLedgerPage(Page page, @Param("invoiceLedgerDto") InvoiceLedgerDto invoiceLedgerDto);
    /**
     * å¼€ç¥¨å°è´¦æŸ¥è¯¢
     * @param invoiceLedgerDto
     * @return
     */
    List<InvoiceLedgerDto> invoiceLedgerList(@Param("invoiceLedgerDto") InvoiceLedgerDto invoiceLedgerDto);
}
src/main/java/com/ruoyi/sales/mapper/ReceiptPaymentMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,13 @@
package com.ruoyi.sales.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.sales.dto.ReceiptPaymentDto;
import com.ruoyi.sales.pojo.ReceiptPayment;
import org.apache.ibatis.annotations.Param;
public interface ReceiptPaymentMapper extends BaseMapper<ReceiptPayment> {
    IPage<ReceiptPaymentDto> receiptPaymentListPage(Page page, @Param("receiptPaymentDto") ReceiptPaymentDto receiptPaymentDto);
}
src/main/java/com/ruoyi/sales/mapper/SalesLedgerMapper.java
@@ -3,6 +3,7 @@
import java.util.List;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ruoyi.sales.pojo.SalesLedger;
import org.apache.ibatis.annotations.Param;
/**
@@ -12,4 +13,10 @@
 * @date 2025-05-08
 */
public interface SalesLedgerMapper extends BaseMapper<SalesLedger> {
    /**
     * æŸ¥è¯¢æŒ‡å®šæ—¥æœŸçš„æ‰€æœ‰åˆåŒåºåˆ—号
     * @param datePart æ—¥æœŸéƒ¨åˆ†ï¼ˆæ ¼å¼ï¼šyyyyMMdd)
     * @return åºåˆ—号列表
     */
    List<Integer> selectSequencesByDate(@Param("datePart") String datePart);
}
src/main/java/com/ruoyi/sales/mapper/SalesLedgerProductMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,13 @@
package com.ruoyi.sales.mapper;
import com.ruoyi.common.config.MyBaseMapper;
import com.ruoyi.sales.pojo.SalesLedgerProduct;
/**
 * äº§å“ä¿¡æ¯Mapper接口
 *
 * @author ruoyi
 * @date 2025-05-08
 */
public interface SalesLedgerProductMapper extends MyBaseMapper<SalesLedgerProduct> {
}
src/main/java/com/ruoyi/sales/pojo/InvoiceLedger.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,72 @@
package com.ruoyi.sales.pojo;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.math.BigDecimal;
import java.text.Format;
import java.time.LocalDate;
import java.time.LocalDateTime;
@Data
public class InvoiceLedger {
    /**
     * åºå·
     */
    @TableId(type = IdType.AUTO)
    private Integer id;
    @ApiModelProperty(value = "销售台账sales_ledger")
    private Integer salesLedgerId;
    @ApiModelProperty(value = "销售合同号")
    private String salesContractNo;
    @ApiModelProperty(value = "客户名称ID")
    private Integer customerId;
    @ApiModelProperty(value = "发票号")
    private String invoiceNo;
    @ApiModelProperty(value = "发票金额")
    private BigDecimal invoiceAmount;
    @ApiModelProperty(value = "税率")
    private BigDecimal taxRate;
    @ApiModelProperty(value = "开票人")
    private String invoicePerson;
    @ApiModelProperty(value = "开票时间")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime invoiceDate;
    @ApiModelProperty(value = "创建时间")
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;
    @ApiModelProperty(value = "创建用户")
    @TableField(fill = FieldFill.INSERT)
    private Integer createUser;
    @ApiModelProperty(value = "修改时间")
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updateTime;
    @ApiModelProperty(value = "修改用户")
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Integer updateUser;
    @ApiModelProperty(value = "租户ID")
    @TableField(fill = FieldFill.INSERT)
    private Integer tenantId;
}
src/main/java/com/ruoyi/sales/pojo/InvoiceLedgerFile.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,44 @@
package com.ruoyi.sales.pojo;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.time.LocalDateTime;
@Data
public class InvoiceLedgerFile {
    @ApiModelProperty(value = "文件名称")
    private String fileName;
    @ApiModelProperty(value = "文件路径")
    private String filePath;
    @ApiModelProperty(value = "文件大小")
    private int fileSize;
    @ApiModelProperty(value = "开票台账ID")
    private Integer invoiceLedgerId;
    @ApiModelProperty(value = "创建时间")
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;
    @ApiModelProperty(value = "修改时间")
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updateTime;
    @ApiModelProperty(value = "创建用户")
    @TableField(fill = FieldFill.INSERT)
    private Integer createUser;
    @ApiModelProperty(value = "修改用户")
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Integer updateUser;
    @ApiModelProperty(value = "租户ID")
    @TableField(fill = FieldFill.INSERT)
    private Integer tenantId;
}
src/main/java/com/ruoyi/sales/pojo/ReceiptPayment.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,79 @@
package com.ruoyi.sales.pojo;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Data
public class ReceiptPayment {
    /**
     * åºå·
     */
    @TableId(type = IdType.AUTO)
    private Integer id;
    @ApiModelProperty(value = "销售台账sales_ledger")
    private Integer salesLedgerId;
    @ApiModelProperty(value = "销售合同号")
    private String salesContractNo;
    @ApiModelProperty(value = "客户名称ID")
    private Integer customerId;
    @ApiModelProperty(value = "发票号")
    private String invoiceNo;
    @ApiModelProperty(value = "发票金额")
    private BigDecimal invoiceAmount;
    @ApiModelProperty(value = "税率")
    private BigDecimal taxRate;
    @ApiModelProperty(value = "回款形式 0电汇1承兑")
    private String receiptPaymentType;
    @ApiModelProperty(value = "回款金额")
    private BigDecimal receiptPaymentAmount;
    @ApiModelProperty(value = "登记人")
    private String registrant;
    @ApiModelProperty(value = "invoice_ledger开票台账主键ID")
    private Integer invoiceLedgerId;
    @ApiModelProperty(value = "来款日期")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime receiptPaymentDate;
    @ApiModelProperty(value = "创建时间")
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;
    @ApiModelProperty(value = "创建用户")
    @TableField(fill = FieldFill.INSERT)
    private Integer createUser;
    @ApiModelProperty(value = "修改时间")
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updateTime;
    @ApiModelProperty(value = "修改用户")
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Integer updateUser;
    @ApiModelProperty(value = "租户ID")
    @TableField(fill = FieldFill.INSERT)
    private Integer tenantId;
}
src/main/java/com/ruoyi/sales/pojo/SalesLedger.java
@@ -1,11 +1,10 @@
package com.ruoyi.sales.pojo;
import java.math.BigDecimal;
import java.util.Date;
import com.baomidou.mybatisplus.annotation.*;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.ruoyi.framework.aspectj.lang.annotation.Excel;
import lombok.Data;
@@ -60,6 +59,11 @@
    /**
     * å®¢æˆ·åç§°
     */
    private Long customerId;
    /**
     * å®¢æˆ·åç§°
     */
    @Excel(name = "客户名称")
    private String customerName;
@@ -78,8 +82,16 @@
    /**
     * é™„件材料,存储文件名等相关信息
     */
    @Excel(name = "附件材料,存储文件名等相关信息")
    private String attachmentMaterials;
    @TableField(fill = FieldFill.INSERT)
    private Long tenantId;
    /**
     * åˆåŒé‡‘额(产品含税总价)
     */
    @Excel(name = "税率")
    private BigDecimal contractAmount;
}
src/main/java/com/ruoyi/sales/pojo/SalesLedgerProduct.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,87 @@
package com.ruoyi.sales.pojo;
import java.math.BigDecimal;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.ruoyi.framework.aspectj.lang.annotation.Excel;
import lombok.Data;
/**
 * äº§å“ä¿¡æ¯å¯¹è±¡ sales_ledger_product
 *
 * @author ruoyi
 * @date 2025-05-08
 */
@TableName("sales_ledger_product")
@Data
public class SalesLedgerProduct {
    private static final long serialVersionUID = 1L;
    /**
     * äº§å“ä¿¡æ¯ä¸»é”®
     */
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;
    /**
     * å…³è”销售台账主表主键
     */
    @Excel(name = "关联销售台账主表主键")
    private Long salesLedgerId;
    /**
     * äº§å“å¤§ç±»
     */
    @Excel(name = "产品大类")
    private String productCategory;
    /**
     * è§„格型号
     */
    @Excel(name = "规格型号")
    private String specificationModel;
    /**
     * å•位
     */
    @Excel(name = "单位")
    private String unit;
    /**
     * æ•°é‡
     */
    @Excel(name = "数量")
    private BigDecimal quantity;
    /**
     * ç¨Žçއ
     */
    @Excel(name = "税率")
    private BigDecimal taxRate;
    /**
     * å«ç¨Žå•ä»·
     */
    @Excel(name = "含税单价")
    private BigDecimal taxInclusiveUnitPrice;
    /**
     * å«ç¨Žæ€»ä»·
     */
    @Excel(name = "含税总价")
    private BigDecimal taxInclusiveTotalPrice;
    /**
     * ä¸å«ç¨Žæ€»ä»·
     */
    @Excel(name = "不含税总价")
    private BigDecimal taxExclusiveTotalPrice;
    /**
     * å‘票类型
     */
    @Excel(name = "发票类型")
    private String invoiceType;
}
src/main/java/com/ruoyi/sales/service/ISalesLedgerProductService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,23 @@
package com.ruoyi.sales.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.sales.pojo.SalesLedgerProduct;
import java.util.List;
/**
 * äº§å“ä¿¡æ¯Service接口
 *
 * @author ruoyi
 * @date 2025-05-08
 */
public interface ISalesLedgerProductService extends IService<SalesLedgerProduct> {
    SalesLedgerProduct selectSalesLedgerProductById(Long id);
    List<SalesLedgerProduct> selectSalesLedgerProductList(SalesLedgerProduct salesLedgerProduct);
    int deleteSalesLedgerProductByIds(Long[] ids);
    int addOrUpdateSalesLedgerProduct(SalesLedgerProduct salesLedgerProduct);
}
src/main/java/com/ruoyi/sales/service/ISalesLedgerService.java
@@ -3,6 +3,7 @@
import java.util.List;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.sales.dto.SalesLedgerDto;
import com.ruoyi.sales.pojo.SalesLedger;
/**
@@ -19,7 +20,7 @@
    int deleteSalesLedgerByIds(Long[] ids);
    int insertSalesLedger(SalesLedger salesLedger);
    int addOrUpdateSalesLedger(SalesLedgerDto salesLedgerDto);
    int updateSalesLedger(SalesLedger salesLedger);
    List<SalesLedgerDto> getSalesLedgerWithProducts();
}
src/main/java/com/ruoyi/sales/service/InvoiceLedgerService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,66 @@
package com.ruoyi.sales.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.common.vo.FileVo;
import com.ruoyi.sales.dto.InvoiceLedgerDto;
import com.ruoyi.sales.pojo.InvoiceLedger;
import com.ruoyi.sales.pojo.InvoiceLedgerFile;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
public interface InvoiceLedgerService {
    /**
     * å¼€ç¥¨å°è´¦æ–°å¢ž
     * @param invoiceLedgerDto
     * @return
     */
    int invoiceLedgerAdd( InvoiceLedgerDto invoiceLedgerDto);
    /**
     * å¼€ç¥¨å°è´¦åˆ é™¤
     * @param ids
     * @return
     */
    int invoiceLedgerDel(List<Integer> ids);
    /**
     * å¼€ç¥¨å°è´¦ä¿®æ”¹
     * @param invoiceLedgerDto
     * @return
     */
    int invoiceLedgerUpdate(InvoiceLedgerDto invoiceLedgerDto);
    /**
     * å¼€ç¥¨å°è´¦åˆ†é¡µæŸ¥è¯¢
     * @param page
     * @param invoiceLedgerDto
     * @return
     */
    IPage<InvoiceLedgerDto> invoiceLedgerPage(Page page, InvoiceLedgerDto invoiceLedgerDto);
    /**
     * å¼€ç¥¨å°è´¦æ–‡ä»¶æŸ¥è¯¢
     * @param invoiceLedgerId
     * @return
     */
    List<InvoiceLedgerFile> invoiceLedgerFileList(Integer invoiceLedgerId);
    /**
     * å¼€ç¥¨å°è´¦æ–‡ä»¶ä¸Šä¼ 
     * @param file
     * @return
     */
    FileVo invoiceLedgerUploadFile(MultipartFile file);
    /**
     * é™„件下载
     * @param response
     * @param invoiceLedgerDto
     * @return
     */
    void invoiceLedgerDownload(HttpServletResponse response ,InvoiceLedgerDto invoiceLedgerDto);
}
src/main/java/com/ruoyi/sales/service/ReceiptPaymentService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,40 @@
package com.ruoyi.sales.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.sales.dto.ReceiptPaymentDto;
import com.ruoyi.sales.pojo.ReceiptPayment;
import java.util.List;
public interface ReceiptPaymentService {
    /**
     * å›žæ¬¾ç™»è®°æ–°å¢ž
     * @param receiptPayment
     * @return
     */
    int receiptPaymentAdd(ReceiptPayment receiptPayment);
    /**
     * å›žæ¬¾ç™»è®°ä¿®æ”¹
     * @param receiptPayment
     * @return
     */
    int receiptPaymentUpdate(ReceiptPayment receiptPayment);
    /**
     * å›žæ¬¾ç™»è®°åˆ é™¤
     * @param ids
     * @return
     */
    int receiptPaymentDel(List<Integer> ids);
    /**
     * å›žæ¬¾ç™»è®°åˆ†é¡µæŸ¥è¯¢
     * @param page
     * @param receiptPaymentDto
     * @return
     */
    IPage<ReceiptPaymentDto> receiptPaymentListPage (Page page, ReceiptPaymentDto receiptPaymentDto);
}
src/main/java/com/ruoyi/sales/service/impl/InvoiceLedgerServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,171 @@
package com.ruoyi.sales.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.basic.dto.SupplierManageDto;
import com.ruoyi.basic.excel.SupplierManageExcelDto;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.utils.file.FileUploadUtils;
import com.ruoyi.common.utils.file.FileUtils;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.common.vo.FileVo;
import com.ruoyi.sales.dto.InvoiceLedgerDto;
import com.ruoyi.sales.excel.InvoiceLedgerExcelDto;
import com.ruoyi.sales.mapper.InvoiceLedgerFileMapper;
import com.ruoyi.sales.mapper.InvoiceLedgerMapper;
import com.ruoyi.sales.pojo.InvoiceLedger;
import com.ruoyi.sales.pojo.InvoiceLedgerFile;
import com.ruoyi.sales.service.InvoiceLedgerService;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.util.List;
import java.util.stream.Collectors;
@Service
public class InvoiceLedgerServiceImpl extends ServiceImpl<InvoiceLedgerMapper, InvoiceLedger> implements InvoiceLedgerService {
    @Value("${ruoyi.profile}")
    private String uploadFile;
    @Autowired
    private InvoiceLedgerMapper invoiceLedgerMapper;
    @Autowired
    private InvoiceLedgerFileMapper invoiceLedgerFileMapper;
    /**
     * å¼€ç¥¨å°è´¦æ–°å¢ž
     * @param invoiceLedgerDto
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int invoiceLedgerAdd(InvoiceLedgerDto invoiceLedgerDto) {
        InvoiceLedger invoiceLedger = new InvoiceLedger();
        BeanUtils.copyProperties(invoiceLedgerDto, invoiceLedger);
        int result = invoiceLedgerMapper.insert(invoiceLedger);
        List<FileVo> fileList = invoiceLedgerDto.getFileList();
        if(CollectionUtils.isNotEmpty(fileList)){
            fileList.forEach(fileVo -> {
                InvoiceLedgerFile invoiceLedgerFile = new InvoiceLedgerFile();
                BeanUtils.copyProperties(fileVo, invoiceLedgerFile);
                invoiceLedgerFile.setInvoiceLedgerId(invoiceLedger.getId());
                invoiceLedgerFileMapper.insert(invoiceLedgerFile);
            });
        }
        return result;
    }
    /**
     * å¼€ç¥¨å°è´¦åˆ é™¤
     * @param ids
     * @return
     */
    @Override
    public int invoiceLedgerDel(List<Integer> ids) {
        LambdaQueryWrapper<InvoiceLedger> delWrapper = new LambdaQueryWrapper<>();
        delWrapper.in(InvoiceLedger::getId, ids);
        return invoiceLedgerMapper.delete(delWrapper);
    }
    /**
     * å¼€ç¥¨å°è´¦ä¿®æ”¹
     * @param invoiceLedgerDto
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int invoiceLedgerUpdate(InvoiceLedgerDto invoiceLedgerDto) {
        InvoiceLedger invoiceLedger = new InvoiceLedger();
        BeanUtils.copyProperties(invoiceLedgerDto, invoiceLedger);
        int result = invoiceLedgerMapper.updateById(invoiceLedger);
        // åˆ é™¤å…³è”附件
        LambdaQueryWrapper<InvoiceLedgerFile> delWrapper = new LambdaQueryWrapper<>();
        delWrapper.eq(InvoiceLedgerFile::getInvoiceLedgerId, invoiceLedger.getId());
        invoiceLedgerFileMapper.delete(delWrapper);
        // é‡æ–°æ’入附件关联表
        List<FileVo> fileList = invoiceLedgerDto.getFileList();
        if(CollectionUtils.isNotEmpty(fileList)){
            fileList.forEach(fileVo -> {
                InvoiceLedgerFile invoiceLedgerFile = new InvoiceLedgerFile();
                BeanUtils.copyProperties(fileVo, invoiceLedgerFile);
                invoiceLedgerFile.setInvoiceLedgerId(invoiceLedger.getId());
                invoiceLedgerFileMapper.insert(invoiceLedgerFile);
            });
        }
        return result;
    }
    /**
     * å¼€ç¥¨å°è´¦åˆ†é¡µæŸ¥è¯¢
     * @param page
     * @param invoiceLedgerDto
     * @return
     */
    @Override
    public IPage<InvoiceLedgerDto> invoiceLedgerPage(Page page, InvoiceLedgerDto invoiceLedgerDto) {
        return invoiceLedgerMapper.invoiceLedgerPage(page, invoiceLedgerDto);
    }
    /**
     * å¼€ç¥¨å°è´¦æ–‡ä»¶æŸ¥è¯¢
     * @param invoiceLedgerId
     * @return
     */
    @Override
    public List<InvoiceLedgerFile> invoiceLedgerFileList(Integer invoiceLedgerId) {
        LambdaQueryWrapper<InvoiceLedgerFile> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(InvoiceLedgerFile::getInvoiceLedgerId, invoiceLedgerId);
        return invoiceLedgerFileMapper.selectList(queryWrapper);
    }
    /**
     * å¼€ç¥¨å°è´¦æ–‡ä»¶ä¸Šä¼ 
     * @param file
     * @return
     */
    @Override
    public FileVo invoiceLedgerUploadFile(MultipartFile file) {
        FileVo fileVo = new FileVo();
        try {
            String baseDir = uploadFile + File.separatorChar + "invoiceLedger";
            String filePath = FileUploadUtils.upload(baseDir, file);
            fileVo.setFileName(file.getOriginalFilename());
            fileVo.setFilePath(filePath);
            fileVo.setFileSize((int)file.getSize());
        }catch (Exception e){
            e.printStackTrace();
            throw new RuntimeException("文件上传失败");
        }
        return fileVo;
    }
    /**
     * é™„件下载
     * @param response
     * @param invoiceLedgerDto
     * @return
     */
    @Override
    public void invoiceLedgerDownload(HttpServletResponse response, InvoiceLedgerDto invoiceLedgerDto) {
        List<InvoiceLedgerDto> invoiceLedgerDtoList = invoiceLedgerMapper.invoiceLedgerList(invoiceLedgerDto);
        List<InvoiceLedgerExcelDto> invoiceLedgerExcelDtoList = invoiceLedgerDtoList.stream().map(item -> {
            InvoiceLedgerExcelDto invoiceLedgerExcelDto = new InvoiceLedgerExcelDto();
            BeanUtils.copyProperties(item, invoiceLedgerExcelDto);
            return invoiceLedgerExcelDto;
        }).collect(Collectors.toList());
        ExcelUtil<InvoiceLedgerExcelDto> util = new ExcelUtil<InvoiceLedgerExcelDto>(InvoiceLedgerExcelDto.class);
        util.exportExcel(response, invoiceLedgerExcelDtoList, "供应商导出");
    }
}
src/main/java/com/ruoyi/sales/service/impl/InvoiceRegistrationServiceImpl.java
@@ -16,8 +16,6 @@
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Collections;
import java.util.List;
@Service
src/main/java/com/ruoyi/sales/service/impl/ReceiptPaymentServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,64 @@
package com.ruoyi.sales.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.sales.dto.ReceiptPaymentDto;
import com.ruoyi.sales.mapper.ReceiptPaymentMapper;
import com.ruoyi.sales.pojo.ReceiptPayment;
import com.ruoyi.sales.service.ReceiptPaymentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class ReceiptPaymentServiceImpl extends ServiceImpl<ReceiptPaymentMapper,ReceiptPayment> implements ReceiptPaymentService {
    @Autowired
    private ReceiptPaymentMapper receiptPaymentMapper;
    /**
     * å›žæ¬¾ç™»è®°æ–°å¢ž
     * @param receiptPayment
     * @return
     */
    @Override
    public int receiptPaymentAdd(ReceiptPayment receiptPayment) {
        return receiptPaymentMapper.insert(receiptPayment);
    }
    /**
     * å›žæ¬¾ç™»è®°ä¿®æ”¹
     * @param receiptPayment
     * @return
     */
    @Override
    public int receiptPaymentUpdate(ReceiptPayment receiptPayment) {
        return receiptPaymentMapper.updateById(receiptPayment);
    }
    /**
     * å›žæ¬¾ç™»è®°åˆ é™¤
     * @param ids
     * @return
     */
    @Override
    public int receiptPaymentDel(List<Integer> ids) {
        LambdaQueryWrapper<ReceiptPayment> delQuery = new LambdaQueryWrapper<>();
        delQuery.in(ReceiptPayment::getId, ids);
        return receiptPaymentMapper.delete(delQuery);
    }
    /**
     * å›žæ¬¾ç™»è®°åˆ†é¡µæŸ¥è¯¢
     * @param page
     * @param receiptPaymentDto
     * @return
     */
    @Override
    public IPage<ReceiptPaymentDto> receiptPaymentListPage(Page page, ReceiptPaymentDto receiptPaymentDto) {
        return receiptPaymentMapper.receiptPaymentListPage(page, receiptPaymentDto);
    }
}
src/main/java/com/ruoyi/sales/service/impl/SalesLedgerProductServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,50 @@
package com.ruoyi.sales.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.sales.mapper.SalesLedgerProductMapper;
import com.ruoyi.sales.pojo.SalesLedgerProduct;
import com.ruoyi.sales.service.ISalesLedgerProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Arrays;
import java.util.List;
/**
 * äº§å“ä¿¡æ¯Service业务层处理
 *
 * @author ruoyi
 * @date 2025-05-08
 */
@Service
public class SalesLedgerProductServiceImpl extends ServiceImpl<SalesLedgerProductMapper, SalesLedgerProduct> implements ISalesLedgerProductService {
    @Autowired
    private SalesLedgerProductMapper salesLedgerProductMapper;
    @Override
    public SalesLedgerProduct selectSalesLedgerProductById(Long id) {
        return salesLedgerProductMapper.selectById(id);
    }
    @Override
    public List<SalesLedgerProduct> selectSalesLedgerProductList(SalesLedgerProduct salesLedgerProduct) {
        LambdaQueryWrapper<SalesLedgerProduct> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(SalesLedgerProduct::getSalesLedgerId,salesLedgerProduct.getSalesLedgerId());
        return salesLedgerProductMapper.selectList(queryWrapper);
    }
    @Override
    public int deleteSalesLedgerProductByIds(Long[] ids) {
        return salesLedgerProductMapper.deleteBatchIds(Arrays.asList(ids));
    }
    @Override
    public int addOrUpdateSalesLedgerProduct(SalesLedgerProduct salesLedgerProduct) {
        if (salesLedgerProduct.getId() == null){
            return salesLedgerProductMapper.insert(salesLedgerProduct);
        }else {
            return  salesLedgerProductMapper.updateById(salesLedgerProduct);
        }
    }
}
src/main/java/com/ruoyi/sales/service/impl/SalesLedgerServiceImpl.java
@@ -1,15 +1,29 @@
package com.ruoyi.sales.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.basic.mapper.CustomerMapper;
import com.ruoyi.basic.pojo.Customer;
import com.ruoyi.common.exception.base.BaseException;
import com.ruoyi.sales.dto.SalesLedgerDto;
import com.ruoyi.sales.mapper.SalesLedgerMapper;
import com.ruoyi.sales.mapper.SalesLedgerProductMapper;
import com.ruoyi.sales.pojo.SalesLedger;
import com.ruoyi.sales.pojo.SalesLedgerProduct;
import com.ruoyi.sales.service.ISalesLedgerService;
import org.springframework.beans.factory.annotation.Autowired;
import lombok.AllArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Arrays;
import java.util.List;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
/**
 * é”€å”®å°è´¦Service业务层处理
@@ -18,13 +32,44 @@
 * @date 2025-05-08
 */
@Service
@AllArgsConstructor
public class SalesLedgerServiceImpl extends ServiceImpl<SalesLedgerMapper, SalesLedger> implements ISalesLedgerService {
    @Autowired
    private SalesLedgerMapper salesLedgerMapper;
    private CustomerMapper customerMapper;
    private SalesLedgerProductMapper salesLedgerProductMapper;
    private static final String LOCK_PREFIX = "contract_no_lock:";
    private static final long LOCK_WAIT_TIMEOUT = 10; // é”ç­‰å¾…超时时间(秒)
    private static final long LOCK_EXPIRE_TIME = 30;  // é”è‡ªåŠ¨è¿‡æœŸæ—¶é—´ï¼ˆç§’ï¼‰
    private final RedisTemplate<String, String> redisTemplate;
    @Override
    public List<SalesLedger> selectSalesLedgerList(SalesLedger salesLedger) {
        return salesLedgerMapper.selectList(new LambdaQueryWrapper<>());
    }
    public List<SalesLedgerDto> getSalesLedgerWithProducts() {
        List<SalesLedger> ledgers = salesLedgerMapper.selectList(new LambdaQueryWrapper<>());
        List<SalesLedgerProduct> products = salesLedgerProductMapper.selectList(new LambdaQueryWrapper<>());
        Map<Long, List<SalesLedgerProduct>> productMap = products.stream()
                .collect(Collectors.groupingBy(SalesLedgerProduct::getSalesLedgerId));
        return ledgers.stream().map(ledger -> {
            SalesLedgerDto dto = new SalesLedgerDto();
            org.springframework.beans.BeanUtils.copyProperties(ledger, dto);
            List<SalesLedgerProduct> ledgerProducts = productMap.getOrDefault(ledger.getId(), Collections.emptyList());
            if (!ledgerProducts.isEmpty()) {
                dto.setHasChildren(true);
                dto.setProductData(ledgerProducts);
            }
            return dto;
        }).collect(Collectors.toList());
    }
    @Override
@@ -33,17 +78,139 @@
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int deleteSalesLedgerByIds(Long[] ids) {
        return salesLedgerMapper.deleteBatchIds(Arrays.asList(ids));
        List<Long> idList = Arrays.stream(ids)
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
        if (CollectionUtils.isEmpty(idList)) {
            return 0;
        }
        // 1. å…ˆåˆ é™¤å­è¡¨æ•°æ®
        LambdaQueryWrapper<SalesLedgerProduct> productWrapper = new LambdaQueryWrapper<>();
        productWrapper.in(SalesLedgerProduct::getSalesLedgerId, idList);
        salesLedgerProductMapper.delete(productWrapper);
        // 2. å†åˆ é™¤ä¸»è¡¨æ•°æ®
        return salesLedgerMapper.deleteBatchIds(idList);
    }
    @Override
    public int insertSalesLedger(SalesLedger salesLedger) {
        return salesLedgerMapper.insert(salesLedger);
    @Transactional(rollbackFor = Exception.class)
    public int addOrUpdateSalesLedger(SalesLedgerDto salesLedgerDto) {
        // 1. æ ¡éªŒå®¢æˆ·ä¿¡æ¯
        Customer customer = customerMapper.selectById(salesLedgerDto.getCustomerId());
        if (customer == null) {
            throw new BaseException("客户不存在");
        }
        // 2. DTO转Entity
        SalesLedger salesLedger = convertToEntity(salesLedgerDto);
        salesLedger.setCustomerName(customer.getCustomerName());
        salesLedger.setTenantId(customer.getTenantId());
        // 3. æ–°å¢žæˆ–更新主表
        if (salesLedger.getId() == null) {
            // ç”ŸæˆåˆåŒç¼–号
            String contractNo = generateSalesContractNo();
            salesLedger.setSalesContractNo(contractNo);
            salesLedgerMapper.insert(salesLedger);
        } else {
            salesLedgerMapper.updateById(salesLedger);
        }
        // 4. å¤„理子表数据
        if (salesLedgerDto.getProductData() != null && !salesLedgerDto.getProductData().isEmpty()) {
            handleSalesLedgerProducts(salesLedger.getId(), salesLedgerDto.getProductData());
        }
        return 1; // æ“ä½œæˆåŠŸè¿”å›ž1
    }
    @Override
    public int updateSalesLedger(SalesLedger salesLedger) {
        return salesLedgerMapper.updateById(salesLedger);
    private void handleSalesLedgerProducts(Long salesLedgerId, List<SalesLedgerProduct> products) {
        // æŒ‰ID分组,区分新增和更新的记录
        Map<Boolean, List<SalesLedgerProduct>> partitionedProducts = products.stream()
                .peek(p -> p.setSalesLedgerId(salesLedgerId))
                .collect(Collectors.partitioningBy(p -> p.getId() != null));
        List<SalesLedgerProduct> updateList = partitionedProducts.get(true);
        List<SalesLedgerProduct> insertList = partitionedProducts.get(false);
        // æ‰§è¡Œæ›´æ–°æ“ä½œ
        if (!updateList.isEmpty()) {
            salesLedgerProductMapper.updateBatchSomeColumn(updateList);
        }
        // æ‰§è¡Œæ’入操作
        if (!insertList.isEmpty()) {
            salesLedgerProductMapper.insertBatchSomeColumn(insertList);
        }
    }
    private SalesLedger convertToEntity(SalesLedgerDto dto) {
        SalesLedger entity = new SalesLedger();
        BeanUtils.copyProperties(dto, entity);
        return entity;
    }
    @Transactional(readOnly = true)
    public String generateSalesContractNo() {
        LocalDate currentDate = LocalDate.now();
        String datePart = currentDate.format(DateTimeFormatter.BASIC_ISO_DATE);
        String lockKey = LOCK_PREFIX + datePart;
        String lockValue = Thread.currentThread().getId() + "-" + System.nanoTime(); // å”¯ä¸€æ ‡è¯†é”æŒæœ‰è€…
        try {
            // 1. å°è¯•获取分布式锁(循环直到超时)
            long startWaitTime = System.currentTimeMillis();
            while (System.currentTimeMillis() - startWaitTime < LOCK_WAIT_TIMEOUT * 1000) {
                // SET key value NX PX 30000:仅当锁不存在时获取,设置30秒过期
                Boolean locked = redisTemplate.opsForValue().setIfAbsent(lockKey, lockValue, LOCK_EXPIRE_TIME, TimeUnit.SECONDS);
                if (Boolean.TRUE.equals(locked)) {
                    break; // æˆåŠŸèŽ·å–é”
                }
                // çŸ­æš‚休眠避免忙等待
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    throw new RuntimeException("获取锁时被中断", e);
                }
            }
            if (!redisTemplate.hasKey(lockKey)) {
                throw new RuntimeException("获取合同编号生成锁失败:超时");
            }
            // 2. æŸ¥è¯¢å½“天已存在的序列号(与原逻辑一致)
            List<Integer> existingSequences = salesLedgerMapper.selectSequencesByDate(datePart);
            int nextSequence = findFirstMissingSequence(existingSequences);
            return datePart + String.format("%02d", nextSequence);
        } finally {
            // 3. é‡Šæ”¾é”ï¼ˆä½¿ç”¨Lua脚本保证原子性,避免误删其他线程的锁)
            String luaScript = "if redis.call('GET', KEYS[1]) == ARGV[1] then return redis.call('DEL', KEYS[1]) else return 0 end";
            redisTemplate.execute(
                    new DefaultRedisScript<>(luaScript, Long.class),
                    Collections.singletonList(lockKey),
                    lockValue // åªæœ‰æŒæœ‰ç›¸åŒå€¼çš„线程才能删除锁
            );
        }
    }
    private int findFirstMissingSequence(List<Integer> sequences) {
        if (sequences.isEmpty()) {
            return 1;
        }
        // æŽ’序后查找第一个缺失的正整数(与原逻辑一致)
        sequences.sort(Integer::compareTo);
        int next = 1;
        for (int seq : sequences) {
            if (seq == next) {
                next++;
            } else if (seq > next) {
                break;
            }
        }
        return next;
    }
}
src/main/resources/application.yml
@@ -100,7 +100,7 @@
# MyBatis Plus配置
mybatis-plus:
  # æœç´¢æŒ‡å®šåŒ…别名   æ ¹æ®è‡ªå·±çš„项目来
  typeAliasesPackage: com.ruoyi.basic.**.pojo
  typeAliasesPackage: com.ruoyi.**.pojo
  # é…ç½®mapper的扫描,找到所有的mapper.xml映射文件
  mapperLocations: classpath*:mapper/**/*Mapper.xml
  # åŠ è½½å…¨å±€çš„é…ç½®æ–‡ä»¶
src/main/resources/mapper/sales/InvoiceLedgerMapper.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,69 @@
<?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="com.ruoyi.sales.mapper.InvoiceLedgerMapper">
    <select id="invoiceLedgerPage" resultType="com.ruoyi.sales.dto.InvoiceLedgerDto">
        SELECT
            T1.id                ,
            T1.sales_ledger_id   ,
            T1.sales_contract_no ,
            T1.customer_id       ,
            T1.invoice_no        ,
            T1.invoice_amount    ,
            T1.tax_rate          ,
            T1.invoice_person    ,
            T1.invoice_date      ,
            T1.create_time       ,
            T1.create_user       ,
            T1.update_time       ,
            T1.update_user       ,
            T1.tenant_id         ,
            T2.customer_name
        FROM invoice_ledger T1
        LEFT JOIN customer T2 ON T1.customer_id = T2.id
        <where>
            <if test="invoiceLedgerDto.searchText != null and invoiceLedgerDto.searchText != ''">
                AND (
                    T2.customer_name LIKE CONCAT('%',#{invoiceLedgerDto.searchText},'%')
                    OR T1.sales_contract_no LIKE CONCAT('%',#{invoiceLedgerDto.searchText},'%')
                )
            </if>
            <if test="invoiceLedgerDto.invoiceDate != null">
                AND DATE_FORMAT(T1.invoice_date,'%Y-%m-%d') = DATE_FORMAT(#{invoiceLedgerDto.invoiceDate},'%Y-%m-%d')
            </if>
        </where>
    </select>
    <select id="invoiceLedgerList" resultType="com.ruoyi.sales.dto.InvoiceLedgerDto">
        SELECT
        T1.id                ,
        T1.sales_ledger_id   ,
        T1.sales_contract_no ,
        T1.customer_id       ,
        T1.invoice_no        ,
        T1.invoice_amount    ,
        T1.tax_rate          ,
        T1.invoice_person    ,
        T1.invoice_date      ,
        T1.create_time       ,
        T1.create_user       ,
        T1.update_time       ,
        T1.update_user       ,
        T1.tenant_id         ,
        T2.customer_name
        FROM invoice_ledger T1
        LEFT JOIN customer T2 ON T1.customer_id = T2.id
        <where>
            <if test="invoiceLedgerDto.searchText != null and invoiceLedgerDto.searchText != ''">
                AND (
                T2.customer_name LIKE CONCAT('%',#{invoiceLedgerDto.searchText},'%')
                OR T1.sales_contract_no LIKE CONCAT('%',#{invoiceLedgerDto.searchText},'%')
                )
            </if>
            <if test="invoiceLedgerDto.invoiceDate != null">
                AND DATE_FORMAT(T1.invoice_date,'%Y-%m-%d') = DATE_FORMAT(#{invoiceLedgerDto.invoiceDate},'%Y-%m-%d')
            </if>
        </where>
    </select>
</mapper>
src/main/resources/mapper/sales/ReceiptPaymentMapper.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,28 @@
<?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="com.ruoyi.sales.mapper.ReceiptPaymentMapper">
    <select id="invoiceLedgerPage" resultType="com.ruoyi.sales.dto.ReceiptPaymentDto">
        SELECT
            T1.id                 ,
            T1.sales_ledger_id        ,
            T1.sales_contract_no      ,
            T1.customer_id            ,
            T1.invoice_no             ,
            T1.invoice_amount         ,
            T1.tax_rate               ,
            T1.receipt_payment_type   ,
            T1.receipt_payment_amount ,
            T1.registrant             ,
            T1.receipt_payment_date   ,
            T1.create_time            ,
            T1.create_user            ,
            T1.update_time            ,
            T1.update_user            ,
            T1.tenant_id              ,
        FROM receipt_payment T1
        LEFT JOIN customer T2 ON T1.customer_id = T2.id
    </select>
</mapper>
src/main/resources/mapper/sales/SalesLedgerMapper.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,12 @@
<?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="com.ruoyi.sales.mapper.SalesLedgerMapper">
    <select id="selectSequencesByDate" resultType="java.lang.Integer">
        SELECT CAST(SUBSTR(sales_contract_no, 9, 2) AS SIGNED)
        FROM sales_ledger
        WHERE SUBSTR(sales_contract_no, 1, 8) = #{datePart}
    </select>
</mapper>