Jelajahi Sumber

feat(finance): 实现企业上账、下账及余额变更功能

- 新增企业账户上账(recharge)与下账(deduct)接口及业务实现
- 实现通用企业余额变更(changeBalance)接口,支持余额调整及流水记录
- 丰富企业信息及用户企业关系数据模型,增加余额及资金相关字段
- 后端查询语句调整,支持余额等财务字段数据的查询及排序
- 新增余额变更操作时的日志记录与流水号生成
- 前端企业信息页面展示当前余额、累计充值、当前盈利、累计盈利等财务指标
- 添加上账、下账弹窗与表单,支持操作输入与表单校验
- 用户企业关系列表新增余额、累计消费、欠费等字段及排序功能
- 优化分页组件新增排序事件支持,大表格支持按财务字段自定义排序展示
- 统一金额格式化显示样式,提升界面信息展示体验
SheepHy 1 hari lalu
induk
melakukan
53dd112fd4

+ 49 - 0
src/main/java/com/zsElectric/boot/business/controller/FirmAccountLogController.java

@@ -0,0 +1,49 @@
+package com.zsElectric.boot.business.controller;
+
+import com.zsElectric.boot.business.service.FirmAccountLogService;
+import lombok.RequiredArgsConstructor;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import com.zsElectric.boot.business.model.query.FirmAccountLogQuery;
+import com.zsElectric.boot.business.model.vo.FirmAccountLogVO;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.zsElectric.boot.core.web.PageResult;
+import com.zsElectric.boot.core.web.Result;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import io.swagger.v3.oas.annotations.Operation;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+
+/**
+ * 企业资金流水前端控制层
+ *
+ * @author zsElectric
+ * @since 2025-12-26
+ */
+@Tag(name = "企业资金流水接口")
+@RestController
+@RequestMapping("/api/v1/firmAccountLog")
+@RequiredArgsConstructor
+public class FirmAccountLogController {
+
+    private final FirmAccountLogService firmAccountLogService;
+
+    @Operation(summary = "企业资金流水分页列表")
+    @GetMapping("/page")
+    @PreAuthorize("@ss.hasPerm('business:firmAccountLog:query')")
+    public PageResult<FirmAccountLogVO> getFirmAccountLogPage(FirmAccountLogQuery queryParams) {
+        IPage<FirmAccountLogVO> result = firmAccountLogService.getFirmAccountLogPage(queryParams);
+        return PageResult.success(result);
+    }
+
+    @Operation(summary = "删除企业资金流水")
+    @DeleteMapping("/{ids}")
+    @PreAuthorize("@ss.hasPerm('business:firmAccountLog:delete')")
+    public Result<Void> deleteFirmAccountLogs(
+            @Parameter(description = "企业资金流水ID,多个以英文逗号(,)分割") @PathVariable String ids
+    ) {
+        boolean result = firmAccountLogService.deleteFirmAccountLogs(ids);
+        return Result.judge(result);
+    }
+}

+ 39 - 0
src/main/java/com/zsElectric/boot/business/controller/FirmInfoController.java

@@ -2,10 +2,13 @@ package com.zsElectric.boot.business.controller;
 
 import com.zsElectric.boot.business.service.FirmInfoService;
 import com.zsElectric.boot.business.converter.FirmInfoConverter;
+import com.zsElectric.boot.common.annotation.Log;
+import com.zsElectric.boot.common.enums.LogModuleEnum;
 import lombok.RequiredArgsConstructor;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 import com.zsElectric.boot.business.model.form.FirmInfoForm;
+import com.zsElectric.boot.business.model.form.FirmBalanceChangeForm;
 import com.zsElectric.boot.business.model.query.FirmInfoQuery;
 import com.zsElectric.boot.business.model.vo.FirmInfoVO;
 import com.baomidou.mybatisplus.core.metadata.IPage;
@@ -19,6 +22,7 @@ import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
 import jakarta.validation.Valid;
 
+import java.math.BigDecimal;
 import java.util.List;
 
 /**
@@ -90,4 +94,39 @@ public class FirmInfoController  {
         boolean result = firmInfoService.deleteFirmInfos(ids);
         return Result.judge(result);
     }
+
+    @Operation(summary = "企业上账(充值)")
+    @PostMapping("/recharge")
+    @PreAuthorize("@ss.hasPerm('business:firmInfo:recharge')")
+    @Log(value = "企业上账", module = LogModuleEnum.OTHER)
+    public Result<Void> recharge(
+            @Parameter(description = "企业ID") @RequestParam Long firmId,
+            @Parameter(description = "上账金额") @RequestParam BigDecimal amount,
+            @Parameter(description = "备注") @RequestParam(required = false) String remark
+    ) {
+        boolean result = firmInfoService.recharge(firmId, amount, remark);
+        return Result.judge(result);
+    }
+
+    @Operation(summary = "企业下账(扣款)")
+    @PostMapping("/deduct")
+    @PreAuthorize("@ss.hasPerm('business:firmInfo:deduct')")
+    @Log(value = "企业下账", module = LogModuleEnum.OTHER)
+    public Result<Void> deduct(
+            @Parameter(description = "企业ID") @RequestParam Long firmId,
+            @Parameter(description = "下账金额") @RequestParam BigDecimal amount,
+            @Parameter(description = "备注") @RequestParam(required = false) String remark
+    ) {
+        boolean result = firmInfoService.deduct(firmId, amount, remark);
+        return Result.judge(result);
+    }
+
+    @Operation(summary = "企业余额变更(通用)")
+    @PostMapping("/changeBalance")
+    @PreAuthorize("@ss.hasPerm('business:firmInfo:recharge')")
+    @Log(value = "企业余额变更", module = LogModuleEnum.OTHER)
+    public Result<Void> changeBalance(@RequestBody @Valid FirmBalanceChangeForm form) {
+        boolean result = firmInfoService.changeBalance(form);
+        return Result.judge(result);
+    }
 }

+ 29 - 0
src/main/java/com/zsElectric/boot/business/mapper/FirmAccountLogMapper.java

@@ -0,0 +1,29 @@
+package com.zsElectric.boot.business.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.zsElectric.boot.business.model.entity.FirmAccountLog;
+import com.zsElectric.boot.business.model.query.FirmAccountLogQuery;
+import com.zsElectric.boot.business.model.vo.FirmAccountLogVO;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+/**
+ * 企业资金流水Mapper接口
+ *
+ * @author zsElectric
+ * @since 2025-12-26
+ */
+@Mapper
+public interface FirmAccountLogMapper extends BaseMapper<FirmAccountLog> {
+
+    /**
+     * 分页查询企业资金流水
+     *
+     * @param page 分页对象
+     * @param queryParams 查询条件
+     * @return 分页结果
+     */
+    IPage<FirmAccountLogVO> getFirmAccountLogPage(Page<FirmAccountLogVO> page, @Param("queryParams") FirmAccountLogQuery queryParams);
+}

+ 122 - 0
src/main/java/com/zsElectric/boot/business/model/entity/FirmAccountLog.java

@@ -0,0 +1,122 @@
+package com.zsElectric.boot.business.model.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableLogic;
+import com.baomidou.mybatisplus.annotation.TableName;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+/**
+ * 企业资金流水实体对象
+ *
+ * @author zsElectric
+ * @since 2025-12-26
+ */
+@Getter
+@Setter
+@TableName("c_firm_account_log")
+@Schema(description = "企业资金流水实体对象")
+public class FirmAccountLog implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键ID
+     */
+    @TableId(value = "id", type = IdType.AUTO)
+    @Schema(description = "主键ID")
+    private Long id;
+
+    /**
+     * 企业ID
+     */
+    @Schema(description = "企业ID")
+    private Long firmId;
+
+    /**
+     * 项目名称
+     */
+    @Schema(description = "项目名称")
+    private String projectName;
+
+    /**
+     * 交易名称
+     */
+    @Schema(description = "交易名称")
+    private String name;
+
+    /**
+     * 流水号
+     */
+    @Schema(description = "流水号")
+    private String serialNo;
+
+    /**
+     * 收支类型(1-转入/上账 2-转出/下账)
+     */
+    @Schema(description = "收支类型(1-转入/上账 2-转出/下账)")
+    private Integer incomeType;
+
+    /**
+     * 改变前金额
+     */
+    @Schema(description = "改变前金额")
+    private BigDecimal beforeChange;
+
+    /**
+     * 改变后金额
+     */
+    @Schema(description = "改变后金额")
+    private BigDecimal afterChange;
+
+    /**
+     * 改变金额
+     */
+    @Schema(description = "改变金额")
+    private BigDecimal moneyChange;
+
+    /**
+     * 改变类型(1-充值 2-提现 3-签到 4-日利宝取出 5-团队升级 6-项目利息 7-下级返利 8-认购 9-日利宝存入 10-日利宝收益 11-红包奖励 12-项目到期返还本金 13-注册赠送)
+     */
+    @Schema(description = "改变类型(1-充值 2-提现 3-其他...)")
+    private Integer changeType;
+
+    /**
+     * 关联用户ID(当改变类型为下级分润的时候填写)
+     */
+    @Schema(description = "关联用户ID")
+    private Long childId;
+
+    /**
+     * 备注
+     */
+    @Schema(description = "备注")
+    private String remark;
+
+    /**
+     * 创建时间
+     */
+    @Schema(description = "创建时间")
+    private LocalDateTime createTime;
+
+    /**
+     * 更新时间
+     */
+    @Schema(description = "更新时间")
+    private LocalDateTime updateTime;
+
+    /**
+     * 逻辑删除标识(0-未删除 1-已删除)
+     */
+    @TableLogic
+    @Schema(description = "逻辑删除标识(0-未删除 1-已删除)")
+    private Integer isDeleted;
+}

+ 45 - 0
src/main/java/com/zsElectric/boot/business/model/form/FirmBalanceChangeForm.java

@@ -0,0 +1,45 @@
+package com.zsElectric.boot.business.model.form;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.DecimalMin;
+import jakarta.validation.constraints.NotNull;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.math.BigDecimal;
+
+/**
+ * 企业余额变更表单对象
+ *
+ * @author zsElectric
+ * @since 2025-12-26
+ */
+@Getter
+@Setter
+@Schema(description = "企业余额变更表单对象")
+public class FirmBalanceChangeForm implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    @Schema(description = "企业ID", requiredMode = Schema.RequiredMode.REQUIRED)
+    @NotNull(message = "企业ID不能为空")
+    private Long firmId;
+
+    @Schema(description = "变更金额(正数)", requiredMode = Schema.RequiredMode.REQUIRED)
+    @NotNull(message = "变更金额不能为空")
+    @DecimalMin(value = "0.01", message = "变更金额必须大于0")
+    private BigDecimal amount;
+
+    @Schema(description = "收支类型(1-上账/转入 2-下账/转出)", requiredMode = Schema.RequiredMode.REQUIRED)
+    @NotNull(message = "收支类型不能为空")
+    private Integer incomeType;
+
+    @Schema(description = "改变类型(1-充值 2-提现 3-其他)")
+    private Integer changeType;
+
+    @Schema(description = "备注")
+    private String remark;
+}

+ 41 - 0
src/main/java/com/zsElectric/boot/business/model/query/FirmAccountLogQuery.java

@@ -0,0 +1,41 @@
+package com.zsElectric.boot.business.model.query;
+
+import com.zsElectric.boot.common.base.BasePageQuery;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.time.LocalDateTime;
+
+/**
+ * 企业资金流水分页查询对象
+ *
+ * @author zsElectric
+ * @since 2025-12-26
+ */
+@Schema(description = "企业资金流水查询对象")
+@Getter
+@Setter
+public class FirmAccountLogQuery extends BasePageQuery {
+
+    @Schema(description = "企业ID")
+    private Long firmId;
+
+    @Schema(description = "收支类型(1-转入/上账 2-转出/下账)")
+    private Integer incomeType;
+
+    @Schema(description = "改变类型")
+    private Integer changeType;
+
+    @Schema(description = "流水号")
+    private String serialNo;
+
+    @Schema(description = "交易名称")
+    private String name;
+
+    @Schema(description = "开始时间")
+    private LocalDateTime startTime;
+
+    @Schema(description = "结束时间")
+    private LocalDateTime endTime;
+}

+ 6 - 0
src/main/java/com/zsElectric/boot/business/model/query/UserFirmQuery.java

@@ -23,4 +23,10 @@ public class UserFirmQuery extends BasePageQuery {
 
     @Schema(description = "身份类型 1 管理员 2普通员工")
     private Integer type;
+
+    @Schema(description = "排序字段")
+    private String sortField;
+
+    @Schema(description = "排序方式:asc/desc")
+    private String sortOrder;
 }

+ 67 - 0
src/main/java/com/zsElectric/boot/business/model/vo/FirmAccountLogVO.java

@@ -0,0 +1,67 @@
+package com.zsElectric.boot.business.model.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+/**
+ * 企业资金流水视图对象
+ *
+ * @author zsElectric
+ * @since 2025-12-26
+ */
+@Getter
+@Setter
+@Schema(description = "企业资金流水视图对象")
+public class FirmAccountLogVO implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    @Schema(description = "主键ID")
+    private Long id;
+
+    @Schema(description = "企业ID")
+    private Long firmId;
+
+    @Schema(description = "企业名称")
+    private String firmName;
+
+    @Schema(description = "项目名称")
+    private String projectName;
+
+    @Schema(description = "交易名称")
+    private String name;
+
+    @Schema(description = "流水号")
+    private String serialNo;
+
+    @Schema(description = "收支类型(1-转入/上账 2-转出/下账)")
+    private Integer incomeType;
+
+    @Schema(description = "改变前金额")
+    private BigDecimal beforeChange;
+
+    @Schema(description = "改变后金额")
+    private BigDecimal afterChange;
+
+    @Schema(description = "改变金额")
+    private BigDecimal moneyChange;
+
+    @Schema(description = "改变类型")
+    private Integer changeType;
+
+    @Schema(description = "关联用户ID")
+    private Long childId;
+
+    @Schema(description = "备注")
+    private String remark;
+
+    @Schema(description = "创建时间")
+    private LocalDateTime createTime;
+}

+ 9 - 1
src/main/java/com/zsElectric/boot/business/model/vo/FirmInfoVO.java

@@ -2,11 +2,11 @@ package com.zsElectric.boot.business.model.vo;
 
 import java.io.Serial;
 import java.io.Serializable;
+import java.math.BigDecimal;
 import java.time.LocalDateTime;
 import io.swagger.v3.oas.annotations.media.Schema;
 import lombok.Getter;
 import lombok.Setter;
-import java.time.LocalDateTime;
 
 /**
  * 企业信息视图对象
@@ -34,6 +34,14 @@ public class FirmInfoVO implements Serializable {
     private String number;
     @Schema(description = "状态 0 已下线  1 上线中")
     private Integer status;
+    @Schema(description = "当前余额")
+    private BigDecimal balance;
+    @Schema(description = "累计充值")
+    private BigDecimal totalRecharge;
+    @Schema(description = "当前盈利")
+    private BigDecimal currentProfit;
+    @Schema(description = "累计盈利")
+    private BigDecimal totalProfit;
     @Schema(description = "创建时间")
     private LocalDateTime createTime;
     @Schema(description = "创建人")

+ 7 - 1
src/main/java/com/zsElectric/boot/business/model/vo/UserFirmVO.java

@@ -2,11 +2,11 @@ package com.zsElectric.boot.business.model.vo;
 
 import java.io.Serial;
 import java.io.Serializable;
+import java.math.BigDecimal;
 import java.time.LocalDateTime;
 import io.swagger.v3.oas.annotations.media.Schema;
 import lombok.Getter;
 import lombok.Setter;
-import java.time.LocalDateTime;
 
 /**
  * 企业与用户关系视图对象
@@ -34,6 +34,12 @@ public class UserFirmVO implements Serializable {
     private String firmName;
     @Schema(description = "身份 1 管理员 2普通员工")
     private Integer type;
+    @Schema(description = "当前余额")
+    private BigDecimal balance;
+    @Schema(description = "累计消费")
+    private BigDecimal totalConsumption;
+    @Schema(description = "欠费金额")
+    private BigDecimal amountOwed;
     @Schema(description = "创建时间")
     private LocalDateTime createTime;
     @Schema(description = "创建人")

+ 32 - 0
src/main/java/com/zsElectric/boot/business/service/FirmAccountLogService.java

@@ -0,0 +1,32 @@
+package com.zsElectric.boot.business.service;
+
+import com.zsElectric.boot.business.model.entity.FirmAccountLog;
+import com.zsElectric.boot.business.model.query.FirmAccountLogQuery;
+import com.zsElectric.boot.business.model.vo.FirmAccountLogVO;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.service.IService;
+
+/**
+ * 企业资金流水服务类
+ *
+ * @author zsElectric
+ * @since 2025-12-26
+ */
+public interface FirmAccountLogService extends IService<FirmAccountLog> {
+
+    /**
+     * 企业资金流水分页列表
+     *
+     * @param queryParams 查询参数
+     * @return {@link IPage<FirmAccountLogVO>} 企业资金流水分页列表
+     */
+    IPage<FirmAccountLogVO> getFirmAccountLogPage(FirmAccountLogQuery queryParams);
+
+    /**
+     * 删除企业资金流水
+     *
+     * @param ids 企业资金流水ID,多个以英文逗号(,)分割
+     * @return 是否删除成功
+     */
+    boolean deleteFirmAccountLogs(String ids);
+}

+ 31 - 0
src/main/java/com/zsElectric/boot/business/service/FirmInfoService.java

@@ -2,11 +2,14 @@ package com.zsElectric.boot.business.service;
 
 import com.zsElectric.boot.business.model.entity.FirmInfo;
 import com.zsElectric.boot.business.model.form.FirmInfoForm;
+import com.zsElectric.boot.business.model.form.FirmBalanceChangeForm;
 import com.zsElectric.boot.business.model.query.FirmInfoQuery;
 import com.zsElectric.boot.business.model.vo.FirmInfoVO;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.service.IService;
 
+import java.math.BigDecimal;
+
 /**
  * 企业信息服务类
  *
@@ -55,4 +58,32 @@ public interface FirmInfoService extends IService<FirmInfo> {
      */
     boolean deleteFirmInfos(String ids);
 
+    /**
+     * 上账(充值)- 增加企业余额
+     *
+     * @param firmId 企业ID
+     * @param amount 上账金额
+     * @param remark 备注
+     * @return 是否成功
+     */
+    boolean recharge(Long firmId, BigDecimal amount, String remark);
+
+    /**
+     * 下账(扣款)- 减少企业余额
+     *
+     * @param firmId 企业ID
+     * @param amount 下账金额
+     * @param remark 备注
+     * @return 是否成功
+     */
+    boolean deduct(Long firmId, BigDecimal amount, String remark);
+
+    /**
+     * 企业账户余额变更
+     *
+     * @param form 余额变更表单
+     * @return 是否成功
+     */
+    boolean changeBalance(FirmBalanceChangeForm form);
+
 }

+ 90 - 0
src/main/java/com/zsElectric/boot/business/service/impl/FirmAccountLogServiceImpl.java

@@ -0,0 +1,90 @@
+package com.zsElectric.boot.business.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.zsElectric.boot.business.mapper.FirmAccountLogMapper;
+import com.zsElectric.boot.business.model.entity.FirmAccountLog;
+import com.zsElectric.boot.business.model.entity.FirmInfo;
+import com.zsElectric.boot.business.model.query.FirmAccountLogQuery;
+import com.zsElectric.boot.business.model.vo.FirmAccountLogVO;
+import com.zsElectric.boot.business.service.FirmAccountLogService;
+import com.zsElectric.boot.business.service.FirmInfoService;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+import cn.hutool.core.util.StrUtil;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * 企业资金流水服务实现类
+ *
+ * @author zsElectric
+ * @since 2025-12-26
+ */
+@Service
+@RequiredArgsConstructor
+public class FirmAccountLogServiceImpl extends ServiceImpl<FirmAccountLogMapper, FirmAccountLog> implements FirmAccountLogService {
+
+    private final FirmInfoService firmInfoService;
+
+    @Override
+    public IPage<FirmAccountLogVO> getFirmAccountLogPage(FirmAccountLogQuery queryParams) {
+        Page<FirmAccountLog> page = new Page<>(queryParams.getPageNum(), queryParams.getPageSize());
+
+        LambdaQueryWrapper<FirmAccountLog> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(queryParams.getFirmId() != null, FirmAccountLog::getFirmId, queryParams.getFirmId())
+                .eq(queryParams.getIncomeType() != null, FirmAccountLog::getIncomeType, queryParams.getIncomeType())
+                .eq(queryParams.getChangeType() != null, FirmAccountLog::getChangeType, queryParams.getChangeType())
+                .like(StrUtil.isNotBlank(queryParams.getSerialNo()), FirmAccountLog::getSerialNo, queryParams.getSerialNo())
+                .like(StrUtil.isNotBlank(queryParams.getName()), FirmAccountLog::getName, queryParams.getName())
+                .orderByDesc(FirmAccountLog::getCreateTime);
+
+        IPage<FirmAccountLog> entityPage = this.page(page, queryWrapper);
+
+        // 转换为VO
+        IPage<FirmAccountLogVO> voPage = entityPage.convert(entity -> {
+            FirmAccountLogVO vo = new FirmAccountLogVO();
+            vo.setId(entity.getId());
+            vo.setFirmId(entity.getFirmId());
+            vo.setProjectName(entity.getProjectName());
+            vo.setName(entity.getName());
+            vo.setSerialNo(entity.getSerialNo());
+            vo.setIncomeType(entity.getIncomeType());
+            vo.setBeforeChange(entity.getBeforeChange());
+            vo.setAfterChange(entity.getAfterChange());
+            vo.setMoneyChange(entity.getMoneyChange());
+            vo.setChangeType(entity.getChangeType());
+            vo.setChildId(entity.getChildId());
+            vo.setRemark(entity.getRemark());
+            vo.setCreateTime(entity.getCreateTime());
+            return vo;
+        });
+
+        // 填充企业名称
+        List<Long> firmIds = voPage.getRecords().stream()
+                .map(FirmAccountLogVO::getFirmId)
+                .distinct()
+                .collect(Collectors.toList());
+
+        if (!firmIds.isEmpty()) {
+            Map<Long, String> firmNameMap = firmInfoService.listByIds(firmIds).stream()
+                    .collect(Collectors.toMap(FirmInfo::getId, FirmInfo::getName));
+            voPage.getRecords().forEach(vo -> vo.setFirmName(firmNameMap.get(vo.getFirmId())));
+        }
+
+        return voPage;
+    }
+
+    @Override
+    public boolean deleteFirmAccountLogs(String ids) {
+        List<Long> idList = Arrays.stream(ids.split(","))
+                .map(Long::valueOf)
+                .collect(Collectors.toList());
+        return this.removeByIds(idList);
+    }
+}

+ 108 - 1
src/main/java/com/zsElectric/boot/business/service/impl/FirmInfoServiceImpl.java

@@ -1,7 +1,13 @@
 package com.zsElectric.boot.business.service.impl;
 
+import com.zsElectric.boot.business.mapper.FirmAccountLogMapper;
+import com.zsElectric.boot.business.model.entity.FirmAccountLog;
+import com.zsElectric.boot.business.model.form.FirmBalanceChangeForm;
+import com.zsElectric.boot.core.exception.BusinessException;
 import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
@@ -13,9 +19,12 @@ import com.zsElectric.boot.business.model.query.FirmInfoQuery;
 import com.zsElectric.boot.business.model.vo.FirmInfoVO;
 import com.zsElectric.boot.business.converter.FirmInfoConverter;
 
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
 import java.util.Arrays;
 import java.util.List;
-import java.util.stream.Collectors;
+import java.util.UUID;
 
 import cn.hutool.core.lang.Assert;
 import cn.hutool.core.util.StrUtil;
@@ -26,11 +35,13 @@ import cn.hutool.core.util.StrUtil;
  * @author zsElectric
  * @since 2025-12-11 10:10
  */
+@Slf4j
 @Service
 @RequiredArgsConstructor
 public class FirmInfoServiceImpl extends ServiceImpl<FirmInfoMapper, FirmInfo> implements FirmInfoService {
 
     private final FirmInfoConverter firmInfoConverter;
+    private final FirmAccountLogMapper firmAccountLogMapper;
 
     /**
     * 获取企业信息分页列表
@@ -100,4 +111,100 @@ public class FirmInfoServiceImpl extends ServiceImpl<FirmInfoMapper, FirmInfo> i
         return this.removeByIds(idList);
     }
 
+    /**
+     * 上账(充值)- 增加企业余额
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public boolean recharge(Long firmId, BigDecimal amount, String remark) {
+        FirmBalanceChangeForm form = new FirmBalanceChangeForm();
+        form.setFirmId(firmId);
+        form.setAmount(amount);
+        form.setIncomeType(1); // 转入
+        form.setChangeType(1); // 充值
+        form.setRemark(remark);
+        return changeBalance(form);
+    }
+
+    /**
+     * 下账(扣款)- 减少企业余额
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public boolean deduct(Long firmId, BigDecimal amount, String remark) {
+        FirmBalanceChangeForm form = new FirmBalanceChangeForm();
+        form.setFirmId(firmId);
+        form.setAmount(amount);
+        form.setIncomeType(2); // 转出
+        form.setChangeType(2); // 提现/扣款
+        form.setRemark(remark);
+        return changeBalance(form);
+    }
+
+    /**
+     * 企业账户余额变更
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public boolean changeBalance(FirmBalanceChangeForm form) {
+        // 1. 查询企业信息
+        FirmInfo firmInfo = this.getById(form.getFirmId());
+        if (firmInfo == null) {
+            throw new BusinessException("企业不存在");
+        }
+
+        // 2. 计算变更前余额
+        BigDecimal beforeBalance = firmInfo.getBalance() != null ? firmInfo.getBalance() : BigDecimal.ZERO;
+        BigDecimal changeAmount = form.getAmount();
+        BigDecimal afterBalance;
+
+        // 3. 根据收支类型计算变更后余额
+        if (form.getIncomeType() == 1) {
+            // 上账/转入
+            afterBalance = beforeBalance.add(changeAmount);
+            // 更新累计充值
+            BigDecimal totalRecharge = firmInfo.getTotalRecharge() != null ? firmInfo.getTotalRecharge() : BigDecimal.ZERO;
+            firmInfo.setTotalRecharge(totalRecharge.add(changeAmount));
+        } else {
+            // 下账/转出
+            if (beforeBalance.compareTo(changeAmount) < 0) {
+                throw new BusinessException("企业余额不足");
+            }
+            afterBalance = beforeBalance.subtract(changeAmount);
+        }
+
+        // 4. 更新企业余额
+        firmInfo.setBalance(afterBalance);
+        this.updateById(firmInfo);
+
+        // 5. 记录资金流水
+        FirmAccountLog accountLog = new FirmAccountLog();
+        accountLog.setFirmId(form.getFirmId());
+        accountLog.setProjectName("企业账户");
+        accountLog.setName(form.getIncomeType() == 1 ? "上账" : "下账");
+        accountLog.setSerialNo(generateSerialNo());
+        accountLog.setIncomeType(form.getIncomeType());
+        accountLog.setBeforeChange(beforeBalance);
+        accountLog.setAfterChange(afterBalance);
+        accountLog.setMoneyChange(changeAmount);
+        accountLog.setChangeType(form.getChangeType() != null ? form.getChangeType() : (form.getIncomeType() == 1 ? 1 : 2));
+        accountLog.setRemark(form.getRemark());
+        accountLog.setCreateTime(LocalDateTime.now());
+        firmAccountLogMapper.insert(accountLog);
+
+        log.info("企业账户余额变更成功 - firmId: {}, incomeType: {}, amount: {}, before: {}, after: {}",
+                form.getFirmId(), form.getIncomeType(), changeAmount, beforeBalance, afterBalance);
+
+        return true;
+    }
+
+    /**
+     * 生成流水号
+     */
+    private String generateSerialNo() {
+        String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS"));
+        String uuid = UUID.randomUUID().toString().replace("-", "").substring(0, 8).toUpperCase();
+        return "FIRM" + timestamp + uuid;
+    }
+
 }

+ 49 - 0
src/main/resources/mapper/business/FirmAccountLogMapper.xml

@@ -0,0 +1,49 @@
+<?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.zsElectric.boot.business.mapper.FirmAccountLogMapper">
+
+    <!-- 获取企业资金流水分页列表 -->
+    <select id="getFirmAccountLogPage" resultType="com.zsElectric.boot.business.model.vo.FirmAccountLogVO">
+        SELECT
+            a.id,
+            a.firm_id,
+            b.name AS firmName,
+            a.project_name,
+            a.name,
+            a.serial_no,
+            a.income_type,
+            a.before_change,
+            a.after_change,
+            a.money_change,
+            a.change_type,
+            a.child_id,
+            a.remark,
+            a.create_time
+        FROM
+            c_firm_account_log a
+        LEFT JOIN c_firm_info b ON a.firm_id = b.id
+        <where>
+            a.is_deleted = 0
+            <if test="queryParams.firmId != null">
+                AND a.firm_id = #{queryParams.firmId}
+            </if>
+            <if test="queryParams.incomeType != null">
+                AND a.income_type = #{queryParams.incomeType}
+            </if>
+            <if test="queryParams.changeType != null">
+                AND a.change_type = #{queryParams.changeType}
+            </if>
+            <if test="queryParams.serialNo != null and queryParams.serialNo != ''">
+                AND a.serial_no LIKE CONCAT('%', #{queryParams.serialNo}, '%')
+            </if>
+            <if test="queryParams.startTime != null">
+                AND a.create_time <![CDATA[>=]]> #{queryParams.startTime}
+            </if>
+            <if test="queryParams.endTime != null">
+                AND a.create_time <![CDATA[<=]]> #{queryParams.endTime}
+            </if>
+        </where>
+        ORDER BY a.create_time DESC
+    </select>
+
+</mapper>

+ 6 - 1
src/main/resources/mapper/business/FirmInfoMapper.xml

@@ -11,6 +11,10 @@
             a.number,
             COUNT( b.id ) AS empNum,
             a.STATUS,
+            IFNULL(a.balance, 0.00) AS balance,
+            IFNULL(a.total_recharge, 0.00) AS totalRecharge,
+            IFNULL(a.current_profit, 0.00) AS currentProfit,
+            IFNULL(a.total_profit, 0.00) AS totalProfit,
             a.create_time,
             a.create_by,
             a.update_time,
@@ -18,7 +22,7 @@
             a.is_deleted
         FROM
         c_firm_info a
-        INNER JOIN c_user_firm b ON a.id = b.firm_id
+        LEFT JOIN c_user_firm b ON a.id = b.firm_id AND b.is_deleted = 0
         <where>
             a.is_deleted = 0
             <if test="queryParams.name != null">
@@ -30,6 +34,7 @@
         </where>
         GROUP BY
         a.id
+        ORDER BY a.create_time DESC
     </select>
 
 </mapper>

+ 35 - 2
src/main/resources/mapper/business/UserFirmMapper.xml

@@ -11,6 +11,9 @@
         a.firm_id,
         b.name AS firmName,
         a.type,
+        c.balance,
+        c.amount_owed AS amountOwed,
+        IFNULL(d.total_consumption, 0) AS totalConsumption,
         a.create_time,
         a.create_by,
         a.update_time,
@@ -18,9 +21,16 @@
         a.is_deleted
         FROM
         c_user_firm a
-        LEFT JOIN c_firm_info b ON a.firm_id = b.id
+        LEFT JOIN c_firm_info b ON a.firm_id = b.id AND b.is_deleted = 0
+        LEFT JOIN c_user_account c ON a.user_id = c.user_id AND c.is_deleted = 0
+        LEFT JOIN (
+            SELECT user_id, SUM(IFNULL(change_balance, 0)) AS total_consumption
+            FROM c_user_account_log
+            WHERE is_deleted = 0 AND change_type = 2 AND account_type = 1
+            GROUP BY user_id
+        ) d ON a.user_id = d.user_id
         <where>
-            a.is_deleted = 0 and b.is_deleted = 0
+            a.is_deleted = 0
             <if test="queryParams.phone != null and queryParams.phone != ''">
                 AND a.phone LIKE CONCAT('%', #{queryParams.phone}, '%')
             </if>
@@ -28,6 +38,29 @@
                 AND a.type = #{queryParams.type}
             </if>
         </where>
+        <choose>
+            <when test="queryParams.sortField == 'balance' and queryParams.sortOrder == 'ascending'">
+                ORDER BY c.balance ASC
+            </when>
+            <when test="queryParams.sortField == 'balance' and queryParams.sortOrder == 'descending'">
+                ORDER BY c.balance DESC
+            </when>
+            <when test="queryParams.sortField == 'totalConsumption' and queryParams.sortOrder == 'ascending'">
+                ORDER BY totalConsumption ASC
+            </when>
+            <when test="queryParams.sortField == 'totalConsumption' and queryParams.sortOrder == 'descending'">
+                ORDER BY totalConsumption DESC
+            </when>
+            <when test="queryParams.sortField == 'amountOwed' and queryParams.sortOrder == 'ascending'">
+                ORDER BY c.amount_owed ASC
+            </when>
+            <when test="queryParams.sortField == 'amountOwed' and queryParams.sortOrder == 'descending'">
+                ORDER BY c.amount_owed DESC
+            </when>
+            <otherwise>
+                ORDER BY a.create_time DESC
+            </otherwise>
+        </choose>
     </select>
 
 </mapper>