This commit is contained in:
=
2026-05-26 15:54:34 +08:00
10 changed files with 585 additions and 40 deletions
@@ -14,6 +14,7 @@ import com.wisedu.casp.sdk.api.mc.recallMsg.RecallMsgResponse;
import com.wisedu.casp.sdk.core.Client;
import com.wisedu.casp.sdk.core.DefaultClient;
import lombok.extern.slf4j.Slf4j;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.json.Json;
import org.nutz.lang.Lang;
@@ -31,6 +32,9 @@ import java.util.List;
@IocBean
public class SmsJshvcServiceImpl implements SmsService {
@Inject
private TodoPlatformConfig todoPlatformConfig;
@Override
public void sendSingleMessage(String loginName, String content) {
this.sendSingleMessage(loginName, "智慧工会", content);
@@ -109,7 +113,7 @@ public class SmsJshvcServiceImpl implements SmsService {
@Override
public void recallMsg(String msgId) {
try {
Client client = new DefaultClient(TodoPlatformConfig.TODO_PLATFORM_BASE_URL, TodoPlatformConfig.TODO_APP_ID, TodoPlatformConfig.TODO_APP_SECRET);
Client client = createClient();
RecallMsgRequest request = new RecallMsgRequest();
request.setMsgId(msgId);
@@ -143,7 +147,7 @@ public class SmsJshvcServiceImpl implements SmsService {
}
try {
// 创建客户端
Client client = new DefaultClient(TodoPlatformConfig.TODO_PLATFORM_BASE_URL, TodoPlatformConfig.TODO_APP_ID, TodoPlatformConfig.TODO_APP_SECRET);
Client client = createClient();
SendMsgRequest request = new SendMsgRequest();
request.setSubject(title);
request.setContent(content);
@@ -179,4 +183,15 @@ public class SmsJshvcServiceImpl implements SmsService {
log.error("发送企业微信通知失败", e);
}
}
/**
* 创建待办平台消息客户端,平台地址、appId、appSecret 均从 application 配置读取。
*/
private Client createClient() {
return new DefaultClient(
todoPlatformConfig.getTodoPlatformBaseUrl(),
todoPlatformConfig.getTodoAppId(),
todoPlatformConfig.getTodoAppSecret()
);
}
}
@@ -63,7 +63,6 @@ public class SysLoginController {
@Inject
protected PropertiesProxy conf;
/*
@At("")
@Ok("re")
@ApiOperation("用户本地登录页面")
@@ -71,7 +70,6 @@ public class SysLoginController {
public String login(HttpServletRequest req, HttpSession session) {
return "beetl:/platform/sys/login.html";
}
*/
@At
@Ok("re")
@@ -1,30 +1,48 @@
package com.budwk.app.todo.platform;
import lombok.Getter;
import org.nutz.ioc.impl.PropertiesProxy;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
/**
* @ClassName TodoPlatformConfig
* @Author JyuHsin
* @Date 2026/2/25 16:26
* @Version 1.0
* @Description TODO
* @Description 待办平台配置,从 application 配置文件读取平台地址、鉴权参数和接口路径。
*/
@IocBean(create = "init")
@Getter
public class TodoPlatformConfig {
// token 获取
public static final String TOKEN_URL = "/accessToken";
// 待办平台基础地址
public static final String TODO_PLATFORM_BASE_URL = "https://gateway.jshvc.edu.cn";
// 待办平台新增或者修改
public static final String TODO_PLATFORM_SAVE_OR_UPDATE_URL = "/saveOrUpdate";
// 待办平台修改地址
public static final String TODO_PLATFORM_MODIFY_URL = "/modifyInfo";
// 待办平台删除任务地址
public static final String TODO_PLATFORM_DELETE_TASK_URL = "/delete";
// 待办平台删除实例地址
public static final String TODO_PLATFORM_DELETE_INSTANCE_URL = "/realDelete";
@Inject
private PropertiesProxy conf;
private String tokenUrl;
private String todoPlatformBaseUrl;
private String todoPlatformSaveOrUpdateUrl;
private String todoPlatformModifyUrl;
private String todoPlatformDeleteTaskUrl;
private String todoPlatformDeleteInstanceUrl;
private String todoAppId;
private String todoAppSecret;
private String redisKeyTodoAccessToken;
private Long todoRequestTimeout;
public void init() {
// 配置项缺失时保留原硬编码默认值,避免未补齐配置导致已有环境启动失败。
tokenUrl = conf.get("todo-platform.token-url", "/accessToken");
todoPlatformBaseUrl = conf.get("todo-platform.base-url", "https://gateway.jshvc.edu.cn");
todoPlatformSaveOrUpdateUrl = conf.get("todo-platform.save-or-update-url", "/saveOrUpdate");
todoPlatformModifyUrl = conf.get("todo-platform.modify-url", "/modifyInfo");
todoPlatformDeleteTaskUrl = conf.get("todo-platform.delete-task-url", "/delete");
todoPlatformDeleteInstanceUrl = conf.get("todo-platform.delete-instance-url", "/realDelete");
todoAppId = conf.get("todo-platform.app-id", "XXXXXXXXXXXXXXXXXXXXXX");
todoAppSecret = conf.get("todo-platform.app-secret", "XXXXXXXXXXXXXXXXXXXXXX");
redisKeyTodoAccessToken = "todo:token:";
todoRequestTimeout = conf.getLong("todo-platform.request-timeout", 5000L);
}
public static final String TODO_APP_ID = "XXXXXXXXXXXXXXXXXXXXXX";
public static final String TODO_APP_SECRET = "XXXXXXXXXXXXXXXXXXXXXX";
public static final String REDIS_KEY_TODO_ACCESS_TOKEN = "todo:token:";
public static final Long TODO_REQUEST_TIMEOUT = 5000L;
}
@@ -58,6 +58,8 @@ public class TodoServiceImpl implements TodoService {
private RedisService redisService;
@Inject
private FlowEngine flowEngine;
@Inject
private TodoPlatformConfig todoPlatformConfig;
@Override
public void saveTask(ProcessTask task) {
@@ -79,7 +81,7 @@ public class TodoServiceImpl implements TodoService {
List<Processor> processorList = taskActors.stream().map(o -> new Processor(o.getActorAccount())).toList();
// 创建客户端
Client client = new DefaultClient(TodoPlatformConfig.TODO_PLATFORM_BASE_URL, TodoPlatformConfig.TODO_APP_ID, TodoPlatformConfig.TODO_APP_SECRET);
Client client = createClient();
// 创建请求对象
SaveOrUpdateRequest request = new SaveOrUpdateRequest();
// 发起任务
@@ -111,7 +113,7 @@ public class TodoServiceImpl implements TodoService {
insertModel.setApplicationModel(applicationModel);
request.setInsertModel(Collections.singletonList(insertModel));
/*Client channelClient = new DefaultClient(TodoPlatformConfig.TODO_PLATFORM_BASE_URL, TodoPlatformConfig.TODO_APP_ID, TodoPlatformConfig.TODO_APP_SECRET);
/*Client channelClient = createClient();
GetEnableChannelByTypeRequest channelByTypeRequest = new GetEnableChannelByTypeRequest();
channelByTypeRequest.setChannelType(ChannelType_Constants.QYWECHAT);
GetEnableChannelByTypeResponse channelByTypeResponse = channelClient.execute(channelByTypeRequest);
@@ -152,7 +154,7 @@ public class TodoServiceImpl implements TodoService {
ProcessTaskVO taskVO = flowEngine.processTaskService().findById(task.getId());
// 创建客户端
Client client = new DefaultClient(TodoPlatformConfig.TODO_PLATFORM_BASE_URL, TodoPlatformConfig.TODO_APP_ID, TodoPlatformConfig.TODO_APP_SECRET);
Client client = createClient();
// 创建请求对象
SaveOrUpdateRequest request = new SaveOrUpdateRequest();
@@ -193,7 +195,7 @@ public class TodoServiceImpl implements TodoService {
log.info("修改待办任务,任务数据:{}", Json.toJson(task));
try {
// 创建客户端
Client client = new DefaultClient(TodoPlatformConfig.TODO_PLATFORM_BASE_URL, TodoPlatformConfig.TODO_APP_ID, TodoPlatformConfig.TODO_APP_SECRET);
Client client = createClient();
// 创建请求对象
TaskModifyInfoRequest request = new TaskModifyInfoRequest();
// 需要修改什么后面扩展吧
@@ -218,7 +220,7 @@ public class TodoServiceImpl implements TodoService {
log.info("删除待办任务,任务ID{}", taskId);
try {
// 创建客户端
Client client = new DefaultClient(TodoPlatformConfig.TODO_PLATFORM_BASE_URL, TodoPlatformConfig.TODO_APP_ID, TodoPlatformConfig.TODO_APP_SECRET);
Client client = createClient();
// 创建请求对象
TaskRealDeleteRequest request = new TaskRealDeleteRequest();
request.setTaskId(taskId);
@@ -240,7 +242,7 @@ public class TodoServiceImpl implements TodoService {
log.info("删除待办流程,实例ID{}", processInstanceId);
try {
// 创建客户端
Client client = new DefaultClient(TodoPlatformConfig.TODO_PLATFORM_BASE_URL, TodoPlatformConfig.TODO_APP_ID, TodoPlatformConfig.TODO_APP_SECRET);
Client client = createClient();
// 创建请求对象
ProcessInstanceRealDeleteRequest request = new ProcessInstanceRealDeleteRequest();
request.setProcessInstanceId(processInstanceId);
@@ -252,4 +254,15 @@ public class TodoServiceImpl implements TodoService {
log.error("删除流程实例失败", e);
}
}
/**
* 创建待办平台 SDK 客户端,平台地址、appId、appSecret 均从 application 配置读取。
*/
private Client createClient() {
return new DefaultClient(
todoPlatformConfig.getTodoPlatformBaseUrl(),
todoPlatformConfig.getTodoAppId(),
todoPlatformConfig.getTodoAppSecret()
);
}
}
@@ -15,6 +15,7 @@ import com.budwk.app.flow.service.FlowCommonService;
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
import com.budwk.app.zhgh.activity.declarereimbursement.reimbursement.models.ActivityReimbursementInfo;
import com.budwk.app.zhgh.activity.declarereimbursement.reimbursement.service.ActivityReimbursementService;
import com.budwk.app.zhgh.activity.declarereimbursement.vo.ActivityReimbursementBudgetSummaryVO;
import com.budwk.app.zhgh.activity.declarereimbursement.vo.CommonPageParam;
import com.budwk.app.zhgh.dayofficework.outlay.activityBudget.models.ActivityBudget;
import io.swagger.annotations.ApiOperation;
@@ -67,6 +68,7 @@ public class ActivityReimbursementDeputySchoolUnionController {
info.userName,
info.mobile,
info.loginName,
info.year,
info.applyTime,
info.activityName,
info.activityAddress,
@@ -78,6 +80,8 @@ public class ActivityReimbursementDeputySchoolUnionController {
info.activityNumber,
info.activityReimbursementMode,
info.outlayManageSource,
info.unionId,
info.clubId,
info.deductionBudgetId,
info.actualMoney,
outlay.budgetReimburseType,
@@ -142,6 +146,25 @@ public class ActivityReimbursementDeputySchoolUnionController {
return Result.success().addData(pagination);
}
@At
@ApiOperation("预算/报销汇总表")
@SaCheckPermission("activityReimbursement.deputySchoolUnion")
public Result budgetSummaryData(CommonPageParam pageParam) {
if (pageParam.getYear() == null) {
return Result.error("请选择年度");
}
if (!List.of("ACTIVITY_BUDGET_TYPE_TWO", "ACTIVITY_BUDGET_TYPE_THREE").contains(pageParam.getOutlayManageSource())) {
return Result.error("请选择分工会或协会");
}
List<ActivityReimbursementBudgetSummaryVO> list = activityReimbursementService.getBudgetSummary(
pageParam.getYear(),
pageParam.getUnionId(),
pageParam.getClubId(),
pageParam.getOutlayManageSource()
);
return Result.success(list);
}
@At
@SLog(tag = "报销", msg = "校工会副主席审核了一条记录")
@@ -2,7 +2,11 @@ package com.budwk.app.zhgh.activity.declarereimbursement.reimbursement.service;
import com.budwk.app.base.result.Result;
import com.budwk.app.base.service.BaseService;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.zhgh.activity.declarereimbursement.reimbursement.models.ActivityReimbursementInfo;
import com.budwk.app.zhgh.activity.declarereimbursement.vo.ActivityReimbursementRecordVO;
import com.budwk.app.zhgh.activity.declarereimbursement.vo.ActivityReimbursementBudgetSummaryVO;
import com.budwk.app.zhgh.activity.declarereimbursement.vo.CommonPageParam;
import org.nutz.dao.sql.Sql;
import org.nutz.lang.util.NutMap;
@@ -34,4 +38,30 @@ public interface ActivityReimbursementService extends BaseService<ActivityReimbu
*/
Result bxAddValidate(String deductionBudgetId, BigDecimal moneyBig, String outlayManageSource, String clubId);
/**
* 查询预算/报销汇总表。
* @param year 汇总年度
* @param unionId 分工会ID,分工会维度筛选时传入
* @param clubId 协会ID,协会维度筛选时传入
* @param outlayManageSource 经费类型,分工会传 ACTIVITY_BUDGET_TYPE_TWO,协会传 ACTIVITY_BUDGET_TYPE_THREE
* @return 指定维度下的预算金额与已完成报销金额汇总列表
*/
List<ActivityReimbursementBudgetSummaryVO> getBudgetSummary(Integer year, String unionId, String clubId, String outlayManageSource);
/**
* 分页查询所有已完成的费用报销记录。
*
* @param pageParam 查询参数,包含年度、申报人关键字、报销模式、经费类型、预算条目、分工会和协会筛选条件
* @return 分页 VO,列表字段用于前端展示申报人、项目名称、报销模式、经费类型、预算条目、报销金额、申报单位/协会和申报时间
*/
Pagination<ActivityReimbursementRecordVO> queryReimbursementRecordPage(CommonPageParam pageParam);
/**
* 按费用报销记录列表的同一套查询条件统计报销金额合计。
*
* @param pageParam 查询参数,包含年度、申报人关键字、报销模式、经费类型、预算条目、分工会和协会筛选条件
* @return 当前查询条件下所有已完成报销记录的金额合计
*/
BigDecimal queryReimbursementRecordSummaryMoney(CommonPageParam pageParam);
}
@@ -3,18 +3,24 @@ package com.budwk.app.zhgh.activity.declarereimbursement.reimbursement.service.i
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.constant.RoleConstant;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.result.Result;
import com.budwk.app.base.service.impl.BaseServiceImpl;
import com.budwk.app.base.utils.PageUtil;
import com.budwk.app.web.commons.auth.utils.AuthUtil;
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
import com.budwk.app.zhgh.activity.declarereimbursement.reimbursement.models.ActivityReimbursementInfo;
import com.budwk.app.zhgh.activity.declarereimbursement.reimbursement.service.ActivityReimbursementService;
import com.budwk.app.zhgh.activity.declarereimbursement.vo.ActivityReimbursementRecordVO;
import com.budwk.app.zhgh.activity.declarereimbursement.vo.ActivityReimbursementBudgetSummaryVO;
import com.budwk.app.zhgh.activity.declarereimbursement.vo.CommonPageParam;
import com.budwk.app.zhgh.dayofficework.outlay.activityBudget.models.ActivityBudget;
import io.swagger.annotations.ApiOperation;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Sql;
import org.nutz.dao.util.cri.SqlExpressionGroup;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.Lang;
import org.nutz.lang.util.NutMap;
@@ -155,4 +161,217 @@ public class ActivityReimbursementServiceImpl extends BaseServiceImpl<ActivityRe
return null;
}
@Override
public List<ActivityReimbursementBudgetSummaryVO> getBudgetSummary(Integer year, String unionId, String clubId, String outlayManageSource) {
if (Objects.equals("ACTIVITY_BUDGET_TYPE_TWO", outlayManageSource)) {
return getUnionBudgetSummary(year, unionId);
}
if (Objects.equals("ACTIVITY_BUDGET_TYPE_THREE", outlayManageSource)) {
return getClubBudgetSummary(year, clubId);
}
return List.of();
}
@Override
public Pagination<ActivityReimbursementRecordVO> queryReimbursementRecordPage(CommonPageParam pageParam) {
Sql sql = Sqls.create("""
SELECT
info.id,
info.userName,
info.loginName,
info.activityName,
info.activityReimbursementMode,
info.outlayManageSource,
outlay.budgetReimburseType,
info.actualMoney,
info.declareUnitName,
info.applyTime,
ins.id AS instanceId,
ins.state AS instanceState,
ins.processDefineId AS instanceProcessDefineId
FROM
activity_reimbursement_info info
LEFT JOIN wf_process_instance ins ON ins.businessNo = info.id
LEFT JOIN activity_budget outlay ON outlay.id = info.deductionBudgetId
$condition
""");
Cnd cnd = buildReimbursementRecordCondition(pageParam);
String orderName = getReimbursementRecordOrderName(pageParam.getPageOrderName());
if (StrUtil.isAllBlank(orderName, pageParam.getPageOrderBy())) {
cnd.desc("info.applyTime");
} else {
cnd.orderBy(orderName, PageUtil.getOrder(pageParam.getPageOrderBy()));
}
sql.setCondition(cnd);
return listPageVO(pageParam, sql, ActivityReimbursementRecordVO.class);
}
@Override
public BigDecimal queryReimbursementRecordSummaryMoney(CommonPageParam pageParam) {
Sql sql = Sqls.create("""
SELECT
COALESCE(SUM(info.actualMoney), 0) AS summaryMoney
FROM
activity_reimbursement_info info
LEFT JOIN wf_process_instance ins ON ins.businessNo = info.id
LEFT JOIN activity_budget outlay ON outlay.id = info.deductionBudgetId
$condition
""");
sql.setCondition(buildReimbursementRecordCondition(pageParam));
NutMap map = fetchMap(sql);
Object summaryMoney = map.get("summaryMoney");
return summaryMoney == null ? BigDecimal.ZERO : new BigDecimal(summaryMoney.toString());
}
/**
* 前端表格排序字段白名单,避免任意字段拼接到 SQL。
*/
private String getReimbursementRecordOrderName(String pageOrderName) {
if (StrUtil.isBlank(pageOrderName)) {
return "";
}
if (Objects.equals("loginName", pageOrderName)) {
return "info.loginName";
}
if (Objects.equals("declareUnitName", pageOrderName)) {
return "info.declareUnitName";
}
if (Objects.equals("applyTime", pageOrderName)) {
return "info.applyTime";
}
if (Objects.equals("actualMoney", pageOrderName)) {
return "info.actualMoney";
}
return "";
}
/**
* 费用报销记录查询条件,列表和汇总金额共用,保证两处统计口径一致。
*/
private Cnd buildReimbursementRecordCondition(CommonPageParam pageParam) {
Cnd cnd = Cnd.NEW();
cnd.and("ins.state", "=", 20);
cnd.andEX("YEAR(info.applyTime)", "=", pageParam.getYear());
cnd.andEX("info.activityReimbursementMode", "=", pageParam.getActivityReimbursementMode());
cnd.andEX("info.outlayManageSource", "=", pageParam.getOutlayManageSource());
cnd.andEX("outlay.budgetReimburseType", "=", pageParam.getBudgetReimburseType());
cnd.andEX("info.unionId", "=", pageParam.getUnionId());
cnd.andEX("info.clubId", "=", pageParam.getClubId());
if (StrUtil.isNotBlank(pageParam.getSearchKeyword())) {
SqlExpressionGroup seg = new SqlExpressionGroup();
seg.orLike("info.userName", pageParam.getSearchKeyword());
seg.orLike("info.loginName", pageParam.getSearchKeyword());
cnd.and(seg);
}
return cnd;
}
/**
* 分工会维度汇总。
* 预算金额取指定年度审核通过的分工会活动预算,费用报销汇总取指定年度已完成流程的报销实际金额。
*/
private List<ActivityReimbursementBudgetSummaryVO> getUnionBudgetSummary(Integer year, String unionId) {
Sql sql = Sqls.create("""
SELECT
@year AS year,
su.name AS name,
COALESCE(budget.totalBudgetMoney, 0) AS budgetMoney,
COALESCE(reimburse.actualMoney, 0) AS reimbursementMoney
FROM
sys_union su
LEFT JOIN (
SELECT
ab.unionId,
SUM(ab.totalBudgetMoney) AS totalBudgetMoney
FROM
activity_budget ab
LEFT JOIN wf_process_instance ins ON ins.businessNo = ab.id
WHERE
ins.state = 20
AND ab.isSchoolBudget = 0
AND ab.outlayManageSource = 'ACTIVITY_BUDGET_TYPE_TWO'
AND YEAR(ab.applyDate) = @year
GROUP BY
ab.unionId
) budget ON budget.unionId = su.id
LEFT JOIN (
SELECT
info.unionId,
SUM(info.actualMoney) AS actualMoney
FROM
activity_reimbursement_info info
LEFT JOIN wf_process_instance ins ON ins.businessNo = info.id
WHERE
ins.state = 20
AND info.outlayManageSource = 'ACTIVITY_BUDGET_TYPE_TWO'
AND info.`year` = @year
GROUP BY
info.unionId
) reimburse ON reimburse.unionId = su.id
$condition
""").setParam("year", year);
Cnd cnd = Cnd.NEW();
cnd.andEX("su.id", "=", unionId);
cnd.and(new SqlExpressionGroup()
.or("budget.totalBudgetMoney", "is not", null)
.or("reimburse.actualMoney", "is not", null));
cnd.asc("su.unionCode");
sql.setCondition(cnd);
return listVO(sql, ActivityReimbursementBudgetSummaryVO.class);
}
/**
* 协会维度汇总。
* 预算金额取指定年度审核通过的协会活动预算,费用报销汇总取指定年度已完成流程的报销实际金额。
*/
private List<ActivityReimbursementBudgetSummaryVO> getClubBudgetSummary(Integer year, String clubId) {
Sql sql = Sqls.create("""
SELECT
@year AS year,
sc.clubName AS name,
COALESCE(budget.totalBudgetMoney, 0) AS budgetMoney,
COALESCE(reimburse.actualMoney, 0) AS reimbursementMoney
FROM
sys_club sc
LEFT JOIN (
SELECT
ab.clubId,
SUM(ab.totalBudgetMoney) AS totalBudgetMoney
FROM
activity_budget ab
LEFT JOIN wf_process_instance ins ON ins.businessNo = ab.id
WHERE
ins.state = 20
AND ab.isSchoolBudget = 0
AND ab.outlayManageSource = 'ACTIVITY_BUDGET_TYPE_THREE'
AND YEAR(ab.applyDate) = @year
GROUP BY
ab.clubId
) budget ON budget.clubId = sc.id
LEFT JOIN (
SELECT
info.clubId,
SUM(info.actualMoney) AS actualMoney
FROM
activity_reimbursement_info info
LEFT JOIN wf_process_instance ins ON ins.businessNo = info.id
WHERE
ins.state = 20
AND info.outlayManageSource = 'ACTIVITY_BUDGET_TYPE_THREE'
AND info.`year` = @year
GROUP BY
info.clubId
) reimburse ON reimburse.clubId = sc.id
$condition
""").setParam("year", year);
Cnd cnd = Cnd.NEW();
cnd.andEX("sc.id", "=", clubId);
cnd.and(new SqlExpressionGroup()
.or("budget.totalBudgetMoney", "is not", null)
.or("reimburse.actualMoney", "is not", null));
cnd.asc("sc.clubCode");
sql.setCondition(cnd);
return listVO(sql, ActivityReimbursementBudgetSummaryVO.class);
}
}
+89 -9
View File
@@ -1,17 +1,85 @@
<?xml version="1.0" encoding="UTF-8" ?>
<configuration scan="false" scanPeriod="60000" debug="false">
<!-- 定义日志文件的存储路径 -->
<!-- 日志存储路径 -->
<property name="LOG_HOME" value="./logs"/>
<!-- 控制台输出配置 -->
<!-- 控制台输出 -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<!-- INFO级别日志文件配置 -->
<!--
#################################################################################
# #
# 全文输出日志(一个文件存储)start #
# #
#################################################################################
-->
<!-- 所有级别日志写入同一个文件 -->
<appender name="ALL_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_HOME}/app.log</file>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_HOME}/app-%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>300</maxHistory>
</rollingPolicy>
</appender>
<!-- 框架日志级别(按需调整) -->
<logger name="org.eclipse.jetty" level="INFO"/>
<logger name="org.quartz" level="INFO"/>
<logger name="org.nutz" level="DEBUG"/>
<!-- root loggerDEBUG 级别,输出到控制台和全量文件 -->
<root level="DEBUG">
<appender-ref ref="STDOUT"/>
<appender-ref ref="ALL_FILE"/>
</root>
<!--
#################################################################################
# #
# 全文输出日志(一个文件存储)end #
# #
#################################################################################
-->
<!--
#################################################################################
# #
# debug、info、error分文件输出 start #
# #
#################################################################################
-->
<!-- DEBUG级别日志文件配置 -->
<!--<appender name="DEBUG_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_HOME}/debug.log</file>
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>DEBUG</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_HOME}/debug-%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>300</maxHistory>
</rollingPolicy>
</appender>
&lt;!&ndash; INFO级别日志文件配置 &ndash;&gt;
<appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_HOME}/info.log</file>
<encoder>
@@ -24,11 +92,11 @@
</filter>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_HOME}/info-%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>30</maxHistory>
<maxHistory>300</maxHistory>
</rollingPolicy>
</appender>
<!-- ERROR级别日志文件配置 -->
&lt;!&ndash; ERROR级别日志文件配置 &ndash;&gt;
<appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_HOME}/error.log</file>
<encoder>
@@ -41,19 +109,31 @@
</filter>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_HOME}/error-%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>30</maxHistory>
<maxHistory>300</maxHistory>
</rollingPolicy>
</appender>
<logger name="java" additivity="false" />
&lt;!&ndash; 框架日志级别 &ndash;&gt;
&lt;!&ndash; <logger name="java" additivity="false" />&ndash;&gt;
<logger name="org.eclipse.jetty" level="INFO"/>
<logger name="org.quartz" level="INFO"/>
<logger name="org.nutz" level="DEBUG"/>
<!-- 日志级别和appender的关联 -->
&lt;!&ndash; 日志级别和appender的关联 &ndash;&gt;
<root level="DEBUG">
<appender-ref ref="STDOUT"/>
<appender-ref ref="DEBUG_FILE"/>
<appender-ref ref="INFO_FILE"/>
<appender-ref ref="ERROR_FILE"/>
</root>
</root>-->
<!--
#################################################################################
# #
# debug、info、error分文件输出 end #
# #
#################################################################################
-->
</configuration>
@@ -64,6 +64,7 @@ layout("/layouts/platform.html"){
<el-radio-button label="true">已审核</el-radio-button>
<el-radio-button label="false">未审核</el-radio-button>
</el-radio-group>
<el-button @click="openBudgetSummary" size="small" type="primary">预算/报销汇总表</el-button>
</table-tool>
<el-table :data="tableData" :size="tableSize" @sort-change="pageOrder"
@@ -94,6 +95,10 @@ layout("/layouts/platform.html"){
<template scope="{row}" v-else-if="column.prop=='activityReimbursementMode'">
<span>{{ row.activityReimbursementMode==='activity'?'活动报销' : '日常报销' }}</span>
</template>
<template scope="{row}" v-else-if="column.prop=='declareUnitName'">
<el-link @click="openUnitSummary(row)" type="primary" v-if="canOpenUnitSummary(row)">{{row.declareUnitName}}</el-link>
<span v-else>{{row.declareUnitName}}</span>
</template>
<template scope="{row}" v-else-if="column.prop=='instanceState'">
<enum-tag :value="row.instanceState" name="ProcessInstanceStateEnum" label_key="message"
size="small"></enum-tag>
@@ -112,6 +117,58 @@ layout("/layouts/platform.html"){
<!--#include("/layouts/pagination.html"){}#-->
</el-card>
<el-dialog :visible.sync="summaryDialogVisible" title="预算/报销汇总表" width="760px">
<el-form :inline="true" :model="summaryForm" size="small">
<el-form-item label="年度">
<el-date-picker
:clearable="false"
placeholder="请选择年度"
type="year"
v-model="summaryForm.year"
value-format="yyyy">
</el-date-picker>
</el-form-item>
<el-form-item label="类型">
<el-radio-group @change="summaryTypeChange" v-model="summaryForm.outlayManageSource">
<el-radio-button label="ACTIVITY_BUDGET_TYPE_TWO">分工会</el-radio-button>
<el-radio-button label="ACTIVITY_BUDGET_TYPE_THREE">协会</el-radio-button>
</el-radio-group>
</el-form-item>
<el-form-item label="分工会" v-if="summaryForm.outlayManageSource === 'ACTIVITY_BUDGET_TYPE_TWO'">
<el-select clearable filterable placeholder="请选择分工会" style="width: 180px;"
v-model="summaryForm.unionId">
<el-option :label="item.name" :value="item.id" v-for="item in unions"></el-option>
</el-select>
</el-form-item>
<el-form-item label="协会" v-if="summaryForm.outlayManageSource === 'ACTIVITY_BUDGET_TYPE_THREE'">
<el-select clearable filterable placeholder="请选择协会" style="width: 180px;"
v-model="summaryForm.clubId">
<el-option :label="item.clubName" :value="item.id" v-for="item in clubs"></el-option>
</el-select>
</el-form-item>
<el-form-item>
<el-button @click="getBudgetSummary" type="primary">查询</el-button>
</el-form-item>
</el-form>
<el-table :data="summaryData" border style="width: 100%" v-loading="summaryLoading">
<el-table-column align="center" label="序号" type="index" width="70"></el-table-column>
<el-table-column align="center" label="年度" prop="year" width="100"></el-table-column>
<el-table-column :label="summaryNameLabel" prop="name"></el-table-column>
<el-table-column align="right" label="预算金额" prop="budgetMoney" width="150"></el-table-column>
<el-table-column align="right" label="费用报销汇总" prop="reimbursementMoney" width="160"></el-table-column>
</el-table>
</el-dialog>
<el-dialog :visible.sync="unitSummaryDialogVisible" title="申报单位/协会汇总" width="720px">
<el-table :data="unitSummaryData" border style="width: 100%" v-loading="unitSummaryLoading">
<el-table-column align="center" label="年度" prop="year" width="100"></el-table-column>
<el-table-column align="center" :label="unitSummaryNameLabel" prop="name"></el-table-column>
<el-table-column align="right" label="年度预算" prop="budgetMoney" width="140"></el-table-column>
<el-table-column align="right" label="年度报销" prop="reimbursementMoney" width="140"></el-table-column>
<el-table-column align="right" label="本次报销" prop="currentReimbursementMoney" width="140"></el-table-column>
</el-table>
</el-dialog>
<template #edit>
<info ref="infoRef">
<div v-if="showApprovalForm">
@@ -174,13 +231,98 @@ layout("/layouts/platform.html"){
// 审核相关
formData: {},
showApprovalForm: false
showApprovalForm: false,
// 预算/报销汇总弹框。year 查询年度;outlayManageSource 为汇总维度;unionId/clubId 为对应维度筛选条件。
summaryDialogVisible: false,
summaryLoading: false,
summaryData: [],
summaryForm: {
year: this.$moment().format("YYYY"),
outlayManageSource: "ACTIVITY_BUDGET_TYPE_TWO",
unionId: "",
clubId: ""
},
// 列表行申报单位/协会汇总弹框。点击列表单位名称时,按当前行的年度、经费类型、分工会或协会 ID 查询。
unitSummaryDialogVisible: false,
unitSummaryLoading: false,
unitSummaryNameLabel: "分工会",
unitSummaryData: []
}
},
components: {
'info': INFO,
},
computed: {
summaryNameLabel() {
return this.summaryForm.outlayManageSource === "ACTIVITY_BUDGET_TYPE_TWO" ? "分工会" : "协会"
}
},
methods: {
openBudgetSummary() {
this.$set(this.summaryForm, "year", this.pageForm.year || this.$moment().format("YYYY"))
this.$set(this, "summaryDialogVisible", true)
this.getBudgetSummary()
},
summaryTypeChange(val) {
this.$set(this.summaryForm, "unionId", "")
this.$set(this.summaryForm, "clubId", "")
this.$set(this.summaryForm, "outlayManageSource", val)
this.getBudgetSummary()
},
getBudgetSummary() {
this.summaryLoading = true
this.$axios.post("/platform/activityReimbursement/deputySchoolUnion/budgetSummaryData", this.summaryForm)
.then((res) => {
if (res.code === 0) {
this.$set(this, "summaryData", res.data || [])
} else {
this.$message.error(res.msg)
}
})
.finally(() => {
this.summaryLoading = false
})
},
canOpenUnitSummary(row) {
return (row.outlayManageSource === "ACTIVITY_BUDGET_TYPE_TWO" && row.unionId)
|| (row.outlayManageSource === "ACTIVITY_BUDGET_TYPE_THREE" && row.clubId)
},
openUnitSummary(row) {
if (row.outlayManageSource !== "ACTIVITY_BUDGET_TYPE_TWO" && row.outlayManageSource !== "ACTIVITY_BUDGET_TYPE_THREE") {
this.$message.warning("仅支持查看分工会或协会汇总")
return
}
if (row.outlayManageSource === "ACTIVITY_BUDGET_TYPE_TWO" && !row.unionId) {
this.$message.warning("当前数据缺少分工会信息")
return
}
if (row.outlayManageSource === "ACTIVITY_BUDGET_TYPE_THREE" && !row.clubId) {
this.$message.warning("当前数据缺少协会信息")
return
}
this.$set(this, "unitSummaryDialogVisible", true)
this.$set(this, "unitSummaryLoading", true)
this.$set(this, "unitSummaryNameLabel", row.outlayManageSource === "ACTIVITY_BUDGET_TYPE_TWO" ? "分工会" : "协会")
this.$axios.post("/platform/activityReimbursement/deputySchoolUnion/budgetSummaryData", {
year: row.year || this.pageForm.year || this.$moment().format("YYYY"),
outlayManageSource: row.outlayManageSource,
unionId: row.outlayManageSource === "ACTIVITY_BUDGET_TYPE_TWO" ? row.unionId : "",
clubId: row.outlayManageSource === "ACTIVITY_BUDGET_TYPE_THREE" ? row.clubId : ""
}).then((res) => {
if (res.code === 0) {
this.$set(this, "unitSummaryData", (res.data || []).map((item) => {
this.$set(item, "currentReimbursementMoney", row.actualMoney)
return item
}))
} else {
this.$message.error(res.msg)
}
}).finally(() => {
this.unitSummaryLoading = false
})
},
openView(row) {
this.$refs.guava.edit(() => {
this.showApprovalForm = false