jinxia.mo 1 месяц назад
Родитель
Сommit
9273954528

+ 9 - 0
back-ui/src/api/dz/cards.js

@@ -198,3 +198,12 @@ export function requestOpenCard(agentId, province, schoolId, beginCardNo, endCar
     },
   });
 }
+
+// 统计学习卡数据
+export function getCardStatistics(query) {
+  return request({
+    url: "/dz/cards/statistics",
+    method: "get",
+    params: query,
+  });
+}

+ 241 - 0
back-ui/src/views/dz/cards/statistics.vue

@@ -0,0 +1,241 @@
+<template>
+  <div class="app-container">
+    <el-row :gutter="20">
+      <splitpanes :horizontal="appStore.device === 'mobile'" class="default-theme">
+        <pane size="100">
+          <el-col>
+            <el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="100px">
+              <el-form-item label="平台机构" prop="deptId">
+                <IeInstitutionSelect v-model="queryParams.deptId" placeholder="请选择平台机构" clearable style="width: 240px" />
+              </el-form-item>
+              <el-form-item label="代理商" prop="agentId">
+                <IeAgentSelect v-model="queryParams.agentId" placeholder="请选择代理商" clearable filterable style="width: 240px" />
+              </el-form-item>
+              <el-form-item label="开卡时间" prop="openTimeRange">
+                <el-date-picker
+                  v-model="openTimeRange"
+                  type="daterange"
+                  range-separator="-"
+                  start-placeholder="开始日期"
+                  end-placeholder="结束日期"
+                  value-format="YYYY-MM-DD"
+                  style="width: 240px"
+                />
+              </el-form-item>
+              <el-form-item>
+                <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
+                <el-button icon="Refresh" @click="resetQuery">重置</el-button>
+              </el-form-item>
+            </el-form>
+
+            <el-row :gutter="10" class="mb8">
+              <el-col :span="1.5">
+                <el-button
+                  type="info"
+                  plain
+                  icon="Sort"
+                  @click="toggleExpandAll"
+                >展开/折叠</el-button>
+              </el-col>
+              <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
+            </el-row>
+
+            <el-table
+              v-if="refreshTable"
+              v-loading="loading"
+              :data="statisticsList"
+              row-key="id"
+              :default-expand-all="isExpandAll"
+              :tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
+              :row-style="getRowStyle"
+            >
+              <el-table-column prop="agentName" label="代理商" align="center" min-width="200">
+                <template #default="scope">
+                  <span v-if="scope.row.leafAgentName && scope.row.leafAgentName !== scope.row.agentName">
+                    {{ scope.row.leafAgentName }}
+                  </span>
+                  <span v-else>{{ scope.row.agentName }}</span>
+                </template>
+              </el-table-column>
+              <el-table-column prop="agentId" label="账号" align="center" />
+              <el-table-column prop="openCard" label="已开卡" align="center" />
+              <el-table-column prop="closeCard" label="已关卡" align="center" />
+              <el-table-column prop="payCard" label="已缴费" align="center" />
+              <el-table-column prop="settlementCard" label="已结算" align="center" />
+            </el-table>
+          </el-col>
+        </pane>
+      </splitpanes>
+    </el-row>
+  </div>
+</template>
+
+<script setup name="CardStatistics">
+import { getCardStatistics } from "@/api/dz/cards"
+import IeInstitutionSelect from '@/components/IeInstitutionSelect/index.vue'
+import IeAgentSelect from '@/components/IeAgentSelect/index.vue'
+import useAppStore from '@/store/modules/app'
+import { Splitpanes, Pane } from "splitpanes"
+import "splitpanes/dist/splitpanes.css"
+
+const { proxy } = getCurrentInstance()
+const appStore = useAppStore()
+
+const statisticsList = ref([])
+const loading = ref(true)
+const showSearch = ref(true)
+const openTimeRange = ref([])
+const isExpandAll = ref(true)
+const refreshTable = ref(true)
+
+const queryParams = reactive({
+  deptId: undefined,
+  agentId: undefined
+})
+
+/** 查询统计列表 */
+function getList() {
+  loading.value = true
+  // 构建查询参数:deptId, agentId, openTimeBegin, openTimeEnd
+  const params = {}
+  
+  // 平台机构
+  if (queryParams.deptId) {
+    params.deptId = queryParams.deptId
+  }
+  
+  // 代理商
+  if (queryParams.agentId) {
+    params.agentId = queryParams.agentId
+  }
+  
+  // 开卡时间范围
+  if (openTimeRange.value && openTimeRange.value.length === 2) {
+    params.openTimeBegin = openTimeRange.value[0]
+    params.openTimeEnd = openTimeRange.value[1]
+  }
+  
+  getCardStatistics(params).then(response => {
+    // 处理数据,构建层级结构
+    const data = response.data || []
+    const map = new Map()
+    const result = []
+    
+    // 先按一级代理商分组
+    data.forEach(item => {
+      const key = item.agentId
+      if (!map.has(key)) {
+        map.set(key, {
+          id: `agent_${item.agentId}`,
+          agentId: item.agentId,
+          agentName: item.agentName,
+          leafAgentId: item.leafAgentId,
+          leafAgentName: item.leafAgentName,
+          openCard: 0,
+          closeCard: 0,
+          payCard: 0,
+          settlementCard: 0,
+          children: []
+        })
+        result.push(map.get(key))
+      }
+      
+      const parent = map.get(key)
+      
+      // 如果是一级代理商自己(leafAgentId === agentId 或 leafAgentName === agentName)
+      if (item.leafAgentId === item.agentId || item.leafAgentName === item.agentName) {
+        parent.openCard += item.openCard || 0
+        parent.closeCard += item.closeCard || 0
+        parent.payCard += item.payCard || 0
+        parent.settlementCard += item.settlementCard || 0
+      } else {
+        // 子代理商
+        parent.children.push({
+          id: `leaf_${item.leafAgentId}`,
+          agentId: item.agentId,
+          agentName: item.agentName,
+          leafAgentId: item.leafAgentId,
+          leafAgentName: item.leafAgentName,
+          openCard: item.openCard || 0,
+          closeCard: item.closeCard || 0,
+          payCard: item.payCard || 0,
+          settlementCard: item.settlementCard || 0
+        })
+        // 累加到父级
+        parent.openCard += item.openCard || 0
+        parent.closeCard += item.closeCard || 0
+        parent.payCard += item.payCard || 0
+        parent.settlementCard += item.settlementCard || 0
+      }
+    })
+    
+    // 过滤掉没有子项的父级(如果父级自己也没有数据)
+    statisticsList.value = result.filter(item => 
+      item.children.length > 0 || item.openCard > 0 || item.closeCard > 0 || item.settlementCard > 0
+    )
+  }).finally(() => {
+    loading.value = false
+  })
+}
+
+/** 搜索按钮操作 */
+function handleQuery() {
+  getList()
+}
+
+/** 重置按钮操作 */
+function resetQuery() {
+  proxy.resetForm("queryRef")
+  openTimeRange.value = []
+  queryParams.deptId = undefined
+  queryParams.agentId = undefined
+  handleQuery()
+}
+
+/** 展开/折叠操作 */
+function toggleExpandAll() {
+  refreshTable.value = false
+  isExpandAll.value = !isExpandAll.value
+  nextTick(() => {
+    refreshTable.value = true
+  })
+}
+
+/** 设置表格行样式 */
+function getRowStyle({ row }) {
+  // 如果是子代理商(有 children 或者是子节点)
+  if (row.children && row.children.length > 0) {
+    return {}
+  }
+  // 子节点使用浅蓝色背景
+  if (row.leafAgentId && row.leafAgentId !== row.agentId) {
+    return {
+      backgroundColor: '#C4E7F8'
+    }
+  }
+  return {}
+}
+
+onMounted(() => {
+  getList()
+})
+</script>
+
+<style scoped>
+.app-container {
+  padding: 20px;
+}
+
+/* 确保 splitpanes 的 pane 可以滚动 */
+:deep(.splitpanes__pane) {
+  overflow: auto !important;
+  height: 100%;
+}
+
+/* 确保 el-col 内容正常显示,不被遮挡 */
+:deep(.splitpanes__pane .el-col) {
+  padding: 0;
+  overflow: visible;
+}
+</style>
+

+ 28 - 0
ie-admin/src/main/java/com/ruoyi/web/controller/dz/DzCardsController.java

@@ -249,6 +249,34 @@ public class DzCardsController extends BaseController
     }
 
 
+    /**
+     * 统计学习卡数据
+     */
+    @PreAuthorize("@ss.hasPermi('dz:cards:list')")
+    @GetMapping("/statistics")
+    @ApiOperation("统计学习卡数据")
+    public AjaxResult statistics(@RequestParam(required = false) Long deptId,
+                                  @RequestParam(required = false) Long agentId,
+                                  @RequestParam(required = false) String openTimeBegin,
+                                  @RequestParam(required = false) String openTimeEnd)
+    {
+        java.util.Map<String, Object> params = new java.util.HashMap<>();
+        if (deptId != null) {
+            params.put("deptId", deptId);
+        }
+        if (agentId != null) {
+            params.put("agentId", agentId);
+        }
+        if (openTimeBegin != null && !openTimeBegin.isEmpty()) {
+            params.put("openTimeBegin", openTimeBegin);
+        }
+        if (openTimeEnd != null && !openTimeEnd.isEmpty()) {
+            params.put("openTimeEnd", openTimeEnd);
+        }
+        List<com.ruoyi.dz.dto.CardStatisticsDTO> list = dzCardsService.statisticCards(params);
+        return success(list);
+    }
+
     private CardType getCardType(Long institutionId, String type) {
         if(!NumberUtils.isNumeric(type)) {
             return CardType.valueOf(type);

+ 133 - 0
ie-system/src/main/java/com/ruoyi/dz/dto/CardStatisticsDTO.java

@@ -0,0 +1,133 @@
+package com.ruoyi.dz.dto;
+
+import java.io.Serializable;
+
+/**
+ * 学习卡统计结果DTO
+ * 
+ * @author ruoyi
+ */
+public class CardStatisticsDTO implements Serializable
+{
+    private static final long serialVersionUID = 1L;
+
+    /** 一级代理商ID */
+    private Long agentId;
+
+    /** 一级代理商名称 */
+    private String agentName;
+
+    /** 末级代理商ID */
+    private Long leafAgentId;
+
+    /** 末级代理商名称 */
+    private String leafAgentName;
+
+    /** 开卡数量 */
+    private Long openCard;
+
+    /** 关卡数量 */
+    private Long closeCard;
+
+    /** 缴费数量 */
+    private Long payCard;
+
+    /** 结算数量 */
+    private Long settlementCard;
+
+    public Long getAgentId()
+    {
+        return agentId;
+    }
+
+    public void setAgentId(Long agentId)
+    {
+        this.agentId = agentId;
+    }
+
+    public String getAgentName()
+    {
+        return agentName;
+    }
+
+    public void setAgentName(String agentName)
+    {
+        this.agentName = agentName;
+    }
+
+    public Long getLeafAgentId()
+    {
+        return leafAgentId;
+    }
+
+    public void setLeafAgentId(Long leafAgentId)
+    {
+        this.leafAgentId = leafAgentId;
+    }
+
+    public String getLeafAgentName()
+    {
+        return leafAgentName;
+    }
+
+    public void setLeafAgentName(String leafAgentName)
+    {
+        this.leafAgentName = leafAgentName;
+    }
+
+    public Long getOpenCard()
+    {
+        return openCard;
+    }
+
+    public void setOpenCard(Long openCard)
+    {
+        this.openCard = openCard;
+    }
+
+    public Long getCloseCard()
+    {
+        return closeCard;
+    }
+
+    public void setCloseCard(Long closeCard)
+    {
+        this.closeCard = closeCard;
+    }
+
+    public Long getPayCard()
+    {
+        return payCard;
+    }
+
+    public void setPayCard(Long payCard)
+    {
+        this.payCard = payCard;
+    }
+
+    public Long getSettlementCard()
+    {
+        return settlementCard;
+    }
+
+    public void setSettlementCard(Long settlementCard)
+    {
+        this.settlementCard = settlementCard;
+    }
+
+    @Override
+    public String toString()
+    {
+        return "CardStatisticsDTO{" +
+                "agentId=" + agentId +
+                ", agentName='" + agentName + '\'' +
+                ", leafAgentId=" + leafAgentId +
+                ", leafAgentName='" + leafAgentName + '\'' +
+                ", openCard=" + openCard +
+                ", closeCard=" + closeCard +
+                ", payCard=" + payCard +
+                ", settlementCard=" + settlementCard +
+                '}';
+    }
+}
+

+ 10 - 0
ie-system/src/main/java/com/ruoyi/dz/mapper/DzCardsMapper.java

@@ -1,9 +1,11 @@
 package com.ruoyi.dz.mapper;
 
 import java.util.List;
+import java.util.Map;
 
 import com.ruoyi.criteria.CardCriteria;
 import com.ruoyi.dz.domain.DzCards;
+import com.ruoyi.dz.dto.CardStatisticsDTO;
 import org.apache.ibatis.annotations.Param;
 
 /**
@@ -68,4 +70,12 @@ public interface DzCardsMapper
     public List<DzCards> selectCardsByCardIds(Long[] cardIds);
 
     public Long selectMaxNo(@Param("type") Integer type);
+
+    /**
+     * 统计学习卡数据
+     * 
+     * @param params 查询参数(deptId, agentId, openTimeBegin, openTimeEnd)
+     * @return 统计结果列表
+     */
+    public List<CardStatisticsDTO> statisticCards(Map<String, Object> params);
 }

+ 9 - 0
ie-system/src/main/java/com/ruoyi/dz/service/IDzCardsService.java

@@ -1,6 +1,7 @@
 package com.ruoyi.dz.service;
 
 import java.util.List;
+import java.util.Map;
 
 import com.ruoyi.common.core.domain.entity.SysUser;
 import com.ruoyi.common.enums.ExamType;
@@ -125,4 +126,12 @@ public interface IDzCardsService
      */
     public Boolean changeCampus(Long campusId, String beginNo, String endNo);
     public List<DzCards> selectCardsByCardIds(List<Long> cardIds);
+
+    /**
+     * 统计学习卡数据
+     * 
+     * @param params 查询参数(deptId, agentId, openTimeBegin, openTimeEnd)
+     * @return 统计结果列表
+     */
+    public List<com.ruoyi.dz.dto.CardStatisticsDTO> statisticCards(Map<String, Object> params);
 }

+ 6 - 0
ie-system/src/main/java/com/ruoyi/dz/service/impl/DzCardsServiceImpl.java

@@ -432,4 +432,10 @@ public class DzCardsServiceImpl implements IDzCardsService
         //cardIds转换为数组
         return dzCardsMapper.selectCardsByCardIds(cardIds.toArray(new Long[0]));
     }
+
+    @Override
+    public List<com.ruoyi.dz.dto.CardStatisticsDTO> statisticCards(java.util.Map<String, Object> params)
+    {
+        return dzCardsMapper.statisticCards(params);
+    }
 }

+ 31 - 0
ie-system/src/main/resources/mapper/dz/DzCardsMapper.xml

@@ -295,4 +295,35 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
     <select id="selectMaxNo" parameterType="Integer" resultType="Long">
         select max(card_no) from dz_cards where type = #{type}
     </select>
+
+    <!-- 统计学习卡数据 -->
+    <select id="statisticCards" parameterType="map" resultType="com.ruoyi.dz.dto.CardStatisticsDTO">
+        SELECT
+            T1.agent_id AS agentId,
+            T1.name AS agentName,
+            T2.agent_id AS leafAgentId,
+            T2.name AS leafAgentName,
+            SUM(CASE WHEN T0.status = 10 OR T0.status = 20 OR T0.status = 30 THEN 1 ELSE 0 END) AS openCard,
+            SUM(CASE WHEN T0.distribute_status = 30 THEN 1 ELSE 0 END) AS closeCard,
+            SUM(CASE WHEN T0.pay_status = 20 THEN 1 ELSE 0 END) AS payCard,
+            SUM(CASE WHEN T0.is_settlement = 1 THEN 1 ELSE 0 END) AS settlementCard
+        FROM `dz_cards` T0
+        LEFT JOIN `dz_agent` T2 ON T0.leaf_agent_id = T2.agent_id
+        LEFT JOIN `dz_agent` T1 ON T0.agent_id = T1.agent_id
+        WHERE T0.leaf_agent_id IS NOT NULL
+        <if test="deptId != null">
+            AND T0.dept_id = #{deptId}
+        </if>
+        <if test="agentId != null">
+            AND (T0.agent_id = #{agentId} OR T0.leaf_agent_id = #{agentId})
+        </if>
+        <if test="openTimeBegin != null and openTimeBegin != ''">
+            AND date(T0.open_time) &gt;= date(#{openTimeBegin})
+        </if>
+        <if test="openTimeEnd != null and openTimeEnd != ''">
+            AND date(T0.open_time) &lt;= date(#{openTimeEnd})
+        </if>
+        GROUP BY T0.agent_id, T1.name, T0.leaf_agent_id, T2.name
+        ORDER BY T1.agent_id, T2.agent_id
+    </select>
 </mapper>