update:对账消息推送,军保账单统计,退款数据统计

This commit is contained in:
Yuan
2025-10-20 14:39:29 +08:00
parent 9fb2ea9cb4
commit ff5bad9967
35 changed files with 3649 additions and 100 deletions

View File

@@ -0,0 +1,107 @@
<?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.saye.hospitalgd.mapper.FinancialReconciliation.RefundStatisticsMapper">
<!-- 查询退款统计报表数据 -->
<select id="findRefundStatistics" parameterType="HashMap" resultType="HashMap">
SELECT
<choose>
<when test="dimension == 'time'">
DATE_FORMAT(trade_date, '%Y-%m-%d') as dimension_value,
'按日期' as dimension_name
</when>
<when test="dimension == 'payType'">
PayType as dimension_value,
'按支付方式' as dimension_name
</when>
<when test="dimension == 'refundType'">
TradingStatus as dimension_value,
'按退款类型' as dimension_name
</when>
<otherwise>
'全部' as dimension_value,
'全部' as dimension_name
</otherwise>
</choose>,
COUNT(*) as refund_count,
SUM(CAST(Amount AS DECIMAL(10,2))) as total_refund_amount,
AVG(CAST(Amount AS DECIMAL(10,2))) as avg_refund_amount,
MAX(CAST(Amount AS DECIMAL(10,2))) as max_refund_amount,
MIN(CAST(Amount AS DECIMAL(10,2))) as min_refund_amount,
COUNT(DISTINCT PatientId) as unique_patients,
COUNT(DISTINCT HisOperCode) as unique_operators
FROM hisbill_history
WHERE TradingStatus = '2'
<if test="startTime != null and startTime != ''">
AND trade_date >= #{startTime}
</if>
<if test="endTime != null and endTime != ''">
AND trade_date &lt;= #{endTime}
</if>
<if test="payType != null and payType != ''">
AND PayType = #{payType}
</if>
<choose>
<when test="dimension == 'time'">
GROUP BY DATE_FORMAT(trade_date, '%Y-%m-%d')
ORDER BY DATE_FORMAT(trade_date, '%Y-%m-%d') DESC
</when>
<when test="dimension == 'payType'">
GROUP BY PayType
ORDER BY total_refund_amount DESC
</when>
<when test="dimension == 'refundType'">
GROUP BY TradingStatus
ORDER BY total_refund_amount DESC
</when>
</choose>
</select>
<!-- 获取退款统计汇总数据 -->
<select id="getRefundSummary" parameterType="HashMap" resultType="HashMap">
SELECT
COUNT(*) as total_refund_count,
SUM(CAST(Amount AS DECIMAL(10,2))) as total_refund_amount,
AVG(CAST(Amount AS DECIMAL(10,2))) as avg_refund_amount,
MAX(CAST(Amount AS DECIMAL(10,2))) as max_refund_amount,
MIN(CAST(Amount AS DECIMAL(10,2))) as min_refund_amount,
COUNT(DISTINCT PatientId) as unique_patients,
COUNT(DISTINCT HisOperCode) as unique_operators,
COUNT(DISTINCT PayType) as pay_type_count,
COUNT(DISTINCT TradingStatus) as refund_type_count
FROM hisbill_history
WHERE TradingStatus = '2'
<if test="startTime != null and startTime != ''">
AND trade_date >= #{startTime}
</if>
<if test="endTime != null and endTime != ''">
AND trade_date &lt;= #{endTime}
</if>
<if test="payType != null and payType != ''">
AND PayType = #{payType}
</if>
</select>
<!-- 获取退款趋势数据 -->
<select id="getRefundTrend" parameterType="HashMap" resultType="HashMap">
SELECT
DATE_FORMAT(trade_date, '%Y-%m-%d') as trend_date,
COUNT(*) as daily_refund_count,
SUM(CAST(Amount AS DECIMAL(10,2))) as daily_refund_amount,
AVG(CAST(Amount AS DECIMAL(10,2))) as daily_avg_amount
FROM hisbill_history
WHERE TradingStatus = '2'
<if test="startTime != null and startTime != ''">
AND trade_date >= #{startTime}
</if>
<if test="endTime != null and endTime != ''">
AND trade_date &lt;= #{endTime}
</if>
<if test="payType != null and payType != ''">
AND PayType = #{payType}
</if>
GROUP BY DATE_FORMAT(trade_date, '%Y-%m-%d')
ORDER BY DATE_FORMAT(trade_date, '%Y-%m-%d') ASC
</select>
</mapper>

View File

@@ -190,10 +190,10 @@
and PayType = #{payType}
</if>
<if test="startTime!=null and startTime!=''">
and TradeTime &gt;= #{startTime}
and (TradeTime &gt;= #{startTime} or trade_date &gt;= #{startTime})
</if>
<if test="endTime!=null and endTime!=''">
and TradeTime &lt;= #{endTime}
and (TradeTime &lt;= #{endTime} or trade_date &lt;= #{endTime})
</if>
<if test="likeFiled!=null and likeFiled!=''">
and PlatformTransId like concat('%',concat(#{likeFiled},'%'))

View File

@@ -24,6 +24,12 @@
select *
from hisbill_history
where trade_date = #{trade_date}
<if test="excludeMilitaryOperators != null and excludeMilitaryOperators.size() > 0">
and HisOperCode not in
<foreach collection="excludeMilitaryOperators" item="operator" open="(" separator="," close=")">
#{operator.HISOPERCODE}
</foreach>
</if>
</select>
<select id="findHisDetailByParam" parameterType="HashMap" resultType="HashMap">
@@ -44,6 +50,12 @@
from hisbills_history
where trade_date=#{trade_date}
<if test="excludeMilitaryOperators != null and excludeMilitaryOperators.size() > 0">
and HisOperCode not in
<foreach collection="excludeMilitaryOperators" item="operator" open="(" separator="," close=")">
#{operator.HISOPERCODE}
</foreach>
</if>
<if test="tranID!=null and tranID!=''">
and HisTransId=#{tranID}
</if>

View File

@@ -6,9 +6,7 @@
select hisoper_id, HisOperCode,user_name,modify_time
from his_operator
<where>
<if test="is_active!=null and is_active!=''">
and is_active=#{is_active}
</if>
is_active = 1
<if test="likeFiled!=null and likeFiled!=''">
and (HisOperCode like concat('%',concat(#{likeFiled},'%'))
or user_name like concat('%',concat(#{likeFiled},'%'))
@@ -24,6 +22,12 @@
where HisOperCode = #{hisOperCode}
</select>
<select id="findMilitaryOperators" parameterType="HashMap" resultType="HashMap">
select HisOperCode
from his_operator
where is_active = 1 and user_name = '军保支付'
</select>
<insert id="addOperator" parameterType="HashMap">
insert into his_operator(HisOperCode, user_name, is_active, modify_time)
values (#{hisOperCode}, #{userName}, #{is_active}, #{modify_time})

View File

@@ -80,6 +80,12 @@
inner join bankbill_history b on a.PlatformTransId = b.C_YSDDH
where b.C_JYRQ &gt;= #{startTime}
and b.C_JYRQ &lt;= #{endTime}
<if test="excludeMilitaryOperators != null and excludeMilitaryOperators.size() > 0">
and a.HisOperCode not in
<foreach collection="excludeMilitaryOperators" item="operator" open="(" separator="," close=")">
#{operator.HisOperCode}
</foreach>
</if>
group by b.C_ZFFS, b.C_JYRQ
</select>
@@ -107,10 +113,17 @@
, ''
, '0'
, '1'
from (select * from hisbill_history where trade_date = #{trade_date} and payType!=#{cash_code}) a
from (select * from hisbill_history where trade_date = #{trade_date} and payType!=#{cash_code}
<if test="excludeMilitaryOperators != null and excludeMilitaryOperators.size() > 0">
and HisOperCode not in
<foreach collection="excludeMilitaryOperators" item="operator" open="(" separator="," close=")">
#{operator.HisOperCode}
</foreach>
</if>
) a
inner join
(select * from bankbill_history where C_JYRQ = #{trade_date}) b
on a.PlatformTransId = b.C_YSDDH and a.TradingStatus = b.C_JYLX and a.Amount+0 = b.C_JYJE+0
on a.PlatformTransId = b.C_SHDDH and a.TradingStatus = b.C_JYLX and a.Amount+0 = b.C_JYJE+0
</insert>
<insert id="insertHisUnilateral" parameterType="HashMap">
@@ -137,11 +150,18 @@
, '1'
, '1'
, '1'
from (select * from hisbill_history where trade_date = #{trade_date} and payType!=#{cash_code}) a
from (select * from hisbill_history where trade_date = #{trade_date} and payType!=#{cash_code}
<if test="excludeMilitaryOperators != null and excludeMilitaryOperators.size() > 0">
and HisOperCode not in
<foreach collection="excludeMilitaryOperators" item="operator" open="(" separator="," close=")">
#{operator.HisOperCode}
</foreach>
</if>
) a
left join
(select * from bankbill_history where C_JYRQ = #{trade_date}) b
on a.PlatformTransId = b.C_YSDDH and a.TradingStatus = b.C_JYLX and a.Amount+0 = b.C_JYJE+0
where b.C_YSDDH is null
on a.PlatformTransId = b.C_SHDDH and a.TradingStatus = b.C_JYLX and a.Amount+0 = b.C_JYJE+0
where b.C_SHDDH is null
</insert>
<insert id="insertThirdUnilateral" parameterType="HashMap">
@@ -168,10 +188,17 @@
, '2'
, '1'
, '1'
from (select * from hisbill_history where trade_date = #{trade_date} and payType!=#{cash_code}) a
from (select * from hisbill_history where trade_date = #{trade_date} and payType!=#{cash_code}
<if test="excludeMilitaryOperators != null and excludeMilitaryOperators.size() > 0">
and HisOperCode not in
<foreach collection="excludeMilitaryOperators" item="operator" open="(" separator="," close=")">
#{operator.HisOperCode}
</foreach>
</if>
) a
right join
(select * from bankbill_history where C_JYRQ = #{trade_date}) b
on a.PlatformTransId = b.C_YSDDH and a.TradingStatus = b.C_JYLX and a.Amount + 0 = b.C_JYJE + 0
on a.PlatformTransId = b.C_SHDDH and a.TradingStatus = b.C_JYLX and a.Amount + 0 = b.C_JYJE + 0
where a.PlatformTransId is null
</insert>
@@ -200,7 +227,14 @@
, ''
, '0'
, '1'
from (select * from hisbill_history where trade_date = #{trade_date} and payType = #{cash_code}) a
from (select * from hisbill_history where trade_date = #{trade_date} and payType = #{cash_code}
<if test="excludeMilitaryOperators != null and excludeMilitaryOperators.size() > 0">
and HisOperCode not in
<foreach collection="excludeMilitaryOperators" item="operator" open="(" separator="," close=")">
#{operator.HisOperCode}
</foreach>
</if>
) a
inner join
(select * from cash_record where trade_date = #{trade_date}) b
on a.HisOperCode = b.czyh
@@ -231,7 +265,14 @@
, '1'
, '1'
, '1'
from (select * from hisbill_history where trade_date = #{trade_date} and payType = #{cash_code}) a
from (select * from hisbill_history where trade_date = #{trade_date} and payType = #{cash_code}
<if test="excludeMilitaryOperators != null and excludeMilitaryOperators.size() > 0">
and HisOperCode not in
<foreach collection="excludeMilitaryOperators" item="operator" open="(" separator="," close=")">
#{operator.HisOperCode}
</foreach>
</if>
) a
left join
(select * from cash_record where trade_date = #{trade_date}) b
on a.HisOperCode = b.czyh
@@ -263,7 +304,14 @@
, '2'
, '1'
, '1'
from (select * from hisbill_history where trade_date = #{trade_date} and payType = #{cash_code}) a
from (select * from hisbill_history where trade_date = #{trade_date} and payType = #{cash_code}
<if test="excludeMilitaryOperators != null and excludeMilitaryOperators.size() > 0">
and HisOperCode not in
<foreach collection="excludeMilitaryOperators" item="operator" open="(" separator="," close=")">
#{operator.HisOperCode}
</foreach>
</if>
) a
right join
(select * from cash_record where trade_date = #{trade_date}) b
on a.HisOperCode = b.czyh

View File

@@ -0,0 +1,124 @@
<?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.saye.hospitalgd.mapper.system.FinanceUserMapper">
<!-- 查询财务人员列表 -->
<select id="findFinanceUserPageList" parameterType="HashMap" resultType="com.saye.hospitalgd.model.FinanceUser">
SELECT
id,
name,
wechat_name as wechatName,
phone,
open_id as openId,
is_active as isActive,
create_time as createTime,
modify_time as modifyTime,
remark
FROM finance_user
<where>
<if test="name != null and name != ''">
AND name LIKE CONCAT('%', #{name}, '%')
</if>
<if test="wechatName != null and wechatName != ''">
AND wechat_name LIKE CONCAT('%', #{wechatName}, '%')
</if>
<if test="phone != null and phone != ''">
AND phone LIKE CONCAT('%', #{phone}, '%')
</if>
<if test="isActive != null and isActive != ''">
AND is_active = #{isActive}
</if>
</where>
ORDER BY create_time DESC
</select>
<!-- 添加财务人员 -->
<insert id="addFinanceUser" parameterType="com.saye.hospitalgd.model.FinanceUser">
INSERT INTO finance_user (
id,
name,
wechat_name,
phone,
open_id,
is_active,
create_time,
modify_time,
remark
) VALUES (
#{id},
#{name},
#{wechatName},
#{phone},
#{openId},
#{isActive},
#{createTime},
#{modifyTime},
#{remark}
)
</insert>
<!-- 修改财务人员 -->
<update id="updateFinanceUser" parameterType="com.saye.hospitalgd.model.FinanceUser">
UPDATE finance_user SET
name = #{name},
wechat_name = #{wechatName},
phone = #{phone},
open_id = #{openId},
is_active = #{isActive},
modify_time = #{modifyTime},
remark = #{remark}
WHERE id = #{id}
</update>
<!-- 删除财务人员 -->
<delete id="deleteFinanceUser" parameterType="String">
DELETE FROM finance_user WHERE id = #{id}
</delete>
<!-- 根据ID查询财务人员 -->
<select id="findFinanceUserById" parameterType="String" resultType="com.saye.hospitalgd.model.FinanceUser">
SELECT
id,
name,
wechat_name as wechatName,
phone,
open_id as openId,
is_active as isActive,
create_time as createTime,
modify_time as modifyTime,
remark
FROM finance_user
WHERE id = #{id}
</select>
<!-- 获取所有启用的财务人员 -->
<select id="findActiveFinanceUsers" resultType="com.saye.hospitalgd.model.FinanceUser">
SELECT
id,
name,
wechat_name as wechatName,
phone,
open_id as openId,
is_active as isActive,
create_time as createTime,
modify_time as modifyTime,
remark
FROM finance_user
WHERE is_active = '1'
ORDER BY create_time DESC
</select>
</mapper>

View File

@@ -188,7 +188,18 @@
align: 'center',
title: '支付方式',
width: 120,
sort: false
sort: false,
templet: function (d) {
let result = "";
for (let i = 0; i < payTypeList.length; i++) {
let obj = payTypeList[i];
if (d.PAYTYPE === obj.dicvalue) {
result = obj.dicname;
break;
}
}
return result || d.PAYTYPE;
}
},

View File

@@ -0,0 +1,351 @@
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>军保对账统计</title>
<link rel="stylesheet" th:href="@{/layui/css/layui.css}">
<link rel="stylesheet" th:href="@{/css/common.css}">
<!-- 引入组件库 -->
<script th:src="@{/layui/jquery-3.4.1.min.js}"></script>
<script th:src="@{/layui/layui.js}"></script>
<script th:src="@{/layui/lay/xmSelect/xm-select.js}"></script>
<script th:src="@{/js/common.js}"></script>
<style>
body {
background: #f2f2f2
}
#boxDiv {
background: #ffffff
}
#titleDiv {
overflow: visible;
}
.content {
padding: 0 8px;
}
.tableTitle {
height: 20px;
margin-top: 10px;
margin-bottom: 10px;
}
.tableTitle > * {
height: 20px;
line-height: 20px;
display: inline-block;
margin-right: 5px;
}
.tableName {
border-left: 3px solid #0000FF;
padding-left: 5px;
font-size: 16px;
font-weight: bold;
}
.selectDate {
font-size: 14px;
color: rgba(0, 0, 0, 0.65)
}
.summary-card {
background: #fff;
border: 1px solid #e6e6e6;
border-radius: 4px;
padding: 20px;
margin-bottom: 20px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.summary-item {
display: inline-block;
margin-right: 40px;
text-align: center;
}
.summary-label {
font-size: 14px;
color: #666;
margin-bottom: 5px;
}
.summary-value {
font-size: 24px;
font-weight: bold;
color: #1890ff;
}
.summary-money {
color: #f5222d;
}
</style>
</head>
<body class="layui-layout-body">
<div id="boxDiv" style="left:8px;right: 8px;bottom:8px;top:8px;position:absolute;">
<div class="toolbar" id="titleDiv">
<div style="display: inline-block;">
<form class="layui-form">
<div class="layui-inline">
<label class="layui-form-label">日期选择</label>
<div class="layui-input-inline">
<input type="text" class="layui-input formWidthTwo" id="searchDate" placeholder=" ~ "
th:value="${startTime+' ~ '+endTime}">
</div>
</div>
<div class="layui-inline">
<div class="layui-input-inline">
<input type="text" class="layui-input" style="width: 240px;" id="likeFiled"
placeholder="请输入平台交易号">
</div>
</div>
</form>
</div>
<div class="layui-inline">
<button class="layui-btn layui-btn-sm layui-btn-normal" data-type="search" id="search" onclick="search()"><i
class="layui-icon layui-icon-search"></i>查询
</button>
<button class="layui-btn layui-btn-sm layui-btn-normal" onclick="exportExcel()"><i class="layui-icon">&#xe67d;</i>导出
</button>
</div>
</div>
<div class="content">
<!-- 汇总信息卡片 -->
<div class="summary-card">
<div class="summary-item">
<div class="summary-label">今日军保交易笔数</div>
<div class="summary-value" id="todayCount">0</div>
</div>
<div class="summary-item">
<div class="summary-label">今日军保交易总金额</div>
<div class="summary-value summary-money" id="todayAmount">¥0.00</div>
</div>
</div>
<div class="tableTitle">
<span class="tableName">军保对账统计明细</span>
<span class="selectDate">&nbsp;</span>
</div>
<table id="demo" lay-filter="test"></table>
</div>
</div>
</body>
<script th:inline="javascript">
let bizTypeList = [[${bizTypeList}]];
let layer, laydate, table, tree, form;
layui.use(['element', 'table', 'laydate', 'layer', 'form', 'tree'], function () {
layer = layui.layer;
table = layui.table;
laydate = layui.laydate;
tree = layui.tree;
form = layui.form;
//表格加载
table.render({
elem: '#demo',
height: 'full-' + ($(".toolbar").height() + 160), // 增加高度以适应汇总卡片
title: '军保对账统计明细',
page: true,//开启分页
limit: 20,
limits: [20, 30, 50],
//toolbar: '', //开启工具栏,此处显示默认图标,可以自定义模板
defaultToolbar: [],
id: 'test',
//skin:'line',
even: 'true',
//size:'lg', 默认普通尺寸 sm 小 lg 大
cols: [
[
{field: 'HISOPERCODE', align: 'center', title: '操作员', width: 120, sort: false},
{
field: 'PAYMETHOD',
align: 'center',
title: '类别',
width: 120,
sort: false,
templet: function (d) {
let result = "";
if (d.PAYMETHOD === '1') {
result = '门诊';
} else if (d.PAYMETHOD === '2') {
result = '住院';
}
return result;
}
},
{
field: 'TRADINGSTATUS',
align: 'center',
title: '交易状态',
width: 120,
sort: false,
templet: function (d) {
let tradingstatusStr = "";
if (d.TRADINGSTATUS === '1') {
tradingstatusStr = '收款记录';
} else if (d.TRADINGSTATUS === '2') {
tradingstatusStr = '退款记录';
}
return tradingstatusStr;
}
},
{
field: 'BIZTYPE',
align: 'center',
title: '业务类型',
width: 120,
sort: false,
templet: function (d) {
let result = "";
for (let i = 0; i < bizTypeList.length; i++) {
let obj = bizTypeList[i];
if (d.BIZTYPE === obj.dicvalue) {
result = obj.dicname;
break;
}
}
return result;
}
},
{
field: 'PAYTYPE',
align: 'center',
title: '支付方式',
width: 120,
sort: false,
templet: function (d) {
return '医院垫支'; // 固定显示为医院垫支因为paytype=3
}
},
{field: 'TRADETIME', align: 'center', title: '时间', width: 120, sort: false},
{field: 'AMOUNT', align: 'center', title: '金额 ', width: 120, sort: false},
{field: 'PLATFORMTRANSID', align: 'center', title: '平台订单号 ', width: 120, sort: false},
{field: 'HISTRANSID', align: 'center', title: 'his订单号 ', width: 120, sort: false},
{field: 'HISTRANSID', align: 'center', title: 'HIS交易ID', width: 120, sort: false},
{field: 'PATIENTID', align: 'center', title: '患者id ', width: 120, sort: false},
{field: 'PATIENTNAME', align: 'center', title: '患者姓名 ', width: 120, sort: false},
{field: 'TRADE_DATE', align: 'center', title: '交易时间', width: 120, sort: false},
{field: 'SOURCE', align: 'center', title: '来源', width: 120, sort: false},
]
],
data: []
});
//时间控件
laydate.render({
elem: '#searchDate'
, type: 'date'
, range: '~'
});
search();
loadSummaryData(); // 加载汇总数据
});
//查询
function search(num) {
num = num === null ? 1 : num;
let url = "/militaryInsurance/findMilitaryInsuranceDetail";
let param = {};
param.likeFiled = $("#likeFiled").val();
let date = $("#searchDate").val();
if (date !== '') {
let time = date.split("~");
let startTime = time[0].trim();
let endTime = time[1].trim();
param.startTime = startTime;
param.endTime = endTime;
$(".selectDate").text(startTime + " ~ " + endTime);
}
table.reload('test', {
method: 'get',
url: url,
where: param,
page: {
curr: num
}
});
// 重新加载汇总数据
loadSummaryData();
}
// 加载汇总数据
function loadSummaryData() {
let url = "/militaryInsurance/findMilitaryInsuranceCountData";
let param = {};
let date = $("#searchDate").val();
if (date !== '') {
let time = date.split("~");
let startTime = time[0].trim();
let endTime = time[1].trim();
// 确保时间格式正确,添加时分秒
if (startTime.length === 10) { // 只有日期,没有时间
startTime += " 00:00:00";
}
if (endTime.length === 10) { // 只有日期,没有时间
endTime += " 23:59:59";
}
param.startTime = startTime;
param.endTime = endTime;
}
AjaxPostJson(url, param, function (data) {
if (data.errCode === "0") {
$("#todayCount").text(data.num || 0);
$("#todayAmount").text("¥" + (parseFloat(data.money || 0).toFixed(2)));
} else {
console.error("加载汇总数据失败:" + data.errMsg);
$("#todayCount").text("0");
$("#todayAmount").text("¥0.00");
}
});
}
//导出
function exportExcel() {
let url = "/militaryInsurance/exportMilitaryInsuranceDetail";
let param = {};
param.likeFiled = $("#likeFiled").val();
let date = $("#searchDate").val();
if (date !== '') {
let time = date.split("~");
let startTime = time[0].trim();
let endTime = time[1].trim();
param.startTime = startTime;
param.endTime = endTime;
}
let dowloadName = "军保对账统计明细";
param.dowloadName = dowloadName;
let load = layer.load();
AjaxPostJson(url, param, function (data) {
layer.close(load);
if (data.errCode === "0") {
let fileName = data.dlName;
location.href = '/download?fileName=' + fileName + '&dowloadName=' + dowloadName;
} else {
layer.alert(data.errMsg);
}
});
}
</script>
</html>

View File

@@ -0,0 +1,514 @@
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>退款统计报表</title>
<link rel="stylesheet" th:href="@{/layui/css/layui.css}">
<link rel="stylesheet" th:href="@{/css/common.css}">
<!-- 引入组件库 -->
<script th:src="@{/layui/jquery-3.4.1.min.js}"></script>
<script th:src="@{/layui/layui.js}"></script>
<script th:src="@{/js/echarts/echarts.common.min.js}"></script>
<script th:src="@{/js/common.js}"></script>
<style>
body {
background: #f2f2f2
}
#boxDiv {
background: #ffffff
}
#titleDiv {
overflow: visible;
}
.content {
padding: 0 8px;
}
.tableTitle {
height: 20px;
margin-top: 10px;
margin-bottom: 10px;
}
.tableTitle > * {
height: 20px;
line-height: 20px;
display: inline-block;
margin-right: 5px;
}
.tableName {
border-left: 3px solid #0000FF;
padding-left: 5px;
font-size: 16px;
font-weight: bold;
}
.selectDate {
font-size: 14px;
color: rgba(0, 0, 0, 0.65)
}
.layui-table-page {
height: 81px;
}
.summary-card {
background: #fff;
padding: 15px;
margin-bottom: 15px;
border-radius: 4px;
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
}
.summary-item {
display: inline-block;
margin-right: 30px;
padding: 10px 0;
}
.summary-label {
font-size: 14px;
color: #666;
}
.summary-value {
font-size: 24px;
font-weight: bold;
color: #333;
margin-top: 5px;
}
.chart-container {
background: #fff;
padding: 15px;
margin-bottom: 15px;
border-radius: 4px;
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
}
#trendChart {
width: 100%;
height: 400px;
}
</style>
</head>
<body class="layui-layout-body">
<div id="boxDiv" style="left:8px;right: 8px;bottom:8px;top:8px;position:absolute;overflow-y: auto;">
<div class="toolbar" id="titleDiv">
<div style="display: inline-block;">
<form class="layui-form">
<div class="layui-inline">
<label class="layui-form-label">支付方式</label>
<div class="layui-input-inline">
<select id="searchPayType" lay-filter="searchPayType">
<option value="">全部</option>
<option th:each="payTypeObj : ${payTypeList}" th:value="${payTypeObj.dicvalue}"
th:text="${payTypeObj.dicname}"></option>
</select>
</div>
</div>
<div class="layui-inline">
<label class="layui-form-label">退款类型</label>
<div class="layui-input-inline">
<select id="searchRefundType" lay-filter="searchRefundType">
<option value="">全部</option>
<option th:each="refundTypeObj : ${refundTypeList}" th:value="${refundTypeObj.dicvalue}"
th:text="${refundTypeObj.dicname}"></option>
</select>
</div>
</div>
<div class="layui-inline">
<label class="layui-form-label">日期选择</label>
<div class="layui-input-inline">
<input type="text" class="layui-input formWidthTwo" id="searchDate" placeholder=" ~ "
th:value="${startTime+' ~ '+endTime}">
</div>
</div>
<div class="layui-inline">
<button type="button" class="layui-btn layui-btn-sm layui-btn-normal" onclick="search()"><i
class="layui-icon layui-icon-search"></i>查询
</button>
<button type="button" class="layui-btn layui-btn-sm layui-btn-normal" onclick="exportExcel()"><i
class="layui-icon">&#xe67d;</i>导出
</button>
</div>
</form>
</div>
</div>
<!-- 汇总数据卡片 -->
<div class="content">
<div class="summary-card">
<div class="summary-item">
<div class="summary-label">退款总笔数</div>
<div class="summary-value" id="totalRefundCount">0</div>
</div>
<div class="summary-item">
<div class="summary-label">退款总金额(元)</div>
<div class="summary-value" id="totalRefundAmount">0.00</div>
</div>
<div class="summary-item">
<div class="summary-label">平均退款金额(元)</div>
<div class="summary-value" id="avgRefundAmount">0.00</div>
</div>
<div class="summary-item">
<div class="summary-label">涉及患者数</div>
<div class="summary-value" id="uniquePatients">0</div>
</div>
<div class="summary-item">
<div class="summary-label">操作员数</div>
<div class="summary-value" id="uniqueOperators">0</div>
</div>
</div>
<!-- 趋势图表 -->
<div class="chart-container">
<div class="tableTitle">
<span class="tableName">退款趋势分析</span>
</div>
<div id="trendChart"></div>
</div>
<!-- 统计维度选择 -->
<div class="layui-tab layui-tab-brief" lay-filter="dimensionTab">
<ul class="layui-tab-title">
<li class="layui-this" lay-id="all">全部统计</li>
<li lay-id="time">按日期统计</li>
<li lay-id="payType">按支付方式统计</li>
<li lay-id="refundType">按退款类型统计</li>
</ul>
</div>
<!-- 详细数据表格 -->
<div class="tableTitle">
<span class="tableName">退款统计明细</span>
<span class="selectDate">&nbsp;</span>
</div>
<table id="demo" lay-filter="test"></table>
</div>
</div>
</body>
<script th:inline="javascript">
let payTypeList = [[${payTypeList}]];
let refundTypeList = [[${refundTypeList}]];
let layer, laydate, table, form, element;
let currentDimension = 'all'; // 当前统计维度
let trendChart; // ECharts实例
layui.use(['element', 'table', 'laydate', 'layer', 'form'], function () {
layer = layui.layer;
table = layui.table;
laydate = layui.laydate;
form = layui.form;
element = layui.element;
// 初始化ECharts
trendChart = echarts.init(document.getElementById('trendChart'));
//表格加载
table.render({
elem: '#demo',
height: 500,
title: '退款统计明细',
page: true,//开启分页
limit: 20,
limits: [20, 30, 50, 100],
defaultToolbar: [],
id: 'test',
even: 'true',
cols: [
[
{field: 'DIMENSION_NAME', align: 'center', title: '统计维度', width: 120},
{field: 'DIMENSION_VALUE', align: 'center', title: '维度值', width: 150},
{field: 'REFUND_COUNT', align: 'center', title: '退款笔数', width: 120, sort: true},
{
field: 'TOTAL_REFUND_AMOUNT',
align: 'center',
title: '退款总金额(元)',
width: 150,
sort: true,
templet: function (d) {
return d.TOTAL_REFUND_AMOUNT ? parseFloat(d.TOTAL_REFUND_AMOUNT).toFixed(2) : '0.00';
}
},
{
field: 'AVG_REFUND_AMOUNT',
align: 'center',
title: '平均退款金额(元)',
width: 150,
sort: true,
templet: function (d) {
return d.AVG_REFUND_AMOUNT ? parseFloat(d.AVG_REFUND_AMOUNT).toFixed(2) : '0.00';
}
},
{
field: 'MAX_REFUND_AMOUNT',
align: 'center',
title: '最大退款金额(元)',
width: 150,
sort: true,
templet: function (d) {
return d.MAX_REFUND_AMOUNT ? parseFloat(d.MAX_REFUND_AMOUNT).toFixed(2) : '0.00';
}
},
{
field: 'MIN_REFUND_AMOUNT',
align: 'center',
title: '最小退款金额(元)',
width: 150,
sort: true,
templet: function (d) {
return d.MIN_REFUND_AMOUNT ? parseFloat(d.MIN_REFUND_AMOUNT).toFixed(2) : '0.00';
}
},
{field: 'UNIQUE_PATIENTS', align: 'center', title: '涉及患者数', width: 120, sort: true},
{field: 'UNIQUE_OPERATORS', align: 'center', title: '操作员数', width: 120, sort: true}
]
],
data: []
});
//时间控件
laydate.render({
elem: '#searchDate',
type: 'date',
range: '~'
});
// 监听维度切换
element.on('tab(dimensionTab)', function (data) {
currentDimension = this.getAttribute('lay-id');
search();
});
// 初始化加载数据
search();
loadSummaryData();
loadTrendData();
});
//查询
function search(num) {
num = num || 1;
let url = "/refundStatistics/findRefundStatistics";
let param = getSearchParams();
param.dimension = currentDimension;
console.log('查询参数:', param); // 调试日志
table.reload('test', {
method: 'get',
url: url,
where: param,
page: {
curr: num
},
done: function(res, curr, count) {
console.log('查询结果:', res); // 调试日志
console.log('数据条数:', count); // 调试日志
}
});
// 更新日期显示
let date = $("#searchDate").val();
if (date !== '') {
$(".selectDate").text(date);
}
// 同时更新汇总数据和趋势图
loadSummaryData();
loadTrendData();
}
// 加载汇总数据
function loadSummaryData() {
let url = "/refundStatistics/getRefundSummary";
let param = getSearchParams();
console.log('加载汇总数据,参数:', param); // 调试日志
$.ajax({
url: url,
type: 'get',
data: param,
success: function (data) {
console.log('汇总数据返回:', data); // 调试日志
if (data.code === 0) {
let summaryData = data.data;
$("#totalRefundCount").text(summaryData.TOTAL_REFUND_COUNT || 0);
$("#totalRefundAmount").text(summaryData.TOTAL_REFUND_AMOUNT ? parseFloat(summaryData.TOTAL_REFUND_AMOUNT).toFixed(2) : '0.00');
$("#avgRefundAmount").text(summaryData.AVG_REFUND_AMOUNT ? parseFloat(summaryData.AVG_REFUND_AMOUNT).toFixed(2) : '0.00');
$("#uniquePatients").text(summaryData.UNIQUE_PATIENTS || 0);
$("#uniqueOperators").text(summaryData.UNIQUE_OPERATORS || 0);
} else {
console.error('汇总数据查询失败:', data.msg);
}
},
error: function(xhr, status, error) {
console.error('汇总数据请求失败:', error);
}
});
}
// 加载趋势数据
function loadTrendData() {
let url = "/refundStatistics/getRefundTrend";
let param = getSearchParams();
console.log('加载趋势数据,参数:', param); // 调试日志
$.ajax({
url: url,
type: 'get',
data: param,
success: function (data) {
console.log('趋势数据返回:', data); // 调试日志
if (data.code === 0) {
let trendData = data.data;
console.log('趋势数据条数:', trendData.length); // 调试日志
renderTrendChart(trendData);
} else {
console.error('趋势数据查询失败:', data.msg);
}
},
error: function(xhr, status, error) {
console.error('趋势数据请求失败:', error);
}
});
}
// 渲染趋势图表
function renderTrendChart(data) {
let dates = [];
let counts = [];
let amounts = [];
for (let i = 0; i < data.length; i++) {
dates.push(data[i].TREND_DATE);
counts.push(data[i].DAILY_REFUND_COUNT);
amounts.push(data[i].DAILY_REFUND_AMOUNT ? parseFloat(data[i].DAILY_REFUND_AMOUNT).toFixed(2) : 0);
}
let option = {
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'cross',
crossStyle: {
color: '#999'
}
}
},
legend: {
data: ['退款笔数', '退款金额']
},
xAxis: [
{
type: 'category',
data: dates,
axisPointer: {
type: 'shadow'
}
}
],
yAxis: [
{
type: 'value',
name: '退款笔数',
min: 0,
axisLabel: {
formatter: '{value}'
}
},
{
type: 'value',
name: '退款金额(元)',
min: 0,
axisLabel: {
formatter: '{value}'
}
}
],
series: [
{
name: '退款笔数',
type: 'bar',
data: counts,
itemStyle: {
color: '#5470c6'
}
},
{
name: '退款金额',
type: 'line',
yAxisIndex: 1,
data: amounts,
itemStyle: {
color: '#ee6666'
}
}
]
};
trendChart.setOption(option);
}
// 获取查询参数
function getSearchParams() {
let param = {};
param.payType = $("#searchPayType").val();
param.refundType = $("#searchRefundType").val();
let date = $("#searchDate").val();
if (date !== '') {
let time = date.split("~");
param.startTime = time[0].trim();
param.endTime = time[1].trim();
}
return param;
}
//导出
function exportExcel() {
let url = "/refundStatistics/exportRefundStatistics";
let param = getSearchParams();
param.dimension = currentDimension;
let load = layer.load();
$.ajax({
url: url,
type: 'get',
data: param,
success: function (data) {
layer.close(load);
if (data.code === 0) {
let fileName = data.fileName;
let dowloadName = "退款统计报表";
location.href = '/download?fileName=' + fileName + '&dowloadName=' + dowloadName;
} else {
layer.alert(data.msg);
}
},
error: function () {
layer.close(load);
layer.alert('导出失败,请稍后重试');
}
});
}
// 窗口大小改变时重新渲染图表
window.addEventListener('resize', function () {
if (trendChart) {
trendChart.resize();
}
});
</script>
</html>

View File

@@ -0,0 +1,349 @@
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>财务人员管理</title>
<link rel="stylesheet" th:href="@{/layui/css/layui.css}">
<link rel="stylesheet" th:href="@{/css/common.css}">
<script th:src="@{/layui/jquery-3.4.1.min.js}"></script>
<script th:src="@{/layui/layui.js}"></script>
<script th:src="@{/js/common.js}"></script>
<style>
body {
background: #f2f2f2
}
#boxDiv {
background: #ffffff
}
#titleDiv {
overflow: visible;
}
.content {
padding: 0 8px;
}
.tableTitle {
height: 20px;
margin-top: 10px;
margin-bottom: 10px;
}
.tableTitle > * {
height: 20px;
line-height: 20px;
display: inline-block;
margin-right: 5px;
}
.tableName {
border-left: 3px solid #0000FF;
padding-left: 5px;
font-size: 16px;
font-weight: bold;
}
.layui-table-page {
height: 81px;
}
</style>
</head>
<body class="layui-layout-body">
<div id="boxDiv" style="left:8px;right: 8px;bottom:8px;top:8px;position:absolute;">
<div class="toolbar" id="titleDiv">
<div style="display: inline-block;">
<form class="layui-form">
<div class="layui-inline">
<label class="layui-form-label">姓名</label>
<div class="layui-input-inline formWidthTwo">
<input type="text" class="layui-input" id="name" placeholder="请输入姓名">
</div>
</div>
<div class="layui-inline">
<label class="layui-form-label">微信名</label>
<div class="layui-input-inline formWidthTwo">
<input type="text" class="layui-input" id="wechatName" placeholder="请输入微信名">
</div>
</div>
<div class="layui-inline">
<label class="layui-form-label">手机号</label>
<div class="layui-input-inline formWidthTwo">
<input type="text" class="layui-input" id="phone" placeholder="请输入手机号">
</div>
</div>
<div class="layui-inline">
<label class="layui-form-label">状态</label>
<div class="layui-input-inline formWidthTwo">
<select id="isActive">
<option value="">全部</option>
<option value="1">启用</option>
<option value="0">禁用</option>
</select>
</div>
</div>
</form>
</div>
<div class="layui-inline">
<button class="layui-btn layui-btn-sm layui-btn-normal" data-type="search" id="search" onclick="search()"><i
class="layui-icon layui-icon-search"></i>查询
</button>
<button class="layui-btn layui-btn-sm layui-btn-normal" onclick="addFinanceUser()"><i class="layui-icon">&#xe654;</i>新增
</button>
</div>
</div>
<div class="content">
<div class="tableTitle">
<span class="tableName">财务人员管理</span>
</div>
<table id="demo" lay-filter="test"></table>
</div>
</div>
<!-- 新增/编辑财务人员弹窗 -->
<div id="financeUserForm" style="display:none;">
<form id="financeUserFormData" class="layui-form" style="margin-top:20px;">
<input type="hidden" id="id">
<div class="layui-form-item">
<label class="layui-form-label">姓名</label>
<div class="layui-input-block">
<input type="text" id="formName" required lay-verify="required" autocomplete="off"
placeholder="请输入姓名" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">微信名</label>
<div class="layui-input-block">
<input type="text" id="formWechatName" autocomplete="off"
placeholder="请输入微信名" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">手机号</label>
<div class="layui-input-block">
<input type="text" id="formPhone" autocomplete="off"
placeholder="请输入手机号" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">状态</label>
<div class="layui-input-block">
<input type="radio" name="formIsActive" value="1" title="启用" checked>
<input type="radio" name="formIsActive" value="0" title="禁用">
</div>
</div>
<div class="layui-form-item layui-form-text">
<label class="layui-form-label">备注</label>
<div class="layui-input-block">
<textarea id="formRemark" placeholder="请输入备注" class="layui-textarea"></textarea>
</div>
</div>
</form>
</div>
<script>
let layer, table, form;
layui.use(['element', 'table', 'layer', 'form'], function () {
layer = layui.layer;
table = layui.table;
form = layui.form;
//表格加载
table.render({
elem: '#demo',
height: 'full-' + ($(".toolbar").height() + 60),
title: '财务人员管理',
page: true,//开启分页
limit: 20,
limits: [20, 30, 50],
defaultToolbar: [],
id: 'test',
even: 'true',
cols: [
[
{field: 'name', align: 'center', title: '姓名', width: 120, sort: false},
{field: 'wechatName', align: 'center', title: '微信名', width: 150, sort: false},
{field: 'phone', align: 'center', title: '手机号', width: 120, sort: false},
{field: 'openId', align: 'center', title: 'OpenID', width: 200, sort: false,
templet: function (d) {
return d.openId ? d.openId.substring(0, 10) + '...' : '未获取';
}},
{
field: 'isActive', align: 'center', title: '状态', width: 80, sort: false,
templet: function (d) {
return d.isActive === '1' ? '<span style="color: green;">启用</span>' : '<span style="color: red;">禁用</span>';
}
},
{field: 'createTime', align: 'center', title: '创建时间', width: 150, sort: false},
{field: 'remark', align: 'center', title: '备注', width: 200, sort: false},
{
field: 'id', align: 'center', title: '操作', width: 200, sort: false,
templet: function (d) {
return '<button class="layui-btn layui-btn-xs" onclick="editFinanceUser(\'' + d.id + '\')">编辑</button>' +
'<button class="layui-btn layui-btn-xs layui-btn-danger" onclick="deleteFinanceUser(\'' + d.id + '\')">删除</button>';
}
}
]
],
data: []
});
// 初始加载数据
search();
});
//查询
function search(num) {
num = num === null ? 1 : num;
let url = "/financeUser/findFinanceUserPageList";
let param = {};
param.name = $("#name").val();
param.wechatName = $("#wechatName").val();
param.phone = $("#phone").val();
param.isActive = $("#isActive").val();
table.reload('test', {
method: 'get',
url: url,
where: param,
page: {
curr: num
}
});
}
//新增财务人员
function addFinanceUser() {
clearForm();
layer.open({
type: 1,
title: "新增财务人员",
shade: 0,
area: ['500px', '500px'],
offset: 'auto',
content: $("#financeUserForm"),
btn: ['确定', '取消'],
btnAlign: 'c',
yes: function (index, layero) {
saveFinanceUser();
},
btn2: function () {
layer.closeAll();
}
});
}
//编辑财务人员
function editFinanceUser(id) {
let url = "/financeUser/findFinanceUserById?id=" + id;
$.ajax({
type: "get",
url: url,
success: function (data) {
if (data.code === 0) {
let financeUser = data.data;
$("#id").val(financeUser.id);
$("#formName").val(financeUser.name);
$("#formWechatName").val(financeUser.wechatName);
$("#formPhone").val(financeUser.phone);
$("#formRemark").val(financeUser.remark);
//设置状态
$("input[name='formIsActive'][value='" + financeUser.isActive + "']").prop("checked", true);
form.render('radio');
layer.open({
type: 1,
title: "编辑财务人员",
shade: 0,
area: ['500px', '500px'],
offset: 'auto',
content: $("#financeUserForm"),
btn: ['确定', '取消'],
btnAlign: 'c',
yes: function (index, layero) {
saveFinanceUser();
},
btn2: function () {
layer.closeAll();
}
});
} else {
layer.alert(data.msg);
}
},
error: function(xhr, status, error) {
layer.alert("查询失败: " + error);
}
});
}
//保存财务人员
function saveFinanceUser() {
let id = $("#id").val();
let url = id ? "/financeUser/updateFinanceUser" : "/financeUser/addFinanceUser";
let param = {
id: id,
name: $("#formName").val(),
wechatName: $("#formWechatName").val(),
phone: $("#formPhone").val(),
isActive: $("input[name='formIsActive']:checked").val(),
remark: $("#formRemark").val()
};
AjaxPostJson(url, param, function (data) {
if (data.code === 0) {
layer.msg(data.msg, {time: 1000}, function () {
layer.closeAll();
search();
});
} else {
layer.alert(data.msg);
}
});
}
//删除财务人员
function deleteFinanceUser(id) {
layer.confirm('确定要删除这个财务人员吗?', {icon: 3, title: '提示'}, function (index) {
let url = "/financeUser/deleteFinanceUser?id=" + id;
$.ajax({
type: "post",
url: url,
success: function (data) {
if (data.code === 0) {
layer.msg(data.msg, {time: 1000}, function () {
search();
});
} else {
layer.alert(data.msg);
}
},
error: function(xhr, status, error) {
layer.alert("删除失败: " + error);
}
});
layer.close(index);
});
}
//清空表单
function clearForm() {
$("#id").val("");
$("#formName").val("");
$("#formWechatName").val("");
$("#formPhone").val("");
$("#formRemark").val("");
$("input[name='formIsActive'][value='1']").prop("checked", true);
form.render('radio');
}
</script>
</body>
</html>

View File

@@ -0,0 +1,69 @@
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>财务人员管理测试</title>
<link rel="stylesheet" th:href="@{/layui/css/layui.css}">
<script th:src="@{/layui/jquery-3.4.1.min.js}"></script>
<script th:src="@{/layui/layui.js}"></script>
<script th:src="@{/js/common.js}"></script>
</head>
<body>
<div style="padding: 20px;">
<h2>财务人员管理功能测试</h2>
<div style="margin: 20px 0;">
<button class="layui-btn" onclick="testQuery()">测试查询</button>
<button class="layui-btn layui-btn-normal" onclick="testAdd()">测试新增</button>
</div>
<div id="result" style="margin-top: 20px; padding: 10px; border: 1px solid #ccc; min-height: 100px;">
测试结果将显示在这里...
</div>
</div>
<script>
function testQuery() {
document.getElementById('result').innerHTML = '正在测试查询功能...';
AjaxPostJson('/financeUser/findFinanceUserPageList', {
page: 1,
limit: 10
}, function(data) {
document.getElementById('result').innerHTML =
'<h3>查询测试结果:</h3>' +
'<pre>' + JSON.stringify(data, null, 2) + '</pre>';
});
}
function testAdd() {
document.getElementById('result').innerHTML = '正在测试新增功能...';
AjaxPostJson('/financeUser/addFinanceUser', {
name: '测试用户',
wechatName: '测试微信名',
phone: '13800138000',
isActive: '1',
remark: '测试备注'
}, function(data) {
document.getElementById('result').innerHTML =
'<h3>新增测试结果:</h3>' +
'<pre>' + JSON.stringify(data, null, 2) + '</pre>';
});
}
</script>
</body>
</html>