From 40dc2b304f1a120aa0fd9355bf04f2abf77771d7 Mon Sep 17 00:00:00 2001 From: = <=> Date: Tue, 21 Apr 2026 15:56:41 +0800 Subject: [PATCH 1/8] =?UTF-8?q?=E5=8D=8F=E4=BC=9A=E6=95=B4=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../examine/ClubExamineApplyController.java | 4 +- ...lubExamineSchoolLeaderAuditController.java | 57 +++++++ .../club/service/SysClubExamineService.java | 16 ++ .../impl/SysClubExamineServiceImpl.java | 38 ++++- .../zhgh/club/examine/apply/index.html | 147 ++++++++++++++---- .../zhgh/club/examine/clubAudit/index.html | 39 +++-- .../club/examine/schoolLeaderAudit/index.html | 49 ++++-- 7 files changed, 286 insertions(+), 64 deletions(-) diff --git a/src/main/java/com/budwk/app/zhgh/club/controller/examine/ClubExamineApplyController.java b/src/main/java/com/budwk/app/zhgh/club/controller/examine/ClubExamineApplyController.java index 92197d3d..9cce435e 100644 --- a/src/main/java/com/budwk/app/zhgh/club/controller/examine/ClubExamineApplyController.java +++ b/src/main/java/com/budwk/app/zhgh/club/controller/examine/ClubExamineApplyController.java @@ -133,6 +133,9 @@ public class ClubExamineApplyController { .and("year", "=", year) .desc("year") ); + if (club == null) { + return Result.success(0f); + } Number number = ObjectUtil.defaultIfNull(club.getTotalQuota(), 0); return Result.success(number.floatValue()); } @@ -154,7 +157,6 @@ public class ClubExamineApplyController { } @At - @SaCheckPermission("club.examine") public Result info(String id) { if(StrUtil.isBlank(id)) { return Result.error("id信息为空,请核查"); diff --git a/src/main/java/com/budwk/app/zhgh/club/controller/examine/ClubExamineSchoolLeaderAuditController.java b/src/main/java/com/budwk/app/zhgh/club/controller/examine/ClubExamineSchoolLeaderAuditController.java index 8630a96c..3bfc9ddb 100644 --- a/src/main/java/com/budwk/app/zhgh/club/controller/examine/ClubExamineSchoolLeaderAuditController.java +++ b/src/main/java/com/budwk/app/zhgh/club/controller/examine/ClubExamineSchoolLeaderAuditController.java @@ -1,17 +1,29 @@ package com.budwk.app.zhgh.club.controller.examine; import cn.dev33.satoken.annotation.SaCheckPermission; +import cn.afterturn.easypoi.excel.ExcelExportUtil; +import cn.afterturn.easypoi.excel.entity.enmus.ExcelType; +import cn.afterturn.easypoi.excel.entity.ExportParams; +import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity; +import com.budwk.app.base.utils.CommonDownloadUtil; import com.budwk.app.base.page.Pagination; import com.budwk.app.base.result.Result; import com.budwk.app.zhgh.club.param.ClubUserPageForm; import com.budwk.app.zhgh.club.service.SysClubExamineService; +import com.budwk.app.zhgh.club.vo.ClubExamineVo; import com.budwk.app.zhgh.club.vo.ClubExaminePageVo; import org.nutz.ioc.loader.annotation.Inject; import org.nutz.ioc.loader.annotation.IocBean; +import org.nutz.lang.Lang; +import org.nutz.lang.util.NutMap; import org.nutz.mvc.annotation.At; import org.nutz.mvc.annotation.Ok; +import org.apache.poi.ss.usermodel.Workbook; +import javax.servlet.http.HttpServletResponse; import javax.validation.Valid; +import java.util.ArrayList; +import java.util.List; /** * @ClassName ClubExamineSchoolLeaderAuditController @@ -39,4 +51,49 @@ public class ClubExamineSchoolLeaderAuditController { Pagination pagination = sysClubExamineService.schoolLeaderAuditPageData(pageForm); return Result.success(pagination); } + + @At + @Ok("void") + @SaCheckPermission("club.examine.schoolLeaderAudit") + public void doExportPlan(String id, HttpServletResponse response) { + ClubExamineVo examineVo = sysClubExamineService.findOne(id); + if (Lang.isEmpty(examineVo)) { + return; + } + // 导出下一年度活动计划,字段顺序与查看页列表保持一致。 + List exportData = sysClubExamineService.getPlanExportData(id); + List entityList = new ArrayList<>(); + entityList.add(new ExcelExportEntity("活动主题/赛事名称", "activityName", 40)); + entityList.add(new ExcelExportEntity("活动/赛事主办单位", "activityUnit", 40)); + entityList.add(new ExcelExportEntity("活动地点", "activityAddress", 40)); + + ExportParams exportParams = new ExportParams(); + exportParams.setType(ExcelType.XSSF); + Workbook workbook = ExcelExportUtil.exportExcel(exportParams, entityList, exportData); + CommonDownloadUtil.download(examineVo.getClubName() + "下一年度活动计划.xlsx", workbook, response); + } + + @At + @Ok("void") + @SaCheckPermission("club.examine.schoolLeaderAudit") + public void doExportActivity(String id, HttpServletResponse response) { + ClubExamineVo examineVo = sysClubExamineService.findOne(id); + if (Lang.isEmpty(examineVo)) { + return; + } + // 导出本年度活动开展情况,字段与年审填报表格列保持一致。 + List exportData = sysClubExamineService.getYearActivityExportData(id); + List entityList = new ArrayList<>(); + entityList.add(new ExcelExportEntity("活动时间", "activityTime", 20)); + entityList.add(new ExcelExportEntity("活动主题/赛事名称", "activityName", 40)); + entityList.add(new ExcelExportEntity("活动/赛事主办单位", "activityUnit", 40)); + entityList.add(new ExcelExportEntity("活动地点", "activityAddress", 40)); + entityList.add(new ExcelExportEntity("参加人数", "joinNum", 20)); + entityList.add(new ExcelExportEntity("获奖情况", "prize", 30)); + + ExportParams exportParams = new ExportParams(); + exportParams.setType(ExcelType.XSSF); + Workbook workbook = ExcelExportUtil.exportExcel(exportParams, entityList, exportData); + CommonDownloadUtil.download(examineVo.getClubName() + "本年度活动开展情况.xlsx", workbook, response); + } } diff --git a/src/main/java/com/budwk/app/zhgh/club/service/SysClubExamineService.java b/src/main/java/com/budwk/app/zhgh/club/service/SysClubExamineService.java index adc8300c..5836c312 100644 --- a/src/main/java/com/budwk/app/zhgh/club/service/SysClubExamineService.java +++ b/src/main/java/com/budwk/app/zhgh/club/service/SysClubExamineService.java @@ -30,4 +30,20 @@ public interface SysClubExamineService extends BaseService schoolLeaderAuditPageData(@Valid ClubUserPageForm pageForm); ClubExamineVo findOne(@Valid String id); + + /** + * 查询本年度活动开展情况导出数据。 + * + * @param id 年审主键 + * @return 导出行数据 + */ + List getYearActivityExportData(@Valid String id); + + /** + * 查询下一年度活动计划导出数据。 + * + * @param id 年审主键 + * @return 导出行数据 + */ + List getPlanExportData(@Valid String id); } diff --git a/src/main/java/com/budwk/app/zhgh/club/service/impl/SysClubExamineServiceImpl.java b/src/main/java/com/budwk/app/zhgh/club/service/impl/SysClubExamineServiceImpl.java index 044e7498..0811b055 100644 --- a/src/main/java/com/budwk/app/zhgh/club/service/impl/SysClubExamineServiceImpl.java +++ b/src/main/java/com/budwk/app/zhgh/club/service/impl/SysClubExamineServiceImpl.java @@ -1,6 +1,7 @@ package com.budwk.app.zhgh.club.service.impl; import cn.hutool.core.date.DateUtil; +import cn.hutool.core.lang.Dict; import cn.hutool.core.util.StrUtil; import cn.hutool.json.JSONObject; import com.budwk.app.base.constant.RoleConstant; @@ -308,7 +309,7 @@ public class SysClubExamineServiceImpl extends BaseServiceImpl getYearActivityExportData(String id) { + ClubExamineVo examineVo = this.findOne(id); + if (Lang.isEmpty(examineVo) || Lang.isEmpty(examineVo.getYearActivityList())) { + return new ArrayList<>(); + } + // 导出字段与前端表格字段保持一致,避免控制层再做字段转换。 + return examineVo.getYearActivityList().stream() + .map(item -> Dict.create() + .set("activityTime", item.getStr("activityTime")) + .set("activityName", item.getStr("activityName")) + .set("activityUnit", item.getStr("activityUnit")) + .set("activityAddress", item.getStr("activityAddress")) + .set("joinNum", item.getStr("joinNum")) + .set("prize", item.getStr("prize"))) + .map(NutMap::new) + .collect(Collectors.toList()); + } + + @Override + public List getPlanExportData(String id) { + ClubExamineVo examineVo = this.findOne(id); + if (Lang.isEmpty(examineVo) || Lang.isEmpty(examineVo.getPlans())) { + return new ArrayList<>(); + } + // 下年度活动计划导出只保留页面已有的三个业务字段。 + return examineVo.getPlans().stream() + .map(item -> Dict.create() + .set("activityName", item.getStr("activityName")) + .set("activityUnit", item.getStr("activityUnit")) + .set("activityAddress", item.getStr("activityAddress"))) + .map(NutMap::new) + .collect(Collectors.toList()); + } + /** * 生成审核分页通用SQL * @param pageForm 分页查询参数 diff --git a/src/main/resources/views/platform/zhgh/club/examine/apply/index.html b/src/main/resources/views/platform/zhgh/club/examine/apply/index.html index 830a2a6e..4dde0117 100644 --- a/src/main/resources/views/platform/zhgh/club/examine/apply/index.html +++ b/src/main/resources/views/platform/zhgh/club/examine/apply/index.html @@ -22,8 +22,9 @@ layout("/layouts/platform.html"){ width: 80px; } /* 财务明细表格高度 */ - .finance-table { + .finance-table >>> .el-table__body-wrapper { max-height: 600px; + overflow-y: auto !important; } /* 统计表格高度 */ .stat-table { @@ -725,7 +726,12 @@ layout("/layouts/platform.html"){ }, // 计算年度结余 calcSurplus(row) { - const total = (row.lastYearSurplus || 0) + (row.income || 0) + (row.allocate || 0) + (row.support || 0) - (row.totalExpend || 0); + const lastYearSurplusVal = this.lastYearSurplus !== null ? this.lastYearSurplus : (row.lastYearSurplus || 0); + const income = row.income || 0; + const allocate = row.allocate || 0; + const support = row.support || 0; + const totalExpend = row.totalExpend || 0; + const total = Number(lastYearSurplusVal) + Number(income) + Number(allocate) + Number(support) - Number(totalExpend); return this.formatNumber(total); }, // 表单校验通用方法 @@ -783,13 +789,43 @@ layout("/layouts/platform.html"){ this.formData = data; // 初始化财务数据 this.financeData.incomeCensus = data.incomeCensus || [{ lastYearSurplus: 0, income: 0, allocate: 0, support: 0, totalExpend: 0, surplus: 0 }]; - this.financeData.incomeDetailed = data.detailedList || []; + + // 从统计数据中提取去年年度结余 + if (this.financeData.incomeCensus && this.financeData.incomeCensus.length > 0) { + this.lastYearSurplus = this.financeData.incomeCensus[0].lastYearSurplus || 0; + } + + if (data.detailedList && data.detailedList.length > 0) { + this.financeData.incomeDetailed = this.sortIncomeDetailed(data.detailedList); + } else { + this.financeData.incomeDetailed = []; + } // 初始化成员数据 await this.fetchClubUserNum(data.clubId); await this.fetchMemberList(data.clubId); // 检查是否已登记 await this.checkIsRegistered(); }, + sortIncomeDetailed(list) { + const defaultContents = [new Date().getFullYear() - 1 + "年度结余", "会费收入", "校工会拨款"]; + const defaultItems = []; + const customItems = []; + + list.forEach(item => { + if (item.content && defaultContents.includes(item.content)) { + defaultItems.push(item); + } else { + customItems.push(item); + } + }); + + // 按默认顺序排列前三条 + const sortedDefaults = defaultContents.map(content => + defaultItems.find(item => item.content === content) + ).filter(item => item !== undefined); + + return [...sortedDefaults, ...customItems]; + }, // 获取社团成员数量 async fetchClubUserNum(clubId) { const resp = await this.$axios.post("/platform/club/examine/apply/getClubUserNum", { @@ -830,6 +866,11 @@ layout("/layouts/platform.html"){ year: this.formData.year }); this.lastYearSurplus = resp.data || 0; + + // 同步更新到统计表格 + if (this.financeData.incomeCensus && this.financeData.incomeCensus.length > 0) { + this.financeData.incomeCensus[0].lastYearSurplus = this.lastYearSurplus; + } }, // 获取上年度社团数据 async fetchLastClubData() { @@ -897,22 +938,25 @@ layout("/layouts/platform.html"){ }, // 删除财务明细项 deleteFinanceItem(row, index) { - // 扣除对应统计金额 - this.financeData.incomeCensus[0].income = (this.financeData.incomeCensus[0].income || 0) - (Number(row.income) || 0); - this.financeData.incomeCensus[0].support = (this.financeData.incomeCensus[0].support || 0) - (Number(row.income) || 0); - this.financeData.incomeCensus[0].totalExpend = (this.financeData.incomeCensus[0].totalExpend || 0) - (Number(row.expend) || 0); + // 根据收支类型扣除对应统计金额 + const income = Number(row.income) || 0; + const expend = Number(row.expend) || 0; + + if (row.incomeType === "会费收入") { + this.financeData.incomeCensus[0].income = (this.financeData.incomeCensus[0].income || 0) - income; + } else if (row.incomeType === "校工会拨款") { + this.financeData.incomeCensus[0].allocate = (this.financeData.incomeCensus[0].allocate || 0) - income; + } else if (row.incomeType === "社会赞助") { + this.financeData.incomeCensus[0].support = (this.financeData.incomeCensus[0].support || 0) - income; + } else if (row.incomeType === "支出") { + this.financeData.incomeCensus[0].totalExpend = (this.financeData.incomeCensus[0].totalExpend || 0) - expend; + } + // 删除明细项 this.financeData.incomeDetailed.splice(index, 1); - // 清空统计数据 - if (this.financeData.incomeDetailed.length === 0) { - this.financeData.incomeCensus = [{ - income: this.dueIncome, - allocate: this.unionAllocate, - support: 0, - totalExpend: 0, - surplus: 0 - }]; - } + + // 重新计算统计数据,确保准确性 + this.recalculateCensus(); }, // 收支类型变化处理 handleIncomeTypeChange(row) { @@ -933,27 +977,31 @@ layout("/layouts/platform.html"){ // 年初余额输入处理 handleQcMoneyInput(row) { this.lastYearSurplus = Number(row.qcMoney) || 0; + // 同步更新到统计表格 + if (this.financeData.incomeCensus && this.financeData.incomeCensus.length > 0) { + this.financeData.incomeCensus[0].lastYearSurplus = this.lastYearSurplus; + } + // 更新后强制重新计算统计数据 + this.$forceUpdate(); }, // 收入输入处理 handleIncomeInput(row) { - // 计算总收入 - let totalIncome = 0; - this.financeData.incomeDetailed.forEach(item => { - totalIncome += Number(item.income) || 0; - }); - this.financeData.incomeCensus[0].income = totalIncome; // 按类型更新对应统计 - if (row.incomeType === "社会赞助") this.calcSupportTotal(); - if (row.incomeType === "校工会拨款") this.calcAllocateTotal(); - if (row.incomeType === "会费收入") this.calcDueIncomeTotal(); + if (row.incomeType === "社会赞助") { + this.calcSupportTotal(); + } else if (row.incomeType === "校工会拨款") { + this.calcAllocateTotal(); + } else if (row.incomeType === "会费收入") { + this.calcDueIncomeTotal(); + } + // 重新计算所有统计数据,确保一致性 + this.recalculateCensus(); }, // 支出输入处理 handleExpendInput: function() { - let totalExpend = 0; - this.financeData.incomeDetailed.forEach(item => { - totalExpend += Number(item.expend) || 0; - }); - this.financeData.incomeCensus[0].totalExpend = totalExpend; + this.calcExpendTotal(); + // 重新计算所有统计数据,确保一致性 + this.recalculateCensus(); }, // 计算校工会拨款合计 calcAllocateTotal() { @@ -984,6 +1032,43 @@ layout("/layouts/platform.html"){ .reduce((sum, item) => sum + (Number(item.expend) || 0), 0); this.financeData.incomeCensus[0].totalExpend = total; }, + // 重新计算所有统计数据 + recalculateCensus() { + // 计算会费收入总额 + const dueIncomeTotal = this.financeData.incomeDetailed + .filter(item => item.incomeType === "会费收入") + .reduce((sum, item) => sum + (Number(item.income) || 0), 0); + + // 计算校工会拨款总额 + const allocateTotal = this.financeData.incomeDetailed + .filter(item => item.incomeType === "校工会拨款") + .reduce((sum, item) => sum + (Number(item.income) || 0), 0); + + // 计算社会赞助总额 + const supportTotal = this.financeData.incomeDetailed + .filter(item => item.incomeType === "社会赞助") + .reduce((sum, item) => sum + (Number(item.income) || 0), 0); + + // 计算总支出 + const totalExpend = this.financeData.incomeDetailed + .filter(item => item.incomeType === "支出") + .reduce((sum, item) => sum + (Number(item.expend) || 0), 0); + + // 更新统计数据 + this.financeData.incomeCensus[0].income = dueIncomeTotal; + this.financeData.incomeCensus[0].allocate = allocateTotal; + this.financeData.incomeCensus[0].support = supportTotal; + this.financeData.incomeCensus[0].totalExpend = totalExpend; + // 确保去年年度结余不丢失 + this.financeData.incomeCensus[0].lastYearSurplus = this.lastYearSurplus; + + // 同步更新全局变量 + this.dueIncome = dueIncomeTotal; + this.unionAllocate = allocateTotal; + + // 强制更新视图,确保年度结余重新计算 + this.$forceUpdate(); + }, /************************ 事件处理方法 ************************/ // 年度变化处理 diff --git a/src/main/resources/views/platform/zhgh/club/examine/clubAudit/index.html b/src/main/resources/views/platform/zhgh/club/examine/clubAudit/index.html index ace4da06..d05c82be 100644 --- a/src/main/resources/views/platform/zhgh/club/examine/clubAudit/index.html +++ b/src/main/resources/views/platform/zhgh/club/examine/clubAudit/index.html @@ -73,6 +73,10 @@ layout("/layouts/platform.html"){ :rules="[{required:true,message:'必填',trigger:['change','blur']}]"> + + + 取消 @@ -124,22 +128,27 @@ layout("/layouts/platform.html"){ }) }, handleTaskAction(val) { - this.$confirm("您确定要提交吗?", "提示", { - confirmButtonText: "确定", - cancelButtonText: "取消", - type: "warning" - }).then(() => { - this.$axios.post("/flow/common/executeTask", { - data: JSON.stringify({ - ...this.formData, - submitType: val + this.$refs.formRef.validate((valid) => { + if (!valid) { + return + } + this.$confirm("您确定要提交吗?", "提示", { + confirmButtonText: "确定", + cancelButtonText: "取消", + type: "warning" + }).then(() => { + this.$axios.post("/flow/common/executeTask", { + data: JSON.stringify({ + ...this.formData, + submitType: val + }) + }).then((res) => { + if (res.code === 0) { + this.$refs.guava.index() + this.$message.success(res.msg) + this.doSearch() + } }) - }).then((res) => { - if (res.code === 0) { - this.$refs.guava.index() - this.$message.success(res.msg) - this.doSearch() - } }) }) }, diff --git a/src/main/resources/views/platform/zhgh/club/examine/schoolLeaderAudit/index.html b/src/main/resources/views/platform/zhgh/club/examine/schoolLeaderAudit/index.html index ea71bcbd..695c3062 100644 --- a/src/main/resources/views/platform/zhgh/club/examine/schoolLeaderAudit/index.html +++ b/src/main/resources/views/platform/zhgh/club/examine/schoolLeaderAudit/index.html @@ -50,11 +50,13 @@ layout("/layouts/platform.html"){ - + @@ -73,6 +75,10 @@ layout("/layouts/platform.html"){ :rules="[{required:true,message:'必填',trigger:['change','blur']}]"> + + + 取消 @@ -124,22 +130,27 @@ layout("/layouts/platform.html"){ }) }, handleTaskAction(val) { - this.$confirm("您确定要提交吗?", "提示", { - confirmButtonText: "确定", - cancelButtonText: "取消", - type: "warning" - }).then(() => { - this.$axios.post("/flow/common/executeTask", { - data: JSON.stringify({ - ...this.formData, - submitType: val + this.$refs.formRef.validate((valid) => { + if (!valid) { + return + } + this.$confirm("您确定要提交吗?", "提示", { + confirmButtonText: "确定", + cancelButtonText: "取消", + type: "warning" + }).then(() => { + this.$axios.post("/flow/common/executeTask", { + data: JSON.stringify({ + ...this.formData, + submitType: val + }) + }).then((res) => { + if (res.code === 0) { + this.$refs.guava.index() + this.$message.success(res.msg) + this.doSearch() + } }) - }).then((res) => { - if (res.code === 0) { - this.$refs.guava.index() - this.$message.success(res.msg) - this.doSearch() - } }) }) }, @@ -157,6 +168,12 @@ layout("/layouts/platform.html"){ }) }) }, + doExportPlan(row) { + this.$downLoad("/platform/club/examine/schoolLeaderAudit/doExportPlan", { id: row.id }) + }, + doExportActivity(row) { + this.$downLoad("/platform/club/examine/schoolLeaderAudit/doExportActivity", { id: row.id }) + }, async pageData() { let form = clone(this.pageForm) if (form.isAudit === null) { From caf9df263301fba512860cfd7cfd74039b89bf61 Mon Sep 17 00:00:00 2001 From: "@jyuhsin" <729757837@qq.com> Date: Wed, 22 Apr 2026 19:04:46 +0800 Subject: [PATCH 2/8] commit --- .../unionReimburse/controller/UnionReimburseDescController.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/unionReimburse/controller/UnionReimburseDescController.java b/src/main/java/com/budwk/app/zhgh/dayofficework/unionReimburse/controller/UnionReimburseDescController.java index b2399497..f9b8ee21 100644 --- a/src/main/java/com/budwk/app/zhgh/dayofficework/unionReimburse/controller/UnionReimburseDescController.java +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/unionReimburse/controller/UnionReimburseDescController.java @@ -84,7 +84,6 @@ public class UnionReimburseDescController { @At @ApiOperation("查询附件说明") - @SaCheckPermission(value = {"unionReimburse.desc", "h5.unionReimburse.desc"}, mode = SaMode.OR) public Result queryFileDesc() { List list = unionReimburseDesc.query(Cnd.NEW().desc(UnionReimburseDesc::getCode)); return Result.success(list); From c2f93eb4b3639140c2f26b47ab974b751252a088 Mon Sep 17 00:00:00 2001 From: "@jyuhsin" <729757837@qq.com> Date: Thu, 23 Apr 2026 15:00:46 +0800 Subject: [PATCH 3/8] commit --- .../outlay/activityBudget/models/ActivityBudget.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/outlay/activityBudget/models/ActivityBudget.java b/src/main/java/com/budwk/app/zhgh/dayofficework/outlay/activityBudget/models/ActivityBudget.java index 4c1e128d..ef9e6a01 100644 --- a/src/main/java/com/budwk/app/zhgh/dayofficework/outlay/activityBudget/models/ActivityBudget.java +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/outlay/activityBudget/models/ActivityBudget.java @@ -93,7 +93,7 @@ public class ActivityBudget extends BaseModel implements Serializable { @Column @Comment("举办单位") - @ColDefine(type = ColType.VARCHAR, width = 32) + @ColDefine(type = ColType.VARCHAR, width = 100) private String helpUnitName; @Column From 3207e0a5944124a62dc8ed318aff6a0edaa4f0ac Mon Sep 17 00:00:00 2001 From: = <=> Date: Fri, 24 Apr 2026 14:27:55 +0800 Subject: [PATCH 4/8] commit --- ...ctivityCultureApplyActivityController.java | 4 +- ...ctivityCultureAuditActivityController.java | 4 +- .../ActivityCultureInfoManageController.java | 12 +- .../service/ActivityCultureService.java | 10 + .../impl/ActivityCultureServiceImpl.java | 37 + ...tivityWorksUploadCollectionController.java | 5 +- .../apply/ClubUserJoinApplyController.java | 27 +- .../ClubChangeManagerController.java | 20 +- .../ClubRefreshReportController.java | 10 +- .../infoManage/ClubRuleUpdateController.java | 10 +- .../app/zhgh/club/model/ClubUserApply.java | 10 + .../club/model/SysClubExamineRegister.java | 2 +- .../club/service/ClubUserJoinService.java | 21 + .../service/SysClubInfoManageService.java | 5 + .../service/impl/ClubUserJoinServiceImpl.java | 84 ++- .../impl/SysClubInfoManageServiceImpl.java | 109 ++- .../zhgh/club/vo/ClubUserCommonPageVo.java | 3 + .../OutlayManageClubQueryController.java | 4 +- .../club/model/OutlayManageClub.java | 2 +- .../UnionReimburseApplyController.java | 104 +-- ...nReimburseCertifierUserSignController.java | 27 +- .../UnionReimburseCollectController.java | 47 +- .../UnionReimburseMineController.java | 35 +- .../UnionReimburseReviewController.java | 39 +- .../UnionReimburseStatisticsController.java | 8 + .../unionReimburse/model/UnionReimburse.java | 5 + .../service/UnionReimburseService.java | 30 + .../impl/UnionReimburseServiceImpl.java | 652 +++++++++++++++++- ...TeacherCongressDelegatePushController.java | 12 +- .../activity/culture/common/applyActivity.js | 2 + .../zhgh/activity/culture/common/infoMange.js | 2 +- .../zhgh/activity/trainSignUp/type/index.html | 2 +- .../zhgh/club/examine/apply/index.html | 41 +- .../zhgh/club/examine/common/examineInfo.js | 17 + .../club/infoManage/manage/clubInfoManage.js | 1 + .../register/apply/clubRegisterSponsor.js | 7 +- .../zhgh/club/register/apply/index.html | 25 +- .../outlay/outlayManage/club/query/index.html | 14 +- .../unionReimburse/apply/index.html | 611 +++++++++++++++- .../certifierUserSign/index.html | 27 +- .../unionReimburse/collect/index.html | 59 +- .../unionReimburse/mine/index.html | 27 +- .../unionReimburse/review/index.html | 53 +- .../unionReimburse/statistics/index.html | 23 +- .../teachercongress/delegate/push/write.js | 6 +- .../unionReimburse/review/index.html | 63 +- 46 files changed, 1993 insertions(+), 325 deletions(-) diff --git a/src/main/java/com/budwk/app/zhgh/activity/culture/controller/ActivityCultureApplyActivityController.java b/src/main/java/com/budwk/app/zhgh/activity/culture/controller/ActivityCultureApplyActivityController.java index c405bc4d..b5101ffe 100644 --- a/src/main/java/com/budwk/app/zhgh/activity/culture/controller/ActivityCultureApplyActivityController.java +++ b/src/main/java/com/budwk/app/zhgh/activity/culture/controller/ActivityCultureApplyActivityController.java @@ -15,7 +15,6 @@ import com.budwk.app.flow.entity.ProcessInstance; import com.budwk.app.flow.entity.ProcessTask; import com.budwk.app.flow.enums.ProcessSubmitTypeEnum; import com.budwk.app.flow.service.FlowCommonService; -import com.budwk.app.sys.models.Sys_home_activity; import com.budwk.app.web.commons.auth.utils.AuthUtil; import com.budwk.app.web.commons.auth.utils.SecurityUtil; import com.budwk.app.zhgh.activity.culture.models.ActivityTissue; @@ -151,8 +150,7 @@ public class ActivityCultureApplyActivityController { return Result.success(); } if (tissue.getIsEnrollSystem()) { - Sys_home_activity sysHomeActivity = tissue.covertToSysHomeActivity(); - activityCultureService.insertOrUpdate(sysHomeActivity); + activityCultureService.syncHomeActivity(tissue); } return Result.success(); } diff --git a/src/main/java/com/budwk/app/zhgh/activity/culture/controller/ActivityCultureAuditActivityController.java b/src/main/java/com/budwk/app/zhgh/activity/culture/controller/ActivityCultureAuditActivityController.java index f5828ba5..805e0366 100644 --- a/src/main/java/com/budwk/app/zhgh/activity/culture/controller/ActivityCultureAuditActivityController.java +++ b/src/main/java/com/budwk/app/zhgh/activity/culture/controller/ActivityCultureAuditActivityController.java @@ -9,7 +9,6 @@ import com.budwk.app.base.param.PageForm; import com.budwk.app.base.result.Result; import com.budwk.app.flow.enums.ProcessTaskStateEnum; import com.budwk.app.flow.service.FlowCommonService; -import com.budwk.app.sys.models.Sys_home_activity; import com.budwk.app.web.commons.auth.utils.SecurityUtil; import com.budwk.app.zhgh.activity.culture.models.ActivityTissue; import com.budwk.app.zhgh.activity.culture.service.ActivityCultureService; @@ -144,8 +143,7 @@ public class ActivityCultureAuditActivityController { tissue.setIsUnseal(true); activityCultureService.update(tissue); if (args.getInt("submitType") == 1) { - Sys_home_activity sysHomeActivity = tissue.covertToSysHomeActivity(); - activityCultureService.insertOrUpdate(sysHomeActivity); + activityCultureService.syncHomeActivity(tissue); } flowCommonService.executeTask(args); return Result.success(); diff --git a/src/main/java/com/budwk/app/zhgh/activity/culture/controller/ActivityCultureInfoManageController.java b/src/main/java/com/budwk/app/zhgh/activity/culture/controller/ActivityCultureInfoManageController.java index b7f3ab57..1884735a 100644 --- a/src/main/java/com/budwk/app/zhgh/activity/culture/controller/ActivityCultureInfoManageController.java +++ b/src/main/java/com/budwk/app/zhgh/activity/culture/controller/ActivityCultureInfoManageController.java @@ -74,9 +74,15 @@ public class ActivityCultureInfoManageController { @SaCheckPermission(value = {"activity.culture.infoManage.school", "activity.culture.infoManage.union", "activity.culture.infoManage.club"}, mode = SaMode.OR) public Result activityStatusChange(@Valid String id, @Valid Boolean isUnseal) { activityCultureService.update(Chain.make("isUnseal", isUnseal), Cnd.where("id", "=", id)); - activityCultureService.dao().update(Sys_home_activity.class, - Chain.make("enable", isUnseal), - Cnd.where("id", "=", id)); + ActivityTissue tissue = activityCultureService.fetch(id); + tissue.setIsUnseal(isUnseal); + if (Boolean.TRUE.equals(isUnseal)) { + activityCultureService.syncHomeActivity(tissue); + } else { + activityCultureService.dao().update(Sys_home_activity.class, + Chain.make("enable", false), + Cnd.where("id", "=", id)); + } return Result.success(); } diff --git a/src/main/java/com/budwk/app/zhgh/activity/culture/service/ActivityCultureService.java b/src/main/java/com/budwk/app/zhgh/activity/culture/service/ActivityCultureService.java index 8c38bed6..e5c86ab5 100644 --- a/src/main/java/com/budwk/app/zhgh/activity/culture/service/ActivityCultureService.java +++ b/src/main/java/com/budwk/app/zhgh/activity/culture/service/ActivityCultureService.java @@ -24,4 +24,14 @@ public interface ActivityCultureService extends BaseService { Object pageData(PageForm page, String year, String name, String unionId, Integer activity_type); NutMap findOne(String id); + + /** + * 同步文化活动到首页活动表。 + * 1. 非报名类活动不推送首页,直接清理首页记录。 + * 2. 校工会活动没有审核流,提交后默认可推首页。 + * 3. 分工会、协会活动仅在已审核通过或已存在首页记录时更新首页信息。 + * + * @param tissue 文化活动 + */ + void syncHomeActivity(ActivityTissue tissue); } diff --git a/src/main/java/com/budwk/app/zhgh/activity/culture/service/impl/ActivityCultureServiceImpl.java b/src/main/java/com/budwk/app/zhgh/activity/culture/service/impl/ActivityCultureServiceImpl.java index cdd2aaf6..a1e06aa2 100644 --- a/src/main/java/com/budwk/app/zhgh/activity/culture/service/impl/ActivityCultureServiceImpl.java +++ b/src/main/java/com/budwk/app/zhgh/activity/culture/service/impl/ActivityCultureServiceImpl.java @@ -5,6 +5,7 @@ import com.budwk.app.base.constant.RoleConstant; import com.budwk.app.base.model.Audit; import com.budwk.app.base.param.PageForm; import com.budwk.app.base.service.impl.BaseServiceImpl; +import com.budwk.app.sys.models.Sys_home_activity; import com.budwk.app.web.commons.auth.utils.AuthUtil; import com.budwk.app.web.commons.auth.utils.SecurityUtil; import com.budwk.app.zhgh.activity.basic.service.ActivityBasicScopeService; @@ -80,6 +81,7 @@ public class ActivityCultureServiceImpl extends BaseServiceImpl @Override public void doEditActivity(ActivityTissue tissue) { update(tissue); + syncHomeActivity(tissue); } @Override @@ -123,6 +125,7 @@ public class ActivityCultureServiceImpl extends BaseServiceImpl cnd.andEX("tissue.projectTypeCode", "!=", "50004"); cnd.andEX("tissue.unionId", "=", unionId); cnd.andEX("tissue.activity_type", "=", activity_type); + cnd.groupBy("tissue.id"); if (Strings.isNotBlank(name)) { cnd.where().andLike("tissue.name", name); @@ -188,4 +191,38 @@ public class ActivityCultureServiceImpl extends BaseServiceImpl return nutMap; } + + @Override + public void syncHomeActivity(ActivityTissue tissue) { + if (tissue == null || Strings.isBlank(tissue.getId())) { + return; + } + // 非报名活动不需要出现在首页,编辑成活动管理模式时同步移除首页记录。 + if (!Boolean.TRUE.equals(tissue.getIsEnrollSystem())) { + dao().delete(Sys_home_activity.class, tissue.getId()); + return; + } + + Sys_home_activity oldHomeActivity = dao().fetch(Sys_home_activity.class, tissue.getId()); + boolean canCreateHomeActivity = (tissue.getActivity_type() != null + && tissue.getActivity_type() == 40001) + || Boolean.TRUE.equals(tissue.getIsUnseal()) + || oldHomeActivity != null; + if (!canCreateHomeActivity) { + return; + } + + Sys_home_activity sysHomeActivity = tissue.covertToSysHomeActivity(); + // 校工会文化活动没有审核环节,前端未回传启用状态时默认直接上首页。 + if (tissue.getActivity_type() != null && tissue.getActivity_type() == 40001 && tissue.getIsUnseal() == null) { + sysHomeActivity.setEnable(true); + } + // 保留首页管理中手工设置的置顶、大图和排序,避免活动编辑时被覆盖。 + if (oldHomeActivity != null) { + sysHomeActivity.setTop(oldHomeActivity.getTop()); + sysHomeActivity.setPush(oldHomeActivity.getPush()); + sysHomeActivity.setSortNo(oldHomeActivity.getSortNo()); + } + dao().insertOrUpdate(sysHomeActivity); + } } diff --git a/src/main/java/com/budwk/app/zhgh/activity/workscollection/controller/h5/H5ActivityWorksUploadCollectionController.java b/src/main/java/com/budwk/app/zhgh/activity/workscollection/controller/h5/H5ActivityWorksUploadCollectionController.java index 73414a94..d87c4573 100644 --- a/src/main/java/com/budwk/app/zhgh/activity/workscollection/controller/h5/H5ActivityWorksUploadCollectionController.java +++ b/src/main/java/com/budwk/app/zhgh/activity/workscollection/controller/h5/H5ActivityWorksUploadCollectionController.java @@ -60,6 +60,7 @@ public class H5ActivityWorksUploadCollectionController { awc.* FROM `activity_works_collection` awc + $condition """); Cnd cnd = Cnd.NEW(); cnd.andEX("YEAR(awc.startDateTime)", "=", year); @@ -68,9 +69,9 @@ public class H5ActivityWorksUploadCollectionController { cnd.and(new Static("now() > awc.startDateTime and now() < awc.endDateTime")); }//查询已结束的 else if (activityType == 3) { - cnd.and(new Static("now() > awc.startDateTime")); + cnd.and(new Static("now() >= awc.endDateTime")); } else if (activityType == 4) { - cnd.and(new Static("now() < awc.endDateTime")); + cnd.and(new Static("now() < awc.startDateTime")); } cnd.and("awc.enable", "=", 1); cnd.and("awc.type", "=", 1); diff --git a/src/main/java/com/budwk/app/zhgh/club/controller/apply/ClubUserJoinApplyController.java b/src/main/java/com/budwk/app/zhgh/club/controller/apply/ClubUserJoinApplyController.java index 1abcdc05..c412c035 100644 --- a/src/main/java/com/budwk/app/zhgh/club/controller/apply/ClubUserJoinApplyController.java +++ b/src/main/java/com/budwk/app/zhgh/club/controller/apply/ClubUserJoinApplyController.java @@ -28,6 +28,7 @@ import com.budwk.app.web.commons.auth.utils.SecurityUtil; import com.budwk.app.web.controllers.open.commons.service.CommonService; import com.budwk.app.zhgh.club.model.ClubUser; import com.budwk.app.zhgh.club.model.ClubUserApply; +import com.budwk.app.zhgh.club.service.ClubUserJoinService; import com.budwk.app.zhgh.staffbenefit.condolence.model.Condolence; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; @@ -64,6 +65,8 @@ public class ClubUserJoinApplyController { private FlowEngine flowEngine; @Inject private FlowCommonService flowCommonService; + @Inject + private ClubUserJoinService clubUserJoinService; @At("/") @SaCheckPermission("club.join.apply") @@ -112,6 +115,8 @@ public class ClubUserJoinApplyController { clubUserApply.setRoleCode(RoleConstant.CLUB_MEMBER.name()); clubUserApply.setMode(true); clubUserApply.setApplyDate(new Date()); + clubUserApply.setJoinTime(clubUserJoinService.resolveJoinTime(clubUserApply.getClubId(), clubUserApply.getUserId())); + clubUserApply.setExitTime(null); dao.insertOrUpdate(clubUserApply); return Result.success(); } @@ -124,12 +129,10 @@ public class ClubUserJoinApplyController { public Result submit(@Param("data") ClubUserApply clubUserApply, @Param("mode") Boolean mode) { if(mode == false) { - clubUserApply = dao.fetch( - ClubUserApply.class, - Cnd.where("userId", "=", SecurityUtil.getUserId()) - .and("clubId", "=", clubUserApply.getClubId()) - .desc("applyDate") - ); + clubUserApply = clubUserJoinService.buildExitApply(clubUserApply.getClubId(), SecurityUtil.getUserId()); + if (ObjectUtil.isEmpty(clubUserApply)) { + return Result.error("当前不是该社团成员,无法申请退出!"); + } } ClubUserApply userApply = dao.fetch(ClubUserApply.class, Cnd.where("userId", "=", clubUserApply.getUserId()).and("clubId", "=", clubUserApply.getClubId()).and("mode", "=", mode).desc(ClubUserApply::getApplyDate)); if (ObjectUtil.isNotEmpty(userApply) && StrUtil.isNotBlank(userApply.getId())) { @@ -142,9 +145,17 @@ public class ClubUserJoinApplyController { if(!mode) { clubUserApply.setId(null); } - clubUserApply.setRoleCode(RoleConstant.CLUB_MEMBER.name()); clubUserApply.setMode(mode); - clubUserApply.setApplyDate(new Date()); + Date applyDate = new Date(); + clubUserApply.setApplyDate(applyDate); + if (mode) { + clubUserApply.setRoleCode(RoleConstant.CLUB_MEMBER.name()); + clubUserApply.setJoinTime(clubUserJoinService.resolveJoinTime(clubUserApply.getClubId(), clubUserApply.getUserId())); + clubUserApply.setExitTime(null); + } else { + clubUserApply.setJoinTime(clubUserJoinService.resolveJoinTime(clubUserApply.getClubId(), clubUserApply.getUserId())); + clubUserApply.setExitTime(applyDate); + } dao.insertOrUpdate(clubUserApply); // 开启流程实例 diff --git a/src/main/java/com/budwk/app/zhgh/club/controller/infoManage/ClubChangeManagerController.java b/src/main/java/com/budwk/app/zhgh/club/controller/infoManage/ClubChangeManagerController.java index 1564057b..83fadd00 100644 --- a/src/main/java/com/budwk/app/zhgh/club/controller/infoManage/ClubChangeManagerController.java +++ b/src/main/java/com/budwk/app/zhgh/club/controller/infoManage/ClubChangeManagerController.java @@ -22,6 +22,7 @@ import com.budwk.app.web.commons.auth.utils.SecurityUtil; import com.budwk.app.zhgh.club.model.*; import com.budwk.app.zhgh.club.param.ClubUserPageForm; import com.budwk.app.zhgh.club.service.SysClubInfoManageService; +import com.budwk.app.zhgh.club.service.SysClubService; import com.budwk.app.zhgh.club.service.impl.SysClubUserServiceImpl; import io.swagger.annotations.ApiOperation; import org.nutz.aop.interceptor.ioc.TransAop; @@ -64,6 +65,8 @@ public class ClubChangeManagerController { private FlowCommonService flowCommonService; @Inject private SysClubInfoManageService infoManageService; + @Inject + private SysClubService sysClubService; @At("") @Ok("beetl:/platform/zhgh/club/infoManage/change/index.html") @@ -102,7 +105,10 @@ public class ClubChangeManagerController { $condition """); Cnd cnd = Cnd.NEW(); - cnd.and("info.userId", "=", SecurityUtil.getUserId()); + if (!AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_CLUB_ADMIN.name())) { + List clubIdList = sysClubService.getMyManageClub().stream().map(SysClub::getId).toList(); + cnd.and("info.clubId", "in", clubIdList); + } cnd.andEX("year(info.creatTime)", "=", pageForm.getYear()); if (StrUtil.isNotBlank(pageForm.getClubName())) { cnd.and("club.clubName", "LIKE", "%" + pageForm.getClubName() + "%"); @@ -112,6 +118,7 @@ public class ClubChangeManagerController { } else { cnd.orderBy(pageForm.getPageOrderName(), PageUtil.getOrder(pageForm.getPageOrderBy())); } + cnd.groupBy("info.id"); sql.setCondition(cnd); Pagination pagination = infoManageService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql); List listMap = pagination.getList(NutMap.class); @@ -130,6 +137,17 @@ public class ClubChangeManagerController { @SaCheckPermission("club.infoManage.change") @SLog(tag = "社团管理系统-信息管理", msg = "提交变更理事机构") public Object submit(@Param("data") SysClubManager clubManager) { + Cnd cnd = Cnd.NEW(); + cnd.and(SysClubManager::getClubId, "=", clubManager.getClubId()); + if (StrUtil.isNotBlank(clubManager.getId())) { + cnd.and(SysClubManager::getId, "!=", clubManager.getId()); + } + List managerList = dao.query(SysClubManager.class, cnd); + List pendingProcessIds = managerList.stream().map(SysClubManager::getId).toList(); + int pendingCount = dao.count(ProcessInstance.class, Cnd.where(ProcessInstance::getBusinessNo, "in", pendingProcessIds).and(ProcessInstance::getState, "not in", List.of(ProcessInstanceStateEnum.FINISHED.getCode(), ProcessInstanceStateEnum.REJECT.getCode()))); + if (pendingCount > 0) { + return Result.error("您有该社团的申请记录尚未完成,请核对!"); + } if (!AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_ADMIN.name())) { String errMsg = "校工会审核" + clubManager.getPeriod() + "的换届报告通过后才能变更理事成员"; diff --git a/src/main/java/com/budwk/app/zhgh/club/controller/infoManage/ClubRefreshReportController.java b/src/main/java/com/budwk/app/zhgh/club/controller/infoManage/ClubRefreshReportController.java index 5acde8f6..0f55c8c8 100644 --- a/src/main/java/com/budwk/app/zhgh/club/controller/infoManage/ClubRefreshReportController.java +++ b/src/main/java/com/budwk/app/zhgh/club/controller/infoManage/ClubRefreshReportController.java @@ -5,6 +5,7 @@ import cn.hutool.core.date.DateUtil; import cn.hutool.core.lang.Dict; import cn.hutool.core.util.StrUtil; import com.budwk.app.base.annotation.SLog; +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.utils.PageUtil; @@ -15,6 +16,7 @@ import com.budwk.app.flow.entity.ProcessTask; import com.budwk.app.flow.enums.ProcessInstanceStateEnum; import com.budwk.app.flow.enums.ProcessSubmitTypeEnum; import com.budwk.app.flow.service.FlowCommonService; +import com.budwk.app.web.commons.auth.utils.AuthUtil; import com.budwk.app.web.commons.auth.utils.SecurityUtil; import com.budwk.app.zhgh.club.model.SysClub; import com.budwk.app.zhgh.club.model.SysClubRefresh; @@ -58,6 +60,8 @@ public class ClubRefreshReportController { private FlowCommonService flowCommonService; @Inject private Dao dao; + @Inject + private SysClubService sysClubService; @At("") @Ok("beetl:/platform/zhgh/club/infoManage/refreshReport/index.html") @@ -96,7 +100,10 @@ public class ClubRefreshReportController { $condition """); Cnd cnd = Cnd.NEW(); - cnd.and("info.userId", "=", SecurityUtil.getUserId()); + if (!AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_CLUB_ADMIN.name())) { + List clubIdList = sysClubService.getMyManageClub().stream().map(SysClub::getId).toList(); + cnd.and("info.clubId", "in", clubIdList); + } cnd.andEX("year(info.creatTime)", "=", pageForm.getYear()); if (StrUtil.isNotBlank(pageForm.getClubName())) { cnd.and("club.clubName", "LIKE", "%" + pageForm.getClubName() + "%"); @@ -119,6 +126,7 @@ public class ClubRefreshReportController { public Object submit(@Param("data") SysClubRefresh clubRefresh) { Cnd cnd = Cnd.NEW(); + cnd.and(SysClubRefresh::getClubId, "=", clubRefresh.getClubId()); if(StrUtil.isNotBlank(clubRefresh.getId())) { cnd.and(SysClubRefresh::getId, "!=", clubRefresh.getId()); } diff --git a/src/main/java/com/budwk/app/zhgh/club/controller/infoManage/ClubRuleUpdateController.java b/src/main/java/com/budwk/app/zhgh/club/controller/infoManage/ClubRuleUpdateController.java index 426fc0a2..b0a73954 100644 --- a/src/main/java/com/budwk/app/zhgh/club/controller/infoManage/ClubRuleUpdateController.java +++ b/src/main/java/com/budwk/app/zhgh/club/controller/infoManage/ClubRuleUpdateController.java @@ -4,6 +4,7 @@ import cn.dev33.satoken.annotation.SaCheckPermission; import cn.hutool.core.date.DateUtil; import cn.hutool.core.lang.Dict; import cn.hutool.core.util.StrUtil; +import com.budwk.app.base.constant.RoleConstant; import com.budwk.app.base.annotation.SLog; import com.budwk.app.base.page.Pagination; import com.budwk.app.base.result.Result; @@ -15,6 +16,7 @@ import com.budwk.app.flow.entity.ProcessTask; import com.budwk.app.flow.enums.ProcessInstanceStateEnum; import com.budwk.app.flow.enums.ProcessSubmitTypeEnum; import com.budwk.app.flow.service.FlowCommonService; +import com.budwk.app.web.commons.auth.utils.AuthUtil; import com.budwk.app.web.commons.auth.utils.SecurityUtil; import com.budwk.app.zhgh.club.model.SysClub; import com.budwk.app.zhgh.club.model.SysClubRefresh; @@ -59,6 +61,8 @@ public class ClubRuleUpdateController { private FlowCommonService flowCommonService; @Inject private Dao dao; + @Inject + private SysClubService sysClubService; @At("") @Ok("beetl:/platform/zhgh/club/infoManage/ruleUpdate/index.html") @@ -97,7 +101,10 @@ public class ClubRuleUpdateController { $condition """); Cnd cnd = Cnd.NEW(); - cnd.and("info.userId", "=", SecurityUtil.getUserId()); + if (!AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_CLUB_ADMIN.name())) { + List clubIdList = sysClubService.getMyManageClub().stream().map(SysClub::getId).toList(); + cnd.and("info.clubId", "in", clubIdList); + } cnd.andEX("year(info.creatTime)", "=", pageForm.getYear()); if (StrUtil.isNotBlank(pageForm.getClubName())) { cnd.and("club.clubName", "LIKE", "%" + pageForm.getClubName() + "%"); @@ -120,6 +127,7 @@ public class ClubRuleUpdateController { public Object submit(@Param("data")SysClubRule clubRule) { Cnd cnd = Cnd.NEW(); + cnd.and(SysClubRule::getClubId, "=", clubRule.getClubId()); if(StrUtil.isNotBlank(clubRule.getId())) { cnd.and(SysClubRule::getId, "!=", clubRule.getId()); } diff --git a/src/main/java/com/budwk/app/zhgh/club/model/ClubUserApply.java b/src/main/java/com/budwk/app/zhgh/club/model/ClubUserApply.java index d755b33b..ff3a21d4 100644 --- a/src/main/java/com/budwk/app/zhgh/club/model/ClubUserApply.java +++ b/src/main/java/com/budwk/app/zhgh/club/model/ClubUserApply.java @@ -85,4 +85,14 @@ public class ClubUserApply extends BaseModel { @Comment("申请时间") @ColDefine(type = ColType.DATETIME) private Date applyDate; + + @Column + @Comment("入会时间") + @ColDefine(type = ColType.DATETIME) + private Date joinTime; + + @Column + @Comment("退会时间") + @ColDefine(type = ColType.DATETIME) + private Date exitTime; } diff --git a/src/main/java/com/budwk/app/zhgh/club/model/SysClubExamineRegister.java b/src/main/java/com/budwk/app/zhgh/club/model/SysClubExamineRegister.java index f0e6f294..3b41d961 100644 --- a/src/main/java/com/budwk/app/zhgh/club/model/SysClubExamineRegister.java +++ b/src/main/java/com/budwk/app/zhgh/club/model/SysClubExamineRegister.java @@ -47,7 +47,7 @@ public class SysClubExamineRegister extends BaseModel { @Column @Comment("社团名称") - @ColDefine(type = ColType.VARCHAR, width = 32) + @ColDefine(type = ColType.VARCHAR) private String clubName; @Column diff --git a/src/main/java/com/budwk/app/zhgh/club/service/ClubUserJoinService.java b/src/main/java/com/budwk/app/zhgh/club/service/ClubUserJoinService.java index e94a9d4b..1168e586 100644 --- a/src/main/java/com/budwk/app/zhgh/club/service/ClubUserJoinService.java +++ b/src/main/java/com/budwk/app/zhgh/club/service/ClubUserJoinService.java @@ -4,4 +4,25 @@ import com.budwk.app.base.service.BaseService; import com.budwk.app.zhgh.club.model.ClubUserApply; public interface ClubUserJoinService extends BaseService { + + /** + * 构造退会申请数据。 + * 当用户没有历史入会申请记录时,使用当前社团成员关系和人员基础信息兜底生成一份退会申请, + * 以保证会长等通过社团注册直接入会的人员也能正常发起退会流程。 + * + * @param clubId 社团ID + * @param userId 用户ID + * @return 退会申请对象,不存在当前成员关系时返回null + */ + ClubUserApply buildExitApply(String clubId, String userId); + + /** + * 获取社团成员的入会时间,优先取历史有效入会申请记录, + * 没有时兜底为社团创办时间。 + * + * @param clubId 社团ID + * @param userId 用户ID + * @return 入会时间 + */ + java.util.Date resolveJoinTime(String clubId, String userId); } diff --git a/src/main/java/com/budwk/app/zhgh/club/service/SysClubInfoManageService.java b/src/main/java/com/budwk/app/zhgh/club/service/SysClubInfoManageService.java index 924626a6..74ff1ba2 100644 --- a/src/main/java/com/budwk/app/zhgh/club/service/SysClubInfoManageService.java +++ b/src/main/java/com/budwk/app/zhgh/club/service/SysClubInfoManageService.java @@ -5,6 +5,7 @@ import com.budwk.app.base.service.BaseService; import com.budwk.app.zhgh.club.param.ClubUserPageForm; import com.budwk.app.zhgh.club.vo.ClubCommonPageVo; import com.budwk.app.zhgh.club.vo.ClubUserCommonPageVo; +import com.budwk.app.zhgh.club.vo.ClubUserJoinVo; import org.nutz.lang.util.NutMap; import javax.servlet.http.HttpServletResponse; @@ -23,8 +24,12 @@ public interface SysClubInfoManageService extends BaseService Pagination infoManageUserPageData(@Valid ClubUserPageForm pageForm); + Pagination exitManagePageData(@Valid ClubUserPageForm pageForm); + Pagination clubManagePersonAuditPageData(@Valid ClubUserPageForm pageForm); + ClubUserJoinVo exitManageInfo(String id); + List getClubTreeData(); /** diff --git a/src/main/java/com/budwk/app/zhgh/club/service/impl/ClubUserJoinServiceImpl.java b/src/main/java/com/budwk/app/zhgh/club/service/impl/ClubUserJoinServiceImpl.java index ad74026e..dc76f774 100644 --- a/src/main/java/com/budwk/app/zhgh/club/service/impl/ClubUserJoinServiceImpl.java +++ b/src/main/java/com/budwk/app/zhgh/club/service/impl/ClubUserJoinServiceImpl.java @@ -1,14 +1,94 @@ -package com.budwk.app.zhgh.club.service.impl; - +import com.budwk.app.base.constant.RoleConstant; import com.budwk.app.base.service.impl.BaseServiceImpl; +import cn.hutool.core.date.DateUtil; +import com.budwk.app.sys.views.View_user; +import com.budwk.app.zhgh.club.model.ClubUser; import com.budwk.app.zhgh.club.model.ClubUserApply; +import com.budwk.app.zhgh.club.model.SysClub; import com.budwk.app.zhgh.club.service.ClubUserJoinService; +import org.nutz.dao.Cnd; import org.nutz.dao.Dao; import org.nutz.ioc.loader.annotation.IocBean; +import java.util.Date; +import java.util.List; + @IocBean(args = {"refer:dao"}) public class ClubUserJoinServiceImpl extends BaseServiceImpl implements ClubUserJoinService { public ClubUserJoinServiceImpl(Dao dao) { super(dao); } + + @Override + public ClubUserApply buildExitApply(String clubId, String userId) { + // 优先复用最近一次申请记录中的补充信息,避免用户重复填写基础资料。 + ClubUserApply latestApply = fetch( + Cnd.where("userId", "=", userId) + .and("clubId", "=", clubId) + .desc(ClubUserApply::getApplyDate) + ); + if (latestApply != null) { + latestApply.setId(null); + return latestApply; + } + + // 对于会长等注册社团时直接入会的人员,没有申请记录时从当前成员信息兜底生成退会申请。 + ClubUser clubUser = dao().fetch(ClubUser.class, Cnd.where("clubId", "=", clubId).and("userId", "=", userId)); + if (clubUser == null) { + return null; + } + View_user viewUser = dao().fetch(View_user.class, Cnd.where("id", "=", userId)); + + ClubUserApply clubUserApply = new ClubUserApply(); + clubUserApply.setClubId(clubId); + clubUserApply.setUserId(userId); + clubUserApply.setClubPosition(clubUser.getClubPosition()); + clubUserApply.setEmail(clubUser.getEmail()); + clubUserApply.setAvatar(clubUser.getAvatar()); + clubUserApply.setSameTimeJoinOtherClubSituation(clubUser.getSameTimeJoinOtherClubSituation()); + clubUserApply.setAwardsExperience(clubUser.getAwardsExperience()); + clubUserApply.setJoinTime(resolveJoinTime(clubId, userId)); + + List roleCodes = clubUser.getRoleCode(); + if (roleCodes != null && !roleCodes.isEmpty()) { + String exitRoleCode = roleCodes.stream() + .filter(roleCode -> !RoleConstant.CLUB_MEMBER.name().equals(roleCode)) + .findFirst() + .orElse(RoleConstant.CLUB_MEMBER.name()); + clubUserApply.setRoleCode(exitRoleCode); + } else { + clubUserApply.setRoleCode(RoleConstant.CLUB_MEMBER.name()); + } + + if (viewUser != null) { + clubUserApply.setBirthday(viewUser.getBirthday()); + clubUserApply.setMobile(viewUser.getMobile()); + if (clubUserApply.getEmail() == null) { + clubUserApply.setEmail(viewUser.getEmail()); + } + if (clubUserApply.getAvatar() == null) { + clubUserApply.setAvatar(viewUser.getAvatar()); + } + } + return clubUserApply; + } + + @Override + public Date resolveJoinTime(String clubId, String userId) { + ClubUserApply latestJoinApply = fetch( + Cnd.where("userId", "=", userId) + .and("clubId", "=", clubId) + .and("mode", "=", true) + .desc(ClubUserApply::getApplyDate) + ); + if (latestJoinApply != null && latestJoinApply.getJoinTime() != null) { + return latestJoinApply.getJoinTime(); + } + + SysClub club = dao().fetch(SysClub.class, clubId); + if (club != null && club.getFoundTime() != null) { + return DateUtil.parse(club.getFoundTime()); + } + return null; + } } diff --git a/src/main/java/com/budwk/app/zhgh/club/service/impl/SysClubInfoManageServiceImpl.java b/src/main/java/com/budwk/app/zhgh/club/service/impl/SysClubInfoManageServiceImpl.java index c2b5062f..9cc4424c 100644 --- a/src/main/java/com/budwk/app/zhgh/club/service/impl/SysClubInfoManageServiceImpl.java +++ b/src/main/java/com/budwk/app/zhgh/club/service/impl/SysClubInfoManageServiceImpl.java @@ -20,6 +20,7 @@ import com.budwk.app.zhgh.club.service.SysClubInfoManageService; import com.budwk.app.zhgh.club.service.SysClubService; import com.budwk.app.zhgh.club.vo.ClubCommonPageVo; import com.budwk.app.zhgh.club.vo.ClubUserCommonPageVo; +import com.budwk.app.zhgh.club.vo.ClubUserJoinVo; import com.deepoove.poi.XWPFTemplate; import com.deepoove.poi.config.Configure; import com.deepoove.poi.plugin.table.LoopRowTableRenderPolicy; @@ -139,11 +140,27 @@ public class SysClubInfoManageServiceImpl extends BaseServiceImpl exitManagePageData(ClubUserPageForm pageForm) { + Sql sql = Sqls.create(""" + SELECT + info.id, + info.clubId, + info.userId, + info.roleCode AS applyRoleCode, + info.clubPosition, + info.email, + info.mobile, + info.birthday, + info.avatar, + info.sameTimeJoinOtherClubSituation, + info.awardsExperience, + info.signature, + info.applyDate, + u.username AS userName, + u.loginname AS loginName, + u.sex, + u.personType, + u.userState, + club.clubName, + u.unitname AS unitName, + DATE_FORMAT(IFNULL(info.joinTime, club.foundTime), '%Y-%m-%d %H:%i:%s') AS joinTime, + DATE_FORMAT(info.exitTime, '%Y-%m-%d %H:%i:%s') AS exitTime, + ins.id AS instanceId, + ins.businessNo, + ins.state instanceState, + ins.variable instanceVariable, + ins.processDefineId instanceProcessDefineId + FROM + club_user_apply info + LEFT JOIN sys_club club ON club.id = info.clubId + LEFT JOIN vw_user u ON u.id = info.userId + LEFT JOIN wf_process_instance ins ON ins.businessNo = info.id + $condition + """); + Cnd cnd = Cnd.NEW(); + cnd.and("info.mode", "=", false); + cnd.and("ins.state", "=", ProcessInstanceStateEnum.FINISHED.getCode()); + cnd.andEX("info.clubId", "=", pageForm.getClubId()); + cnd.andEX("u.personType", "=", pageForm.getPersonType()); + cnd.andEX("u.userState", "=", pageForm.getUserState()); + if (StrUtil.isNotBlank(pageForm.getSearchKeyword())) { + cnd.and(Cnd.exps("u.loginname", "like", "%" + pageForm.getSearchKeyword() + "%").or("u.username", "like", "%" + pageForm.getSearchKeyword() + "%")); + } + if (!AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_ADMIN.name(), RoleConstant.SCHOOL_UNION_CLUB_ADMIN.name())) { + List clubIdList = commonService.findUserRoleByRoleCode(List.of(RoleConstant.CLUB_PRESIDENT.name(), RoleConstant.CLUB_VICE_PRESIDENT.name(), RoleConstant.CLUB_SECRETARY.name(), RoleConstant.CLUB_VICE_SECRETARY.name(), RoleConstant.CLUB_OPERATOR.name())); + List clubList = dao().query(Sys_user_role.class, Cnd.where("roleId", "in", clubIdList).and("userId", "=", SecurityUtil.getUserId()).and("clubId", "is not", null)); + cnd.and("info.clubId", "in", clubList.stream().map(Sys_user_role::getClubId).toList()); + } + cnd.groupBy("info.id"); + cnd.desc("info.applyDate"); + sql.setCondition(cnd); + Pagination pagination = listPageVO(pageForm, sql, ClubUserCommonPageVo.class); + List list = pagination.getList(ClubUserCommonPageVo.class); + for (ClubUserCommonPageVo vo : list) { + vo.setRoleName(SysClubUserServiceImpl.convertRoleName(List.of(vo.getApplyRoleCode()))); + } + pagination.setList(list); + return pagination; + } + + @Override + public ClubUserJoinVo exitManageInfo(String id) { + Sql sql = Sqls.create(""" + SELECT + cua.*, + club.clubName, + u.loginname AS loginName, + u.username AS userName, + u.sex, + u.mobile, + u.unitName, + u.unionName, + u.technicalTitle, + u.education, + u.academicDegree, + u.position + FROM + club_user_apply cua + LEFT JOIN vw_user u ON u.id = cua.userId + LEFT JOIN sys_club club ON club.id = cua.clubId + WHERE cua.id = @id + """); + sql.setParam("id", id); + return fetchVO(sql, ClubUserJoinVo.class); + } + @Override public Pagination clubManagePersonAuditPageData(ClubUserPageForm pageForm) { Sql sql = Sqls.create(""" diff --git a/src/main/java/com/budwk/app/zhgh/club/vo/ClubUserCommonPageVo.java b/src/main/java/com/budwk/app/zhgh/club/vo/ClubUserCommonPageVo.java index 6411de53..fb421266 100644 --- a/src/main/java/com/budwk/app/zhgh/club/vo/ClubUserCommonPageVo.java +++ b/src/main/java/com/budwk/app/zhgh/club/vo/ClubUserCommonPageVo.java @@ -29,6 +29,9 @@ public class ClubUserCommonPageVo extends ClubUser { private String unionName; private String birthday; private String unionId; + private String joinTime; + private String exitTime; + private String applyRoleCode; private String roleName; } diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/outlay/outlayManage/club/controller/OutlayManageClubQueryController.java b/src/main/java/com/budwk/app/zhgh/dayofficework/outlay/outlayManage/club/controller/OutlayManageClubQueryController.java index b32670f5..8c7743bb 100644 --- a/src/main/java/com/budwk/app/zhgh/dayofficework/outlay/outlayManage/club/controller/OutlayManageClubQueryController.java +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/outlay/outlayManage/club/controller/OutlayManageClubQueryController.java @@ -80,7 +80,9 @@ public class OutlayManageClubQueryController { SELECT * FROM `outlay_manage_club` $condition """); cnd.andEX("year", "=", year); - if (!AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_ADMIN.name())) { + // if (!AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_ADMIN.name())) { + // 现逻辑:系统管理员、校工会社团管理员可查看全部社团分页数据,会长仅查看自己负责的社团。 + if (!AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_CLUB_ADMIN.name())) { List roleIds = sysRoleService.getRoleIdsByCode(List.of(RoleConstant.CLUB_PRESIDENT, RoleConstant.CLUB_SECRETARY)); List userRoles = clubQueryService.dao().query(Sys_user_role.class, Cnd.where("userId", "=", SecurityUtil.getUserId()) diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/outlay/outlayManage/club/model/OutlayManageClub.java b/src/main/java/com/budwk/app/zhgh/dayofficework/outlay/outlayManage/club/model/OutlayManageClub.java index 1bc1ef10..b2570efa 100644 --- a/src/main/java/com/budwk/app/zhgh/dayofficework/outlay/outlayManage/club/model/OutlayManageClub.java +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/outlay/outlayManage/club/model/OutlayManageClub.java @@ -75,7 +75,7 @@ public class OutlayManageClub extends BaseModel implements Serializable { @Column @Comment("社团名称") - @ColDefine(type = ColType.VARCHAR, width = 32) + @ColDefine(type = ColType.VARCHAR) private String clubName; @Column diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/unionReimburse/controller/UnionReimburseApplyController.java b/src/main/java/com/budwk/app/zhgh/dayofficework/unionReimburse/controller/UnionReimburseApplyController.java index b10116f4..b53b50b6 100644 --- a/src/main/java/com/budwk/app/zhgh/dayofficework/unionReimburse/controller/UnionReimburseApplyController.java +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/unionReimburse/controller/UnionReimburseApplyController.java @@ -27,6 +27,7 @@ import com.budwk.app.zhgh.staffbenefit.condolence.service.CondolenceTypeService; import com.budwk.app.zhgh.dayofficework.unionReimburse.model.UnionReimburse; import com.budwk.app.zhgh.staffbenefit.condolence.model.CondolenceType; import com.budwk.app.zhgh.dayofficework.unionReimburse.service.UnionReimburseService; +import com.budwk.app.zhgh.dayofficework.unionReimburse.vo.UnionReimburseBankHistoryVO; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.extern.slf4j.Slf4j; @@ -85,19 +86,7 @@ public class UnionReimburseApplyController { @SaCheckPermission(value = {"unionReimburse.apply", "h5.unionReimburse.apply"}, mode = SaMode.OR) @SLog(type = "unionReimburse", tag = "工会报销-报销申请", msg = "保存报销申请") public Result save(@Param("data") UnionReimburse unionReimburse) { - if (StrUtil.isBlank(unionReimburse.getId())) { - unionReimburse.setCreateTime(new Date()); - String documentNo = generateDocumentNo(); - unionReimburse.setDocumentNo(documentNo); - } - unionReimburse.setStateId(1);//待提交 - if (("UNION_REIMBURSE_PROJECT_1").equals(unionReimburse.getReimburseProject())){ - unionReimburse.setRealMoney(unionReimburse.getCondolenceMoney()); - }else { - unionReimburse.setRealMoney(unionReimburse.getMoney()); - } - dao.insertOrUpdate(unionReimburse); - return Result.success(); + return unionReimburseService.saveApply(unionReimburse, 1); } @At @@ -105,18 +94,10 @@ public class UnionReimburseApplyController { @Aop(TransAop.READ_COMMITTED) @SaCheckPermission(value = {"unionReimburse.apply", "h5.unionReimburse.apply"}, mode = SaMode.OR) public Result submit(@Param("data") UnionReimburse unionReimburse) { - if (StrUtil.isBlank(unionReimburse.getId())) { - unionReimburse.setCreateTime(new Date()); - String documentNo = generateDocumentNo(); - unionReimburse.setDocumentNo(documentNo); + Result result = unionReimburseService.saveApply(unionReimburse, 2); + if (result.getCode() != 0) { + return result; } - unionReimburse.setStateId(2);//待审核 - if (("UNION_REIMBURSE_PROJECT_1").equals(unionReimburse.getReimburseProject())){ - unionReimburse.setRealMoney(unionReimburse.getCondolenceMoney()); - }else { - unionReimburse.setRealMoney(unionReimburse.getMoney()); - } - dao.insertOrUpdate(unionReimburse); // // 开启流程实例 // Dict args = Dict.create(); // args.set(FlowConst.SUBMIT_TYPE, ProcessSubmitTypeEnum.APPLY.getCode()); @@ -153,7 +134,7 @@ public class UnionReimburseApplyController { @SaCheckLogin @ApiOperation("获取当前登录人的申请信息") public Result info(@Param("id") String id) { - UnionReimburse unionReimburse = dao.fetch(UnionReimburse.class, id); + UnionReimburse unionReimburse = unionReimburseService.getApplyForm(id); return Result.success(unionReimburse); } @@ -190,14 +171,9 @@ public class UnionReimburseApplyController { cnd.and(seg); } cnd.and(View_user::getId, "!=", SecurityUtil.getUserId()); - /*if (!AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_ADMIN.name())) { - if (AuthUtil.hasRoleOr(RoleConstant.BRANCH_UNION_ADMIN.name(), RoleConstant.BRANCH_UNION_CHAIRMAN.name())) { - cnd.and(View_user::getUnionId, "=", SecurityUtil.getUnionId()); - } else { - cnd.and(View_user::getId, "=", SecurityUtil.getUserId()); - } - - }*/ + if (!AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_ADMIN.name())) { + cnd.and(View_user::getUnionId, "=", SecurityUtil.getUnionId()); + } sql.setCondition(cnd); Pagination pagination = unionReimburseService.listPageMap(1, 50, sql); return Result.success(pagination.getList()); @@ -215,20 +191,25 @@ public class UnionReimburseApplyController { @ApiOperation("查询厉害账号信息") @SaCheckPermission(value = {"unionReimburse.apply", "h5.unionReimburse.apply"}, mode = SaMode.OR) public Result findUserBankHistory(String payer) { - Sql sql = Sqls.create(""" - SELECT DISTINCT - bankCardNumber, - bankOfDeposit - FROM union_reimburse - $condition - """); - Cnd cnd = Cnd.NEW(); - cnd.andEX("payer", "=", payer); - sql.setCondition(cnd); - List result = unionReimburseService.listMap(sql); + List result = unionReimburseService.getUserBankHistory(payer); return Result.success(result); } + @At + @ApiOperation("校验发票号码是否重复") + @SaCheckPermission(value = {"unionReimburse.apply", "h5.unionReimburse.apply"}, mode = SaMode.OR) + public Result checkInvoiceDuplicate(String invoiceNo, String reimburseId) { + Result result = unionReimburseService.checkInvoiceDuplicate(invoiceNo, reimburseId); + return result == null ? Result.success() : result; + } + + @At + @ApiOperation("自动识别发票信息") + @SaCheckPermission(value = {"unionReimburse.apply", "h5.unionReimburse.apply"}, mode = SaMode.OR) + public Result recognizeInvoice(String fileId, @Param("verifySwitch") Boolean verifySwitch) { + return unionReimburseService.recognizeInvoice(fileId, verifySwitch); + } + @At @ApiOperation("查询校工会经费余额") @@ -281,39 +262,4 @@ public class UnionReimburseApplyController { // 如果没有找到该社团的经费记录,返回0 return Result.success(0.0); } - @At - @ApiOperation("生成文件编号") - private String generateDocumentNo() { - int currentYear = DateUtil.thisYear(); - String yearPrefix = String.valueOf(currentYear); - - org.nutz.dao.Cnd cnd = org.nutz.dao.Cnd.where("documentNo", "LIKE", yearPrefix + "%"); - cnd.desc("documentNo"); - cnd.limit(1); - - List list = dao.query(UnionReimburse.class, cnd); - - int nextSeq = 1; // 默认从01开始 - if (!list.isEmpty()) { - String maxNo = list.get(0).getDocumentNo(); - if (maxNo != null && maxNo.startsWith(yearPrefix)) { - // 提取序号部分(去掉年份前缀) - String seqPart = maxNo.substring(yearPrefix.length()); - try { - int currentMaxSeq = Integer.parseInt(seqPart); - nextSeq = currentMaxSeq + 1; - } catch (NumberFormatException e) { - // 如果解析失败,使用默认值1 - nextSeq = 1; - } - } - } - - // 格式化序号为两位数 - String seqStr = String.format("%02d", nextSeq); - return yearPrefix + seqStr; - } - - - } diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/unionReimburse/controller/UnionReimburseCertifierUserSignController.java b/src/main/java/com/budwk/app/zhgh/dayofficework/unionReimburse/controller/UnionReimburseCertifierUserSignController.java index ce76cc30..c5524f81 100644 --- a/src/main/java/com/budwk/app/zhgh/dayofficework/unionReimburse/controller/UnionReimburseCertifierUserSignController.java +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/unionReimburse/controller/UnionReimburseCertifierUserSignController.java @@ -3,11 +3,13 @@ package com.budwk.app.zhgh.dayofficework.unionReimburse.controller; import cn.dev33.satoken.annotation.SaCheckPermission; import cn.dev33.satoken.annotation.SaMode; 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.param.PageForm; import com.budwk.app.base.result.Result; import com.budwk.app.base.utils.PageUtil; import com.budwk.app.flow.enums.ProcessTaskStateEnum; +import com.budwk.app.web.commons.auth.utils.AuthUtil; import com.budwk.app.web.commons.auth.utils.SecurityUtil; import com.budwk.app.zhgh.dayofficework.unionReimburse.service.UnionReimburseService; import io.swagger.annotations.Api; @@ -105,6 +107,9 @@ public class UnionReimburseCertifierUserSignController { // 工会、单位名称查询条件 cnd.andEX("info.unionId", "=", unionId); cnd.andEX("info.unitId", "=", unitId); + if (!isAdmin()) { + cnd.andEX("info.unionId", "=", SecurityUtil.getUnionId()); + } // 报销项目查询条件 cnd.andEX("info.reimburseProject", "=", reimburseProject); @@ -116,10 +121,11 @@ public class UnionReimburseCertifierUserSignController { seg.orLike("info.loginName",pageForm.getSearchKeyword()); cnd.and(seg); } - if (StrUtil.isAllBlank(pageForm.getPageOrderName(), pageForm.getPageOrderBy())) { + String orderField = normalizeSortField(pageForm.getPageOrderName()); + if (StrUtil.isAllBlank(orderField, pageForm.getPageOrderBy())) { cnd.desc("info.createTime"); } else { - cnd.orderBy(pageForm.getPageOrderName(), PageUtil.getOrder(pageForm.getPageOrderBy())); + cnd.orderBy(orderField, PageUtil.getOrder(pageForm.getPageOrderBy())); } cnd.groupBy("t.id"); cnd.desc("t.createdAt"); @@ -128,4 +134,21 @@ public class UnionReimburseCertifierUserSignController { return Result.success(pagination); } + private boolean isAdmin() { + return AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_ADMIN.name()); + } + + private String normalizeSortField(String pageOrderName) { + if (StrUtil.isBlank(pageOrderName)) { + return null; + } + return switch (pageOrderName) { + case "loginName" -> "info.loginName"; + case "unionName" -> "info.unionName"; + case "unitName" -> "info.unitName"; + case "createTime" -> "info.createTime"; + case "reimburseProject" -> "info.reimburseProject"; + default -> null; + }; + } } diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/unionReimburse/controller/UnionReimburseCollectController.java b/src/main/java/com/budwk/app/zhgh/dayofficework/unionReimburse/controller/UnionReimburseCollectController.java index 4896f3d3..7c2dda77 100644 --- a/src/main/java/com/budwk/app/zhgh/dayofficework/unionReimburse/controller/UnionReimburseCollectController.java +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/unionReimburse/controller/UnionReimburseCollectController.java @@ -12,6 +12,7 @@ import com.budwk.app.base.constant.RoleConstant; import com.budwk.app.base.page.Pagination; import com.budwk.app.base.param.PageForm; import com.budwk.app.base.result.Result; +import com.budwk.app.base.utils.PageUtil; import com.budwk.app.base.utils.CommonDownloadUtil; import com.budwk.app.flow.engine.FlowEngine; import com.budwk.app.sys.models.Sys_dict; @@ -93,6 +94,9 @@ public class UnionReimburseCollectController { // 工会、单位名称查询条件 cnd.andEX("info.unionId", "=", unionId); cnd.andEX("info.unitId", "=", unitId); + if (!isAdmin()) { + cnd.andEX("info.unionId", "=", SecurityUtil.getUnionId()); + } // 报销项目查询条件 cnd.andEX("info.reimburseProject", "=", reimburseProject); @@ -106,7 +110,7 @@ public class UnionReimburseCollectController { cnd.and("info.stateId", "in", List.of(3, 2)); - cnd.desc("info.createTime"); + applyListSort(cnd, pageForm); sql.setCondition(cnd); Pagination pagination = unionReimburseService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql); return Result.success(pagination); @@ -149,7 +153,7 @@ public class UnionReimburseCollectController { cnd.andEX("YEAR(rei.createTime)", "=", year); cnd.andEX("u.unitid", "=", unitId); cnd.andEX("u.unionid", "=", unionId); - if (!AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_ADMIN.name())) { + if (!isAdmin()) { cnd.andEX("u.unionid", "=", SecurityUtil.getUnionId()); } cnd.desc("rei.createTime"); @@ -166,8 +170,10 @@ public class UnionReimburseCollectController { try{ response.setContentType("application/octet-stream"); - response.setHeader("Content-Disposition", "attachment;filename=" + new String(("收款人名册.xls").getBytes("utf-8"), "ISO8859-1")); - Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams(), excelExportEntities, list); + response.setHeader("Content-Disposition", "attachment;filename=" + new String(("收款人名册.xlsx").getBytes("utf-8"), "ISO8859-1")); + ExportParams exportParams = new ExportParams(); + exportParams.setType(ExcelType.XSSF); + Workbook workbook = ExcelExportUtil.exportExcel(exportParams, excelExportEntities, list); workbook.write(response.getOutputStream()); }catch (Exception e){ e.printStackTrace(); @@ -197,6 +203,9 @@ public class UnionReimburseCollectController { Cnd cnd = buildCondition(year, unionId, unitId, reimburseProject, userName, "t1."); // 只查询流程实例状态为20的数据(已完成状态) cnd.and("ins.state", "=", 20); + if (!isAdmin()) { + cnd.andEX("t1.unionId", "=", SecurityUtil.getUnionId()); + } cnd.desc("t1.createTime"); sql.setCondition(cnd); @@ -275,4 +284,34 @@ public class UnionReimburseCollectController { return cnd; } + + private boolean isAdmin() { + return AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_ADMIN.name()); + } + + /** + * 收集页列表只允许固定字段排序,前端传入原序时回退到默认申请时间倒序。 + */ + private void applyListSort(Cnd cnd, PageForm pageForm) { + String orderField = normalizeSortField(pageForm.getPageOrderName()); + if (StrUtil.isAllBlank(orderField, pageForm.getPageOrderBy())) { + cnd.desc("info.createTime"); + return; + } + cnd.orderBy(orderField, PageUtil.getOrder(pageForm.getPageOrderBy())); + } + + private String normalizeSortField(String pageOrderName) { + if (StrUtil.isBlank(pageOrderName)) { + return null; + } + return switch (pageOrderName) { + case "loginName" -> "info.loginName"; + case "unionName" -> "info.unionName"; + case "unitName" -> "info.unitName"; + case "createTime" -> "info.createTime"; + case "reimburseProject" -> "info.reimburseProject"; + default -> null; + }; + } } diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/unionReimburse/controller/UnionReimburseMineController.java b/src/main/java/com/budwk/app/zhgh/dayofficework/unionReimburse/controller/UnionReimburseMineController.java index 99658c4f..779b5b30 100644 --- a/src/main/java/com/budwk/app/zhgh/dayofficework/unionReimburse/controller/UnionReimburseMineController.java +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/unionReimburse/controller/UnionReimburseMineController.java @@ -9,14 +9,17 @@ import cn.hutool.core.io.IoUtil; import cn.hutool.core.util.StrUtil; import com.budwk.app.base.annotation.SLog; import com.budwk.app.base.model.Audit; +import com.budwk.app.base.constant.RoleConstant; import com.budwk.app.base.page.Pagination; import com.budwk.app.base.param.PageForm; import com.budwk.app.base.result.Result; import com.budwk.app.base.utils.CommonDownloadUtil; import com.budwk.app.base.utils.MoneyUtil; import com.budwk.app.base.utils.OfficePlusUtil; +import com.budwk.app.base.utils.PageUtil; import com.budwk.app.base.utils.SysOfficeTemplateUtil; import com.budwk.app.flow.engine.FlowEngine; +import com.budwk.app.web.commons.auth.utils.AuthUtil; import com.budwk.app.web.commons.auth.utils.SecurityUtil; import com.budwk.app.zhgh.dayofficework.unionReimburse.model.UnionReimburse; import com.budwk.app.zhgh.dayofficework.unionReimburse.service.UnionReimburseService; @@ -133,6 +136,9 @@ public class UnionReimburseMineController { // 工会、单位名称查询条件 cnd.andEX("info.unionId", "=", unionId); cnd.andEX("info.unitId", "=", unitId); + if (!isAdmin()) { + cnd.andEX("info.unionId", "=", SecurityUtil.getUnionId()); + } // 报销项目查询条件 cnd.andEX("info.reimburseProject", "=", reimburseProject); @@ -143,13 +149,40 @@ public class UnionReimburseMineController { seg.orLike("info.loginName", "%" + searchKeyword + "%"); cnd.and(seg); } - cnd.desc("info.createTime"); + applyListSort(cnd, pageForm); cnd.and("info.userId", "=", SecurityUtil.getUserId()); sql.setCondition(cnd); Pagination pagination = unionReimburseService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql); return Result.success(pagination); } + private boolean isAdmin() { + return AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_ADMIN.name()); + } + + private void applyListSort(Cnd cnd, PageForm pageForm) { + String orderField = normalizeSortField(pageForm.getPageOrderName()); + if (StrUtil.isAllBlank(orderField, pageForm.getPageOrderBy())) { + cnd.desc("info.createTime"); + return; + } + cnd.orderBy(orderField, PageUtil.getOrder(pageForm.getPageOrderBy())); + } + + private String normalizeSortField(String pageOrderName) { + if (StrUtil.isBlank(pageOrderName)) { + return null; + } + return switch (pageOrderName) { + case "loginName" -> "info.loginName"; + case "unionName" -> "info.unionName"; + case "unitName" -> "info.unitName"; + case "createTime" -> "info.createTime"; + case "reimburseProject" -> "info.reimburseProject"; + default -> null; + }; + } + @At @ApiOperation("删除") @Aop(TransAop.READ_COMMITTED) diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/unionReimburse/controller/UnionReimburseReviewController.java b/src/main/java/com/budwk/app/zhgh/dayofficework/unionReimburse/controller/UnionReimburseReviewController.java index 0df7de86..5f3fa5f9 100644 --- a/src/main/java/com/budwk/app/zhgh/dayofficework/unionReimburse/controller/UnionReimburseReviewController.java +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/unionReimburse/controller/UnionReimburseReviewController.java @@ -1,7 +1,5 @@ package com.budwk.app.zhgh.dayofficework.unionReimburse.controller; - -import cn.dev33.satoken.annotation.SaCheckLogin; import cn.dev33.satoken.annotation.SaCheckPermission; import cn.dev33.satoken.annotation.SaMode; import cn.hutool.core.util.StrUtil; @@ -10,15 +8,12 @@ import com.budwk.app.base.page.Pagination; import com.budwk.app.base.param.PageForm; import com.budwk.app.base.result.Result; import com.budwk.app.base.utils.PageUtil; -import com.budwk.app.flow.enums.ProcessTaskStateEnum; -import com.budwk.app.web.commons.auth.utils.SecurityUtil; import com.budwk.app.zhgh.dayofficework.unionReimburse.model.UnionReimburse; import com.budwk.app.zhgh.dayofficework.unionReimburse.service.UnionReimburseService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.extern.slf4j.Slf4j; import org.nutz.aop.interceptor.ioc.TransAop; -import org.nutz.dao.Chain; import org.nutz.dao.Cnd; import org.nutz.dao.Dao; import org.nutz.dao.Sqls; @@ -35,8 +30,6 @@ import org.nutz.mvc.annotation.Param; import java.util.Date; import java.util.List; -import static org.openjdk.nashorn.internal.runtime.regexp.joni.Config.log; - @IocBean @At("/platform/unionReimburse/review") @Ok("json:full") @@ -49,12 +42,12 @@ public class UnionReimburseReviewController { @Inject private UnionReimburseService unionReimburseService; - @At("/index") @Ok("beetl:/platform/zhgh/dayofficework/unionReimburse/review/index.html") @SaCheckPermission("unionReimburse.review") public void index() { } + @At("/h5") @Ok("beetl:/platform/zhghh5/dayofficework/unionReimburse/review/index.html") @SaCheckPermission("h5.unionReimburse.review") @@ -71,37 +64,26 @@ public class UnionReimburseReviewController { String unitId, String reimburseProject) { Sql sql = Sqls.create(""" - SELECT + SELECT info.* FROM union_reimburse info - $condition + $condition """); Cnd cnd = Cnd.NEW(); - if (approval) { - // 已审核:查询 stateId 为 3(报销成功)、4(拒绝)、5(退回)的记录 cnd.and("info.stateId", "in", List.of(3, 4, 5)); } else { - // 未审核:只查询 stateId 为 2(待审核确认)的记录 cnd.and("info.stateId", "=", 2); } - // 年度查询条件 cnd.andEX("year(info.createTime)", "=", year); - - // 工会、单位名称查询条件 cnd.andEX("info.unionId", "=", unionId); cnd.andEX("info.unitId", "=", unitId); - - // 报销项目查询条件 cnd.andEX("info.reimburseProject", "=", reimburseProject); - - - // 姓名和工号查询条件 if (StrUtil.isNotBlank(pageForm.getSearchKeyword())) { SqlExpressionGroup seg = new SqlExpressionGroup(); - seg.orLike("info.userName",pageForm.getSearchKeyword()); - seg.orLike("info.loginName",pageForm.getSearchKeyword()); + seg.orLike("info.userName", pageForm.getSearchKeyword()); + seg.orLike("info.loginName", pageForm.getSearchKeyword()); cnd.and(seg); } if (StrUtil.isAllBlank(pageForm.getPageOrderName(), pageForm.getPageOrderBy())) { @@ -119,21 +101,18 @@ public class UnionReimburseReviewController { @ApiOperation("审核") @Aop(TransAop.READ_COMMITTED) @SaCheckPermission(value = {"unionReimburse.review", "h5.unionReimburse.review"}, mode = SaMode.OR) - @SLog( tag = "审核工会报销", msg = "审核工会报销") - public Result reviewTask(@Param("data") UnionReimburse unionReimburse,String submitType) { + @SLog(tag = "审核工会报销", msg = "审核工会报销") + public Result reviewTask(@Param("data") UnionReimburse unionReimburse, String submitType) { if (submitType == null || !List.of("3", "4", "5").contains(submitType)) { return Result.error("无效的审核操作类型"); } - UnionReimburse dbRecord = dao.fetch(UnionReimburse.class, unionReimburse.getId()); if (dbRecord == null) { return Result.error("记录不存在"); } - dbRecord.setReviewTime(new Date()); dbRecord.setStateId(Integer.parseInt(submitType)); dbRecord.setReviewOpinion(unionReimburse.getReviewOpinion()); - dao.update(dbRecord); return Result.success(); } @@ -142,7 +121,7 @@ public class UnionReimburseReviewController { @ApiOperation("一键审核") @Aop(TransAop.READ_COMMITTED) @SaCheckPermission(value = {"unionReimburse.review", "h5.unionReimburse.review"}, mode = SaMode.OR) - @SLog( tag = "一键审核", msg = "一键审核") + @SLog(tag = "一键审核", msg = "一键审核") public Result allReview() { try { List reimbursements = unionReimburseService.query(Cnd.where("stateId", "=", 2)); @@ -158,6 +137,4 @@ public class UnionReimburseReviewController { return Result.error("一键审核失败: " + e.getMessage()); } } - - } diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/unionReimburse/controller/UnionReimburseStatisticsController.java b/src/main/java/com/budwk/app/zhgh/dayofficework/unionReimburse/controller/UnionReimburseStatisticsController.java index b4c0dd22..315ad87b 100644 --- a/src/main/java/com/budwk/app/zhgh/dayofficework/unionReimburse/controller/UnionReimburseStatisticsController.java +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/unionReimburse/controller/UnionReimburseStatisticsController.java @@ -6,11 +6,14 @@ import cn.afterturn.easypoi.excel.entity.enmus.ExcelType; import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity; import cn.dev33.satoken.annotation.SaCheckPermission; import cn.dev33.satoken.annotation.SaMode; +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.utils.CommonDownloadUtil; import com.budwk.app.flow.engine.FlowEngine; import com.budwk.app.sys.services.SysDictService; +import com.budwk.app.web.commons.auth.utils.AuthUtil; +import com.budwk.app.web.commons.auth.utils.SecurityUtil; import com.budwk.app.zhgh.dayofficework.unionReimburse.param.UnionReimbursePageForm; import com.budwk.app.zhgh.dayofficework.unionReimburse.service.UnionReimburseService; import io.swagger.annotations.Api; @@ -93,6 +96,8 @@ public class UnionReimburseStatisticsController { } if (Strings.isNotBlank(unionId)) { sql.setVar("unionIdCnd", String.format("AND us.unionId = '%s' ", unionId)); + } else if (!isAdmin()) { + sql.setVar("unionIdCnd", String.format("AND us.unionId = '%s' ", SecurityUtil.getUnionId())); } if (Strings.isNotBlank(condolenceTypeId)) { sql.setVar("typeCnd", String.format("AND con.condolenceTypeId = '%s' ", condolenceTypeId)); @@ -150,4 +155,7 @@ public class UnionReimburseStatisticsController { } + private boolean isAdmin() { + return AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_ADMIN.name()); + } } diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/unionReimburse/model/UnionReimburse.java b/src/main/java/com/budwk/app/zhgh/dayofficework/unionReimburse/model/UnionReimburse.java index c84b5c42..d4235db4 100644 --- a/src/main/java/com/budwk/app/zhgh/dayofficework/unionReimburse/model/UnionReimburse.java +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/unionReimburse/model/UnionReimburse.java @@ -415,4 +415,9 @@ public class UnionReimburse extends BaseModel { @ColDefine(type = ColType.DATETIME) private Date reviewTime; + /** + * 发票明细列表仅用于申请页表单回显和提交,不直接映射主表字段。 + */ + private List invoiceDetails; + } diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/unionReimburse/service/UnionReimburseService.java b/src/main/java/com/budwk/app/zhgh/dayofficework/unionReimburse/service/UnionReimburseService.java index cac64d45..546506e9 100644 --- a/src/main/java/com/budwk/app/zhgh/dayofficework/unionReimburse/service/UnionReimburseService.java +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/unionReimburse/service/UnionReimburseService.java @@ -1,11 +1,41 @@ package com.budwk.app.zhgh.dayofficework.unionReimburse.service; import com.budwk.app.base.service.BaseService; +import com.budwk.app.base.result.Result; import com.budwk.app.zhgh.dayofficework.unionReimburse.model.UnionReimburse; import com.budwk.app.zhgh.dayofficework.unionReimburse.param.UnionReimbursePageForm; +import com.budwk.app.zhgh.dayofficework.unionReimburse.vo.UnionReimburseBankHistoryVO; import org.nutz.dao.sql.Sql; +import java.util.List; + public interface UnionReimburseService extends BaseService { Sql getSql(UnionReimbursePageForm pageForm); + + /** + * 保存或提交报销申请,并同步保存发票明细。 + */ + Result saveApply(UnionReimburse unionReimburse, int stateId); + + /** + * 查询申请表单详情,补齐发票明细回显数据。 + */ + UnionReimburse getApplyForm(String id); + + /** + * 查询付款人的历史收款信息。 + */ + List getUserBankHistory(String payer); + + /** + * 校验发票号码是否与历史报销记录重复。 + */ + Result checkInvoiceDuplicate(String invoiceNo, String reimburseId); + + /** + * 识别单张发票文件,并在需要时调用腾讯云验真接口补齐票面信息。 + */ + Result recognizeInvoice(String fileId, Boolean verifySwitch); + } diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/unionReimburse/service/impl/UnionReimburseServiceImpl.java b/src/main/java/com/budwk/app/zhgh/dayofficework/unionReimburse/service/impl/UnionReimburseServiceImpl.java index 55a91f56..32f2c050 100644 --- a/src/main/java/com/budwk/app/zhgh/dayofficework/unionReimburse/service/impl/UnionReimburseServiceImpl.java +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/unionReimburse/service/impl/UnionReimburseServiceImpl.java @@ -1,24 +1,69 @@ package com.budwk.app.zhgh.dayofficework.unionReimburse.service.impl; - +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; +import com.budwk.app.base.constant.RoleConstant; +import com.budwk.app.base.result.Result; import com.budwk.app.base.service.impl.BaseServiceImpl; +import com.budwk.app.sys.models.Sys_file; +import com.budwk.app.sys.services.SysFileService; import com.budwk.app.zhgh.dayofficework.unionReimburse.model.UnionReimburse; +import com.budwk.app.zhgh.dayofficework.unionReimburse.model.UnionReimburseInvoiceDetail; import com.budwk.app.zhgh.dayofficework.unionReimburse.param.UnionReimbursePageForm; import com.budwk.app.zhgh.dayofficework.unionReimburse.service.UnionReimburseService; +import com.budwk.app.zhgh.dayofficework.unionReimburse.vo.UnionReimburseBankHistoryVO; +import com.budwk.app.web.commons.auth.utils.AuthUtil; +import com.budwk.app.web.commons.auth.utils.SecurityUtil; import lombok.extern.slf4j.Slf4j; 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.impl.PropertiesProxy; +import org.nutz.ioc.loader.annotation.Inject; import org.nutz.ioc.loader.annotation.IocBean; +import org.nutz.lang.Lang; +import org.nutz.lang.util.NutMap; +import com.tencentcloudapi.common.Credential; +import com.tencentcloudapi.common.exception.TencentCloudSDKException; +import com.tencentcloudapi.common.profile.ClientProfile; +import com.tencentcloudapi.common.profile.HttpProfile; +import com.tencentcloudapi.ocr.v20181119.OcrClient; +import com.tencentcloudapi.ocr.v20181119.models.TextVatInvoice; +import com.tencentcloudapi.ocr.v20181119.models.VatInvoice; +import com.tencentcloudapi.ocr.v20181119.models.VatInvoiceItem; +import com.tencentcloudapi.ocr.v20181119.models.VatInvoiceOCRRequest; +import com.tencentcloudapi.ocr.v20181119.models.VatInvoiceOCRResponse; +import com.tencentcloudapi.ocr.v20181119.models.VatInvoiceVerifyRequest; +import com.tencentcloudapi.ocr.v20181119.models.VatInvoiceVerifyResponse; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Base64; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.Date; +import java.util.stream.Collectors; @Slf4j @IocBean(args = {"refer:dao"}) -public class UnionReimburseServiceImpl extends BaseServiceImpl implements UnionReimburseService { +public class UnionReimburseServiceImpl extends BaseServiceImpl implements UnionReimburseService { + @Inject + private SysFileService sysFileService; + @Inject + private PropertiesProxy conf; public UnionReimburseServiceImpl(Dao dao) { super(dao); } @@ -35,30 +80,609 @@ public class UnionReimburseServiceImpl extends BaseServiceImpl $condition """); Cnd cnd = Cnd.NEW(); - // 年度查询条件 cnd.andEX("year(info.createTime)", "=", pageForm.getYear()); - - // 工会、单位名称查询条件 cnd.andEX("info.condolenceUnionId", "=", pageForm.getUnionId()); cnd.andEX("info.unitId", "=", pageForm.getUnitId()); - cnd.andEX("info.condolenceTypeId", "=", pageForm.getCondolenceTypeId()); cnd.andEX("info.way", "=", pageForm.getWay()); - - // 报销项目查询条件 cnd.andEX("info.reimburseProject", "=", pageForm.getReimburseProject()); - // 姓名和工号查询条件 + if (!isAdmin()) { + cnd.andEX("info.condolenceUnionId", "=", SecurityUtil.getUnionId()); + } if (StrUtil.isNotBlank(pageForm.getSearchKeyword())) { SqlExpressionGroup seg = new SqlExpressionGroup(); - seg.orLike("info.condolenceUserName",pageForm.getSearchKeyword()); - seg.orLike("info.condolenceLoginName",pageForm.getSearchKeyword()); + seg.orLike("info.condolenceUserName", pageForm.getSearchKeyword()); + seg.orLike("info.condolenceLoginName", pageForm.getSearchKeyword()); cnd.and(seg); } - cnd.and("info.stateId", "in", List.of(3, 2)); - - cnd.desc("info.createTime"); + applyStatisticsSort(cnd, pageForm); sql.setCondition(cnd); return sql; } + + @Override + public Result saveApply(UnionReimburse unionReimburse, int stateId) { + if (unionReimburse == null) { + return Result.error("报销数据不能为空"); + } + UnionReimburse oldRecord = null; + if (StrUtil.isNotBlank(unionReimburse.getId())) { + oldRecord = this.fetch(unionReimburse.getId()); + if (oldRecord == null) { + return Result.error("未找到对应的报销记录"); + } + } + + normalizeInvoiceDetails(unionReimburse); + if (needInvoiceDetails(unionReimburse)) { + fillInvoiceSummary(unionReimburse); + } + if (stateId == 2) { + Result validateResult = validateBeforeSubmit(unionReimburse); + if (validateResult != null) { + return validateResult; + } + Result duplicateResult = validateInvoiceDuplicate(unionReimburse); + if (duplicateResult != null) { + return duplicateResult; + } + } + + if (oldRecord == null) { + unionReimburse.setCreateTime(DateUtil.date()); + unionReimburse.setDocumentNo(generateDocumentNo()); + } else { + unionReimburse.setCreateTime(oldRecord.getCreateTime()); + unionReimburse.setDocumentNo(oldRecord.getDocumentNo()); + } + unionReimburse.setStateId(stateId); + if ("UNION_REIMBURSE_PROJECT_1".equals(unionReimburse.getReimburseProject())) { + unionReimburse.setRealMoney(unionReimburse.getCondolenceMoney()); + } else { + unionReimburse.setRealMoney(unionReimburse.getMoney()); + } + this.dao().insertOrUpdate(unionReimburse); + saveInvoiceDetails(unionReimburse.getId(), unionReimburse.getInvoiceDetails()); + return Result.success(stateId == 2 ? "提交成功" : "保存成功"); + } + + @Override + public UnionReimburse getApplyForm(String id) { + UnionReimburse unionReimburse = this.fetch(id); + if (unionReimburse == null) { + return null; + } + List invoiceDetails = this.dao().query( + UnionReimburseInvoiceDetail.class, + Cnd.where("reimburseId", "=", id).asc("createdAt") + ); + unionReimburse.setInvoiceDetails(invoiceDetails == null ? new ArrayList<>() : invoiceDetails); + return unionReimburse; + } + + @Override + public List getUserBankHistory(String payer) { + if (StrUtil.isBlank(payer)) { + return new ArrayList<>(); + } + Sql sql = Sqls.create(""" + SELECT + bankUserName, + bankCardNumber, + bankOfDeposit + FROM + union_reimburse + $condition + GROUP BY + bankUserName, + bankCardNumber, + bankOfDeposit + ORDER BY + MAX(createTime) DESC + """); + Cnd cnd = Cnd.NEW(); + cnd.and("payer", "=", payer); + cnd.and("bankUserName", "is not", null); + cnd.and("bankCardNumber", "is not", null); + cnd.and("bankOfDeposit", "is not", null); + sql.setCondition(cnd); + return this.listVO(sql, UnionReimburseBankHistoryVO.class); + } + + @Override + public Result checkInvoiceDuplicate(String invoiceNo, String reimburseId) { + String currentInvoiceNo = StrUtil.blankToDefault(StrUtil.trim(invoiceNo), ""); + if (StrUtil.isBlank(currentInvoiceNo)) { + return Result.error("发票号码不能为空"); + } + return queryHistoryInvoiceDuplicate(currentInvoiceNo, reimburseId); + } + + @Override + public Result recognizeInvoice(String fileId, Boolean verifySwitch) { + if (StrUtil.isBlank(fileId)) { + return Result.error("请先上传发票文件"); + } + Sys_file sysFile = sysFileService.fetch(fileId); + if (sysFile == null) { + return Result.error("发票文件不存在"); + } + Credential credential = getInvoiceCredential(); + if (credential == null) { + return Result.error("未配置腾讯云发票识别密钥,请先在 application-dev.yaml 中配置 invoice.tencent.secret-id 和 invoice.tencent.secret-key"); + } + try { + byte[] fileBytes = sysFileService.download(fileId); + VatInvoiceOCRResponse response = executeOcr(credential, sysFile, fileBytes); + NutMap result = buildOcrResult(response); + String msg = "发票识别成功"; + if (Boolean.TRUE.equals(verifySwitch)) { + String verifyMsg = enrichByVerify(credential, result); + msg = StrUtil.isBlank(verifyMsg) ? "发票识别并校验成功" : verifyMsg; + } + return Result.success(msg, result); + } catch (TencentCloudSDKException e) { + log.error("腾讯云发票识别失败,fileId:{}", fileId, e); + return Result.error("腾讯云发票识别失败:" + e.getMessage()); + } catch (Exception e) { + log.error("发票识别失败,fileId:{}", fileId, e); + return Result.error("发票识别失败:" + e.getMessage()); + } + } + + /** + * 统一规整发票明细中的文本和金额,避免空格、空数组等脏数据进入库表。 + */ + private void normalizeInvoiceDetails(UnionReimburse unionReimburse) { + if (unionReimburse.getInvoiceDetails() == null) { + unionReimburse.setInvoiceDetails(new ArrayList<>()); + return; + } + List invoiceDetails = unionReimburse.getInvoiceDetails().stream() + .filter(Objects::nonNull) + .collect(Collectors.toList()); + invoiceDetails.forEach(detail -> { + detail.setInvoiceNo(StrUtil.trim(detail.getInvoiceNo())); + detail.setSellerName(StrUtil.trim(detail.getSellerName())); + detail.setItemName(StrUtil.trim(detail.getItemName())); + detail.setRemark(StrUtil.trim(detail.getRemark())); + if (detail.getInvoiceFiles() == null) { + detail.setInvoiceFiles(new ArrayList<>()); + } + if (detail.getInvoiceAmount() != null) { + detail.setInvoiceAmount(BigDecimal.valueOf(detail.getInvoiceAmount()) + .setScale(2, RoundingMode.HALF_UP) + .doubleValue()); + } + }); + unionReimburse.setInvoiceDetails(invoiceDetails); + } + + /** + * 活动类报销由发票明细自动汇总金额和发票张数,减少手工输入出错。 + */ + private void fillInvoiceSummary(UnionReimburse unionReimburse) { + double totalMoney = 0D; + int invoiceFileCount = 0; + for (UnionReimburseInvoiceDetail detail : unionReimburse.getInvoiceDetails()) { + if (detail.getInvoiceAmount() != null) { + totalMoney = BigDecimal.valueOf(totalMoney) + .add(BigDecimal.valueOf(detail.getInvoiceAmount())) + .doubleValue(); + } + if (Lang.isNotEmpty(detail.getInvoiceFiles())) { + invoiceFileCount++; + } + } + unionReimburse.setMoney(BigDecimal.valueOf(totalMoney).setScale(2, RoundingMode.HALF_UP).doubleValue()); + unionReimburse.setInvoiceNumber(String.valueOf(invoiceFileCount)); + } + + /** + * 提交前补一层后端校验,防止前端绕过校验直接提交。 + */ + private Result validateBeforeSubmit(UnionReimburse unionReimburse) { + if (StrUtil.isBlank(unionReimburse.getMobile())) { + return Result.error("请填写联系方式"); + } + if (!isValidContact(unionReimburse.getMobile())) { + return Result.error("请输入正确的联系方式"); + } + if (StrUtil.isBlank(unionReimburse.getBankUserName())) { + return Result.error("请填写户名"); + } + if (StrUtil.isBlank(unionReimburse.getBankCardNumber())) { + return Result.error("请填写银行账号"); + } + if (StrUtil.isBlank(unionReimburse.getBankOfDeposit())) { + return Result.error("请填写开户行"); + } + if (StrUtil.isBlank(unionReimburse.getPaymentNotes())) { + return Result.error("请填写支付内容"); + } + if (!needInvoiceDetails(unionReimburse)) { + return null; + } + if (StrUtil.isBlank(unionReimburse.getActivityName())) { + return Result.error("请填写活动名称"); + } + if (StrUtil.isBlank(unionReimburse.getActivityPlace())) { + return Result.error("请填写活动地点"); + } + if (unionReimburse.getActivityTime() == null) { + return Result.error("请选择活动时间"); + } + if (Lang.isEmpty(unionReimburse.getFiles())) { + return Result.error("请上传附件"); + } + if (Lang.isEmpty(unionReimburse.getInvoiceDetails())) { + return Result.error("请至少维护一条发票明细"); + } + for (int i = 0; i < unionReimburse.getInvoiceDetails().size(); i++) { + UnionReimburseInvoiceDetail detail = unionReimburse.getInvoiceDetails().get(i); + int rowIndex = i + 1; + if (Lang.isEmpty(detail.getInvoiceFiles())) { + return Result.error("第" + rowIndex + "条发票明细请上传发票文件"); + } + if (StrUtil.isBlank(detail.getInvoiceNo())) { + return Result.error("第" + rowIndex + "条发票明细请填写发票号码"); + } + if (detail.getInvoiceAmount() == null || detail.getInvoiceAmount() <= 0) { + return Result.error("第" + rowIndex + "条发票明细请填写正确的发票金额"); + } + if (StrUtil.isBlank(detail.getSellerName())) { + return Result.error("第" + rowIndex + "条发票明细请填写销售方信息名称"); + } + if (StrUtil.isBlank(detail.getItemName())) { + return Result.error("第" + rowIndex + "条发票明细请填写项目名称"); + } + } + return null; + } + + /** + * 同次提交和历史记录都要拦截重复发票,避免重复报销。 + */ + private Result validateInvoiceDuplicate(UnionReimburse unionReimburse) { + Map> currentInvoiceMap = new LinkedHashMap<>(); + List details = unionReimburse.getInvoiceDetails(); + for (int i = 0; i < details.size(); i++) { + String invoiceNo = StrUtil.trim(details.get(i).getInvoiceNo()); + if (StrUtil.isBlank(invoiceNo)) { + continue; + } + currentInvoiceMap.computeIfAbsent(invoiceNo, key -> new ArrayList<>()).add(i + 1); + } + for (Map.Entry> entry : currentInvoiceMap.entrySet()) { + if (entry.getValue().size() > 1) { + String rowText = entry.getValue().stream().map(String::valueOf).collect(Collectors.joining("、")); + return Result.error("发票号码【" + entry.getKey() + "】与本次提交的第" + rowText + "条发票重复,不能提交"); + } + } + for (String invoiceNo : currentInvoiceMap.keySet()) { + Result duplicateResult = queryHistoryInvoiceDuplicate(invoiceNo, unionReimburse.getId()); + if (duplicateResult != null) { + return duplicateResult; + } + } + return null; + } + + /** + * 历史重复校验会排除当前单据自身,避免编辑回显时误判。 + */ + private Result queryHistoryInvoiceDuplicate(String invoiceNo, String reimburseId) { + Sql sql = Sqls.create(""" + SELECT + d.invoiceNo, + r.activityName + FROM + union_reimburse_invoice_detail d + LEFT JOIN union_reimburse r ON r.id = d.reimburseId + WHERE + d.invoiceNo = @invoiceNo + AND (@reimburseId = '' OR d.reimburseId <> @reimburseId) + LIMIT 1 + """); + sql.params().set("invoiceNo", invoiceNo); + sql.params().set("reimburseId", StrUtil.blankToDefault(StrUtil.trim(reimburseId), "")); + List repeatList = this.listMap(sql); + if (Lang.isEmpty(repeatList)) { + return null; + } + String activityName = repeatList.get(0).getString("activityName"); + if (StrUtil.isBlank(activityName)) { + activityName = "历史报销记录"; + } + return Result.error("发票号码【" + invoiceNo + "】与以往活动名称【" + activityName + "】中提交的发票重复,不能提交"); + } + + /** + * 每次按当前表单全量覆盖明细,避免旧明细残留。 + */ + /** + * 发票识别优先读取当前项目配置的 SecretId/SecretKey,没有时再回退读取用户提供的 csv 文件。 + */ + private Credential getInvoiceCredential() { + String secretId = StrUtil.trim(conf.get("invoice.tencent.secret-id")); + String secretKey = StrUtil.trim(conf.get("invoice.tencent.secret-key")); + if (StrUtil.isNotBlank(secretId) && StrUtil.isNotBlank(secretKey)) { + return new Credential(secretId, secretKey); + } + return loadCredentialFromSecretFile(); + } + + /** + * 兼容腾讯云控制台下载的密钥 csv,便于本地开发时不把明文密钥直接写进配置文件。 + */ + private Credential loadCredentialFromSecretFile() { + String secretFile = StrUtil.trim(conf.get("invoice.tencent.secret-file")); + if (StrUtil.isBlank(secretFile)) { + return null; + } + try { + Path path = Paths.get(secretFile); + if (!Files.exists(path)) { + return null; + } + List lines = Files.readAllLines(path); + if (lines.size() < 2) { + return null; + } + String[] values = lines.get(1).split(",", -1); + if (values.length < 2) { + return null; + } + String secretId = StrUtil.trim(values[0]); + String secretKey = StrUtil.trim(values[1]); + if (StrUtil.isBlank(secretId) || StrUtil.isBlank(secretKey)) { + return null; + } + return new Credential(secretId, secretKey); + } catch (Exception e) { + log.warn("读取腾讯云发票识别密钥文件失败,file:{}", secretFile, e); + return null; + } + } + + /** + * 统一创建腾讯云 OCR 客户端,避免控制器直接依赖第三方 SDK。 + */ + private OcrClient createOcrClient(Credential credential) { + HttpProfile httpProfile = new HttpProfile(); + httpProfile.setEndpoint("ocr.tencentcloudapi.com"); + ClientProfile clientProfile = new ClientProfile(); + clientProfile.setHttpProfile(httpProfile); + return new OcrClient(credential, "", clientProfile); + } + + private VatInvoiceOCRResponse executeOcr(Credential credential, Sys_file sysFile, byte[] fileBytes) throws TencentCloudSDKException { + OcrClient client = createOcrClient(credential); + VatInvoiceOCRRequest request = new VatInvoiceOCRRequest(); + request.setImageBase64(Base64.getEncoder().encodeToString(fileBytes)); + String fileName = StrUtil.blankToDefault(sysFile.getName(), "").toLowerCase(); + if (fileName.endsWith(".pdf")) { + request.setIsPdf(true); + request.setPdfPageNumber(1L); + } + return client.VatInvoiceOCR(request); + } + + /** + * 将腾讯云 OCR 返回的票面字段规整成页面可直接回填的键值结构。 + */ + private NutMap buildOcrResult(VatInvoiceOCRResponse response) { + NutMap result = NutMap.NEW(); + TextVatInvoice[] infos = response.getVatInvoiceInfos(); + VatInvoiceItem[] items = response.getItems(); + result.setv("invoiceNo", pickValue(infos, "发票号码", "号码", "No")); + result.setv("invoiceCode", pickValue(infos, "发票代码", "代码", "Code")); + result.setv("invoiceDate", normalizeDate(pickValue(infos, "开票日期", "日期", "Date"))); + result.setv("invoiceCheckCode", pickValue(infos, "校验码", "校验码后6位", "CheckCode")); + result.setv("invoiceAmount", parseMoney(pickValue(infos, "小写金额", "价税合计(小写)", "价税合计", "合计金额", "AmountWithTax"))); + result.setv("sellerName", pickValue(infos, "销售方名称", "销售方信息名称", "销售方", "SellerName")); + result.setv("itemName", joinItemNames(items)); + return result; + } + + /** + * 自动校验需要票面关键信息,缺字段时只提示人工核对,不阻塞用户继续录入。 + */ + private String enrichByVerify(Credential credential, NutMap result) { + String invoiceCode = result.getString("invoiceCode"); + String invoiceNo = result.getString("invoiceNo"); + String invoiceDate = result.getString("invoiceDate"); + String checkCode = lastDigits(result.getString("invoiceCheckCode"), 6); + if (StrUtil.isBlank(checkCode)) { + checkCode = moneyToVerifyValue(result.get("invoiceAmount")); + } + if (StrUtil.hasBlank(invoiceCode, invoiceNo, invoiceDate, checkCode)) { + return "发票识别成功,但自动校验信息不完整,请人工核对后提交"; + } + try { + OcrClient client = createOcrClient(credential); + VatInvoiceVerifyRequest request = new VatInvoiceVerifyRequest(); + request.setInvoiceCode(invoiceCode); + request.setInvoiceNo(invoiceNo); + request.setInvoiceDate(invoiceDate.replace("-", "")); + request.setAdditional(checkCode); + VatInvoiceVerifyResponse response = client.VatInvoiceVerify(request); + VatInvoice invoice = response.getInvoice(); + if (invoice == null) { + return "发票识别成功,但自动校验未返回有效结果,请人工核对后提交"; + } + result.setv("invoiceCode", invoice.getCode()); + result.setv("invoiceNo", invoice.getNumber()); + result.setv("invoiceDate", normalizeDate(invoice.getDate())); + result.setv("invoiceCheckCode", invoice.getCheckCode()); + result.setv("sellerName", invoice.getSellerName()); + result.setv("invoiceAmount", parseMoney(invoice.getAmountWithTax())); + result.setv("itemName", joinItemNames(invoice.getItems())); + return null; + } catch (Exception e) { + log.warn("腾讯云发票验真失败,invoiceNo:{}", invoiceNo, e); + return "发票识别成功,但自动校验失败,请人工核对后提交"; + } + } + + private String pickValue(TextVatInvoice[] infos, String... aliases) { + if (infos == null || aliases == null) { + return ""; + } + Set aliasSet = new LinkedHashSet<>(); + for (String alias : aliases) { + aliasSet.add(normalizeFieldName(alias)); + } + for (TextVatInvoice info : infos) { + if (info == null) { + continue; + } + if (aliasSet.contains(normalizeFieldName(info.getName()))) { + return StrUtil.blankToDefault(StrUtil.trim(info.getValue()), ""); + } + } + return ""; + } + + private String normalizeFieldName(String name) { + return StrUtil.blankToDefault(name, "") + .replace(":", "") + .replace(":", "") + .replace(" ", "") + .replace("\u00A0", "") + .trim(); + } + + private String normalizeDate(String dateText) { + String date = StrUtil.blankToDefault(StrUtil.trim(dateText), ""); + if (StrUtil.isBlank(date)) { + return ""; + } + String digits = date.replaceAll("[^0-9]", ""); + if (digits.length() == 8) { + return digits.substring(0, 4) + "-" + digits.substring(4, 6) + "-" + digits.substring(6); + } + return date.replace("年", "-").replace("月", "-").replace("日", ""); + } + + private Double parseMoney(Object moneyObj) { + String moneyText = StrUtil.blankToDefault(StrUtil.trim(ObjectUtil.defaultIfNull(moneyObj, "").toString()), ""); + if (StrUtil.isBlank(moneyText)) { + return null; + } + moneyText = moneyText.replace(",", "").replace("¥", "").replace("¥", ""); + try { + return BigDecimal.valueOf(Double.parseDouble(moneyText)).setScale(2, RoundingMode.HALF_UP).doubleValue(); + } catch (Exception e) { + return null; + } + } + + private String joinItemNames(VatInvoiceItem[] items) { + if (items == null || items.length == 0) { + return ""; + } + return Arrays.stream(items) + .map(VatInvoiceItem::getName) + .filter(StrUtil::isNotBlank) + .distinct() + .collect(Collectors.joining(";")); + } + + private String lastDigits(String text, int length) { + String digits = StrUtil.blankToDefault(text, "").replaceAll("[^0-9]", ""); + if (digits.length() <= length) { + return digits; + } + return digits.substring(digits.length() - length); + } + + private String moneyToVerifyValue(Object moneyObj) { + Double money = parseMoney(moneyObj); + if (money == null) { + return ""; + } + return BigDecimal.valueOf(money).setScale(2, RoundingMode.HALF_UP).toPlainString(); + } + + private void saveInvoiceDetails(String reimburseId, List invoiceDetails) { + this.dao().clear(UnionReimburseInvoiceDetail.class, Cnd.where("reimburseId", "=", reimburseId)); + if (Lang.isEmpty(invoiceDetails)) { + return; + } + invoiceDetails.forEach(detail -> { + detail.setId(null); + detail.setReimburseId(reimburseId); + this.dao().insert(detail); + }); + } + + /** + * 当前仅活动类报销维护发票明细,慰问类仍按原有逻辑处理金额。 + */ + private boolean needInvoiceDetails(UnionReimburse unionReimburse) { + return unionReimburse != null && !"UNION_REIMBURSE_PROJECT_1".equals(unionReimburse.getReimburseProject()); + } + + /** + * 生成年度流水号,保持与现有单据编号规则一致。 + */ + private String generateDocumentNo() { + int currentYear = DateUtil.thisYear(); + String yearPrefix = String.valueOf(currentYear); + Cnd cnd = Cnd.where("documentNo", "like", yearPrefix + "%"); + cnd.desc("documentNo"); + cnd.limit(1); + List list = this.query(cnd); + int nextSeq = 1; + if (Lang.isNotEmpty(list) && StrUtil.isNotBlank(list.get(0).getDocumentNo())) { + String maxNo = list.get(0).getDocumentNo(); + if (maxNo.startsWith(yearPrefix)) { + String seqPart = maxNo.substring(yearPrefix.length()); + if (StrUtil.isNumeric(seqPart)) { + nextSeq = Integer.parseInt(seqPart) + 1; + } + } + } + return yearPrefix + String.format("%02d", nextSeq); + } + + /** + * 统计页排序字段仅允许白名单列,避免传入非法字段。 + */ + private void applyStatisticsSort(Cnd cnd, UnionReimbursePageForm pageForm) { + String orderField = normalizeStatisticsSortField(pageForm.getPageOrderName()); + if (StrUtil.isBlank(orderField) || StrUtil.isBlank(pageForm.getPageOrderBy())) { + cnd.desc("info.createTime"); + return; + } + cnd.orderBy(orderField, "ascending".equals(pageForm.getPageOrderBy()) ? "asc" : "desc"); + } + + private String normalizeStatisticsSortField(String pageOrderName) { + if (StrUtil.isBlank(pageOrderName)) { + return null; + } + return switch (pageOrderName) { + case "condolenceLoginName" -> "info.condolenceLoginName"; + case "condolenceUnionName" -> "info.condolenceUnionName"; + case "unitName" -> "info.unitName"; + case "newCreateTime", "createTime" -> "info.createTime"; + case "newCondolenceTime" -> "info.condolenceTime"; + case "reimburseProject" -> "info.reimburseProject"; + default -> null; + }; + } + + private boolean isAdmin() { + return AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_ADMIN.name()); + } + + /** + * 联系方式支持手机号和座机,和前端规则保持一致。 + */ + private boolean isValidContact(String mobile) { + return mobile.matches("^(1[3-9]\\d{9}|0\\d{2,3}-?\\d{7,8})$"); + } } diff --git a/src/main/java/com/budwk/app/zhgh/democratic/teachercongress/delegate/controller/TeacherCongressDelegatePushController.java b/src/main/java/com/budwk/app/zhgh/democratic/teachercongress/delegate/controller/TeacherCongressDelegatePushController.java index 02dd6746..b597d30c 100644 --- a/src/main/java/com/budwk/app/zhgh/democratic/teachercongress/delegate/controller/TeacherCongressDelegatePushController.java +++ b/src/main/java/com/budwk/app/zhgh/democratic/teachercongress/delegate/controller/TeacherCongressDelegatePushController.java @@ -150,7 +150,7 @@ public class TeacherCongressDelegatePushController { @Aop(TransAop.READ_COMMITTED) @SaCheckPermission("tc.delegate.push") // @SLog(tag = "民主管理-教代会代表推选", msg = "获取本分工会下的非代表用户") - public Result getUnionUser(String sessionId, String unionId) { + public Result getUnionUser(String sessionId, String unionId, String keyword) { // 查询预选中的代表 Cnd cndx = Cnd.where("sessionId", "=", sessionId); cndx.andEX("unionId", "=", SecurityUtil.getUnionId()); @@ -173,7 +173,7 @@ public class TeacherCongressDelegatePushController { `vw_user` u LEFT JOIN teacher_congress_delegate tcd ON tcd.userId = u.id AND tcd.sessionId = @sessionId - LEFT JOIN teacher_congress_delegate_push tcdp ON tcdp.userId = tcdp.id + LEFT JOIN teacher_congress_delegate_push tcdp ON tcdp.userId = u.id AND tcdp.sessionId = @sessionId $condition """); @@ -185,6 +185,14 @@ public class TeacherCongressDelegatePushController { cnd.and("tcd.userId", "is", null); cnd.and("tcdp.id", "is", null); cnd.and("u.unionId", "=", SecurityUtil.getUnionId()); + if (StrUtil.isNotBlank(keyword)) { + SqlExpressionGroup group = new SqlExpressionGroup(); + group.orLike("u.username", keyword); + group.orLike("u.loginname", keyword); + cnd.and(group); + } + // 搜索候选人时只返回少量命中数据,避免前端大列表筛选导致严重卡顿 + cnd.limit(1, 100); sql.setCondition(cnd); List userList = baseService.listMap(sql); diff --git a/src/main/resources/views/platform/zhgh/activity/culture/common/applyActivity.js b/src/main/resources/views/platform/zhgh/activity/culture/common/applyActivity.js index 855b1a6c..a75d39e6 100644 --- a/src/main/resources/views/platform/zhgh/activity/culture/common/applyActivity.js +++ b/src/main/resources/views/platform/zhgh/activity/culture/common/applyActivity.js @@ -456,6 +456,8 @@ const ACTIVITY_CULTURE_APPLY_ACTIVITY = { complete_result> + + 编辑 - 撤回 diff --git a/src/main/resources/views/platform/zhgh/activity/trainSignUp/type/index.html b/src/main/resources/views/platform/zhgh/activity/trainSignUp/type/index.html index b57eb1a3..b0038e99 100644 --- a/src/main/resources/views/platform/zhgh/activity/trainSignUp/type/index.html +++ b/src/main/resources/views/platform/zhgh/activity/trainSignUp/type/index.html @@ -80,7 +80,7 @@ layout("/layouts/platform.html"){ }) }, openAnnualLimit() { - window.location.href = '/platform/trainSignUp/annualLimit' + commonUtil.pjaxPush('/platform/trainSignUp/annualLimit') }, openAdd() { this.$refs.guava.edit(() => { diff --git a/src/main/resources/views/platform/zhgh/club/examine/apply/index.html b/src/main/resources/views/platform/zhgh/club/examine/apply/index.html index 4dde0117..ed4547fa 100644 --- a/src/main/resources/views/platform/zhgh/club/examine/apply/index.html +++ b/src/main/resources/views/platform/zhgh/club/examine/apply/index.html @@ -571,6 +571,11 @@ layout("/layouts/platform.html"){ {{formatNumber(row.support)}} + + +