Jelajahi Sumber

卓美:添加公司提现明细,独特需求

yuhongqi 4 hari lalu
induk
melakukan
99ccb83415

+ 114 - 0
fs-admin/src/main/java/com/fs/company/controller/CompanyWithdrawDetailAdminController.java

@@ -0,0 +1,114 @@
+package com.fs.company.controller;
+
+import cn.hutool.json.JSONUtil;
+import com.fs.common.annotation.Log;
+import com.fs.common.core.controller.BaseController;
+import com.fs.common.core.domain.AjaxResult;
+import com.fs.common.core.domain.model.LoginUser;
+import com.fs.common.core.page.PageDomain;
+import com.fs.common.core.page.TableDataInfo;
+import com.fs.common.core.page.TableSupport;
+import com.fs.common.enums.BusinessType;
+import com.fs.common.utils.ServletUtils;
+import com.fs.common.utils.StringUtils;
+import com.fs.common.utils.poi.ExcelUtil;
+import com.fs.company.param.CompanyParam;
+import com.fs.company.param.CompanyWithdrawDetailAdminParam;
+import com.fs.company.service.ICompanyService;
+import com.fs.company.service.ICompanyWithdrawDetailService;
+import com.fs.company.vo.CompanyNameVO;
+import com.fs.company.vo.CompanyWithdrawDetailVO;
+import com.fs.course.config.CourseConfig;
+import com.fs.framework.web.service.TokenService;
+import com.fs.system.service.ISysConfigService;
+import com.github.pagehelper.PageHelper;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+
+/**
+ * 管理后台-提现明细(多公司、类型筛选;与销售端数据源一致)
+ */
+@RestController
+@RequestMapping("/company/withdrawDetailAdmin")
+public class CompanyWithdrawDetailAdminController extends BaseController {
+
+    @Autowired
+    private ICompanyWithdrawDetailService companyWithdrawDetailService;
+
+    @Autowired
+    private ICompanyService companyService;
+
+    @Autowired
+    private TokenService tokenService;
+
+    @Autowired
+    private ISysConfigService configService;
+
+    /**
+     * 分公司下拉:关键词模糊匹配公司名称(最多 500 条)
+     */
+    @PreAuthorize("@ss.hasPermi('company:companyMoneyLogsDetail:list')")
+    @GetMapping("/companyOptions")
+    public AjaxResult companyOptions(@RequestParam(value = "keyword", required = false) String keyword) {
+        CompanyParam param = new CompanyParam();
+        if (StringUtils.isNotEmpty(keyword)) {
+            param.setCompanyName(keyword.trim());
+        }
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        String json = configService.selectConfigByKey("course.config");
+        CourseConfig config = JSONUtil.toBean(json, CourseConfig.class);
+        if (!loginUser.isAdmin() && config.getDept() != null && config.getDept()) {
+            param.setDeptId(loginUser.getDeptId());
+        }
+        PageHelper.startPage(1, 500);
+        List<CompanyNameVO> list = companyService.selectCompanyNameVOList(param);
+        return AjaxResult.success(list);
+    }
+
+    @PreAuthorize("@ss.hasPermi('company:companyMoneyLogsDetail:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(
+            @RequestParam(required = false) List<Long> companyIds,
+            @RequestParam(required = false) List<Integer> detailTypes) {
+        PageDomain page = TableSupport.buildPageRequest();
+        int pageNum = page.getPageNum() != null && page.getPageNum() > 0 ? page.getPageNum() : 1;
+        int pageSize = page.getPageSize() != null && page.getPageSize() > 0 ? page.getPageSize() : 10;
+        PageHelper.startPage(pageNum, pageSize);
+
+        CompanyWithdrawDetailAdminParam p = new CompanyWithdrawDetailAdminParam();
+        p.setCompanyIds(companyIds);
+        p.setDetailTypes(detailTypes);
+
+        List<CompanyWithdrawDetailVO> list = companyWithdrawDetailService.selectWithdrawDetailListAdmin(p);
+        companyWithdrawDetailService.normalizeDisplayAmounts(list);
+        long start = (long) (pageNum - 1) * pageSize;
+        for (int i = 0; i < list.size(); i++) {
+            list.get(i).setSerialNo((int) (start + i + 1));
+        }
+        return getDataTable(list);
+    }
+
+    @PreAuthorize("@ss.hasPermi('company:companyMoneyLogsDetail:export')")
+    @Log(title = "提现明细", businessType = BusinessType.EXPORT)
+    @GetMapping("/export")
+    public AjaxResult export(
+            @RequestParam(required = false) List<Long> companyIds,
+            @RequestParam(required = false) List<Integer> detailTypes) {
+        CompanyWithdrawDetailAdminParam p = new CompanyWithdrawDetailAdminParam();
+        p.setCompanyIds(companyIds);
+        p.setDetailTypes(detailTypes);
+        List<CompanyWithdrawDetailVO> list = companyWithdrawDetailService.selectWithdrawDetailListAdmin(p);
+        companyWithdrawDetailService.normalizeDisplayAmounts(list);
+        for (int i = 0; i < list.size(); i++) {
+            list.get(i).setSerialNo(i + 1);
+        }
+        ExcelUtil<CompanyWithdrawDetailVO> util = new ExcelUtil<>(CompanyWithdrawDetailVO.class);
+        return util.exportExcel(list, "提现明细");
+    }
+}

+ 88 - 0
fs-company/src/main/java/com/fs/company/controller/company/CompanyWithdrawDetailController.java

@@ -0,0 +1,88 @@
+package com.fs.company.controller.company;
+
+import com.fs.common.annotation.Log;
+import com.fs.common.core.controller.BaseController;
+import com.fs.common.core.domain.AjaxResult;
+import com.fs.common.core.domain.R;
+import com.fs.common.core.page.PageDomain;
+import com.fs.common.core.page.TableDataInfo;
+import com.fs.common.core.page.TableSupport;
+import com.github.pagehelper.PageHelper;
+import com.fs.common.enums.BusinessType;
+import com.fs.common.utils.ServletUtils;
+import com.fs.common.utils.poi.ExcelUtil;
+import com.fs.company.service.ICompanyWithdrawDetailService;
+import com.fs.company.vo.CompanyWithdrawDetailVO;
+import com.fs.framework.security.LoginUser;
+import com.fs.framework.service.TokenService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+
+/**
+ * 销售端-提现明细(新接口,数据自 2026-03-27 起)
+ */
+@RestController
+@RequestMapping("/company/withdrawDetail")
+public class CompanyWithdrawDetailController extends BaseController {
+
+    @Autowired
+    private TokenService tokenService;
+
+    @Autowired
+    private ICompanyWithdrawDetailService companyWithdrawDetailService;
+
+    /**
+     * 分公司名称 + 可提现金额(实时,取 company.money)
+     */
+    @PreAuthorize("@ss.hasPermi('company:companyMoneyLogsDetail:list')")
+    @GetMapping("/summary")
+    public R summary() {
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        Long companyId = loginUser.getCompany().getCompanyId();
+        return R.ok(companyWithdrawDetailService.summary(companyId));
+    }
+
+    /**
+     * 分页列表:订单记录时间倒序,默认每页 10 条(由前端传 pageSize)
+     */
+    @PreAuthorize("@ss.hasPermi('company:companyMoneyLogsDetail:list')")
+    @GetMapping("/list")
+    public TableDataInfo list() {
+        PageDomain page = TableSupport.buildPageRequest();
+        int pageNum = page.getPageNum() != null && page.getPageNum() > 0 ? page.getPageNum() : 1;
+        int pageSize = page.getPageSize() != null && page.getPageSize() > 0 ? page.getPageSize() : 10;
+        PageHelper.startPage(pageNum, pageSize);
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        Long companyId = loginUser.getCompany().getCompanyId();
+        List<CompanyWithdrawDetailVO> list = companyWithdrawDetailService.selectWithdrawDetailList(companyId);
+        companyWithdrawDetailService.normalizeDisplayAmounts(list);
+        long start = (long) (pageNum - 1) * pageSize;
+        for (int i = 0; i < list.size(); i++) {
+            list.get(i).setSerialNo((int) (start + i + 1));
+        }
+        return getDataTable(list);
+    }
+
+    /**
+     * 导出全部字段(同列表,不受分页限制)
+     */
+    @PreAuthorize("@ss.hasPermi('company:companyMoneyLogsDetail:export')")
+    @Log(title = "提现明细", businessType = BusinessType.EXPORT)
+    @GetMapping("/export")
+    public AjaxResult export() {
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        Long companyId = loginUser.getCompany().getCompanyId();
+        List<CompanyWithdrawDetailVO> list = companyWithdrawDetailService.selectWithdrawDetailList(companyId);
+        companyWithdrawDetailService.normalizeDisplayAmounts(list);
+        for (int i = 0; i < list.size(); i++) {
+            list.get(i).setSerialNo(i + 1);
+        }
+        ExcelUtil<CompanyWithdrawDetailVO> util = new ExcelUtil<>(CompanyWithdrawDetailVO.class);
+        return util.exportExcel(list, "提现明细");
+    }
+}

+ 17 - 0
fs-service/src/main/java/com/fs/company/mapper/CompanyWithdrawDetailMapper.java

@@ -0,0 +1,17 @@
+package com.fs.company.mapper;
+
+import com.fs.company.param.CompanyWithdrawDetailAdminParam;
+import com.fs.company.vo.CompanyWithdrawDetailVO;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+/**
+ * 提现明细(基于 company_money_logs 聚合展示)
+ */
+public interface CompanyWithdrawDetailMapper {
+
+    List<CompanyWithdrawDetailVO> selectWithdrawDetailList(@Param("companyId") Long companyId);
+
+    List<CompanyWithdrawDetailVO> selectWithdrawDetailListAdmin(@Param("p") CompanyWithdrawDetailAdminParam p);
+}

+ 20 - 0
fs-service/src/main/java/com/fs/company/param/CompanyWithdrawDetailAdminParam.java

@@ -0,0 +1,20 @@
+package com.fs.company.param;
+
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * 管理端-提现明细查询(多公司、多明细类型)
+ */
+@Data
+public class CompanyWithdrawDetailAdminParam {
+
+    /** 分公司 ID,空表示不限 */
+    private List<Long> companyIds;
+
+    /**
+     * 明细类型:1订单金额入账 2订单金额扣减 3总公司充值 4总公司扣款 5分公司提现 6总公司驳回;空表示全部
+     */
+    private List<Integer> detailTypes;
+}

+ 24 - 0
fs-service/src/main/java/com/fs/company/service/ICompanyWithdrawDetailService.java

@@ -0,0 +1,24 @@
+package com.fs.company.service;
+
+import com.fs.company.param.CompanyWithdrawDetailAdminParam;
+import com.fs.company.vo.CompanyWithdrawDetailVO;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 销售端提现明细
+ */
+public interface ICompanyWithdrawDetailService {
+
+    Map<String, Object> summary(Long companyId);
+
+    List<CompanyWithdrawDetailVO> selectWithdrawDetailList(Long companyId);
+
+    List<CompanyWithdrawDetailVO> selectWithdrawDetailListAdmin(CompanyWithdrawDetailAdminParam param);
+
+    /**
+     * PRD:订单金额扣减类中 logs_type=6 在库中为成本返还入账,展示为与「扣减」一致的符号口径
+     */
+    void normalizeDisplayAmounts(List<CompanyWithdrawDetailVO> list);
+}

+ 58 - 13
fs-service/src/main/java/com/fs/company/service/impl/CompanyServiceImpl.java

@@ -1,6 +1,7 @@
 package com.fs.company.service.impl;
 
 import java.math.BigDecimal;
+import java.math.RoundingMode;
 import java.time.LocalTime;
 import java.util.*;
 import java.util.concurrent.TimeUnit;
@@ -20,6 +21,7 @@ import com.fs.company.mapper.*;
 import com.fs.company.param.CompanyLiveShowParam;
 import com.fs.company.param.CompanyParam;
 import com.fs.company.service.*;
+import com.fs.company.util.CompanyTuiMoneyCalc;
 import com.fs.company.vo.*;
 import com.fs.course.config.CourseConfig;
 import com.fs.course.config.RedPacketConfig;
@@ -33,8 +35,10 @@ import com.fs.his.domain.FsStorePayment;
 import com.fs.his.dto.InquiryConfigDTO;
 import com.fs.his.mapper.FsStoreOrderMapper;
 import com.fs.his.vo.OptionsVO;
+import com.fs.hisStore.domain.FsStoreAfterSalesScrm;
 import com.fs.hisStore.domain.FsStoreOrderScrm;
 import com.fs.hisStore.domain.FsStorePaymentScrm;
+import com.fs.hisStore.mapper.FsStoreAfterSalesScrmMapper;
 import com.fs.hisStore.mapper.FsStoreOrderScrmMapper;
 import com.fs.live.domain.LiveOrder;
 import com.fs.live.mapper.LiveOrderMapper;
@@ -60,6 +64,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.context.annotation.Lazy;
 import org.springframework.scheduling.annotation.Async;
 import org.springframework.stereotype.Service;
@@ -143,6 +148,15 @@ public class CompanyServiceImpl implements ICompanyService
     @Autowired
     CompanyBindGatewayMapper companyBindGatewayMapper;
 
+    @Value("${cloud_host.company_name}")
+    private String companyName;
+
+
+    private static final BigDecimal BJZM_PARTIAL_REFUND_COMMISSION_RATE = new BigDecimal("0.20");
+
+    @Autowired
+    private FsStoreAfterSalesScrmMapper fsStoreAfterSalesScrmMapper;
+
     @Override
     public List<CompanyVO> liveShowList(CompanyParam param) {
         return companyMapper.liveShowList(param);
@@ -162,9 +176,12 @@ public class CompanyServiceImpl implements ICompanyService
             if(company!=null){
                 String json =configService.selectConfigByKey("store.config");
                 com.fs.store.config.StoreConfig config= JSONUtil.toBean(json, com.fs.store.config.StoreConfig.class);
-                //支付金额-(订单金额*rate%)
+                //(实付金额 - 运费)× 费率
                 Double rate=config.getTuiMoneyRate()/100d;
-                BigDecimal tuiMoney=order.getPayPrice().subtract(order.getTotalPrice().multiply(new BigDecimal(rate)));
+                BigDecimal base = CompanyTuiMoneyCalc.commissionBase(
+                        CompanyTuiMoneyCalc.paidAmount(order),
+                        CompanyTuiMoneyCalc.freight(order));
+                BigDecimal tuiMoney = CompanyTuiMoneyCalc.multiplyRate(base, rate);
                 logger.info("写入公司推广佣金:"+tuiMoney);
                 company.setTuiMoney(company.getTuiMoney().add(tuiMoney));
                 companyMapper.updateCompany(company);
@@ -626,9 +643,12 @@ public class CompanyServiceImpl implements ICompanyService
             if(company!=null){
                 String json =configService.selectConfigByKey("his.store");
                 StoreConfig config= JSONUtil.toBean(json,StoreConfig.class);
-                //支付金额-(订单金额*rate%)
+                //(实付金额 - 运费)× 费率
                 Double rate=config.getTuiMoneyRate()/100d;
-                BigDecimal tuiMoney=order.getPayPrice().subtract(order.getTotalPrice().multiply(new BigDecimal(rate)));
+                BigDecimal base = CompanyTuiMoneyCalc.commissionBase(
+                        CompanyTuiMoneyCalc.paidAmount(order),
+                        CompanyTuiMoneyCalc.freight(order));
+                BigDecimal tuiMoney = CompanyTuiMoneyCalc.multiplyRate(base, rate);
                 logger.info("写入公司推广佣金:"+tuiMoney);
                 company.setTuiMoney(company.getTuiMoney().add(tuiMoney));
                 companyMapper.updateCompany(company);
@@ -648,10 +668,13 @@ public class CompanyServiceImpl implements ICompanyService
             if(company!=null){
                 String json =configService.selectConfigByKey("his.store");
                 StoreConfig config= JSONUtil.toBean(json,StoreConfig.class);
-                //支付金额-(订单金额*rate%)
+                //(实付金额 - 运费)× 费率
                 if (config.getTuiMoneyRate()!=null){
                     Double rate=config.getTuiMoneyRate()/100d;
-                    BigDecimal tuiMoney=order.getPayPrice().subtract(order.getTotalPrice().multiply(new BigDecimal(rate)));
+                    BigDecimal base = CompanyTuiMoneyCalc.commissionBase(
+                            CompanyTuiMoneyCalc.paidAmount(order),
+                            CompanyTuiMoneyCalc.freight(order));
+                    BigDecimal tuiMoney = CompanyTuiMoneyCalc.multiplyRate(base, rate);
                     logger.info("写入公司推广佣金:"+tuiMoney);
                     company.setTuiMoney(company.getTuiMoney().add(tuiMoney));
                     companyMapper.updateCompany(company);
@@ -708,11 +731,14 @@ public class CompanyServiceImpl implements ICompanyService
                 // 卓美,按照润天进行百分比进行分佣
                 String json =configService.selectConfigByKey("store.config");
                 com.fs.store.config.StoreConfig config= JSONUtil.toBean(json, com.fs.store.config.StoreConfig.class);
-                //支付金额-(订单金额*rate%)
+                //(实付金额 - 运费)× 费率;未配置费率时仍为应付 payPrice
                 BigDecimal tuiMoney = BigDecimal.ZERO;
                 if (config != null && config.getTuiMoneyRate() != null) {
                     Double rate = config.getTuiMoneyRate() / 100d;
-                    tuiMoney = order.getTotalPrice().multiply(new BigDecimal(rate));
+                    BigDecimal base = CompanyTuiMoneyCalc.commissionBase(
+                            CompanyTuiMoneyCalc.paidAmount(order),
+                            CompanyTuiMoneyCalc.freight(order));
+                    tuiMoney = CompanyTuiMoneyCalc.multiplyRate(base, rate);
                 } else {
                     tuiMoney = order.getPayPrice();
                 }
@@ -731,6 +757,7 @@ public class CompanyServiceImpl implements ICompanyService
                 FsStoreOrderScrm fsStoreOrder = new FsStoreOrderScrm();
                 fsStoreOrder.setId(order.getId());
                 fsStoreOrder.setTuiMoneyStatus(1);
+                fsStoreOrder.setTuiMoney(tuiMoney);
                 storeOrderScrmMapper.updateFsStoreOrder(fsStoreOrder);
             }
         }
@@ -744,11 +771,14 @@ public class CompanyServiceImpl implements ICompanyService
                 // 卓美,按照润天进行百分比进行分佣
                 String json =configService.selectConfigByKey("store.config");
                 com.fs.store.config.StoreConfig config= JSONUtil.toBean(json, com.fs.store.config.StoreConfig.class);
-                //支付金额-(订单金额*rate%)
+                //(实付金额 - 运费)× 费率;未配置费率时仍为 payPrice
                 BigDecimal tuiMoney = BigDecimal.ZERO;
                 if (config != null && config.getTuiMoneyRate() != null) {
                     double rate = config.getTuiMoneyRate() / 100d;
-                    tuiMoney = order.getTotalPrice().multiply(new BigDecimal(rate));
+                    BigDecimal base = CompanyTuiMoneyCalc.commissionBase(
+                            CompanyTuiMoneyCalc.paidAmount(order),
+                            CompanyTuiMoneyCalc.freight(order));
+                    tuiMoney = CompanyTuiMoneyCalc.multiplyRate(base, rate);
                 } else {
                     tuiMoney = order.getPayPrice();
                 }
@@ -806,14 +836,29 @@ public class CompanyServiceImpl implements ICompanyService
         if(order.getCompanyId()>0){
             Company company=companyMapper.selectCompanyByIdForUpdate(order.getCompanyId());
             if(company!=null){
-                company.setMoney(company.getMoney().subtract(order.getTuiMoney()));
-                company.setTuiMoney(company.getTuiMoney().subtract(order.getTuiMoney()));
+                BigDecimal clawback = order.getTuiMoney();
+                BigDecimal logMoney = order.getTuiMoney().multiply(new BigDecimal(-1));
+
+                // 260415 卓美财务需求,希望部分退款:部分退款金额 * 20%
+                if ("北京卓美".equals(companyName)) {
+                    FsStoreAfterSalesScrm after = fsStoreAfterSalesScrmMapper.selectFsStoreAfterSalesByOrderCode(order.getOrderCode());
+                    BigDecimal payMoney = order.getPayMoney() != null ? order.getPayMoney() : BigDecimal.ZERO;
+                    BigDecimal refundAmount = after != null && after.getRefundAmount() != null ? after.getRefundAmount() : null;
+                    if (refundAmount != null && payMoney.compareTo(refundAmount) != 0) {
+                        // 部分退款:公司扣回金额 / 日志金额 = -(退款金额 × 20%)
+                        clawback = refundAmount.multiply(BJZM_PARTIAL_REFUND_COMMISSION_RATE).setScale(2, RoundingMode.HALF_UP);
+                        logMoney = clawback.negate();
+                    }
+                }
+
+                company.setMoney(company.getMoney().subtract(clawback));
+                company.setTuiMoney(company.getTuiMoney().subtract(clawback));
                 companyMapper.updateCompany(company);
                 //写入日志
                 CompanyMoneyLogs log=new CompanyMoneyLogs();
                 log.setCompanyId(order.getCompanyId());
                 log.setRemark("订单佣金退款");
-                log.setMoney(order.getTuiMoney().multiply(new BigDecimal(-1)));
+                log.setMoney(logMoney);
                 log.setLogsType(4);
                 log.setBalance(company.getMoney());
                 log.setCreateTime(new Date());

+ 64 - 0
fs-service/src/main/java/com/fs/company/service/impl/CompanyWithdrawDetailServiceImpl.java

@@ -0,0 +1,64 @@
+package com.fs.company.service.impl;
+
+import com.fs.company.mapper.CompanyWithdrawDetailMapper;
+import com.fs.company.param.CompanyWithdrawDetailAdminParam;
+import com.fs.company.service.ICompanyService;
+import com.fs.company.service.ICompanyWithdrawDetailService;
+import com.fs.company.vo.CompanyWithdrawDetailVO;
+import com.fs.company.domain.Company;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.math.BigDecimal;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@Service
+public class CompanyWithdrawDetailServiceImpl implements ICompanyWithdrawDetailService {
+
+    @Autowired
+    private CompanyWithdrawDetailMapper companyWithdrawDetailMapper;
+
+    @Autowired
+    private ICompanyService companyService;
+
+    @Override
+    public Map<String, Object> summary(Long companyId) {
+        Company company = companyService.selectCompanyById(companyId);
+        Map<String, Object> m = new HashMap<>(4);
+        if (company != null) {
+            m.put("companyName", company.getCompanyName());
+            m.put("withdrawableMoney", company.getMoney() != null ? company.getMoney() : BigDecimal.ZERO);
+        } else {
+            m.put("companyName", "");
+            m.put("withdrawableMoney", BigDecimal.ZERO);
+        }
+        return m;
+    }
+
+    @Override
+    public List<CompanyWithdrawDetailVO> selectWithdrawDetailList(Long companyId) {
+        return companyWithdrawDetailMapper.selectWithdrawDetailList(companyId);
+    }
+
+    @Override
+    public List<CompanyWithdrawDetailVO> selectWithdrawDetailListAdmin(CompanyWithdrawDetailAdminParam param) {
+        if (param == null) {
+            param = new CompanyWithdrawDetailAdminParam();
+        }
+        return companyWithdrawDetailMapper.selectWithdrawDetailListAdmin(param);
+    }
+
+    @Override
+    public void normalizeDisplayAmounts(List<CompanyWithdrawDetailVO> list) {
+        if (list == null) {
+            return;
+        }
+        for (CompanyWithdrawDetailVO vo : list) {
+            if (vo.getLogsType() != null && vo.getLogsType() == 6 && vo.getAmount() != null) {
+                vo.setAmount(vo.getAmount().negate());
+            }
+        }
+    }
+}

+ 90 - 0
fs-service/src/main/java/com/fs/company/util/CompanyTuiMoneyCalc.java

@@ -0,0 +1,90 @@
+package com.fs.company.util;
+
+import com.fs.his.domain.FsStoreOrder;
+import com.fs.hisStore.domain.FsStoreOrderScrm;
+import com.fs.live.domain.LiveOrder;
+
+import java.math.BigDecimal;
+
+/**
+ * 分公司佣金/入账基数:(实付金额 - 运费)× 费率
+ */
+public final class CompanyTuiMoneyCalc {
+
+    private CompanyTuiMoneyCalc() {
+    }
+
+    /** 实付金额:优先取 payMoney,否则 payPrice */
+    public static BigDecimal paidAmount(FsStoreOrder order) {
+        if (order == null) {
+            return BigDecimal.ZERO;
+        }
+        if (order.getPayMoney() != null) {
+            return order.getPayMoney();
+        }
+        return order.getPayPrice() != null ? order.getPayPrice() : BigDecimal.ZERO;
+    }
+
+    /** 运费:his 订单取 freightPrice */
+    public static BigDecimal freight(FsStoreOrder order) {
+        if (order == null || order.getFreightPrice() == null) {
+            return BigDecimal.ZERO;
+        }
+        return order.getFreightPrice();
+    }
+
+    public static BigDecimal paidAmount(FsStoreOrderScrm order) {
+        if (order == null) {
+            return BigDecimal.ZERO;
+        }
+        if (order.getPayPrice() != null) {
+            return order.getPayPrice();
+        }
+        return order.getPayMoney() != null ? order.getPayMoney() : BigDecimal.ZERO;
+    }
+
+    /** 运费:商城 SCRM 订单取用户实付邮费 payPostage */
+    public static BigDecimal freight(FsStoreOrderScrm order) {
+        if (order == null || order.getPayPostage() == null) {
+            return BigDecimal.ZERO;
+        }
+
+        return order.getPayPostage();
+    }
+
+    public static BigDecimal paidAmount(LiveOrder order) {
+        if (order == null) {
+            return BigDecimal.ZERO;
+        }
+        if (order.getPayMoney() != null) {
+            return order.getPayMoney();
+        }
+        return order.getPayPrice() != null ? order.getPayPrice() : BigDecimal.ZERO;
+    }
+
+    /** 运费:直播单优先 payPostage,否则 totalPostage */
+    public static BigDecimal freight(LiveOrder order) {
+        if (order == null) {
+            return BigDecimal.ZERO;
+        }
+        if (order.getPayPostage() != null) {
+            return order.getPayPostage();
+        }
+        if (order.getTotalPostage() != null) {
+            return order.getTotalPostage();
+        }
+        return BigDecimal.ZERO;
+    }
+
+    /** 计佣基数:max(0, 实付 - 运费) */
+    public static BigDecimal commissionBase(BigDecimal paid, BigDecimal freightAmt) {
+        BigDecimal f = freightAmt != null ? freightAmt : BigDecimal.ZERO;
+        BigDecimal p = paid != null ? paid : BigDecimal.ZERO;
+        BigDecimal b = p.subtract(f);
+        return b.compareTo(BigDecimal.ZERO) < 0 ? BigDecimal.ZERO : b;
+    }
+
+    public static BigDecimal multiplyRate(BigDecimal base, double rateFraction) {
+        return base.multiply(BigDecimal.valueOf(rateFraction));
+    }
+}

+ 57 - 0
fs-service/src/main/java/com/fs/company/vo/CompanyWithdrawDetailVO.java

@@ -0,0 +1,57 @@
+package com.fs.company.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fs.common.annotation.Excel;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 销售端-提现明细分页/导出
+ */
+@Data
+public class CompanyWithdrawDetailVO implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /** 内部用:原始 logs_type */
+    private Integer logsType;
+
+    private Long logsId;
+
+    @Excel(name = "序号", sort = 1)
+    private Integer serialNo;
+
+    @Excel(name = "公司名称", sort = 2)
+    private String companyName;
+
+    @Excel(name = "所属销售", sort = 3)
+    private String salesName;
+
+    @Excel(name = "订单号", sort = 4)
+    private String orderCode;
+
+    @Excel(name = "交易单号", sort = 5)
+    private String tradeNo;
+
+    @Excel(name = "订单状态", sort = 6)
+    private String orderStatusText;
+
+    /** 订单原始状态码,与商城订单列表字典 store_order_status 一致;非订单行无此值 */
+    private Integer orderStatus;
+
+    @Excel(name = "售后状态", sort = 7)
+    private String afterSalesStatusText;
+
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @Excel(name = "订单记录时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss", sort = 8)
+    private Date recordTime;
+
+    @Excel(name = "明细类型", sort = 9)
+    private String detailTypeText;
+
+    @Excel(name = "金额", sort = 10)
+    private BigDecimal amount;
+}

+ 1 - 1
fs-service/src/main/java/com/fs/his/config/StoreConfig.java

@@ -11,7 +11,7 @@ public class StoreConfig implements Serializable {
 
     private Integer payRate;//支付比例
     private Integer unPayTime;//未支付订单自动取消时间
-    private Integer tuiMoneyRate;//公司佣金比例 支付金额-(订单金额*rate%)
+    private Integer tuiMoneyRate;// 公司佣金比例%:(实付金额-运费)× rate%
     private Integer deductMoneyRate;
     private Integer createMoneyRate;//公司制单金额最低比例
     private Integer storeAfterSalesDay;//已完成订单售后有效天数

+ 1 - 1
fs-service/src/main/java/com/fs/hisStore/config/StoreConfig.java

@@ -15,7 +15,7 @@ public class StoreConfig implements Serializable {
     private Integer payRate;//支付比例
     private Integer payAmount;//货到付款自定义金额
     private Integer unPayTime;//未支付订单自动取消时间
-    private Integer tuiMoneyRate;//公司佣金比例 支付金额-(订单金额*rate%)
+    private Integer tuiMoneyRate;// 公司佣金比例%:(实付金额-运费)× rate%
     private Integer createMoneyRate;//公司制单金额最低比例
     private Integer storeAfterSalesDay;//已完成订单售后有效天数
     private String refundConsignee;

+ 1 - 1
fs-service/src/main/java/com/fs/store/config/StoreConfig.java

@@ -15,7 +15,7 @@ public class StoreConfig implements Serializable {
     private Integer payRate;//支付比例
     private Integer payAmount;//货到付款自定义金额
     private Integer unPayTime;//未支付订单自动取消时间
-    private Integer tuiMoneyRate;//公司佣金比例 支付金额-(订单金额*rate%)
+    private Integer tuiMoneyRate;// 公司佣金比例%:(实付金额-运费)× rate%
     private Integer createMoneyRate;//公司制单金额最低比例
     private Integer storeAfterSalesDay;//已完成订单售后有效天数
     private String refundConsignee;

+ 7 - 3
fs-service/src/main/resources/application-druid-bjzm-test.yml

@@ -137,13 +137,17 @@ spring:
                         config:
                             multi-statement-allow: true
 rocketmq:
-    name-server: localhost:8080
+    name-server: rmq-1gawpw9p3.rocketmq.gz.public.tencenttdmq.com:8080
     producer:
-        group: my-producer-group
+        group: course-finish-group
         access-key: ak1gawpw9p33db21b4aaaf2 # 替换为实际的 accessKey
         secret-key: sk90be66c23f39ad85 # 替换为实际的 secretKey
     consumer:
-        group: common-group
+        #        group: common-group #这里有两个group 打标签的mq是course-finish-group 直播的是common-group
+        #        access-key: ak1gawpw9p33db21b4aaaf2 # 替换为实际的 accessKey
+        #        secret-key: sk90be66c23f39ad85 # 替换为实际的 secretKey
+        group: course-finish-group #这里有两个group 打标签的mq是course-finish-group 直播的是common-group
+        TOPIC: course-finish-note
         access-key: ak1gawpw9p33db21b4aaaf2 # 替换为实际的 accessKey
         secret-key: sk90be66c23f39ad85 # 替换为实际的 secretKey
 openIM:

+ 5 - 2
fs-service/src/main/resources/application-druid-bjzm.yml

@@ -217,11 +217,14 @@ spring:
 rocketmq:
     name-server: rmq-1gawpw9p3.rocketmq.gz.qcloud.tencenttdmq.com:8080
     producer:
-        group: my-producer-group
+        group: course-finish-group
         access-key: ak1gawpw9p33db21b4aaaf2 # 替换为实际的 accessKey
         secret-key: sk90be66c23f39ad85 # 替换为实际的 secretKey
     consumer:
-        group: common-group
+#        group: common-group #这里有两个group 打标签的mq是course-finish-group 直播的是common-group
+#        access-key: ak1gawpw9p33db21b4aaaf2 # 替换为实际的 accessKey
+#        secret-key: sk90be66c23f39ad85 # 替换为实际的 secretKey
+        group: course-finish-group #这里有两个group 打标签的mq是course-finish-group 直播的是common-group
         access-key: ak1gawpw9p33db21b4aaaf2 # 替换为实际的 accessKey
         secret-key: sk90be66c23f39ad85 # 替换为实际的 secretKey
 openIM:

+ 177 - 0
fs-service/src/main/resources/mapper/company/CompanyWithdrawDetailMapper.xml

@@ -0,0 +1,177 @@
+<?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.fs.company.mapper.CompanyWithdrawDetailMapper">
+
+    <sql id="withdrawDetailSelectBody">
+        SELECT
+            l.logs_id AS logsId,
+            l.logs_type AS logsType,
+            c.company_name AS companyName,
+            CASE
+                WHEN IFNULL(l.type, 0) = 0 THEN cu.nick_name
+                WHEN IFNULL(l.type, 0) = 1 THEN cul.nick_name
+                ELSE NULL
+            END AS salesName,
+            CASE
+                WHEN IFNULL(l.type, 0) = 0 THEN o.order_code
+                WHEN IFNULL(l.type, 0) = 1 THEN lo.order_code
+                ELSE NULL
+            END AS orderCode,
+            CASE
+                WHEN IFNULL(l.type, 0) = 0 THEN p.bank_transaction_id
+                WHEN IFNULL(l.type, 0) = 1 THEN lop.bank_transaction_id
+                ELSE NULL
+            END AS tradeNo,
+            CASE
+                WHEN IFNULL(l.type, 0) = 0 AND o.id IS NOT NULL THEN
+                    CASE o.status
+                        WHEN -3 THEN '已取消'
+                        WHEN -2 THEN '已退款'
+                        WHEN -1 THEN '退款中'
+                        WHEN 0 THEN '待支付'
+                        WHEN 1 THEN '待发货'
+                        WHEN 2 THEN '待收货'
+                        WHEN 3 THEN '交易完成'
+                        ELSE CAST(o.status AS CHAR)
+                    END
+                WHEN IFNULL(l.type, 0) = 1 AND lo.order_id IS NOT NULL THEN
+                    CASE lo.status
+                        WHEN -1 THEN '退款中'
+                        WHEN -2 THEN '已退款'
+                        WHEN 0 THEN '已取消'
+                        WHEN 1 THEN '待支付'
+                        WHEN 2 THEN '待发货'
+                        WHEN 3 THEN '待收货'
+                        WHEN 4 THEN '交易完成'
+                        WHEN 5 THEN '交易完成'
+                        WHEN 6 THEN '被拆分'
+                        ELSE CAST(lo.status AS CHAR)
+                    END
+                ELSE '-'
+            END AS orderStatusText,
+            CASE
+                WHEN IFNULL(l.type, 0) = 0 AND o.id IS NOT NULL THEN o.status
+                WHEN IFNULL(l.type, 0) = 1 AND lo.order_id IS NOT NULL THEN
+                    CASE lo.status
+                        WHEN -1 THEN -1
+                        WHEN -2 THEN -2
+                        WHEN 0 THEN -3
+                        WHEN 1 THEN 0
+                        WHEN 2 THEN 1
+                        WHEN 3 THEN 2
+                        WHEN 4 THEN 3
+                        WHEN 5 THEN 3
+                        ELSE lo.status
+                    END
+                ELSE NULL
+            END AS orderStatus,
+            CASE
+                WHEN a.id IS NOT NULL
+                    AND (
+                        l.logs_type IN (4, 6)
+                        OR (l.logs_type = 5 AND IFNULL(l.remark, '') LIKE '%退款%')
+                        OR (l.logs_type = 4 AND l.money &lt; 0)
+                    )
+                THEN
+                    CASE a.service_type
+                        WHEN 0 THEN '仅退款'
+                        WHEN 1 THEN '退货退款'
+                        ELSE '-'
+                    END
+                ELSE '-'
+            END AS afterSalesStatusText,
+            l.create_time AS recordTime,
+            CASE l.logs_type
+                WHEN 1 THEN '总公司充值'
+                WHEN 2 THEN '总公司扣款'
+                WHEN 7 THEN '分公司提现'
+                WHEN 8 THEN '总公司驳回提现'
+                WHEN 3 THEN '订单金额入账'
+                WHEN 6 THEN '订单金额扣减'
+                WHEN 4 THEN CASE WHEN l.money &gt;= 0 THEN '订单金额入账' ELSE '订单金额扣减' END
+                WHEN 5 THEN CASE
+                    WHEN IFNULL(l.remark, '') LIKE '%退款%' OR l.money &lt; 0 THEN '订单金额扣减'
+                    ELSE '订单金额入账'
+                END
+                ELSE '其它'
+            END AS detailTypeText,
+            l.money AS amount
+        FROM company_money_logs l
+        INNER JOIN company c ON c.company_id = l.company_id
+        LEFT JOIN fs_store_order_scrm o ON (
+            IFNULL(l.type, 0) = 0
+            AND l.logs_type IN (3, 4, 5, 6)
+            AND o.id = CAST(NULLIF(l.business_id, '') AS UNSIGNED)
+            AND o.company_id = l.company_id
+        )
+        LEFT JOIN live_order lo ON (
+            IFNULL(l.type, 0) = 1
+            AND l.logs_type IN (3, 4, 5, 6)
+            AND lo.order_id = CAST(NULLIF(l.business_id, '') AS UNSIGNED)
+            AND lo.company_id = l.company_id
+        )
+        LEFT JOIN company_user cu ON cu.user_id = o.company_user_id
+        LEFT JOIN company_user cul ON cul.user_id = lo.company_user_id
+        LEFT JOIN fs_store_payment_scrm p ON (
+            p.business_code = o.order_code AND p.status = 1 AND IFNULL(l.type, 0) = 0
+        )
+        LEFT JOIN live_order_payment lop ON (
+            lop.business_code = lo.order_code AND lop.status = 1 AND IFNULL(l.type, 0) = 1
+        )
+        LEFT JOIN (
+            SELECT a1.*
+            FROM fs_store_after_sales_scrm a1
+            INNER JOIN (
+                SELECT order_code AS oc, MAX(id) AS mid
+                FROM fs_store_after_sales_scrm
+                WHERE IFNULL(is_del, 0) = 0 AND status = 3
+                GROUP BY order_code
+            ) am ON a1.id = am.mid
+        ) a ON a.order_code = COALESCE(o.order_code, lo.order_code)
+    </sql>
+
+    <sql id="withdrawDetailBaseWhere">
+        WHERE l.create_time &gt;= '2026-03-27 00:00:00'
+          AND l.logs_type IN (1, 2, 3, 4, 5, 6, 7, 8)
+          AND NOT (l.logs_type = 5 AND IFNULL(l.remark, '') = '订单佣金冻结')
+    </sql>
+
+    <select id="selectWithdrawDetailList" resultType="com.fs.company.vo.CompanyWithdrawDetailVO">
+        <include refid="withdrawDetailSelectBody"/>
+        <include refid="withdrawDetailBaseWhere"/>
+          AND l.company_id = #{companyId}
+        ORDER BY l.create_time DESC, l.logs_id DESC
+    </select>
+
+    <select id="selectWithdrawDetailListAdmin" resultType="com.fs.company.vo.CompanyWithdrawDetailVO">
+        <include refid="withdrawDetailSelectBody"/>
+        <include refid="withdrawDetailBaseWhere"/>
+        <if test="p.companyIds != null and p.companyIds.size() &gt; 0">
+            AND l.company_id IN
+            <foreach collection="p.companyIds" item="cid" open="(" separator="," close=")">
+                #{cid}
+            </foreach>
+        </if>
+        <if test="p.detailTypes != null and p.detailTypes.size() &gt; 0">
+            AND (
+            <foreach collection="p.detailTypes" item="dt" separator=" OR ">
+                <choose>
+                    <when test="dt == 1">
+                        (l.logs_type = 3 OR (l.logs_type = 4 AND l.money &gt;= 0)
+                        OR (l.logs_type = 5 AND IFNULL(l.remark, '') NOT LIKE '%退款%' AND l.money &gt;= 0))
+                    </when>
+                    <when test="dt == 2">
+                        (l.logs_type = 6 OR (l.logs_type = 4 AND l.money &lt; 0)
+                        OR (l.logs_type = 5 AND (IFNULL(l.remark, '') LIKE '%退款%' OR l.money &lt; 0)))
+                    </when>
+                    <when test="dt == 3">l.logs_type = 1</when>
+                    <when test="dt == 4">l.logs_type = 2</when>
+                    <when test="dt == 5">l.logs_type = 7</when>
+                    <when test="dt == 6">l.logs_type = 8</when>
+                </choose>
+            </foreach>
+            )
+        </if>
+        ORDER BY l.create_time DESC, l.logs_id DESC
+    </select>
+</mapper>