This commit is contained in:
zhouhefeng
2026-05-20 08:54:05 +08:00
parent b3a84786c3
commit 3f76d2c6cb
105 changed files with 28881 additions and 296 deletions
@@ -213,6 +213,7 @@ public class FlowTodoCenterController {
public Result statistics() {
// 查询我发起的流程
int startedCount = dao.count(ProcessInstance.class, Cnd.where(ProcessInstance::getOperator, "=", SecurityUtil.getUserId()));
int completedCount = dao.count(ProcessInstance.class, Cnd.where(ProcessInstance::getOperator, "=", SecurityUtil.getUserId()).and(ProcessInstance::getState, "=", 20));
// 查询我的待办任务
Sql todoSql = Sqls.create("""
SELECT
@@ -262,7 +263,7 @@ public class FlowTodoCenterController {
dao.execute(msgSql);
int msgCount = msgSql.getInt();
return Result.success(Map.of("startedCount", startedCount, "todoCount", todoCount, "doneCount", doneCount, "notifications", msgCount));
return Result.success(Map.of("startedCount", startedCount, "completedCount", completedCount, "todoCount", todoCount, "doneCount", doneCount, "notifications", msgCount));
}
}
@@ -0,0 +1,625 @@
package com.budwk.app.zhgh.dayofficework.tour.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
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.CommonDownloadUtil;
import com.budwk.app.sys.models.Sys_union;
import com.budwk.app.web.commons.auth.utils.AuthUtil;
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
import com.budwk.app.zhgh.dayofficework.tour.service.TourLedgerService;
import com.budwk.app.zhgh.dayofficework.tour.service.TourMatterService;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.BorderStyle;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.Font;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.VerticalAlignment;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.util.CellRangeAddress;
import org.nutz.dao.Cnd;
import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Sql;
import org.nutz.dao.util.cri.SqlExpressionGroup;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.util.NutMap;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
import javax.servlet.http.HttpServletResponse;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@IocBean
@Ok("json:full")
@At("/platform/tour/group")
public class TourGroupController {
@Inject
private TourMatterService tourMatterService;
@Inject
private TourLedgerService tourLedgerService;
@At("")
@Ok("beetl:/platform/zhgh/dayofficework/Tour/group/index.html")
@SaCheckPermission("tour.group")
public void index() {
}
@At
@SaCheckPermission("tour.group")
public Result pageData(PageForm pageForm, Integer year, String lineName, String lineType, String unionId) {
if (StrUtil.isBlank(pageForm.getPageOrderName())) {
pageForm.defaultSortAsc("lineName");
}
Cnd cnd = buildQueryCnd(year, lineName, lineType, unionId);
Sql countSql = Sqls.create("""
SELECT COUNT(1)
FROM tour_matter m
INNER JOIN tour_line l ON l.id = m.lineId
LEFT JOIN sys_union u ON u.id = m.unionId
$condition
""");
countSql.setCondition(cnd);
countSql.setCallback(Sqls.callback.integer());
tourMatterService.dao().execute(countSql);
int count = countSql.getInt();
Sql listSql = Sqls.create("""
SELECT
m.id AS matterId,
m.`year`,
m.matterName,
m.unionId,
COALESCE(u.name, '校工会') AS unionName,
m.lineId,
l.lineName,
l.lineType,
l.directFamilyUnitLine,
m.travelStartTime,
m.travelEndTime,
CASE
WHEN IFNULL(m.travelStartTime, '') <> '' AND IFNULL(m.travelEndTime, '') <> ''
THEN CONCAT(m.travelStartTime, ' 至 ', m.travelEndTime)
ELSE ''
END AS travelPeriod,
m.minGroupPeople,
m.maxGroupPeople,
IFNULL(sc.signupCount, 0) AS signupCount,
CASE
WHEN m.maxGroupPeople IS NOT NULL AND m.maxGroupPeople > 0 AND IFNULL(sc.signupCount, 0) > m.maxGroupPeople THEN 'over'
WHEN m.minGroupPeople IS NOT NULL AND m.minGroupPeople > 0 AND IFNULL(sc.signupCount, 0) >= m.minGroupPeople THEN 'formed'
ELSE 'unformed'
END AS groupStatus,
CASE
WHEN m.maxGroupPeople IS NOT NULL AND m.maxGroupPeople > 0 AND IFNULL(sc.signupCount, 0) > m.maxGroupPeople THEN '超员'
WHEN m.minGroupPeople IS NOT NULL AND m.minGroupPeople > 0 AND IFNULL(sc.signupCount, 0) >= m.minGroupPeople THEN '已成团'
ELSE '未成团'
END AS groupStatusName
FROM tour_matter m
INNER JOIN tour_line l ON l.id = m.lineId
LEFT JOIN sys_union u ON u.id = m.unionId
LEFT JOIN (
SELECT t.matterId, SUM(1 + IFNULL(f.familyCount, 0)) AS signupCount
FROM tour_ledger t
LEFT JOIN (
SELECT ledgerId, COUNT(1) AS familyCount
FROM tour_ledger_family
WHERE delFlag = 0
GROUP BY ledgerId
) f ON f.ledgerId = t.id
WHERE t.delFlag = 0
AND t.matterId IS NOT NULL
AND t.matterId <> ''
GROUP BY t.matterId
) sc ON sc.matterId = m.id
$condition
ORDER BY $orderColumn $orderBy, m.`year` DESC, m.travelStartTime ASC, l.lineName ASC, m.createdAt DESC
""");
listSql.setCondition(cnd);
listSql.setVar("orderColumn", getOrderColumn(pageForm.getPageOrderName()));
listSql.setVar("orderBy", "descending".equals(pageForm.getPageOrderBy()) ? "desc" : "asc");
listSql.setPager(tourMatterService.dao().createPager(pageForm.getPageNumber(), pageForm.getPageSize()));
listSql.setCallback(Sqls.callback.maps());
tourMatterService.dao().execute(listSql);
Pagination<NutMap> pagination = new Pagination<>(pageForm.getPageNumber(), pageForm.getPageSize(), count, listSql.getList(NutMap.class));
return Result.success(pagination);
}
@At
@SaCheckPermission("tour.group")
public Result unionOptions() {
Cnd cnd = Cnd.NEW();
cnd.asc("unionCode");
cnd.asc("name");
return Result.success(tourMatterService.dao().query(Sys_union.class, cnd));
}
@At
@SaCheckPermission("tour.group")
public Result lineTypeOptions(Integer year) {
Cnd cnd = buildQueryCnd(year, null, null, null);
Sql sql = Sqls.create("""
SELECT DISTINCT l.lineType
FROM tour_matter m
INNER JOIN tour_line l ON l.id = m.lineId
LEFT JOIN sys_union u ON u.id = m.unionId
$condition
ORDER BY l.lineType ASC
""");
sql.setCondition(cnd);
sql.setCallback(Sqls.callback.maps());
tourMatterService.dao().execute(sql);
return Result.success(sql.getList(NutMap.class));
}
@At
@SaCheckPermission("tour.group")
public Result signupPageData(PageForm pageForm, String matterId, String keyword, String unionId) {
if (StrUtil.isBlank(matterId)) {
return Result.error("参数错误");
}
if (StrUtil.isBlank(pageForm.getPageOrderName())) {
pageForm.defaultSortDesc("signupTime");
}
Cnd cnd = buildSignupQueryCnd(matterId, keyword, unionId);
Sql countSql = Sqls.create("""
SELECT COUNT(1)
FROM tour_ledger t
INNER JOIN tour_matter m ON m.id = t.matterId
INNER JOIN tour_line l ON l.id = m.lineId
$condition
""");
countSql.setCondition(cnd);
countSql.setCallback(Sqls.callback.integer());
tourLedgerService.dao().execute(countSql);
int count = countSql.getInt();
Sql listSql = Sqls.create("""
SELECT
t.id,
t.jobNo,
t.userName,
'' AS mobile,
t.idCard,
t.unionId,
t.unionName,
t.signupTime
FROM tour_ledger t
INNER JOIN tour_matter m ON m.id = t.matterId
INNER JOIN tour_line l ON l.id = m.lineId
$condition
ORDER BY $orderColumn $orderBy, t.signupTime DESC, t.createdAt DESC
""");
listSql.setCondition(cnd);
listSql.setVar("orderColumn", getSignupOrderColumn(pageForm.getPageOrderName()));
listSql.setVar("orderBy", "ascending".equals(pageForm.getPageOrderBy()) ? "asc" : "desc");
listSql.setPager(tourLedgerService.dao().createPager(pageForm.getPageNumber(), pageForm.getPageSize()));
listSql.setCallback(Sqls.callback.maps());
tourLedgerService.dao().execute(listSql);
List<NutMap> list = listSql.getList(NutMap.class);
fillSignupMobile(list);
Pagination<NutMap> pagination = new Pagination<>(pageForm.getPageNumber(), pageForm.getPageSize(), count, list);
return Result.success(pagination);
}
@At
@Ok("void")
@SaCheckPermission("tour.group")
public void exportParticipants(String matterId, HttpServletResponse response) {
if (StrUtil.isBlank(matterId)) {
return;
}
NutMap matter = fetchExportMatter(matterId);
if (matter == null || matter.isEmpty()) {
return;
}
List<NutMap> list = queryExportParticipants(matterId);
Workbook workbook = buildParticipantsWorkbook(matter, list);
String lineName = StrUtil.blankToDefault(matter.getString("lineName", ""), "线路");
CommonDownloadUtil.download(lineName + "参加人员名单.xls", workbook, response);
}
private Cnd buildSignupQueryCnd(String matterId, String keyword, String unionId) {
Cnd cnd = Cnd.NEW();
cnd.and("t.delFlag", "=", false);
cnd.and("t.matterId", "=", matterId);
cnd.and("m.delFlag", "=", false);
cnd.and("m.enabled", "=", true);
cnd.and("m.lineId", "IS NOT", null);
cnd.and("m.lineId", "<>", "");
cnd.and("l.delFlag", "=", false);
cnd.and("l.enabled", "=", true);
if (StrUtil.isNotBlank(keyword)) {
SqlExpressionGroup keywordGroup = new SqlExpressionGroup();
keywordGroup.orLike("t.userName", keyword.trim());
keywordGroup.orLike("t.jobNo", keyword.trim());
cnd.and(keywordGroup);
}
cnd.andEX("t.unionId", "=", unionId);
applyMatterDataScope(cnd);
return cnd;
}
private NutMap fetchExportMatter(String matterId) {
Cnd cnd = Cnd.NEW();
cnd.and("m.delFlag", "=", false);
cnd.and("m.enabled", "=", true);
cnd.and("m.id", "=", matterId);
cnd.and("m.lineId", "IS NOT", null);
cnd.and("m.lineId", "<>", "");
cnd.and("l.delFlag", "=", false);
cnd.and("l.enabled", "=", true);
applyMatterDataScope(cnd);
Sql sql = Sqls.create("""
SELECT
m.id AS matterId,
m.`year`,
m.unionId,
COALESCE(u.name, '校工会') AS unionName,
l.lineName,
l.lineType,
m.travelStartTime,
m.travelEndTime,
CASE
WHEN IFNULL(m.travelStartTime, '') <> '' AND IFNULL(m.travelEndTime, '') <> ''
THEN CONCAT(m.travelStartTime, ' 至 ', m.travelEndTime)
ELSE ''
END AS travelPeriod
FROM tour_matter m
INNER JOIN tour_line l ON l.id = m.lineId
LEFT JOIN sys_union u ON u.id = m.unionId
$condition
""");
sql.setCondition(cnd);
sql.setCallback(Sqls.callback.map());
tourMatterService.dao().execute(sql);
return sql.getObject(NutMap.class);
}
private List<NutMap> queryExportParticipants(String matterId) {
Cnd cnd = buildSignupQueryCnd(matterId, null, null);
Sql sql = Sqls.create("""
SELECT
t.id AS ledgerId,
t.jobNo,
t.userName,
t.idCard,
vu.mobile,
t.unionName,
l.lineName,
CASE
WHEN IFNULL(m.travelStartTime, '') <> '' AND IFNULL(m.travelEndTime, '') <> ''
THEN CONCAT(m.travelStartTime, ' 至 ', m.travelEndTime)
ELSE ''
END AS travelPeriod,
t.signupTime
FROM tour_ledger t
INNER JOIN tour_matter m ON m.id = t.matterId
INNER JOIN tour_line l ON l.id = m.lineId
LEFT JOIN vw_user vu ON vu.loginname = t.jobNo
$condition
ORDER BY t.signupTime DESC, t.createdAt DESC
""");
sql.setCondition(cnd);
sql.setCallback(Sqls.callback.maps());
tourLedgerService.dao().execute(sql);
List<NutMap> list = sql.getList(NutMap.class);
fillExportFamilies(list);
return list;
}
private void fillExportFamilies(List<NutMap> list) {
if (list == null || list.isEmpty()) {
return;
}
List<String> ledgerIds = new ArrayList<>();
for (NutMap item : list) {
String ledgerId = item.getString("ledgerId", "");
if (StrUtil.isNotBlank(ledgerId)) {
ledgerIds.add(ledgerId);
}
}
if (ledgerIds.isEmpty()) {
return;
}
Sql sql = Sqls.create("""
SELECT
ledgerId,
familyName,
relationship,
idCard
FROM tour_ledger_family
WHERE delFlag = 0
AND ledgerId IN (@ledgerIds)
ORDER BY createdAt ASC
""");
sql.setParam("ledgerIds", ledgerIds.toArray(new String[0]));
sql.setCallback(Sqls.callback.maps());
tourLedgerService.dao().execute(sql);
Map<String, List<NutMap>> familyMap = new HashMap<>();
for (NutMap family : sql.getList(NutMap.class)) {
familyMap.computeIfAbsent(family.getString("ledgerId", ""), key -> new ArrayList<>()).add(family);
}
for (NutMap item : list) {
item.put("families", familyMap.getOrDefault(item.getString("ledgerId", ""), List.of()));
}
}
private Workbook buildParticipantsWorkbook(NutMap matter, List<NutMap> list) {
Workbook workbook = new HSSFWorkbook();
Sheet sheet = workbook.createSheet("参加人员名单");
sheet.addMergedRegion(new CellRangeAddress(0, 0, 0, 10));
sheet.addMergedRegion(new CellRangeAddress(1, 1, 0, 10));
double[] widths = {6, 14, 12, 22, 16, 24, 34, 26, 12, 12, 16};
for (int i = 0; i < widths.length; i++) {
sheet.setColumnWidth(i, (int) (widths[i] * 256));
}
CellStyle titleStyle = createStyle(workbook, "黑体", (short) 16, true, HorizontalAlignment.CENTER, false, false);
CellStyle unionStyle = createStyle(workbook, "宋体", (short) 12, true, HorizontalAlignment.LEFT, false, false);
CellStyle headerStyle = createStyle(workbook, "宋体", (short) 11, true, HorizontalAlignment.CENTER, true, true);
CellStyle bodyStyle = createStyle(workbook, "宋体", (short) 11, false, HorizontalAlignment.CENTER, true, true);
Row titleRow = sheet.createRow(0);
titleRow.setHeightInPoints(30);
setCell(titleRow, 0, StrUtil.blankToDefault(matter.getString("lineName", ""), ""), titleStyle);
fillMergedCells(titleRow, 1, 10, titleStyle);
Row unionRow = sheet.createRow(1);
unionRow.setHeightInPoints(26);
setCell(unionRow, 0, "分工会:" + StrUtil.blankToDefault(matter.getString("unionName", ""), ""), unionStyle);
fillMergedCells(unionRow, 1, 10, unionStyle);
String[] headers = {"序号", "工号", "姓名", "身份证号码", "电话号码", "所属分工会", "所选线路名称", "疗休养时间", "亲属关系", "姓名", "备注"};
Row headerRow = sheet.createRow(2);
headerRow.setHeightInPoints(35);
for (int i = 0; i < headers.length; i++) {
setCell(headerRow, i, headers[i], headerStyle);
}
int rowIndex = 3;
int seq = 1;
for (NutMap item : list == null ? List.<NutMap>of() : list) {
Row staffRow = sheet.createRow(rowIndex++);
staffRow.setHeightInPoints(24);
setParticipantRow(staffRow, seq++, item.getString("jobNo", ""), item.getString("userName", ""),
item.getString("idCard", ""), item.getString("mobile", ""), item.getString("unionName", ""),
item.getString("lineName", matter.getString("lineName", "")), item.getString("travelPeriod", matter.getString("travelPeriod", "")),
"", item.getString("userName", ""), bodyStyle);
Object familiesObj = item.get("families");
if (familiesObj instanceof List<?> families) {
for (Object obj : families) {
if (!(obj instanceof NutMap family)) {
continue;
}
Row familyRow = sheet.createRow(rowIndex++);
familyRow.setHeightInPoints(24);
setParticipantRow(familyRow, seq++, "", family.getString("familyName", ""),
family.getString("idCard", ""), "", "",
item.getString("lineName", matter.getString("lineName", "")),
item.getString("travelPeriod", matter.getString("travelPeriod", "")),
family.getString("relationship", ""), item.getString("userName", ""), bodyStyle);
}
}
}
int minRows = Math.max(rowIndex, 23);
while (rowIndex < minRows) {
Row row = sheet.createRow(rowIndex++);
row.setHeightInPoints(24);
for (int i = 0; i < headers.length; i++) {
setCell(row, i, "", bodyStyle);
}
}
return workbook;
}
private void setParticipantRow(Row row, int seq, String jobNo, String userName, String idCard, String mobile,
String unionName, String lineName, String travelPeriod, String relationship,
String participantName, CellStyle style) {
setCell(row, 0, String.valueOf(seq), style);
setCell(row, 1, jobNo, style);
setCell(row, 2, userName, style);
setCell(row, 3, idCard, style);
setCell(row, 4, mobile, style);
setCell(row, 5, unionName, style);
setCell(row, 6, lineName, style);
setCell(row, 7, travelPeriod, style);
setCell(row, 8, relationship, style);
setCell(row, 9, participantName, style);
setCell(row, 10, "", style);
}
private CellStyle createStyle(Workbook workbook, String fontName, short fontSize, boolean bold, HorizontalAlignment alignment, boolean wrap, boolean border) {
Font font = workbook.createFont();
font.setFontName(fontName);
font.setFontHeightInPoints(fontSize);
font.setBold(bold);
CellStyle style = workbook.createCellStyle();
style.setFont(font);
style.setAlignment(alignment);
style.setVerticalAlignment(VerticalAlignment.CENTER);
style.setWrapText(wrap);
if (border) {
style.setBorderLeft(BorderStyle.THIN);
style.setBorderTop(BorderStyle.THIN);
style.setBorderRight(BorderStyle.THIN);
style.setBorderBottom(BorderStyle.THIN);
}
return style;
}
private void setCell(Row row, int col, String value, CellStyle style) {
Cell cell = row.createCell(col);
cell.setCellValue(StrUtil.blankToDefault(value, ""));
cell.setCellStyle(style);
}
private void fillMergedCells(Row row, int startCol, int endCol, CellStyle style) {
for (int i = startCol; i <= endCol; i++) {
setCell(row, i, "", style);
}
}
private void fillSignupMobile(List<NutMap> list) {
if (list == null || list.isEmpty()) {
return;
}
List<String> jobNos = new ArrayList<>();
for (NutMap item : list) {
String jobNo = item.getString("jobNo", "");
if (StrUtil.isNotBlank(jobNo) && !jobNos.contains(jobNo)) {
jobNos.add(jobNo);
}
}
if (jobNos.isEmpty()) {
return;
}
Sql sql = Sqls.create("SELECT loginname, mobile FROM vw_user WHERE loginname IN (@jobNos)");
sql.setParam("jobNos", jobNos.toArray(new String[0]));
sql.setCallback(Sqls.callback.maps());
tourLedgerService.dao().execute(sql);
Map<String, String> mobileMap = new HashMap<>();
for (NutMap user : sql.getList(NutMap.class)) {
mobileMap.put(user.getString("loginname", ""), user.getString("mobile", ""));
}
for (NutMap item : list) {
item.put("mobile", mobileMap.getOrDefault(item.getString("jobNo", ""), ""));
}
}
private Cnd buildQueryCnd(Integer year, String lineName, String lineType, String unionId) {
Cnd cnd = Cnd.NEW();
cnd.and("m.delFlag", "=", false);
cnd.and("m.enabled", "=", true);
cnd.and("m.lineId", "IS NOT", null);
cnd.and("m.lineId", "<>", "");
cnd.and("l.delFlag", "=", false);
cnd.and("l.enabled", "=", true);
cnd.andEX("m.`year`", "=", year == null ? LocalDate.now().getYear() : year);
cnd.and(Cnd.likeEX("l.lineName", lineName));
cnd.andEX("l.lineType", "=", lineType);
cnd.andEX("m.unionId", "=", unionId);
applyMatterDataScope(cnd);
return cnd;
}
/**
* 线路成团按事项归属控制数据范围:校工会查看全部;分工会查看本工会事项;其它角色仅查看自己创建的事项。
*/
private void applyMatterDataScope(Cnd cnd) {
if (hasSchoolUnionScope()) {
return;
}
if (hasBranchUnionScope()) {
cnd.and("m.unionId", "=", SecurityUtil.getUnionId());
return;
}
SqlExpressionGroup creatorGroup = new SqlExpressionGroup();
creatorGroup.or("m.creatorUserId", "=", SecurityUtil.getUserId());
creatorGroup.or("m.createdBy", "=", SecurityUtil.getUserId());
cnd.and(creatorGroup);
}
private boolean hasSchoolUnionScope() {
return AuthUtil.hasRole(RoleConstant.SYSADMIN.name())
|| AuthUtil.hasRole(RoleConstant.SCHOOL_UNION_ADMIN.name())
|| AuthUtil.hasRole(RoleConstant.SCHOOL_UNION_CHAIRMAN.name())
|| AuthUtil.hasRole(RoleConstant.SCHOOL_UNION_VICE_CHAIRMAN.name())
|| AuthUtil.hasRole(RoleConstant.SCHOOL_UNION_WELFARE_ADMIN.name())
|| AuthUtil.hasRole(RoleConstant.SCHOOL_UNION_MEMBER_ADMIN.name())
|| AuthUtil.hasRole(RoleConstant.SCHOOL_UNION_TC_ADMIN.name())
|| AuthUtil.hasRole(RoleConstant.SCHOOL_UNION_WC_ADMIN.name())
|| AuthUtil.hasRole(RoleConstant.SCHOOL_UNION_DC_ADMIN.name())
|| AuthUtil.hasRole(RoleConstant.SCHOOL_UNION_PROPOSAL_ADMIN.name())
|| AuthUtil.hasRole(RoleConstant.SCHOOL_UNION_CLUB_ADMIN.name())
|| AuthUtil.hasRole(RoleConstant.SCHOOL_UNION_ACTIVITY_ADMIN.name());
}
private boolean hasBranchUnionScope() {
return AuthUtil.hasRole(RoleConstant.BRANCH_UNION_ADMIN.name())
|| AuthUtil.hasRole(RoleConstant.BRANCH_UNION_CHAIRMAN.name())
|| AuthUtil.hasRole(RoleConstant.BRANCH_UNION_VICE_CHAIRMAN.name())
|| AuthUtil.hasRole(RoleConstant.BRANCH_UNION_OPERATOR.name())
|| AuthUtil.hasRole(RoleConstant.BRANCH_UNION_GROUP_LEADER.name())
|| AuthUtil.hasRole(RoleConstant.BRANCH_UNION_ARTICLE_WRITER.name())
|| AuthUtil.hasRole(RoleConstant.BRANCH_UNION_WY.name())
|| AuthUtil.hasRole(RoleConstant.BRANCH_UNION_ZUZHI_WY.name())
|| AuthUtil.hasRole(RoleConstant.BRANCH_UNION_XUANCHUAN_WY.name())
|| AuthUtil.hasRole(RoleConstant.BRANCH_UNION_NVGONG_WY.name())
|| AuthUtil.hasRole(RoleConstant.BRANCH_UNION_QINGNIAN_WY.name())
|| AuthUtil.hasRole(RoleConstant.BRANCH_UNION_WENTI_WY.name())
|| AuthUtil.hasRole(RoleConstant.BRANCH_UNION_SHENGGHUO_WY.name())
|| AuthUtil.hasRole(RoleConstant.BRANCH_UNION_TIAOJIE_WY.name())
|| AuthUtil.hasRole(RoleConstant.BRANCH_UNION_WENTI_SPORTS.name());
}
private String getOrderColumn(String orderName) {
if ("lineName".equals(orderName)) {
return "l.lineName";
}
if ("travelPeriod".equals(orderName)) {
return "m.travelStartTime";
}
if ("matterName".equals(orderName)) {
return "m.matterName";
}
if ("unionName".equals(orderName)) {
return "u.name";
}
if ("lineType".equals(orderName)) {
return "l.lineType";
}
if ("signupCount".equals(orderName)) {
return "signupCount";
}
if ("minGroupPeople".equals(orderName)) {
return "m.minGroupPeople";
}
if ("maxGroupPeople".equals(orderName)) {
return "m.maxGroupPeople";
}
if ("groupStatusName".equals(orderName)) {
return "groupStatus";
}
return "l.lineName";
}
private String getSignupOrderColumn(String orderName) {
if ("jobNo".equals(orderName)) {
return "t.jobNo";
}
if ("userName".equals(orderName)) {
return "t.userName";
}
if ("idCard".equals(orderName)) {
return "t.idCard";
}
if ("unionName".equals(orderName)) {
return "t.unionName";
}
return "t.signupTime";
}
}
@@ -0,0 +1,959 @@
package com.budwk.app.zhgh.dayofficework.tour.controller;
import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.ExcelImportUtil;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.ImportParams;
import cn.afterturn.easypoi.excel.entity.enmus.ExcelType;
import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.annotation.SLog;
import com.budwk.app.base.model.ExcelImportRes;
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.flow.enums.ProcessInstanceStateEnum;
import com.budwk.app.sys.models.Sys_union;
import com.budwk.app.sys.views.View_user;
import com.budwk.app.zhgh.dayofficework.tour.mode.TourLedgerImportExcelMode;
import com.budwk.app.zhgh.dayofficework.tour.models.TourLedger;
import com.budwk.app.zhgh.dayofficework.tour.models.TourLedgerDirectRelative;
import com.budwk.app.zhgh.dayofficework.tour.models.TourLedgerFamily;
import com.budwk.app.zhgh.dayofficework.tour.models.TourLine;
import com.budwk.app.zhgh.dayofficework.tour.models.TourTravelAgency;
import com.budwk.app.zhgh.dayofficework.tour.service.TourLedgerDirectRelativeService;
import com.budwk.app.zhgh.dayofficework.tour.service.TourLedgerFamilyService;
import com.budwk.app.zhgh.dayofficework.tour.service.TourLedgerService;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.BorderStyle;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.FillPatternType;
import org.apache.poi.ss.usermodel.Font;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.VerticalAlignment;
import org.apache.poi.ss.util.CellRangeAddress;
import org.nutz.aop.interceptor.ioc.TransAop;
import org.nutz.dao.Chain;
import org.nutz.dao.Cnd;
import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Sql;
import org.nutz.dao.util.cri.SqlExpressionGroup;
import org.nutz.ioc.aop.Aop;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.json.Json;
import org.nutz.lang.util.NutMap;
import org.nutz.mvc.annotation.AdaptBy;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
import org.nutz.mvc.annotation.Param;
import org.nutz.mvc.upload.TempFile;
import org.nutz.mvc.upload.UploadAdaptor;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedOutputStream;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
@IocBean
@Ok("json:full")
@At("/platform/tour/ledger")
public class TourLedgerController {
@Inject
private TourLedgerService tourLedgerService;
@Inject
private TourLedgerFamilyService tourLedgerFamilyService;
@Inject
private TourLedgerDirectRelativeService tourLedgerDirectRelativeService;
@At("")
@Ok("beetl:/platform/zhgh/dayofficework/Tour/ledger/index.html")
@SaCheckPermission("tour.ledger")
public void index() {
}
@At
@SaCheckPermission("tour.ledger")
public Result pageData(PageForm pageForm, Integer startYear, Integer endYear, String keyword, String unionId, String lineId, String travelPeriod, String lineType, Boolean directFamilyOnly, Boolean overCostOnly) {
Cnd cnd = buildQueryCnd(startYear, endYear, keyword, unionId, lineId, travelPeriod, lineType, directFamilyOnly, overCostOnly);
Sql countSql = Sqls.create("""
SELECT COUNT(DISTINCT t.id)
FROM tour_ledger t
LEFT JOIN wf_process_instance ins ON ins.businessNo = t.id
LEFT JOIN tour_matter m ON m.id = t.matterId AND m.delFlag = 0
LEFT JOIN tour_line l ON l.id = t.lineId AND l.delFlag = 0
LEFT JOIN tour_ledger_direct_relative dr ON dr.ledgerId = t.id AND dr.delFlag = 0
$condition
""");
countSql.setCondition(cnd);
countSql.setCallback(Sqls.callback.integer());
tourLedgerService.dao().execute(countSql);
int count = countSql.getInt();
Sql listSql = Sqls.create("""
SELECT
t.*,
ins.id AS instanceId,
ins.state AS instanceState,
ins.processDefineId AS instanceProcessDefineId,
IF(IFNULL(l.directFamilyUnitLine, 0) = 1 OR dr.id IS NOT NULL, 1, 0) AS directFamilyUnitLine,
dr.id AS directRelativeId,
COALESCE(NULLIF(l.lineName, ''), t.lineName) AS currentLineName,
IFNULL(f.familyCount, 0) AS familyCount,
CASE
WHEN IFNULL(m.travelStartTime, '') <> '' AND IFNULL(m.travelEndTime, '') <> ''
THEN CONCAT(m.travelStartTime, ' 至 ', m.travelEndTime)
ELSE tp.travelPeriod
END AS travelPeriod
FROM tour_ledger t
LEFT JOIN wf_process_instance ins ON ins.businessNo = t.id
LEFT JOIN (
SELECT ledgerId, COUNT(1) AS familyCount
FROM tour_ledger_family
WHERE delFlag = 0
GROUP BY ledgerId
) f ON f.ledgerId = t.id
LEFT JOIN tour_matter m ON m.id = t.matterId AND m.delFlag = 0
LEFT JOIN tour_line l ON l.id = t.lineId AND l.delFlag = 0
LEFT JOIN tour_ledger_direct_relative dr ON dr.ledgerId = t.id AND dr.delFlag = 0
LEFT JOIN (
SELECT
`year`,
lineId,
GROUP_CONCAT(
DISTINCT CASE
WHEN IFNULL(travelStartTime, '') <> '' AND IFNULL(travelEndTime, '') <> ''
THEN CONCAT(travelStartTime, ' 至 ', travelEndTime)
ELSE NULL
END
ORDER BY travelStartTime ASC
SEPARATOR ''
) AS travelPeriod
FROM tour_matter
WHERE delFlag = 0
AND lineId IS NOT NULL
AND lineId <> ''
GROUP BY `year`, lineId
) tp ON (t.matterId IS NULL OR t.matterId = '') AND tp.`year` = t.`year` AND tp.lineId = t.lineId
$condition
GROUP BY t.id
ORDER BY $orderColumn $orderBy, t.`year` DESC, t.signupTime DESC, t.createdAt DESC
""");
listSql.setCondition(cnd);
listSql.setVar("orderColumn", getOrderColumn(pageForm.getPageOrderName()));
listSql.setVar("orderBy", "descending".equals(pageForm.getPageOrderBy()) ? "desc" : "asc");
listSql.setPager(tourLedgerService.dao().createPager(pageForm.getPageNumber(), pageForm.getPageSize()));
listSql.setCallback(Sqls.callback.maps());
tourLedgerService.dao().execute(listSql);
var list = listSql.getList(NutMap.class);
list.forEach(item -> item.put("lineName", item.getString("currentLineName")));
Pagination<NutMap> pagination = new Pagination<>(pageForm.getPageNumber(), pageForm.getPageSize(), count, list);
return Result.success(pagination);
}
@At
@SaCheckPermission("tour.ledger")
public Result detail(String id) {
if (StrUtil.isBlank(id)) {
return Result.error("参数错误");
}
TourLedger ledger = tourLedgerService.fetch(id);
if (ledger == null) {
return Result.error("台账记录不存在");
}
ledger.setLineName(getCurrentLineName(ledger));
Cnd familyCnd = Cnd.NEW();
familyCnd.and(TourLedgerFamily::getLedgerId, "=", id);
familyCnd.and(TourLedgerFamily::getDelFlag, "=", false);
familyCnd.asc(TourLedgerFamily::getCreatedAt);
TourLedgerDirectRelative directRelative = tourLedgerDirectRelativeService.fetch(
Cnd.where(TourLedgerDirectRelative::getDelFlag, "=", false)
.and(TourLedgerDirectRelative::getLedgerId, "=", ledger.getId()));
return Result.success(NutMap.NEW()
.addv("ledger", ledger)
.addv("travelPeriod", getTravelPeriod(ledger))
.addv("families", tourLedgerFamilyService.query(familyCnd))
.addv("directRelative", directRelative)
.addv("fillBedInfo", isFillBedInfo(ledger))
.addv("directFamilyUnitLine", isDirectFamilyUnitLine(ledger)));
}
@At
@Aop(TransAop.READ_COMMITTED)
@SaCheckPermission("tour.ledger")
@SLog(type = "tour", tag = "疗休养台账", msg = "删除疗休养台账")
public Result doDelete(String id) {
if (StrUtil.isBlank(id)) {
return Result.error("参数错误");
}
TourLedger ledger = tourLedgerService.fetch(id);
if (ledger == null) {
return Result.error("台账记录不存在");
}
tourLedgerFamilyService.clear(Cnd.where(TourLedgerFamily::getLedgerId, "=", id));
tourLedgerService.delete(id);
return Result.success();
}
@At
@Aop(TransAop.READ_COMMITTED)
@SaCheckPermission("tour.ledger")
@SLog(type = "tour", tag = "疗休养台账", msg = "设置参加人员")
public Result setParticipants(@Param("ids") String ids) {
List<String> idList = StrUtil.isBlank(ids) ? Collections.emptyList() : Json.fromJsonAsList(String.class, ids);
if (idList.isEmpty()) {
return Result.error("请选择要设置的参加人员");
}
tourLedgerService.dao().update(TourLedger.class, Chain.make("joined", true), Cnd.where("id", "in", idList));
return Result.success();
}
@At
@Aop(TransAop.READ_COMMITTED)
@SaCheckPermission("tour.ledger")
@SLog(type = "tour", tag = "疗休养台账", msg = "参加人员导入")
@AdaptBy(type = UploadAdaptor.class, args = {"ioc:fileUpload"})
public Result importParticipants(@Param("file") TempFile file) {
if (file == null) {
return Result.error("请选择导入文件");
}
List<TourLedgerImportExcelMode> importRows = ExcelImportUtil.importExcel(file.getFile(), TourLedgerImportExcelMode.class, new ImportParams());
ExcelImportRes<TourLedgerImportExcelMode> excelImportRes = new ExcelImportRes<>();
excelImportRes.setTotalRecords(importRows.size());
for (int i = 0; i < importRows.size(); i++) {
TourLedgerImportExcelMode row = importRows.get(i);
if (!validateImportRow(row, i)) {
continue;
}
Boolean joined = parseJoined(row.getJoined());
if (joined == null) {
row.setErrInfo("是否参加只能填写1、0、是、否、true、false、已参加、未参加", i);
continue;
}
TourLine line = fetchImportLine(row);
if (line == null) {
row.setErrInfo("未找到唯一匹配的线路", i);
continue;
}
List<TourLedger> ledgers = queryImportLedgerCandidates(row);
if (ledgers.isEmpty()) {
Result createResult = insertImportLedger(row, line, joined);
if (createResult.isSuccess()) {
continue;
}
row.setErrInfo(createResult.getMsg(), i);
continue;
}
TourLedger ledger = resolveImportLedger(row, ledgers, line);
if (ledger == null) {
row.setErrInfo("匹配到多条台账记录,报名时间或线路未能唯一匹配,请核对年度、工号、报名时间、线路名称", i);
continue;
}
Chain chain = Chain.make("joined", joined)
.add("lineId", line.getId())
.add("lineName", line.getLineName())
.add("lineType", line.getLineType());
String signupTime = normalizeSignupTime(row.getSignupTime());
if (StrUtil.isNotBlank(signupTime)) {
chain.add("signupTime", signupTime);
}
tourLedgerService.dao().update(TourLedger.class, chain, Cnd.where("id", "=", ledger.getId()));
}
excelImportRes.setErrorDetails(importRows.stream().filter(s -> StrUtil.isNotBlank(s.getErrMsg())).collect(Collectors.toList()));
excelImportRes.setFailedCount(excelImportRes.getErrorDetails().size());
excelImportRes.setSuccessCount(Math.max(importRows.size() - excelImportRes.getFailedCount(), 0));
return Result.success(excelImportRes);
}
@At
@SaCheckPermission("tour.ledger")
@Ok("void")
public void downloadTemplate(HttpServletResponse response) {
List<ExcelExportEntity> entities = new ArrayList<>();
entities.add(new ExcelExportEntity("年度", "year", 12));
entities.add(new ExcelExportEntity("工号", "jobNo", 18));
entities.add(new ExcelExportEntity("姓名", "userName", 18));
entities.add(new ExcelExportEntity("报名时间", "signupTime", 24));
entities.add(new ExcelExportEntity("线路名称", "lineName", 30));
entities.add(new ExcelExportEntity("线路类型", "lineType", 18));
entities.add(new ExcelExportEntity("是否参加", "joined", 14));
ExportParams exportParams = new ExportParams();
exportParams.setSheetName("tour_ledger");
exportParams.setType(ExcelType.XSSF);
Workbook workbook = ExcelExportUtil.exportExcel(exportParams, entities, Collections.emptyList());
CommonDownloadUtil.download("参加人员导入模板.xlsx", workbook, response);
}
@At
@SaCheckPermission("tour.ledger")
@Ok("void")
public void exportUnionSignupZip(Integer startYear, Integer endYear, String keyword, String unionId, String lineId,
String travelPeriod, String lineType, Boolean directFamilyOnly, Boolean overCostOnly,
HttpServletResponse response) throws Exception {
List<NutMap> rows = queryUnionSignupRows(startYear, endYear, keyword, unionId, lineId, travelPeriod, lineType,
directFamilyOnly, overCostOnly);
Map<String, List<NutMap>> unionRows = groupRowsByUnion(rows);
String zipName = URLEncoder.encode("疗休养分工会报名汇总表.zip", StandardCharsets.UTF_8.toString());
response.setContentType("application/zip");
response.setHeader("Content-Disposition", "attachment;filename=" + zipName + ";filename*=UTF-8''" + zipName);
try (ZipOutputStream zipOutputStream = new ZipOutputStream(new BufferedOutputStream(response.getOutputStream()))) {
if (unionRows.isEmpty()) {
zipOutputStream.putNextEntry(new ZipEntry("无数据报名汇总表.xls"));
try (Workbook workbook = buildUnionSignupWorkbook("无数据", Collections.emptyList())) {
workbook.write(zipOutputStream);
}
zipOutputStream.closeEntry();
} else {
for (Map.Entry<String, List<NutMap>> entry : unionRows.entrySet()) {
String unionName = entry.getValue().isEmpty() ? entry.getKey() : entry.getValue().get(0).getString("unionName", entry.getKey());
String fileName = safeFileName(unionName + "报名汇总表.xls");
zipOutputStream.putNextEntry(new ZipEntry(fileName));
try (Workbook workbook = buildUnionSignupWorkbook(unionName, entry.getValue())) {
workbook.write(zipOutputStream);
}
zipOutputStream.closeEntry();
}
}
zipOutputStream.flush();
}
}
private List<NutMap> queryUnionSignupRows(Integer startYear, Integer endYear, String keyword, String unionId, String lineId,
String travelPeriod, String lineType, Boolean directFamilyOnly, Boolean overCostOnly) {
Cnd cnd = buildQueryCnd(startYear, endYear, keyword, unionId, lineId, travelPeriod, lineType, directFamilyOnly, overCostOnly);
Sql sql = Sqls.create("""
SELECT
t.id,
t.jobNo,
t.userName,
t.idCard,
t.unionId,
COALESCE(NULLIF(t.unionName, ''), su.name, vu.unionName, '') AS unionName,
COALESCE(NULLIF(su.unionCode, ''), vu.unionCode, '') AS unionCode,
COALESCE(NULLIF(vu.mobile, ''), '') AS mobile,
COALESCE(NULLIF(l.lineName, ''), t.lineName, '') AS lineName,
CASE
WHEN IFNULL(m.travelStartTime, '') <> '' AND IFNULL(m.travelEndTime, '') <> ''
THEN CONCAT(m.travelStartTime, ' 至 ', m.travelEndTime)
ELSE tp.travelPeriod
END AS travelPeriod
FROM tour_ledger t
LEFT JOIN wf_process_instance ins ON ins.businessNo = t.id
LEFT JOIN tour_matter m ON m.id = t.matterId AND m.delFlag = 0
LEFT JOIN tour_line l ON l.id = t.lineId AND l.delFlag = 0
LEFT JOIN tour_ledger_direct_relative dr ON dr.ledgerId = t.id AND dr.delFlag = 0
LEFT JOIN vw_user vu ON vu.loginname = t.jobNo
LEFT JOIN sys_union su ON su.id = t.unionId
LEFT JOIN (
SELECT
`year`,
lineId,
GROUP_CONCAT(
DISTINCT CASE
WHEN IFNULL(travelStartTime, '') <> '' AND IFNULL(travelEndTime, '') <> ''
THEN CONCAT(travelStartTime, ' 至 ', travelEndTime)
ELSE NULL
END
ORDER BY travelStartTime ASC
SEPARATOR ''
) AS travelPeriod
FROM tour_matter
WHERE delFlag = 0
AND lineId IS NOT NULL
AND lineId <> ''
GROUP BY `year`, lineId
) tp ON (t.matterId IS NULL OR t.matterId = '') AND tp.`year` = t.`year` AND tp.lineId = t.lineId
$condition
GROUP BY t.id
ORDER BY unionCode ASC, unionName ASC, t.signupTime DESC, t.createdAt DESC
""");
sql.setCondition(cnd);
sql.setCallback(Sqls.callback.maps());
tourLedgerService.dao().execute(sql);
return sql.getList(NutMap.class);
}
private Map<String, List<NutMap>> groupRowsByUnion(List<NutMap> rows) {
Map<String, List<NutMap>> unionRows = new LinkedHashMap<>();
for (NutMap row : rows) {
String unionName = StrUtil.blankToDefault(row.getString("unionName"), "未分工会");
String unionCode = StrUtil.blankToDefault(row.getString("unionCode"), "");
String key = StrUtil.isBlank(unionCode) ? unionName : unionCode + "_" + unionName;
unionRows.computeIfAbsent(key, item -> new ArrayList<>()).add(row);
}
return unionRows;
}
private Workbook buildUnionSignupWorkbook(String unionName, List<NutMap> rows) {
HSSFWorkbook workbook = new HSSFWorkbook();
Sheet sheet = workbook.createSheet("报名汇总表");
sheet.setColumnWidth(0, 8 * 256);
sheet.setColumnWidth(1, 14 * 256);
sheet.setColumnWidth(2, 14 * 256);
sheet.setColumnWidth(3, 24 * 256);
sheet.setColumnWidth(4, 16 * 256);
sheet.setColumnWidth(5, 24 * 256);
sheet.setColumnWidth(6, 36 * 256);
sheet.setColumnWidth(7, 26 * 256);
sheet.setColumnWidth(8, 18 * 256);
CellStyle titleStyle = createUnionSignupTitleStyle(workbook);
CellStyle headerStyle = createUnionSignupHeaderStyle(workbook);
CellStyle bodyStyle = createUnionSignupBodyStyle(workbook);
Row titleRow = sheet.createRow(0);
titleRow.setHeightInPoints(28);
Cell titleCell = titleRow.createCell(0);
titleCell.setCellValue(StrUtil.blankToDefault(unionName, "未分工会") + "报名汇总表");
titleCell.setCellStyle(titleStyle);
sheet.addMergedRegion(new CellRangeAddress(0, 0, 0, 8));
for (int i = 1; i <= 8; i++) {
titleRow.createCell(i).setCellStyle(titleStyle);
}
Row headerRow = sheet.createRow(1);
headerRow.setHeightInPoints(24);
String[] headers = {"序号", "工号", "姓名", "身份证号码", "电话号码", "所属分工会", "所选线路名称", "疗休养时间", "备注"};
for (int i = 0; i < headers.length; i++) {
setCell(headerRow, i, headers[i], headerStyle);
}
int rowIndex = 2;
int seq = 1;
for (NutMap row : rows) {
Row dataRow = sheet.createRow(rowIndex++);
dataRow.setHeightInPoints(22);
setCell(dataRow, 0, String.valueOf(seq++), bodyStyle);
setCell(dataRow, 1, row.getString("jobNo", ""), bodyStyle);
setCell(dataRow, 2, row.getString("userName", ""), bodyStyle);
setCell(dataRow, 3, row.getString("idCard", ""), bodyStyle);
setCell(dataRow, 4, row.getString("mobile", ""), bodyStyle);
setCell(dataRow, 5, row.getString("unionName", ""), bodyStyle);
setCell(dataRow, 6, row.getString("lineName", ""), bodyStyle);
setCell(dataRow, 7, row.getString("travelPeriod", ""), bodyStyle);
setCell(dataRow, 8, "", bodyStyle);
}
return workbook;
}
private CellStyle createUnionSignupTitleStyle(Workbook workbook) {
CellStyle style = workbook.createCellStyle();
style.setAlignment(HorizontalAlignment.CENTER);
style.setVerticalAlignment(VerticalAlignment.CENTER);
style.setBorderTop(BorderStyle.THIN);
style.setBorderRight(BorderStyle.THIN);
style.setBorderBottom(BorderStyle.THIN);
style.setBorderLeft(BorderStyle.THIN);
Font font = workbook.createFont();
font.setBold(true);
font.setFontHeightInPoints((short) 16);
style.setFont(font);
return style;
}
private CellStyle createUnionSignupHeaderStyle(Workbook workbook) {
CellStyle style = createUnionSignupBodyStyle(workbook);
style.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
Font font = workbook.createFont();
font.setBold(true);
style.setFont(font);
return style;
}
private CellStyle createUnionSignupBodyStyle(Workbook workbook) {
CellStyle style = workbook.createCellStyle();
style.setAlignment(HorizontalAlignment.CENTER);
style.setVerticalAlignment(VerticalAlignment.CENTER);
style.setWrapText(true);
style.setBorderTop(BorderStyle.THIN);
style.setBorderRight(BorderStyle.THIN);
style.setBorderBottom(BorderStyle.THIN);
style.setBorderLeft(BorderStyle.THIN);
return style;
}
private void setCell(Row row, int index, String value, CellStyle style) {
Cell cell = row.createCell(index);
cell.setCellValue(StrUtil.blankToDefault(value, ""));
cell.setCellStyle(style);
}
private String safeFileName(String fileName) {
String safeName = StrUtil.blankToDefault(fileName, "报名汇总表.xls")
.replaceAll("[\\\\/:*?\"<>|]", "");
return safeName.length() > 120 ? safeName.substring(0, 120) : safeName;
}
private boolean validateImportRow(TourLedgerImportExcelMode row, int index) {
if (row.getYear() == null) {
row.setErrInfo("年度为空", index);
return false;
}
if (StrUtil.isBlank(row.getJobNo())) {
row.setErrInfo("工号为空", index);
return false;
}
if (StrUtil.isBlank(row.getLineName())) {
row.setErrInfo("线路名称为空", index);
return false;
}
if (StrUtil.isBlank(row.getLineType())) {
row.setErrInfo("线路类型为空", index);
return false;
}
if (StrUtil.isBlank(row.getJoined())) {
row.setErrInfo("是否参加为空", index);
return false;
}
return true;
}
private TourLine fetchImportLine(TourLedgerImportExcelMode row) {
Cnd cnd = Cnd.where(TourLine::getDelFlag, "=", false)
.and(TourLine::getYear, "=", row.getYear())
.and(TourLine::getLineName, "=", row.getLineName().trim())
.and(TourLine::getLineType, "=", row.getLineType().trim());
List<TourLine> lines = tourLedgerService.dao().query(TourLine.class, cnd);
return lines.size() == 1 ? lines.get(0) : null;
}
private List<TourLedger> queryImportLedgerCandidates(TourLedgerImportExcelMode row) {
Cnd cnd = Cnd.where(TourLedger::getDelFlag, "=", false)
.and(TourLedger::getYear, "=", row.getYear())
.and(TourLedger::getJobNo, "=", row.getJobNo().trim());
return tourLedgerService.query(cnd);
}
private TourLedger resolveImportLedger(TourLedgerImportExcelMode row, List<TourLedger> ledgers, TourLine line) {
if (ledgers.size() == 1) {
return ledgers.get(0);
}
List<TourLedger> matched = ledgers;
if (row.getSignupTime() != null) {
String importSignupTime = normalizeSignupTime(row.getSignupTime());
matched = matched.stream()
.filter(item -> signupTimeEquals(importSignupTime, item.getSignupTime()))
.collect(Collectors.toList());
if (matched.size() == 1) {
return matched.get(0);
}
}
matched = ledgers.stream()
.filter(item -> ledgerLineEquals(item, line))
.collect(Collectors.toList());
return matched.size() == 1 ? matched.get(0) : null;
}
private boolean ledgerLineEquals(TourLedger ledger, TourLine line) {
if (ledger == null || line == null) {
return false;
}
if (StrUtil.isNotBlank(ledger.getLineId()) && ledger.getLineId().equals(line.getId())) {
return true;
}
return StrUtil.equals(ledger.getLineName(), line.getLineName())
&& StrUtil.equals(ledger.getLineType(), line.getLineType());
}
private Result insertImportLedger(TourLedgerImportExcelMode row, TourLine line, Boolean joined) {
View_user user = fetchImportUser(row.getJobNo());
if (user == null) {
return Result.error("系统用户中未找到该工号");
}
TourLedger ledger = new TourLedger();
ledger.setYear(row.getYear());
ledger.setJobNo(StrUtil.blankToDefault(user.getLoginname(), row.getJobNo().trim()));
ledger.setUserName(StrUtil.blankToDefault(user.getUsername(), row.getUserName()));
ledger.setGender(user.getSex());
ledger.setIdCard(user.getIdCard());
ledger.setUnitId(user.getUnitId());
ledger.setUnitName(user.getUnitName());
ledger.setUnionId(user.getUnionId());
ledger.setUnionName(user.getUnionName());
ledger.setSignupTime(normalizeSignupTime(row.getSignupTime()));
ledger.setLineId(line.getId());
ledger.setLineName(line.getLineName());
ledger.setLineType(line.getLineType());
ledger.setTravelAgencyId(line.getTravelAgencyId());
ledger.setTravelAgencyName(getTravelAgencyName(line.getTravelAgencyId()));
ledger.setHasFamily(false);
ledger.setJoined(joined);
ledger.setReimbursed(false);
ledger.setOverCostReimbursed(false);
tourLedgerService.insert(ledger);
return Result.success();
}
private View_user fetchImportUser(String jobNo) {
if (StrUtil.isBlank(jobNo)) {
return null;
}
return tourLedgerService.dao().fetch(View_user.class, Cnd.where(View_user::getLoginname, "=", jobNo.trim()));
}
private String getTravelAgencyName(String travelAgencyId) {
if (StrUtil.isBlank(travelAgencyId)) {
return "";
}
TourTravelAgency agency = tourLedgerService.dao().fetch(TourTravelAgency.class, travelAgencyId);
return agency == null ? "" : StrUtil.blankToDefault(agency.getAgencyName(), "");
}
private boolean signupTimeEquals(String importSignupTime, String ledgerSignupTime) {
if (StrUtil.isBlank(importSignupTime) || StrUtil.isBlank(ledgerSignupTime)) {
return false;
}
String ledgerTime = normalizeSignupTime(ledgerSignupTime);
if (importSignupTime.equals(ledgerTime)) {
return true;
}
return importSignupTime.length() == 16 && ledgerTime.startsWith(importSignupTime)
|| ledgerTime.length() == 16 && importSignupTime.startsWith(ledgerTime);
}
private String normalizeSignupTime(Object value) {
if (value instanceof Date date) {
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(date);
}
String text = StrUtil.trimToEmpty(value == null ? "" : String.valueOf(value)).replace('T', ' ');
if (StrUtil.isBlank(text)) {
return "";
}
text = text.replaceAll("\\.0$", "");
String[] dateTimePatterns = {
"yyyy-MM-dd HH:mm:ss",
"yyyy-MM-dd HH:mm",
"yyyy/M/d H:mm:ss",
"yyyy/M/d H:mm",
"M/d/yy H:mm:ss",
"M/d/yy H:mm",
"M/d/yyyy H:mm:ss",
"M/d/yyyy H:mm"
};
for (String pattern : dateTimePatterns) {
try {
LocalDateTime dateTime = LocalDateTime.parse(text, DateTimeFormatter.ofPattern(pattern));
return dateTime.format(DateTimeFormatter.ofPattern(pattern.contains(":ss") ? "yyyy-MM-dd HH:mm:ss" : "yyyy-MM-dd HH:mm"));
} catch (DateTimeParseException ignored) {
}
}
String[] datePatterns = {"yyyy-MM-dd", "yyyy/M/d", "M/d/yy", "M/d/yyyy"};
for (String pattern : datePatterns) {
try {
LocalDate date = LocalDate.parse(text, DateTimeFormatter.ofPattern(pattern));
return date.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
} catch (DateTimeParseException ignored) {
}
}
return text;
}
private Boolean parseJoined(String value) {
String joined = StrUtil.trimToEmpty(value).toLowerCase();
if ("1".equals(joined) || "1.0".equals(joined) || "".equals(joined) || "true".equals(joined) || "已参加".equals(joined)) {
return true;
}
if ("0".equals(joined) || "0.0".equals(joined) || "".equals(joined) || "false".equals(joined) || "未参加".equals(joined)) {
return false;
}
return null;
}
@At
@SaCheckPermission("tour.ledger")
public Result unionOptions() {
Cnd cnd = Cnd.NEW();
cnd.asc("unionCode");
cnd.asc("name");
return Result.success(tourLedgerService.dao().query(Sys_union.class, cnd));
}
@At
@SaCheckPermission("tour.ledger")
public Result lineOptions(Integer startYear, Integer endYear, String travelPeriod, String lineType, String unionId, String keyword, Boolean directFamilyOnly, Boolean overCostOnly) {
Cnd cnd = Cnd.NEW();
cnd.and("t.delFlag", "=", false);
cnd.andEX("t.`year`", ">=", startYear);
cnd.andEX("t.`year`", "<=", endYear == null ? LocalDate.now().getYear() : endYear);
cnd.and("t.lineName", "IS NOT", null);
cnd.and("t.lineName", "<>", "");
cnd.andEX("t.unionId", "=", unionId);
cnd.andEX("t.lineType", "=", lineType);
appendTravelPeriodFilter(cnd, travelPeriod);
appendKeywordFilter(cnd, keyword);
appendScopeFilter(cnd, directFamilyOnly, overCostOnly);
appendApprovedWorkflowFilter(cnd);
Sql sql = Sqls.create("""
SELECT DISTINCT
CASE
WHEN IFNULL(t.lineId, '') <> '' THEN t.lineId
ELSE CONCAT('legacy:', t.lineName)
END AS lineId,
COALESCE(NULLIF(l.lineName, ''), t.lineName) AS lineName
FROM tour_ledger t
LEFT JOIN wf_process_instance ins ON ins.businessNo = t.id
LEFT JOIN tour_matter m ON m.id = t.matterId AND m.delFlag = 0
LEFT JOIN tour_line l ON l.id = t.lineId AND l.delFlag = 0
LEFT JOIN tour_ledger_direct_relative dr ON dr.ledgerId = t.id AND dr.delFlag = 0
$condition
ORDER BY lineName ASC
""");
sql.setCondition(cnd);
sql.setCallback(Sqls.callback.maps());
tourLedgerService.dao().execute(sql);
return Result.success(sql.getList(NutMap.class));
}
@At
@SaCheckPermission("tour.ledger")
public Result travelPeriodOptions(Integer startYear, Integer endYear, String lineId, String lineType, String unionId, String keyword, Boolean directFamilyOnly, Boolean overCostOnly) {
Cnd cnd = Cnd.NEW();
cnd.and("t.delFlag", "=", false);
cnd.andEX("t.`year`", ">=", startYear);
cnd.andEX("t.`year`", "<=", endYear == null ? LocalDate.now().getYear() : endYear);
cnd.and("m.travelStartTime", "IS NOT", null);
cnd.and("m.travelStartTime", "<>", "");
cnd.and("m.travelEndTime", "IS NOT", null);
cnd.and("m.travelEndTime", "<>", "");
cnd.andEX("t.unionId", "=", unionId);
cnd.andEX("t.lineType", "=", lineType);
appendLineFilter(cnd, lineId);
appendKeywordFilter(cnd, keyword);
appendScopeFilter(cnd, directFamilyOnly, overCostOnly);
appendApprovedWorkflowFilter(cnd);
Sql sql = Sqls.create("""
SELECT DISTINCT CONCAT(m.travelStartTime, ' 至 ', m.travelEndTime) AS travelPeriod
FROM tour_ledger t
LEFT JOIN wf_process_instance ins ON ins.businessNo = t.id
LEFT JOIN tour_matter m ON m.id = t.matterId AND m.delFlag = 0
LEFT JOIN tour_line l ON l.id = t.lineId AND l.delFlag = 0
LEFT JOIN tour_ledger_direct_relative dr ON dr.ledgerId = t.id AND dr.delFlag = 0
$condition
ORDER BY travelPeriod ASC
""");
sql.setCondition(cnd);
sql.setCallback(Sqls.callback.maps());
tourLedgerService.dao().execute(sql);
return Result.success(sql.getList(NutMap.class));
}
private Cnd buildQueryCnd(Integer startYear, Integer endYear, String keyword, String unionId, String lineId, String travelPeriod, String lineType, Boolean directFamilyOnly, Boolean overCostOnly) {
Cnd cnd = Cnd.NEW();
cnd.and("t.delFlag", "=", false);
cnd.andEX("t.`year`", ">=", startYear);
cnd.andEX("t.`year`", "<=", endYear == null ? LocalDate.now().getYear() : endYear);
cnd.andEX("t.unionId", "=", unionId);
cnd.andEX("t.lineType", "=", lineType);
appendLineFilter(cnd, lineId);
appendTravelPeriodFilter(cnd, travelPeriod);
appendKeywordFilter(cnd, keyword);
appendScopeFilter(cnd, directFamilyOnly, overCostOnly);
appendApprovedWorkflowFilter(cnd);
return cnd;
}
private void appendApprovedWorkflowFilter(Cnd cnd) {
SqlExpressionGroup group = new SqlExpressionGroup();
group.or("ins.id", "IS", null);
group.or("ins.state", "=", ProcessInstanceStateEnum.FINISHED.getCode());
cnd.and(group);
}
private void appendScopeFilter(Cnd cnd, Boolean directFamilyOnly, Boolean overCostOnly) {
if (Boolean.TRUE.equals(directFamilyOnly)) {
cnd.and("IF(IFNULL(l.directFamilyUnitLine, 0) = 1 OR dr.id IS NOT NULL, 1, 0)", "=", 1);
}
if (Boolean.TRUE.equals(overCostOnly)) {
cnd.and("t.overCostReimbursed", "=", true);
}
}
private void appendKeywordFilter(Cnd cnd, String keyword) {
if (StrUtil.isNotBlank(keyword)) {
SqlExpressionGroup group = new SqlExpressionGroup();
group.orLike("t.userName", keyword.trim());
group.orLike("t.jobNo", keyword.trim());
cnd.and(group);
}
}
private String getOrderColumn(String orderName) {
if ("year".equals(orderName)) {
return "t.`year`";
}
if ("jobNo".equals(orderName)) {
return "t.jobNo";
}
if ("userName".equals(orderName)) {
return "t.userName";
}
if ("lineName".equals(orderName)) {
return "COALESCE(NULLIF(l.lineName, ''), t.lineName)";
}
if ("travelPeriod".equals(orderName)) {
return "COALESCE(m.travelStartTime, tp.travelPeriod)";
}
if ("lineType".equals(orderName)) {
return "t.lineType";
}
if ("signupTime".equals(orderName)) {
return "t.signupTime";
}
if ("familyCount".equals(orderName)) {
return "familyCount";
}
if ("joined".equals(orderName)) {
return "t.joined";
}
return "t.`year`";
}
private String getTravelPeriod(TourLedger ledger) {
if (ledger == null || ledger.getYear() == null || StrUtil.isBlank(ledger.getLineId())) {
return "";
}
if (StrUtil.isNotBlank(ledger.getMatterId())) {
Sql exactSql = Sqls.create("""
SELECT CASE
WHEN IFNULL(travelStartTime, '') <> '' AND IFNULL(travelEndTime, '') <> ''
THEN CONCAT(travelStartTime, ' 至 ', travelEndTime)
ELSE ''
END AS travelPeriod
FROM tour_matter
WHERE delFlag = 0
AND id = @matterId
""");
exactSql.setParam("matterId", ledger.getMatterId());
exactSql.setCallback(Sqls.callback.str());
tourLedgerService.dao().execute(exactSql);
return exactSql.getString();
}
Sql sql = Sqls.create("""
SELECT GROUP_CONCAT(
DISTINCT CASE
WHEN IFNULL(travelStartTime, '') <> '' AND IFNULL(travelEndTime, '') <> ''
THEN CONCAT(travelStartTime, ' 至 ', travelEndTime)
ELSE NULL
END
ORDER BY travelStartTime ASC
SEPARATOR ''
) AS travelPeriod
FROM tour_matter
WHERE delFlag = 0
AND `year` = @year
AND lineId = @lineId
""");
sql.setParam("year", ledger.getYear());
sql.setParam("lineId", ledger.getLineId());
sql.setCallback(Sqls.callback.str());
tourLedgerService.dao().execute(sql);
return sql.getString();
}
private boolean isDirectFamilyUnitLine(TourLedger ledger) {
if (ledger == null || StrUtil.isBlank(ledger.getLineId())) {
return false;
}
Sql sql = Sqls.create("""
SELECT IFNULL(directFamilyUnitLine, 0)
FROM tour_line
WHERE delFlag = 0
AND id = @lineId
""");
sql.setParam("lineId", ledger.getLineId());
sql.setCallback(Sqls.callback.integer());
tourLedgerService.dao().execute(sql);
return sql.getInt() > 0;
}
private boolean isFillBedInfo(TourLedger ledger) {
if (ledger == null || StrUtil.isBlank(ledger.getMatterId())) {
return true;
}
Sql sql = Sqls.create("""
SELECT IFNULL(MAX(s.fillBedInfo), 1)
FROM tour_ledger t
LEFT JOIN tour_matter m ON m.id = t.matterId AND m.delFlag = 0
LEFT JOIN tour_setting s ON s.id = m.settingId AND s.delFlag = 0
WHERE t.id = @ledgerId
""");
sql.setParam("ledgerId", ledger.getId());
sql.setCallback(Sqls.callback.integer());
tourLedgerService.dao().execute(sql);
return sql.getInt() > 0;
}
private void appendTravelPeriodFilter(Cnd cnd, String travelPeriod) {
if (StrUtil.isBlank(travelPeriod)) {
return;
}
cnd.and("CONCAT(m.travelStartTime, ' 至 ', m.travelEndTime)", "=", travelPeriod);
}
private void appendLineFilter(Cnd cnd, String lineId) {
if (StrUtil.isBlank(lineId)) {
return;
}
if (lineId.startsWith("legacy:")) {
SqlExpressionGroup legacyGroup = new SqlExpressionGroup();
legacyGroup.or("t.lineId", "IS", null);
legacyGroup.or("t.lineId", "=", "");
cnd.and(legacyGroup);
cnd.and("t.lineName", "=", lineId.substring("legacy:".length()));
return;
}
cnd.and("t.lineId", "=", lineId);
}
private String getCurrentLineName(TourLedger ledger) {
if (ledger == null) {
return "";
}
if (StrUtil.isBlank(ledger.getLineId())) {
return ledger.getLineName();
}
Sql sql = Sqls.create("""
SELECT lineName
FROM tour_line
WHERE delFlag = 0
AND id = @lineId
""");
sql.setParam("lineId", ledger.getLineId());
sql.setCallback(Sqls.callback.str());
tourLedgerService.dao().execute(sql);
return StrUtil.blankToDefault(sql.getString(), ledger.getLineName());
}
}
@@ -0,0 +1,297 @@
package com.budwk.app.zhgh.dayofficework.tour.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
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.param.PageForm;
import com.budwk.app.base.result.Result;
import com.budwk.app.sys.models.Sys_dict;
import com.budwk.app.sys.services.SysDictService;
import com.budwk.app.sys.views.View_user;
import com.budwk.app.web.commons.auth.utils.AuthUtil;
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
import com.budwk.app.zhgh.dayofficework.tour.models.TourLine;
import com.budwk.app.zhgh.dayofficework.tour.models.TourTravelAgency;
import com.budwk.app.zhgh.dayofficework.tour.service.TourLineService;
import org.nutz.aop.interceptor.ioc.TransAop;
import org.nutz.dao.Cnd;
import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Sql;
import org.nutz.dao.util.cri.SqlExpressionGroup;
import org.nutz.ioc.aop.Aop;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.util.NutMap;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
@IocBean
@Ok("json:full")
@At("/platform/tour/route")
public class TourLineController {
@Inject
private TourLineService tourLineService;
@Inject
private SysDictService sysDictService;
@At("")
@Ok("beetl:/platform/zhgh/dayofficework/Tour/route/index.html")
@SaCheckPermission("tour.route")
public void index() {
}
@At
@SaCheckPermission("tour.route")
public Result pageData(PageForm pageForm, Integer year, String lineName, String lotId) {
if (StrUtil.isBlank(pageForm.getPageOrderName())) {
pageForm.defaultSortDesc("enabled");
}
Cnd cnd = Cnd.NEW();
cnd.and("l.delFlag", "=", false);
cnd.andEX("l.`year`", "=", year);
cnd.and(Cnd.likeEX("l.lineName", lineName));
cnd.andEX("l.lotId", "=", lotId);
boolean sysAdmin = AuthUtil.hasRole(RoleConstant.SYSADMIN.name());
boolean branchUnionScope = !sysAdmin && AuthUtil.hasRole(RoleConstant.BRANCH_UNION_CHAIRMAN.name());
applyDataScope(cnd, sysAdmin, branchUnionScope);
String creatorJoin = branchUnionScope ? """
LEFT JOIN sys_unit lineUnit ON lineUnit.id = l.unitId
LEFT JOIN sys_user creatorUser ON creatorUser.id = COALESCE(NULLIF(l.creatorUserId, ''), l.createdBy)
LEFT JOIN sys_unit creatorUnit ON creatorUnit.id = creatorUser.unitId
""" : "";
Sql countSql = Sqls.create("""
SELECT COUNT(1)
FROM tour_line l
$creatorJoin
$condition
""");
countSql.setVar("creatorJoin", creatorJoin);
countSql.setCondition(cnd);
countSql.setCallback(Sqls.callback.integer());
tourLineService.dao().execute(countSql);
int count = countSql.getInt();
Sql listSql = Sqls.create("""
SELECT
l.*,
a.agencyName AS travelAgencyName,
lot.lotName AS lotName
FROM tour_line l
LEFT JOIN tour_travel_agency a ON a.id = l.travelAgencyId
LEFT JOIN tour_setting_lot lot ON lot.id = l.lotId
$creatorJoin
$condition
ORDER BY $orderColumn $orderBy, l.`year` DESC, l.lineCode ASC, l.createdAt DESC
""");
listSql.setVar("creatorJoin", creatorJoin);
listSql.setCondition(cnd);
listSql.setVar("orderColumn", getOrderColumn(pageForm.getPageOrderName()));
listSql.setVar("orderBy", "descending".equals(pageForm.getPageOrderBy()) ? "desc" : "asc");
listSql.setPager(tourLineService.dao().createPager(pageForm.getPageNumber(), pageForm.getPageSize()));
listSql.setCallback(Sqls.callback.maps());
tourLineService.dao().execute(listSql);
Pagination<NutMap> pagination = new Pagination<>(pageForm.getPageNumber(), pageForm.getPageSize(), count, listSql.getList(NutMap.class));
return Result.success(pagination);
}
@At
@SaCheckPermission("tour.route")
public Result detail(String id) {
if (StrUtil.isBlank(id)) {
return Result.error("参数错误");
}
TourLine line = tourLineService.fetch(id);
return line == null ? Result.error("线路不存在") : Result.success(line);
}
@At
@SaCheckPermission("tour.route")
public Result travelAgencyOptions(Integer year) {
Cnd cnd = Cnd.where(TourTravelAgency::getEnabled, "=", true);
cnd.andEX(TourTravelAgency::getYear, "=", year);
cnd.asc(TourTravelAgency::getAgencyCode).asc(TourTravelAgency::getAgencyName);
return Result.success(tourLineService.dao().query(TourTravelAgency.class, cnd));
}
@At
@SaCheckPermission("tour.route")
public Result lotOptions(Integer year) {
Cnd cnd = Cnd.NEW();
cnd.andEX("s.`year`", "=", year);
Sql sql = Sqls.create("""
SELECT lot.id, lot.lotName, lot.lotValue, lot.activityCost
FROM tour_setting_lot lot
INNER JOIN tour_setting s ON s.id = lot.settingId
$condition
ORDER BY lot.lotValue DESC, lot.lotName ASC
""");
sql.setCondition(cnd);
sql.setCallback(Sqls.callback.maps());
tourLineService.dao().execute(sql);
return Result.success(sql.getList(NutMap.class));
}
@At
@SaCheckPermission("tour.route")
public Result lineTypeOptions() {
List<Sys_dict> list = sysDictService.getSubListByCode("lineType");
if (list == null) {
return Result.success(Collections.emptyList());
}
return Result.success(list.stream().filter(item -> !item.isDisabled()).collect(Collectors.toList()));
}
@At
@Aop(TransAop.READ_COMMITTED)
@SaCheckPermission("tour.route")
@SLog(type = "tour", tag = "线路管理", msg = "保存线路信息")
public Result doSubmit(TourLine line) {
Result checkResult = check(line);
if (checkResult != null) {
return checkResult;
}
Cnd sameCodeCnd = Cnd.where(TourLine::getYear, "=", line.getYear())
.and(TourLine::getLineCode, "=", line.getLineCode());
if (StrUtil.isNotBlank(line.getId())) {
sameCodeCnd.and(TourLine::getId, "<>", line.getId());
}
if (tourLineService.count(sameCodeCnd) > 0) {
return Result.error("同年度下线路编号已存在");
}
if (line.getEnabled() == null) {
line.setEnabled(true);
}
if (line.getOpenFlag() == null) {
line.setOpenFlag(true);
}
if (line.getDirectFamilyUnitLine() == null) {
line.setDirectFamilyUnitLine(false);
}
if (StrUtil.isBlank(line.getId())) {
fillCreatorInfo(line, null);
tourLineService.insert(line);
} else {
fillCreatorInfo(line, tourLineService.fetch(line.getId()));
tourLineService.updateIgnoreNull(line);
}
return Result.success();
}
@At
@Aop(TransAop.READ_COMMITTED)
@SaCheckPermission("tour.route")
@SLog(type = "tour", tag = "线路管理", msg = "删除线路信息")
public Result doDelete(String id) {
if (StrUtil.isBlank(id)) {
return Result.error("参数错误");
}
tourLineService.delete(id);
return Result.success();
}
private Result check(TourLine line) {
if (line == null) {
return Result.error("参数错误");
}
if (line.getYear() == null) {
return Result.error("创建年度不能为空");
}
if (StrUtil.isBlank(line.getTravelAgencyId())) {
return Result.error("旅行社名称不能为空");
}
if (StrUtil.isBlank(line.getLineName())) {
return Result.error("线路名称不能为空");
}
if (StrUtil.isBlank(line.getLineCode())) {
return Result.error("线路编号不能为空");
}
if (StrUtil.isBlank(line.getLineType())) {
return Result.error("线路类型不能为空");
}
if (StrUtil.isBlank(line.getLotId())) {
return Result.error("时间标段不能为空");
}
if (StrUtil.isBlank(line.getMobileThumb())) {
return Result.error("移动端缩略图不能为空");
}
return null;
}
/**
* 创建人和所在单位由当前登录人生成,编辑时保留原创建信息。
*/
private void fillCreatorInfo(TourLine line, TourLine oldLine) {
if (oldLine != null) {
line.setCreatorUserId(defaultIfBlank(oldLine.getCreatorUserId(), SecurityUtil.getUserId()));
line.setCreatorName(defaultIfBlank(oldLine.getCreatorName(), SecurityUtil.getUserUsername()));
line.setUnitId(defaultIfBlank(oldLine.getUnitId(), SecurityUtil.getUnitId()));
line.setUnitName(defaultIfBlank(oldLine.getUnitName(), getCurrentUnitName()));
return;
}
line.setCreatorUserId(SecurityUtil.getUserId());
line.setCreatorName(SecurityUtil.getUserUsername());
line.setUnitId(SecurityUtil.getUnitId());
line.setUnitName(getCurrentUnitName());
}
private String getCurrentUnitName() {
View_user user = tourLineService.dao().fetch(View_user.class, Cnd.where("id", "=", SecurityUtil.getUserId()));
return user == null ? "" : defaultIfBlank(user.getUnitName(), "");
}
private String defaultIfBlank(String value, String defaultValue) {
return StrUtil.isBlank(value) ? defaultValue : value;
}
/**
* 线路数据范围:
* SYSADMIN 可查看全部;分工会主席查看本工会创建的线路;其它角色保持原逻辑,仅查看自己创建的线路。
*/
private void applyDataScope(Cnd cnd, boolean sysAdmin, boolean branchUnionScope) {
if (sysAdmin) {
return;
}
if (branchUnionScope) {
SqlExpressionGroup unionGroup = new SqlExpressionGroup();
unionGroup.or("lineUnit.unionId", "=", SecurityUtil.getUnionId());
unionGroup.or("creatorUnit.unionId", "=", SecurityUtil.getUnionId());
cnd.and(unionGroup);
return;
}
SqlExpressionGroup creatorGroup = new SqlExpressionGroup();
creatorGroup.or("l.creatorUserId", "=", SecurityUtil.getUserId());
creatorGroup.or("l.createdBy", "=", SecurityUtil.getUserId());
cnd.and(creatorGroup);
}
private String getOrderColumn(String orderName) {
if ("year".equals(orderName)) {
return "l.`year`";
}
if ("lineCode".equals(orderName)) {
return "l.lineCode";
}
if ("lotName".equals(orderName)) {
return "lot.lotName";
}
if ("lineType".equals(orderName)) {
return "l.lineType";
}
if ("enabled".equals(orderName)) {
return "l.enabled";
}
return "l.`year`";
}
}
@@ -0,0 +1,441 @@
package com.budwk.app.zhgh.dayofficework.tour.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
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.param.PageForm;
import com.budwk.app.base.result.Result;
import com.budwk.app.sys.models.Sys_dict;
import com.budwk.app.sys.models.Sys_union;
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.tour.models.TourLedger;
import com.budwk.app.zhgh.dayofficework.tour.models.TourMatter;
import com.budwk.app.zhgh.dayofficework.tour.models.TourSetting;
import com.budwk.app.zhgh.dayofficework.tour.service.TourMatterService;
import org.nutz.aop.interceptor.ioc.TransAop;
import org.nutz.dao.Cnd;
import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Sql;
import org.nutz.dao.util.cri.SqlExpressionGroup;
import org.nutz.ioc.aop.Aop;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.util.NutMap;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
@IocBean
@Ok("json:full")
@At("/platform/tour/matter")
public class TourMatterController {
private static final String MOBILE_PATTERN = "^1[3-9]\\d{9}$";
@Inject
private TourMatterService tourMatterService;
@Inject
private SysDictService sysDictService;
@At("")
@Ok("beetl:/platform/zhgh/dayofficework/Tour/matter/index.html")
@SaCheckPermission("tour.matter")
public void index() {
}
@At
@SaCheckPermission("tour.matter")
public Result pageData(PageForm pageForm, Integer year, String matterName, String unionId, String organizationType) {
Cnd cnd = Cnd.NEW();
cnd.and("m.delFlag", "=", false);
cnd.andEX("m.`year`", "=", year);
cnd.and(Cnd.likeEX("m.matterName", matterName));
cnd.andEX("m.unionId", "=", unionId);
cnd.andEX("m.organizationType", "=", organizationType);
applyMatterDataScope(cnd);
Sql countSql = Sqls.create("""
SELECT COUNT(1)
FROM tour_matter m
$condition
""");
countSql.setCondition(cnd);
countSql.setCallback(Sqls.callback.integer());
tourMatterService.dao().execute(countSql);
int count = countSql.getInt();
Sql listSql = Sqls.create("""
SELECT
m.*,
s.configName AS settingName,
u.name AS unionName,
d.name AS organizationTypeName,
l.lineName AS lineName,
l.lineType AS lineType,
l.directFamilyUnitLine AS directFamilyUnitLine
FROM tour_matter m
LEFT JOIN tour_setting s ON s.id = m.settingId
LEFT JOIN sys_union u ON u.id = m.unionId
LEFT JOIN tour_line l ON l.id = m.lineId
LEFT JOIN sys_dict d ON d.`code` = m.organizationType
AND d.parentId = (SELECT id FROM sys_dict WHERE `code` = 'organizationType' LIMIT 1)
$condition
ORDER BY $orderColumn $orderBy, m.`year` DESC, m.createdAt DESC
""");
listSql.setCondition(cnd);
listSql.setVar("orderColumn", getOrderColumn(pageForm.getPageOrderName()));
listSql.setVar("orderBy", "descending".equals(pageForm.getPageOrderBy()) ? "desc" : "asc");
listSql.setPager(tourMatterService.dao().createPager(pageForm.getPageNumber(), pageForm.getPageSize()));
listSql.setCallback(Sqls.callback.maps());
tourMatterService.dao().execute(listSql);
Pagination<NutMap> pagination = new Pagination<>(pageForm.getPageNumber(), pageForm.getPageSize(), count, listSql.getList(NutMap.class));
return Result.success(pagination);
}
@At
@SaCheckPermission("tour.matter")
public Result detail(String id) {
if (StrUtil.isBlank(id)) {
return Result.error("参数错误");
}
TourMatter matter = tourMatterService.fetch(id);
return matter == null ? Result.error("事项不存在") : Result.success(matter);
}
@At
@SaCheckPermission("tour.matter")
public Result settingOptions(Integer year) {
Cnd cnd = Cnd.where(TourSetting::getEnabled, "=", true);
cnd.andEX(TourSetting::getYear, "=", year);
cnd.desc(TourSetting::getCreatedAt);
cnd.desc(TourSetting::getUpdatedAt);
cnd.asc(TourSetting::getConfigName);
return Result.success(tourMatterService.dao().query(TourSetting.class, cnd));
}
@At
@SaCheckPermission("tour.matter")
public Result unionOptions() {
Cnd cnd = Cnd.NEW();
cnd.asc("unionCode");
cnd.asc("name");
return Result.success(tourMatterService.dao().query(Sys_union.class, cnd));
}
@At
@SaCheckPermission("tour.matter")
public Result lineOptions(Integer year) {
Cnd cnd = Cnd.NEW();
cnd.and("l.delFlag", "=", false);
cnd.and("l.enabled", "=", true);
cnd.and("l.openFlag", "=", true);
Sql sql = Sqls.create("""
SELECT
l.*,
lot.lotName AS lotName,
lot.lotValue AS lotValue,
lot.lotValue AS lotDays,
lot.activityCost AS activityCost
FROM tour_line l
LEFT JOIN tour_setting_lot lot ON lot.id = l.lotId
$condition
ORDER BY l.lineCode ASC, l.lineName ASC
""");
sql.setCondition(cnd);
sql.setCallback(Sqls.callback.maps());
tourMatterService.dao().execute(sql);
return Result.success(sql.getList(NutMap.class));
}
@At
@SaCheckPermission("tour.matter")
public Result organizationTypeOptions() {
List<Sys_dict> list = sysDictService.getSubListByCode("organizationType");
if (list == null) {
return Result.success(Collections.emptyList());
}
return Result.success(list.stream().filter(item -> !item.isDisabled()).collect(Collectors.toList()));
}
@At
@Aop(TransAop.READ_COMMITTED)
@SaCheckPermission("tour.matter")
@SLog(type = "tour", tag = "疗休养事项", msg = "保存疗休养事项")
public Result doSubmit(TourMatter matter) {
Result checkResult = check(matter);
if (checkResult != null) {
return checkResult;
}
Cnd sameNameCnd = Cnd.where(TourMatter::getYear, "=", matter.getYear())
.and(TourMatter::getMatterName, "=", matter.getMatterName());
if (StrUtil.isNotBlank(matter.getId())) {
sameNameCnd.and(TourMatter::getId, "<>", matter.getId());
}
if (tourMatterService.count(sameNameCnd) > 0) {
return Result.error("同年度下事项名称已存在");
}
if (matter.getEnabled() == null) {
matter.setEnabled(true);
}
fillCreatorAndUnion(matter);
if (StrUtil.isBlank(matter.getId())) {
tourMatterService.insert(matter);
} else {
tourMatterService.updateIgnoreNull(matter);
}
return Result.success();
}
@At
@Aop(TransAop.READ_COMMITTED)
@SaCheckPermission("tour.matter")
@SLog(type = "tour", tag = "疗休养事项", msg = "删除疗休养事项")
public Result doDelete(String id) {
if (StrUtil.isBlank(id)) {
return Result.error("参数错误");
}
TourMatter matter = tourMatterService.fetch(id);
if (matter == null) {
return Result.error("事项不存在");
}
long signupCount = countSignup(id);
if (signupCount > 0) {
return Result.error("当前事项对应线路已有人员报名,不能删除");
}
tourMatterService.delete(id);
return Result.success();
}
@At
@SaCheckPermission("tour.matter")
public Result deleteInfo(String id) {
if (StrUtil.isBlank(id)) {
return Result.error("参数错误");
}
TourMatter matter = tourMatterService.fetch(id);
if (matter == null) {
return Result.error("事项不存在");
}
long signupCount = countSignup(id);
return Result.success(NutMap.NEW()
.addv("signupCount", signupCount)
.addv("canDelete", signupCount <= 0)
.addv("message", signupCount > 0 ? "当前事项已有人员报名,不能删除" : "当前事项暂无人员报名,可删除"));
}
@At
@SaCheckPermission("tour.matter")
public Result lineConfig(String matterId) {
if (StrUtil.isBlank(matterId)) {
return Result.error("参数错误");
}
TourMatter matter = tourMatterService.fetch(matterId);
return matter == null ? Result.error("事项不存在") : Result.success(matter);
}
@At
@SaCheckPermission("tour.matter")
public Result settingPeople(String matterId) {
if (StrUtil.isBlank(matterId)) {
return Result.error("参数错误");
}
TourMatter matter = tourMatterService.fetch(matterId);
if (matter == null || StrUtil.isBlank(matter.getSettingId())) {
return Result.success(NutMap.NEW());
}
TourSetting setting = tourMatterService.dao().fetch(TourSetting.class, matter.getSettingId());
if (setting == null) {
return Result.success(NutMap.NEW());
}
return Result.success(NutMap.NEW()
.addv("minGroupPeople", setting.getMinGroupPeople())
.addv("maxGroupPeople", setting.getMaxGroupPeople()));
}
@At
@Aop(TransAop.READ_COMMITTED)
@SaCheckPermission("tour.matter")
@SLog(type = "tour", tag = "疗休养事项", msg = "保存事项线路配置")
public Result lineConfigDoSubmit(TourMatter matter) {
Result checkResult = checkLineConfig(matter);
if (checkResult != null) {
return checkResult;
}
TourMatter oldMatter = tourMatterService.fetch(matter.getId());
if (oldMatter == null) {
return Result.error("事项不存在");
}
oldMatter.setLineId(matter.getLineId());
oldMatter.setSignupStartTime(matter.getSignupStartTime());
oldMatter.setSignupEndTime(matter.getSignupEndTime());
oldMatter.setTravelStartTime(matter.getTravelStartTime());
oldMatter.setTravelEndTime(matter.getTravelEndTime());
oldMatter.setContactName(matter.getContactName());
oldMatter.setContactPhone(matter.getContactPhone());
oldMatter.setMinGroupPeople(matter.getMinGroupPeople());
oldMatter.setMaxGroupPeople(matter.getMaxGroupPeople());
oldMatter.setEstimatedCost(matter.getEstimatedCost());
tourMatterService.update(oldMatter);
return Result.success();
}
private Result check(TourMatter matter) {
if (matter == null) {
return Result.error("参数错误");
}
if (matter.getYear() == null) {
return Result.error("年度不能为空");
}
if (StrUtil.isBlank(matter.getMatterName())) {
return Result.error("事项名称不能为空");
}
if (StrUtil.isBlank(matter.getSettingId())) {
return Result.error("疗休养配置不能为空");
}
if (StrUtil.isBlank(matter.getOrganizationType())) {
return Result.error("组织形式不能为空");
}
if (!"schoolUnion".equals(matter.getOrganizationType()) && StrUtil.isBlank(matter.getUnionId())) {
return Result.error("所属工会不能为空");
}
return null;
}
private Result checkLineConfig(TourMatter matter) {
if (matter == null) {
return Result.error("参数错误");
}
if (StrUtil.isBlank(matter.getId())) {
return Result.error("事项不能为空");
}
if (StrUtil.isBlank(matter.getLineId())) {
return Result.error("线路不能为空");
}
if (StrUtil.isBlank(matter.getSignupStartTime()) || StrUtil.isBlank(matter.getSignupEndTime())
|| StrUtil.isBlank(matter.getTravelStartTime()) || StrUtil.isBlank(matter.getTravelEndTime())) {
return Result.error("时间不能为空");
}
if (matter.getSignupStartTime().compareTo(matter.getSignupEndTime()) >= 0) {
return Result.error("报名开始时间必须小于报名结束时间");
}
if (matter.getTravelStartTime().compareTo(matter.getTravelEndTime()) > 0) {
return Result.error("出行开始时间不能晚于出行结束时间");
}
if (matter.getSignupEndTime().compareTo(matter.getTravelStartTime()) >= 0) {
return Result.error("报名结束时间必须小于出行开始时间");
}
if (StrUtil.isBlank(matter.getContactName())) {
return Result.error("联系人不能为空");
}
if (StrUtil.isBlank(matter.getContactPhone()) || !matter.getContactPhone().matches(MOBILE_PATTERN)) {
return Result.error("联系方式格式不正确");
}
if (matter.getMinGroupPeople() == null || matter.getMinGroupPeople() <= 0) {
return Result.error("最少成团人数必须大于0");
}
if (matter.getMaxGroupPeople() == null || matter.getMaxGroupPeople() <= 0) {
return Result.error("最多成团人数必须大于0");
}
if (matter.getMinGroupPeople() > matter.getMaxGroupPeople()) {
return Result.error("最少成团人数不能大于最多成团人数");
}
if (matter.getEstimatedCost() == null || matter.getEstimatedCost().signum() < 0) {
return Result.error("预计费用不能小于0");
}
return null;
}
private void fillCreatorAndUnion(TourMatter matter) {
if (StrUtil.isBlank(matter.getId())) {
matter.setCreatorUserId(SecurityUtil.getUserId());
matter.setCreatorName(SecurityUtil.getUserUsername());
} else {
TourMatter oldMatter = tourMatterService.fetch(matter.getId());
if (oldMatter != null) {
matter.setCreatorUserId(oldMatter.getCreatorUserId());
matter.setCreatorName(oldMatter.getCreatorName());
}
}
if ("schoolUnion".equals(matter.getOrganizationType())) {
matter.setUnionId("");
}
}
private long countSignup(String matterId) {
return tourMatterService.dao().count(TourLedger.class, Cnd.where(TourLedger::getDelFlag, "=", false)
.and(TourLedger::getMatterId, "=", matterId));
}
/**
* 事项数据范围:SYSADMIN 查看全部;分工会主席查看本工会事项;其它角色仅查看自己创建的事项。
*/
private void applyMatterDataScope(Cnd cnd) {
if (AuthUtil.hasRole(RoleConstant.SYSADMIN.name())) {
return;
}
if (AuthUtil.hasRole(RoleConstant.BRANCH_UNION_CHAIRMAN.name())) {
cnd.and("m.unionId", "=", SecurityUtil.getUnionId());
return;
}
SqlExpressionGroup creatorGroup = new SqlExpressionGroup();
creatorGroup.or("m.creatorUserId", "=", SecurityUtil.getUserId());
creatorGroup.or("m.createdBy", "=", SecurityUtil.getUserId());
cnd.and(creatorGroup);
}
/**
* 事项批次选择线路时沿用线路管理的数据范围,避免在事项页面选到权限外线路。
*/
private void applyLineDataScope(Cnd cnd, boolean sysAdmin, boolean branchUnionScope) {
if (sysAdmin) {
return;
}
if (branchUnionScope) {
SqlExpressionGroup unionGroup = new SqlExpressionGroup();
unionGroup.or("lineUnit.unionId", "=", SecurityUtil.getUnionId());
unionGroup.or("creatorUnit.unionId", "=", SecurityUtil.getUnionId());
cnd.and(unionGroup);
return;
}
SqlExpressionGroup creatorGroup = new SqlExpressionGroup();
creatorGroup.or("l.creatorUserId", "=", SecurityUtil.getUserId());
creatorGroup.or("l.createdBy", "=", SecurityUtil.getUserId());
cnd.and(creatorGroup);
}
private String getOrderColumn(String orderName) {
if ("year".equals(orderName)) {
return "m.`year`";
}
if ("matterName".equals(orderName)) {
return "m.matterName";
}
if ("unionName".equals(orderName)) {
return "u.name";
}
if ("lineName".equals(orderName)) {
return "l.lineName";
}
if ("settingName".equals(orderName)) {
return "s.configName";
}
if ("organizationTypeName".equals(orderName)) {
return "d.location";
}
if ("enabled".equals(orderName)) {
return "m.enabled";
}
return "m.`year`";
}
}
@@ -0,0 +1,13 @@
package com.budwk.app.zhgh.dayofficework.tour.controller;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.mvc.annotation.At;
/**
* 分阶段建设的菜单占位入口。
*/
@IocBean
@At("/platform/tour")
public class TourPlaceholderController {
}
@@ -0,0 +1,580 @@
package com.budwk.app.zhgh.dayofficework.tour.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.util.StrUtil;
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.flow.enums.ProcessTaskStateEnum;
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
import com.budwk.app.zhgh.dayofficework.tour.models.TourLedger;
import com.budwk.app.zhgh.dayofficework.tour.models.TourLedgerDirectRelative;
import com.budwk.app.zhgh.dayofficework.tour.models.TourLedgerFamily;
import com.budwk.app.zhgh.dayofficework.tour.service.TourLedgerDirectRelativeService;
import com.budwk.app.zhgh.dayofficework.tour.service.TourLedgerFamilyService;
import com.budwk.app.zhgh.dayofficework.tour.service.TourLedgerService;
import org.nutz.dao.Cnd;
import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Sql;
import org.nutz.dao.util.cri.SqlExpressionGroup;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.util.NutMap;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
import java.time.LocalDate;
import java.util.List;
@IocBean
@Ok("json:full")
@At("/platform/tour/schoolUnionApproval")
public class TourSchoolUnionApprovalController {
private static final String WORKFLOW_KEY = "LXYBZXQSXL";
private static final String TASK_DISPLAY_NAME = "校工会审核";
@Inject
private TourLedgerService tourLedgerService;
@Inject
private TourLedgerFamilyService tourLedgerFamilyService;
@Inject
private TourLedgerDirectRelativeService tourLedgerDirectRelativeService;
@At("")
@Ok("beetl:/platform/zhgh/dayofficework/Tour/schoolUnionApproval/index.html")
@SaCheckPermission("tour.schoolUnionApproval")
public void index() {
}
@At("/h5")
@Ok("beetl:/platform/zhghh5/dayofficework/tour/schoolUnionApproval/index.html")
@SaCheckPermission("tour.schoolUnionApproval")
public void h5() {
}
@At
@SaCheckPermission("tour.schoolUnionApproval")
public Result pageData(PageForm pageForm, Boolean audit, Integer startYear, Integer endYear, String keyword, String unionId, String lineId, String travelPeriod, String lineType) {
Cnd cnd = buildAuditCnd(audit, startYear, endYear, keyword, unionId, lineId, travelPeriod, lineType);
Sql countSql = Sqls.create("""
SELECT COUNT(DISTINCT task.id)
FROM wf_process_task task
LEFT JOIN wf_process_instance ins ON ins.id = task.processInstanceId
LEFT JOIN wf_process_define def ON def.id = ins.processDefineId
LEFT JOIN wf_process_task_actor ta ON ta.processTaskId = task.id
INNER JOIN tour_ledger t ON t.id = ins.businessNo
LEFT JOIN tour_matter m ON m.id = t.matterId AND m.delFlag = 0
LEFT JOIN tour_line l ON l.id = t.lineId AND l.delFlag = 0
$condition
""");
countSql.setCondition(cnd);
countSql.setCallback(Sqls.callback.integer());
tourLedgerService.dao().execute(countSql);
int count = countSql.getInt();
Sql listSql = Sqls.create("""
SELECT
t.*,
ins.id AS instanceId,
ins.businessNo,
ins.state AS instanceState,
ins.variable AS instanceVariable,
ins.processDefineId AS instanceProcessDefineId,
task.id AS taskId,
task.taskName AS taskKey,
task.displayName AS taskName,
task.taskType,
task.performType AS taskPerformType,
task.taskState,
task.finishTime,
task.taskParentId,
task.variable AS taskVariable,
IFNULL(GROUP_CONCAT(DISTINCT nt.displayName), '结束') AS curTaskName,
CASE
WHEN m.id IS NULL THEN COALESCE(NULLIF(l.lineName, ''), t.lineName)
ELSE CONCAT(
COALESCE(NULLIF(l.lineName, ''), t.lineName),
'',
CASE
WHEN IFNULL(m.unionId, '') = '' THEN '校工会'
ELSE IFNULL(mu.name, '')
END,
''
)
END AS currentLineName,
CASE
WHEN IFNULL(m.travelStartTime, '') <> '' AND IFNULL(m.travelEndTime, '') <> ''
THEN CONCAT(m.travelStartTime, ' 至 ', m.travelEndTime)
ELSE tp.travelPeriod
END AS travelPeriod,
IFNULL(f.familyCount, 0) AS familyCount
FROM wf_process_task task
LEFT JOIN wf_process_instance ins ON ins.id = task.processInstanceId
LEFT JOIN wf_process_define def ON def.id = ins.processDefineId
LEFT JOIN wf_process_task_actor ta ON ta.processTaskId = task.id
INNER JOIN tour_ledger t ON t.id = ins.businessNo
LEFT JOIN wf_process_task nt ON nt.processInstanceId = ins.id AND nt.taskState = 10
LEFT JOIN (
SELECT ledgerId, COUNT(1) AS familyCount
FROM tour_ledger_family
WHERE delFlag = 0
GROUP BY ledgerId
) f ON f.ledgerId = t.id
LEFT JOIN tour_matter m ON m.id = t.matterId AND m.delFlag = 0
LEFT JOIN tour_line l ON l.id = t.lineId AND l.delFlag = 0
LEFT JOIN sys_union mu ON mu.id = m.unionId
LEFT JOIN (
SELECT
`year`,
lineId,
GROUP_CONCAT(
DISTINCT CASE
WHEN IFNULL(travelStartTime, '') <> '' AND IFNULL(travelEndTime, '') <> ''
THEN CONCAT(travelStartTime, ' 至 ', travelEndTime)
ELSE NULL
END
ORDER BY travelStartTime ASC
SEPARATOR ''
) AS travelPeriod
FROM tour_matter
WHERE delFlag = 0
AND lineId IS NOT NULL
AND lineId <> ''
GROUP BY `year`, lineId
) tp ON (t.matterId IS NULL OR t.matterId = '') AND tp.`year` = t.`year` AND tp.lineId = t.lineId
$condition
GROUP BY task.id
ORDER BY $orderColumn $orderBy, task.createdAt DESC, t.signupTime DESC, t.createdAt DESC
""");
listSql.setCondition(cnd);
listSql.setVar("orderColumn", getOrderColumn(pageForm.getPageOrderName()));
listSql.setVar("orderBy", "ascending".equals(pageForm.getPageOrderBy()) ? "asc" : "desc");
listSql.setPager(tourLedgerService.dao().createPager(pageForm.getPageNumber(), pageForm.getPageSize()));
listSql.setCallback(Sqls.callback.maps());
tourLedgerService.dao().execute(listSql);
List<NutMap> list = listSql.getList(NutMap.class);
list.forEach(item -> item.put("lineName", item.getString("currentLineName")));
Pagination<NutMap> pagination = new Pagination<>(pageForm.getPageNumber(), pageForm.getPageSize(), count, list);
return Result.success(pagination);
}
@At
@SaCheckPermission("tour.schoolUnionApproval")
public Result detail(String id) {
TourLedger ledger = fetchAuditLedger(id);
if (ledger == null) {
return Result.error("报名记录不存在或无权查看");
}
ledger.setLineName(getCurrentLineName(ledger));
Cnd familyCnd = Cnd.where(TourLedgerFamily::getLedgerId, "=", ledger.getId())
.and(TourLedgerFamily::getDelFlag, "=", false);
familyCnd.asc(TourLedgerFamily::getCreatedAt);
TourLedgerDirectRelative directRelative = tourLedgerDirectRelativeService.fetch(
Cnd.where(TourLedgerDirectRelative::getLedgerId, "=", ledger.getId())
.and(TourLedgerDirectRelative::getDelFlag, "=", false));
NutMap signupConfig = getSignupConfig(ledger);
return Result.success(NutMap.NEW()
.addv("ledger", ledger)
.addv("travelPeriod", getTravelPeriod(ledger))
.addv("families", tourLedgerFamilyService.query(familyCnd))
.addv("directRelative", directRelative)
.addv("allowFamily", signupConfig.getBoolean("allowFamily", false))
.addv("fillBedInfo", signupConfig.getBoolean("fillBedInfo", true))
.addv("directFamilyUnitLine", signupConfig.getBoolean("directFamilyUnitLine", false)));
}
@At
@SaCheckPermission("tour.schoolUnionApproval")
public Result unionOptions(Boolean audit, Integer startYear, Integer endYear, String keyword, String lineId, String travelPeriod, String lineType) {
Cnd cnd = buildAuditCnd(audit, startYear, endYear, keyword, null, lineId, travelPeriod, lineType);
cnd.and("t.unionId", "IS NOT", null);
cnd.and("t.unionId", "<>", "");
Sql sql = Sqls.create("""
SELECT DISTINCT
t.unionId AS id,
COALESCE(NULLIF(u.name, ''), t.unionName) AS name,
u.unionCode
FROM wf_process_task task
LEFT JOIN wf_process_instance ins ON ins.id = task.processInstanceId
LEFT JOIN wf_process_define def ON def.id = ins.processDefineId
LEFT JOIN wf_process_task_actor ta ON ta.processTaskId = task.id
INNER JOIN tour_ledger t ON t.id = ins.businessNo
LEFT JOIN tour_matter m ON m.id = t.matterId AND m.delFlag = 0
LEFT JOIN tour_line l ON l.id = t.lineId AND l.delFlag = 0
LEFT JOIN sys_union u ON u.id = t.unionId
$condition
ORDER BY u.unionCode ASC, name ASC
""");
sql.setCondition(cnd);
sql.setCallback(Sqls.callback.maps());
tourLedgerService.dao().execute(sql);
return Result.success(sql.getList(NutMap.class));
}
@At
@SaCheckPermission("tour.schoolUnionApproval")
public Result lineOptions(Boolean audit, Integer startYear, Integer endYear, String travelPeriod, String lineType, String unionId, String keyword) {
Cnd cnd = buildAuditCnd(audit, startYear, endYear, keyword, unionId, null, travelPeriod, lineType);
cnd.and("t.lineName", "IS NOT", null);
cnd.and("t.lineName", "<>", "");
Sql sql = Sqls.create("""
SELECT DISTINCT
CASE
WHEN IFNULL(t.lineId, '') <> '' THEN CONCAT(t.lineId, '|', IFNULL(m.unionId, ''))
ELSE CONCAT('legacy:', t.lineName, '|', IFNULL(m.unionId, ''))
END AS lineId,
CASE
WHEN m.id IS NULL THEN COALESCE(NULLIF(l.lineName, ''), t.lineName)
ELSE CONCAT(
COALESCE(NULLIF(l.lineName, ''), t.lineName),
'',
CASE
WHEN IFNULL(m.unionId, '') = '' THEN '校工会'
ELSE IFNULL(mu.name, '')
END,
''
)
END AS lineName
FROM wf_process_task task
LEFT JOIN wf_process_instance ins ON ins.id = task.processInstanceId
LEFT JOIN wf_process_define def ON def.id = ins.processDefineId
LEFT JOIN wf_process_task_actor ta ON ta.processTaskId = task.id
INNER JOIN tour_ledger t ON t.id = ins.businessNo
LEFT JOIN tour_matter m ON m.id = t.matterId AND m.delFlag = 0
LEFT JOIN tour_line l ON l.id = t.lineId AND l.delFlag = 0
LEFT JOIN sys_union mu ON mu.id = m.unionId
$condition
ORDER BY lineName ASC
""");
sql.setCondition(cnd);
sql.setCallback(Sqls.callback.maps());
tourLedgerService.dao().execute(sql);
return Result.success(sql.getList(NutMap.class));
}
@At
@SaCheckPermission("tour.schoolUnionApproval")
public Result travelPeriodOptions(Boolean audit, Integer startYear, Integer endYear, String lineId, String lineType, String unionId, String keyword) {
Cnd cnd = buildAuditCnd(audit, startYear, endYear, keyword, unionId, lineId, null, lineType);
cnd.and("m.travelStartTime", "IS NOT", null);
cnd.and("m.travelStartTime", "<>", "");
cnd.and("m.travelEndTime", "IS NOT", null);
cnd.and("m.travelEndTime", "<>", "");
Sql sql = Sqls.create("""
SELECT DISTINCT CONCAT(m.travelStartTime, ' 至 ', m.travelEndTime) AS travelPeriod
FROM wf_process_task task
LEFT JOIN wf_process_instance ins ON ins.id = task.processInstanceId
LEFT JOIN wf_process_define def ON def.id = ins.processDefineId
LEFT JOIN wf_process_task_actor ta ON ta.processTaskId = task.id
INNER JOIN tour_ledger t ON t.id = ins.businessNo
LEFT JOIN tour_matter m ON m.id = t.matterId AND m.delFlag = 0
LEFT JOIN tour_line l ON l.id = t.lineId AND l.delFlag = 0
$condition
ORDER BY travelPeriod ASC
""");
sql.setCondition(cnd);
sql.setCallback(Sqls.callback.maps());
tourLedgerService.dao().execute(sql);
return Result.success(sql.getList(NutMap.class));
}
@At
@SaCheckPermission("tour.schoolUnionApproval")
public Result lineTypeOptions(Boolean audit, Integer startYear, Integer endYear, String lineId, String travelPeriod, String unionId, String keyword) {
Cnd cnd = buildAuditCnd(audit, startYear, endYear, keyword, unionId, lineId, travelPeriod, null);
cnd.and("t.lineType", "IS NOT", null);
cnd.and("t.lineType", "<>", "");
Sql sql = Sqls.create("""
SELECT DISTINCT t.lineType
FROM wf_process_task task
LEFT JOIN wf_process_instance ins ON ins.id = task.processInstanceId
LEFT JOIN wf_process_define def ON def.id = ins.processDefineId
LEFT JOIN wf_process_task_actor ta ON ta.processTaskId = task.id
INNER JOIN tour_ledger t ON t.id = ins.businessNo
LEFT JOIN tour_matter m ON m.id = t.matterId AND m.delFlag = 0
LEFT JOIN tour_line l ON l.id = t.lineId AND l.delFlag = 0
$condition
ORDER BY t.lineType ASC
""");
sql.setCondition(cnd);
sql.setCallback(Sqls.callback.maps());
tourLedgerService.dao().execute(sql);
return Result.success(sql.getList(NutMap.class));
}
private Cnd buildAuditCnd(Boolean audit, Integer startYear, Integer endYear, String keyword, String unionId, String lineId, String travelPeriod, String lineType) {
Cnd cnd = Cnd.NEW();
cnd.and("t.delFlag", "=", false);
cnd.and("def.name", "=", WORKFLOW_KEY);
cnd.and("task.displayName", "=", TASK_DISPLAY_NAME);
cnd.and("ta.actorId", "in", List.of(SecurityUtil.getUserId()));
appendAuditStateFilter(cnd, audit);
cnd.andEX("t.`year`", ">=", startYear);
cnd.andEX("t.`year`", "<=", endYear == null ? LocalDate.now().getYear() : endYear);
cnd.andEX("t.unionId", "=", unionId);
cnd.andEX("t.lineType", "=", lineType);
appendLineFilter(cnd, lineId);
appendTravelPeriodFilter(cnd, travelPeriod);
appendKeywordFilter(cnd, keyword);
return cnd;
}
private void appendAuditStateFilter(Cnd cnd, Boolean audit) {
if (Boolean.TRUE.equals(audit)) {
cnd.and("task.taskState", "in", List.of(
ProcessTaskStateEnum.FINISHED.getCode(),
ProcessTaskStateEnum.WITHDRAW.getCode(),
ProcessTaskStateEnum.INTERRUPT.getCode()));
return;
}
cnd.and("task.taskState", "=", ProcessTaskStateEnum.DOING.getCode());
}
private void appendKeywordFilter(Cnd cnd, String keyword) {
if (StrUtil.isBlank(keyword)) {
return;
}
SqlExpressionGroup group = new SqlExpressionGroup();
group.orLike("t.userName", keyword.trim());
group.orLike("t.jobNo", keyword.trim());
group.orLike("t.lineName", keyword.trim());
group.orLike("l.lineName", keyword.trim());
cnd.and(group);
}
private void appendTravelPeriodFilter(Cnd cnd, String travelPeriod) {
if (StrUtil.isBlank(travelPeriod)) {
return;
}
cnd.and("CONCAT(m.travelStartTime, ' 至 ', m.travelEndTime)", "=", travelPeriod);
}
private void appendLineFilter(Cnd cnd, String lineId) {
if (StrUtil.isBlank(lineId)) {
return;
}
LineFilter lineFilter = parseLineFilter(lineId);
if (lineFilter.getLineId().startsWith("legacy:")) {
SqlExpressionGroup legacyGroup = new SqlExpressionGroup();
legacyGroup.or("t.lineId", "IS", null);
legacyGroup.or("t.lineId", "=", "");
cnd.and(legacyGroup);
cnd.and("t.lineName", "=", lineFilter.getLineId().substring("legacy:".length()));
} else {
cnd.and("t.lineId", "=", lineFilter.getLineId());
}
appendMatterUnionFilter(cnd, lineFilter.getUnionId());
}
private LineFilter parseLineFilter(String value) {
int delimiterIndex = value.indexOf("|");
if (delimiterIndex < 0) {
return new LineFilter(value, null);
}
return new LineFilter(value.substring(0, delimiterIndex), value.substring(delimiterIndex + 1));
}
private void appendMatterUnionFilter(Cnd cnd, String unionId) {
if (unionId == null) {
return;
}
if (StrUtil.isBlank(unionId)) {
SqlExpressionGroup group = new SqlExpressionGroup();
group.or("m.unionId", "IS", null);
group.or("m.unionId", "=", "");
cnd.and(group);
return;
}
cnd.and("m.unionId", "=", unionId);
}
private String getOrderColumn(String orderName) {
if ("jobNo".equals(orderName)) {
return "t.jobNo";
}
if ("userName".equals(orderName)) {
return "t.userName";
}
if ("lineName".equals(orderName)) {
return """
CASE
WHEN m.id IS NULL THEN COALESCE(NULLIF(l.lineName, ''), t.lineName)
ELSE CONCAT(
COALESCE(NULLIF(l.lineName, ''), t.lineName),
'',
CASE
WHEN IFNULL(m.unionId, '') = '' THEN '校工会'
ELSE IFNULL(mu.name, '')
END,
''
)
END
""";
}
if ("travelPeriod".equals(orderName)) {
return "COALESCE(m.travelStartTime, tp.travelPeriod)";
}
if ("lineType".equals(orderName)) {
return "t.lineType";
}
if ("signupTime".equals(orderName)) {
return "t.signupTime";
}
if ("familyCount".equals(orderName)) {
return "familyCount";
}
if ("instanceState".equals(orderName)) {
return "ins.state";
}
return "task.createdAt";
}
private TourLedger fetchAuditLedger(String id) {
if (StrUtil.isBlank(id)) {
return null;
}
Sql sql = Sqls.create("""
SELECT COUNT(1)
FROM wf_process_task task
LEFT JOIN wf_process_instance ins ON ins.id = task.processInstanceId
LEFT JOIN wf_process_define def ON def.id = ins.processDefineId
LEFT JOIN wf_process_task_actor ta ON ta.processTaskId = task.id
INNER JOIN tour_ledger t ON t.id = ins.businessNo
WHERE t.delFlag = 0
AND t.id = @id
AND def.name = @workflowKey
AND task.displayName = @taskDisplayName
AND ta.actorId = @actorId
""");
sql.setParam("id", id);
sql.setParam("workflowKey", WORKFLOW_KEY);
sql.setParam("taskDisplayName", TASK_DISPLAY_NAME);
sql.setParam("actorId", SecurityUtil.getUserId());
sql.setCallback(Sqls.callback.integer());
tourLedgerService.dao().execute(sql);
return sql.getInt() > 0 ? tourLedgerService.fetch(id) : null;
}
private String getCurrentLineName(TourLedger ledger) {
if (ledger == null) {
return "";
}
Sql sql = Sqls.create("""
SELECT CASE
WHEN m.id IS NULL THEN COALESCE(NULLIF(l.lineName, ''), t.lineName)
ELSE CONCAT(
COALESCE(NULLIF(l.lineName, ''), t.lineName),
'',
CASE
WHEN IFNULL(m.unionId, '') = '' THEN '校工会'
ELSE IFNULL(mu.name, '')
END,
''
)
END AS lineName
FROM tour_ledger t
LEFT JOIN tour_matter m ON m.id = t.matterId AND m.delFlag = 0
LEFT JOIN tour_line l ON l.id = t.lineId AND l.delFlag = 0
LEFT JOIN sys_union mu ON mu.id = m.unionId
WHERE t.id = @ledgerId
""");
sql.setParam("ledgerId", ledger.getId());
sql.setCallback(Sqls.callback.str());
tourLedgerService.dao().execute(sql);
return StrUtil.blankToDefault(sql.getString(), ledger.getLineName());
}
private String getTravelPeriod(TourLedger ledger) {
if (ledger == null || ledger.getYear() == null || StrUtil.isBlank(ledger.getLineId())) {
return "";
}
if (StrUtil.isNotBlank(ledger.getMatterId())) {
Sql exactSql = Sqls.create("""
SELECT CASE
WHEN IFNULL(travelStartTime, '') <> '' AND IFNULL(travelEndTime, '') <> ''
THEN CONCAT(travelStartTime, ' 至 ', travelEndTime)
ELSE ''
END AS travelPeriod
FROM tour_matter
WHERE delFlag = 0
AND id = @matterId
""");
exactSql.setParam("matterId", ledger.getMatterId());
exactSql.setCallback(Sqls.callback.str());
tourLedgerService.dao().execute(exactSql);
return exactSql.getString();
}
Sql sql = Sqls.create("""
SELECT GROUP_CONCAT(
DISTINCT CASE
WHEN IFNULL(travelStartTime, '') <> '' AND IFNULL(travelEndTime, '') <> ''
THEN CONCAT(travelStartTime, ' 至 ', travelEndTime)
ELSE NULL
END
ORDER BY travelStartTime ASC
SEPARATOR ''
) AS travelPeriod
FROM tour_matter
WHERE delFlag = 0
AND `year` = @year
AND lineId = @lineId
""");
sql.setParam("year", ledger.getYear());
sql.setParam("lineId", ledger.getLineId());
sql.setCallback(Sqls.callback.str());
tourLedgerService.dao().execute(sql);
return sql.getString();
}
private NutMap getSignupConfig(TourLedger ledger) {
if (ledger == null || StrUtil.isBlank(ledger.getLineId())) {
return NutMap.NEW().addv("allowFamily", false).addv("fillBedInfo", true).addv("directFamilyUnitLine", false);
}
Sql sql = Sqls.create("""
SELECT
IFNULL(s.allowFamily, 0) AS allowFamily,
IFNULL(s.fillBedInfo, 1) AS fillBedInfo,
IFNULL(l.directFamilyUnitLine, 0) AS directFamilyUnitLine
FROM tour_ledger t
LEFT JOIN tour_matter m ON m.id = t.matterId AND m.delFlag = 0
LEFT JOIN tour_setting s ON s.id = m.settingId AND s.delFlag = 0
LEFT JOIN tour_line l ON l.id = t.lineId AND l.delFlag = 0
WHERE t.id = @ledgerId
""");
sql.setParam("ledgerId", ledger.getId());
sql.setCallback(Sqls.callback.map());
tourLedgerService.dao().execute(sql);
NutMap map = sql.getObject(NutMap.class);
if (map == null || map.isEmpty()) {
return NutMap.NEW().addv("allowFamily", false).addv("fillBedInfo", true).addv("directFamilyUnitLine", false);
}
return map;
}
private static class LineFilter {
private final String lineId;
private final String unionId;
private LineFilter(String lineId, String unionId) {
this.lineId = lineId;
this.unionId = unionId;
}
private String getLineId() {
return lineId;
}
private String getUnionId() {
return unionId;
}
}
}
@@ -0,0 +1,246 @@
package com.budwk.app.zhgh.dayofficework.tour.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.annotation.SLog;
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.zhgh.dayofficework.tour.models.TourSetting;
import com.budwk.app.zhgh.dayofficework.tour.models.TourSettingLot;
import com.budwk.app.zhgh.dayofficework.tour.service.TourSettingService;
import org.nutz.aop.interceptor.ioc.TransAop;
import org.nutz.dao.Cnd;
import org.nutz.ioc.aop.Aop;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.json.Json;
import org.nutz.lang.Lang;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
import org.nutz.mvc.annotation.Param;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
@IocBean
@Ok("json:full")
@At("/platform/tour/setting")
public class TourSettingController {
@Inject
private TourSettingService tourSettingService;
@At("")
@Ok("beetl:/platform/zhgh/dayofficework/Tour/setting/index.html")
@SaCheckPermission("tour.setting")
public void index() {
}
@At
@SaCheckPermission("tour.setting")
public Result pageData(PageForm pageForm, Integer year, String configName) {
Cnd cnd = Cnd.NEW();
cnd.andEX(TourSetting::getYear, "=", year);
cnd.and(Cnd.likeEX(TourSetting::getConfigName, configName));
cnd.desc(TourSetting::getYear).asc(TourSetting::getSortNo).desc(TourSetting::getCreatedAt);
Pagination<TourSetting> pagination = tourSettingService.listPage(
pageForm.getPageNumber(),
pageForm.getPageSize(),
TourSetting.class,
cnd
);
return Result.success(pagination);
}
@At
@SaCheckPermission("tour.setting")
public Result detail(String id) {
if (StrUtil.isBlank(id)) {
return Result.error("参数错误");
}
TourSetting tourSetting = tourSettingService.fetch(id);
if (tourSetting == null) {
return Result.error("配置不存在");
}
// 编辑页面需要一起带出标段,按标段值倒序保持与老疗休养配置一致。
Cnd lotCnd = Cnd.NEW();
lotCnd.desc(TourSettingLot::getLotValue);
tourSettingService.dao().fetchLinks(tourSetting, "lots", lotCnd);
return Result.success(tourSetting);
}
@At
@SaCheckPermission("tour.setting")
public Result previousYearInfo(Integer year) {
if (year == null) {
return Result.error("请先选择年度");
}
List<TourSetting> settings = tourSettingService.query(Cnd.where(TourSetting::getYear, "=", year - 1)
.and(TourSetting::getDelFlag, "=", false)
.desc(TourSetting::getUpdatedAt)
.desc(TourSetting::getCreatedAt));
if (Lang.isEmpty(settings)) {
return Result.error("未找到上一年度配置");
}
TourSetting tourSetting = settings.get(0);
Cnd lotCnd = Cnd.NEW();
lotCnd.desc(TourSettingLot::getLotValue);
tourSettingService.dao().fetchLinks(tourSetting, "lots", lotCnd);
return Result.success(tourSetting);
}
@At
@Aop(TransAop.READ_COMMITTED)
@SaCheckPermission("tour.setting")
@SLog(type = "tour", tag = "疗休养设置", msg = "保存疗休养配置")
public Result doSubmit(TourSetting tourSetting,
@Param(value = "lots") String lots,
@Param(value = "lotDeleteList") String[] lotDeleteList) {
Result checkResult = check(tourSetting);
if (checkResult != null) {
return checkResult;
}
Cnd sameNameCnd = Cnd.where(TourSetting::getYear, "=", tourSetting.getYear())
.and(TourSetting::getConfigName, "=", tourSetting.getConfigName());
if (StrUtil.isNotBlank(tourSetting.getId())) {
sameNameCnd.and(TourSetting::getId, "<>", tourSetting.getId());
}
if (tourSettingService.count(sameNameCnd) > 0) {
return Result.error("同年度下配置名称已存在");
}
// 布尔值给默认值,避免前端未传时出现空状态。
if (tourSetting.getEnabled() == null) {
tourSetting.setEnabled(true);
}
if (tourSetting.getAllowFamily() == null) {
tourSetting.setAllowFamily(false);
}
if (tourSetting.getFillBedInfo() == null) {
tourSetting.setFillBedInfo(true);
}
if (StrUtil.isBlank(tourSetting.getOutProvinceRatioType())) {
tourSetting.setOutProvinceRatioType("当年报名人数");
}
if (!"固定人数".equals(tourSetting.getOutProvinceRatioType())) {
tourSetting.setOutProvinceFixedPeople(0);
}
// 前端按项目既有约定把子表数组序列化提交,这里显式解析,避免自动绑定漏掉标段。
if (StrUtil.isNotBlank(lots)) {
try {
tourSetting.setLots(Json.fromJsonAsList(TourSettingLot.class, lots));
} catch (Exception e) {
return Result.error("标段值和标段费用必须为整数");
}
}
tourSetting.setLots(normalizeLots(tourSetting));
Result lotCheckResult = checkLots(tourSetting.getLots());
if (lotCheckResult != null) {
return lotCheckResult;
}
if (StrUtil.isBlank(tourSetting.getId())) {
if (Lang.isEmpty(tourSetting.getLots())) {
tourSettingService.insert(tourSetting);
} else {
tourSettingService.insertWith(tourSetting, "lots");
}
} else {
// 编辑时先处理页面删除的标段,再保存配置和当前标段行。
if (Lang.isNotEmpty(lotDeleteList)) {
tourSettingService.dao().clear(TourSettingLot.class, Cnd.where(TourSettingLot::getId, "in", lotDeleteList));
}
tourSettingService.updateIgnoreNull(tourSetting);
saveLots(tourSetting);
}
return Result.success();
}
@At
@Aop(TransAop.READ_COMMITTED)
@SaCheckPermission("tour.setting")
@SLog(type = "tour", tag = "疗休养设置", msg = "删除疗休养配置")
public Result doDelete(String id) {
if (StrUtil.isBlank(id)) {
return Result.error("参数错误");
}
tourSettingService.dao().clear(TourSettingLot.class, Cnd.where(TourSettingLot::getSettingId, "=", id));
tourSettingService.delete(id);
return Result.success();
}
private List<TourSettingLot> normalizeLots(TourSetting tourSetting) {
if (tourSetting == null || Lang.isEmpty(tourSetting.getLots())) {
return Collections.emptyList();
}
// 过滤前端空行,避免误写入只有ID或空字段的标段记录。
return tourSetting.getLots().stream()
.filter(item -> item != null
&& (StrUtil.isNotBlank(item.getLotName())
|| StrUtil.isNotBlank(item.getLotValue())
|| item.getActivityCost() != null))
.peek(item -> item.setSettingId(tourSetting.getId()))
.collect(Collectors.toList());
}
private void saveLots(TourSetting tourSetting) {
List<TourSettingLot> lots = tourSetting.getLots();
if (Lang.isEmpty(lots)) {
return;
}
lots.forEach(item -> item.setSettingId(tourSetting.getId()));
tourSettingService.dao().insertOrUpdate(lots);
}
private Result check(TourSetting tourSetting) {
if (tourSetting == null) {
return Result.error("参数错误");
}
if (tourSetting.getYear() == null) {
return Result.error("年度不能为空");
}
if (StrUtil.isBlank(tourSetting.getConfigName())) {
return Result.error("配置名称不能为空");
}
if (tourSetting.getMinGroupPeople() != null && tourSetting.getMaxGroupPeople() != null
&& tourSetting.getMinGroupPeople() > tourSetting.getMaxGroupPeople()) {
return Result.error("最少成团人数不能大于最多成团人数");
}
if (tourSetting.getCycleStartYear() != null && tourSetting.getCycleEndYear() != null
&& tourSetting.getCycleStartYear() > tourSetting.getCycleEndYear()) {
return Result.error("周期开始年度不能大于周期结束年度");
}
if (StrUtil.isNotBlank(tourSetting.getOutProvinceRatioType())
&& !"当年参加人数".equals(tourSetting.getOutProvinceRatioType())
&& !"当年报名人数".equals(tourSetting.getOutProvinceRatioType())
&& !"可参加教职工人数".equals(tourSetting.getOutProvinceRatioType())
&& !"固定人数".equals(tourSetting.getOutProvinceRatioType())) {
return Result.error("省外人数占比类型不正确");
}
if ("固定人数".equals(tourSetting.getOutProvinceRatioType())
&& (tourSetting.getOutProvinceFixedPeople() == null || tourSetting.getOutProvinceFixedPeople() < 0)) {
return Result.error("固定人数必须大于等于0");
}
return null;
}
private Result checkLots(List<TourSettingLot> lots) {
if (Lang.isEmpty(lots)) {
return null;
}
for (int i = 0; i < lots.size(); i++) {
TourSettingLot lot = lots.get(i);
String rowNo = "" + (i + 1) + "";
if (StrUtil.isBlank(lot.getLotValue()) || !lot.getLotValue().matches("^\\d+$")) {
return Result.error(rowNo + "标段值必须为整数");
}
if (lot.getActivityCost() == null || lot.getActivityCost() < 0) {
return Result.error(rowNo + "标段费用必须为整数");
}
}
return null;
}
}
@@ -0,0 +1,160 @@
package com.budwk.app.zhgh.dayofficework.tour.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.annotation.SLog;
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.zhgh.dayofficework.tour.models.TourTravelAgency;
import com.budwk.app.zhgh.dayofficework.tour.service.TourTravelAgencyService;
import org.nutz.aop.interceptor.ioc.TransAop;
import org.nutz.dao.Cnd;
import org.nutz.ioc.aop.Aop;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
@IocBean
@Ok("json:full")
@At("/platform/tour/travelAgency")
public class TourTravelAgencyController {
private static final String MOBILE_PATTERN = "^1[3-9]\\d{9}$";
private static final String EMAIL_PATTERN = "^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+$";
@Inject
private TourTravelAgencyService travelAgencyService;
@At("")
@Ok("beetl:/platform/zhgh/dayofficework/Tour/travelAgency/index.html")
@SaCheckPermission("tour.travelAgency")
public void index() {
}
@At
@SaCheckPermission("tour.travelAgency")
public Result pageData(PageForm pageForm, Integer year, String agencyName, String contactName, String contactPhone) {
Cnd cnd = Cnd.NEW();
cnd.andEX(TourTravelAgency::getYear, "=", year);
cnd.and(Cnd.likeEX(TourTravelAgency::getAgencyName, agencyName));
cnd.and(Cnd.likeEX(TourTravelAgency::getContactName, contactName));
cnd.and(Cnd.likeEX(TourTravelAgency::getContactPhone, contactPhone));
applyOrder(cnd, pageForm);
Pagination<TourTravelAgency> pagination = travelAgencyService.listPage(
pageForm.getPageNumber(),
pageForm.getPageSize(),
TourTravelAgency.class,
cnd
);
return Result.success(pagination);
}
@At
@SaCheckPermission("tour.travelAgency")
public Result detail(String id) {
if (StrUtil.isBlank(id)) {
return Result.error("参数错误");
}
TourTravelAgency agency = travelAgencyService.fetch(id);
return agency == null ? Result.error("旅行社不存在") : Result.success(agency);
}
@At
@Aop(TransAop.READ_COMMITTED)
@SaCheckPermission("tour.travelAgency")
@SLog(type = "tour", tag = "旅行社管理", msg = "保存旅行社信息")
public Result doSubmit(TourTravelAgency agency) {
Result checkResult = check(agency);
if (checkResult != null) {
return checkResult;
}
Cnd sameCodeCnd = Cnd.where(TourTravelAgency::getYear, "=", agency.getYear())
.and(TourTravelAgency::getAgencyCode, "=", agency.getAgencyCode());
if (StrUtil.isNotBlank(agency.getId())) {
sameCodeCnd.and(TourTravelAgency::getId, "<>", agency.getId());
}
if (travelAgencyService.count(sameCodeCnd) > 0) {
return Result.error("同年度下旅行社编号已存在");
}
if (agency.getEnabled() == null) {
agency.setEnabled(true);
}
if (StrUtil.isBlank(agency.getId())) {
travelAgencyService.insert(agency);
} else {
travelAgencyService.updateIgnoreNull(agency);
}
return Result.success();
}
@At
@Aop(TransAop.READ_COMMITTED)
@SaCheckPermission("tour.travelAgency")
@SLog(type = "tour", tag = "旅行社管理", msg = "删除旅行社信息")
public Result doDelete(String id) {
if (StrUtil.isBlank(id)) {
return Result.error("参数错误");
}
travelAgencyService.delete(id);
return Result.success();
}
private Result check(TourTravelAgency agency) {
if (agency == null) {
return Result.error("参数错误");
}
if (agency.getYear() == null) {
return Result.error("年度不能为空");
}
if (StrUtil.isBlank(agency.getAgencyName())) {
return Result.error("旅行社名称不能为空");
}
if (StrUtil.isBlank(agency.getAgencyCode())) {
return Result.error("旅行社编号不能为空");
}
if (StrUtil.isBlank(agency.getContactName())) {
return Result.error("联系人不能为空");
}
if (StrUtil.isBlank(agency.getContactPhone())) {
return Result.error("联系人手机不能为空");
}
if (!agency.getContactPhone().matches(MOBILE_PATTERN)) {
return Result.error("联系人手机格式不正确");
}
if (StrUtil.isBlank(agency.getEmail())) {
return Result.error("邮箱不能为空");
}
if (!agency.getEmail().matches(EMAIL_PATTERN)) {
return Result.error("邮箱格式不正确");
}
return null;
}
private void applyOrder(Cnd cnd, PageForm pageForm) {
String orderName = pageForm.getPageOrderName();
String orderBy = pageForm.getPageOrderBy();
if (StrUtil.isBlank(orderName)) {
cnd.asc(TourTravelAgency::getYear).asc(TourTravelAgency::getAgencyCode).desc(TourTravelAgency::getCreatedAt);
return;
}
boolean descending = "descending".equals(orderBy);
if ("year".equals(orderName)) {
if (descending) {
cnd.desc(TourTravelAgency::getYear);
} else {
cnd.asc(TourTravelAgency::getYear);
}
} else if ("agencyCode".equals(orderName)) {
if (descending) {
cnd.desc(TourTravelAgency::getAgencyCode);
} else {
cnd.asc(TourTravelAgency::getAgencyCode);
}
} else {
cnd.asc(TourTravelAgency::getYear).asc(TourTravelAgency::getAgencyCode).desc(TourTravelAgency::getCreatedAt);
}
}
}
@@ -0,0 +1,580 @@
package com.budwk.app.zhgh.dayofficework.tour.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.util.StrUtil;
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.flow.enums.ProcessTaskStateEnum;
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
import com.budwk.app.zhgh.dayofficework.tour.models.TourLedger;
import com.budwk.app.zhgh.dayofficework.tour.models.TourLedgerDirectRelative;
import com.budwk.app.zhgh.dayofficework.tour.models.TourLedgerFamily;
import com.budwk.app.zhgh.dayofficework.tour.service.TourLedgerDirectRelativeService;
import com.budwk.app.zhgh.dayofficework.tour.service.TourLedgerFamilyService;
import com.budwk.app.zhgh.dayofficework.tour.service.TourLedgerService;
import org.nutz.dao.Cnd;
import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Sql;
import org.nutz.dao.util.cri.SqlExpressionGroup;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.util.NutMap;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
import java.time.LocalDate;
import java.util.List;
@IocBean
@Ok("json:full")
@At("/platform/tour/unionApproval")
public class TourUnionApprovalController {
private static final String WORKFLOW_KEY = "LXYBZXQSXL";
private static final String TASK_DISPLAY_NAME = "分工会审核";
@Inject
private TourLedgerService tourLedgerService;
@Inject
private TourLedgerFamilyService tourLedgerFamilyService;
@Inject
private TourLedgerDirectRelativeService tourLedgerDirectRelativeService;
@At("")
@Ok("beetl:/platform/zhgh/dayofficework/Tour/unionApproval/index.html")
@SaCheckPermission("tour.unionApproval")
public void index() {
}
@At("/h5")
@Ok("beetl:/platform/zhghh5/dayofficework/tour/unionApproval/index.html")
@SaCheckPermission("tour.unionApproval")
public void h5() {
}
@At
@SaCheckPermission("tour.unionApproval")
public Result pageData(PageForm pageForm, Boolean audit, Integer startYear, Integer endYear, String keyword, String unionId, String lineId, String travelPeriod, String lineType) {
Cnd cnd = buildAuditCnd(audit, startYear, endYear, keyword, unionId, lineId, travelPeriod, lineType);
Sql countSql = Sqls.create("""
SELECT COUNT(DISTINCT task.id)
FROM wf_process_task task
LEFT JOIN wf_process_instance ins ON ins.id = task.processInstanceId
LEFT JOIN wf_process_define def ON def.id = ins.processDefineId
LEFT JOIN wf_process_task_actor ta ON ta.processTaskId = task.id
INNER JOIN tour_ledger t ON t.id = ins.businessNo
LEFT JOIN tour_matter m ON m.id = t.matterId AND m.delFlag = 0
LEFT JOIN tour_line l ON l.id = t.lineId AND l.delFlag = 0
$condition
""");
countSql.setCondition(cnd);
countSql.setCallback(Sqls.callback.integer());
tourLedgerService.dao().execute(countSql);
int count = countSql.getInt();
Sql listSql = Sqls.create("""
SELECT
t.*,
ins.id AS instanceId,
ins.businessNo,
ins.state AS instanceState,
ins.variable AS instanceVariable,
ins.processDefineId AS instanceProcessDefineId,
task.id AS taskId,
task.taskName AS taskKey,
task.displayName AS taskName,
task.taskType,
task.performType AS taskPerformType,
task.taskState,
task.finishTime,
task.taskParentId,
task.variable AS taskVariable,
IFNULL(GROUP_CONCAT(DISTINCT nt.displayName), '结束') AS curTaskName,
CASE
WHEN m.id IS NULL THEN COALESCE(NULLIF(l.lineName, ''), t.lineName)
ELSE CONCAT(
COALESCE(NULLIF(l.lineName, ''), t.lineName),
'',
CASE
WHEN IFNULL(m.unionId, '') = '' THEN '校工会'
ELSE IFNULL(mu.name, '')
END,
''
)
END AS currentLineName,
CASE
WHEN IFNULL(m.travelStartTime, '') <> '' AND IFNULL(m.travelEndTime, '') <> ''
THEN CONCAT(m.travelStartTime, ' 至 ', m.travelEndTime)
ELSE tp.travelPeriod
END AS travelPeriod,
IFNULL(f.familyCount, 0) AS familyCount
FROM wf_process_task task
LEFT JOIN wf_process_instance ins ON ins.id = task.processInstanceId
LEFT JOIN wf_process_define def ON def.id = ins.processDefineId
LEFT JOIN wf_process_task_actor ta ON ta.processTaskId = task.id
INNER JOIN tour_ledger t ON t.id = ins.businessNo
LEFT JOIN wf_process_task nt ON nt.processInstanceId = ins.id AND nt.taskState = 10
LEFT JOIN (
SELECT ledgerId, COUNT(1) AS familyCount
FROM tour_ledger_family
WHERE delFlag = 0
GROUP BY ledgerId
) f ON f.ledgerId = t.id
LEFT JOIN tour_matter m ON m.id = t.matterId AND m.delFlag = 0
LEFT JOIN tour_line l ON l.id = t.lineId AND l.delFlag = 0
LEFT JOIN sys_union mu ON mu.id = m.unionId
LEFT JOIN (
SELECT
`year`,
lineId,
GROUP_CONCAT(
DISTINCT CASE
WHEN IFNULL(travelStartTime, '') <> '' AND IFNULL(travelEndTime, '') <> ''
THEN CONCAT(travelStartTime, ' 至 ', travelEndTime)
ELSE NULL
END
ORDER BY travelStartTime ASC
SEPARATOR ''
) AS travelPeriod
FROM tour_matter
WHERE delFlag = 0
AND lineId IS NOT NULL
AND lineId <> ''
GROUP BY `year`, lineId
) tp ON (t.matterId IS NULL OR t.matterId = '') AND tp.`year` = t.`year` AND tp.lineId = t.lineId
$condition
GROUP BY task.id
ORDER BY $orderColumn $orderBy, task.createdAt DESC, t.signupTime DESC, t.createdAt DESC
""");
listSql.setCondition(cnd);
listSql.setVar("orderColumn", getOrderColumn(pageForm.getPageOrderName()));
listSql.setVar("orderBy", "ascending".equals(pageForm.getPageOrderBy()) ? "asc" : "desc");
listSql.setPager(tourLedgerService.dao().createPager(pageForm.getPageNumber(), pageForm.getPageSize()));
listSql.setCallback(Sqls.callback.maps());
tourLedgerService.dao().execute(listSql);
List<NutMap> list = listSql.getList(NutMap.class);
list.forEach(item -> item.put("lineName", item.getString("currentLineName")));
Pagination<NutMap> pagination = new Pagination<>(pageForm.getPageNumber(), pageForm.getPageSize(), count, list);
return Result.success(pagination);
}
@At
@SaCheckPermission("tour.unionApproval")
public Result detail(String id) {
TourLedger ledger = fetchAuditLedger(id);
if (ledger == null) {
return Result.error("报名记录不存在或无权查看");
}
ledger.setLineName(getCurrentLineName(ledger));
Cnd familyCnd = Cnd.where(TourLedgerFamily::getLedgerId, "=", ledger.getId())
.and(TourLedgerFamily::getDelFlag, "=", false);
familyCnd.asc(TourLedgerFamily::getCreatedAt);
TourLedgerDirectRelative directRelative = tourLedgerDirectRelativeService.fetch(
Cnd.where(TourLedgerDirectRelative::getLedgerId, "=", ledger.getId())
.and(TourLedgerDirectRelative::getDelFlag, "=", false));
NutMap signupConfig = getSignupConfig(ledger);
return Result.success(NutMap.NEW()
.addv("ledger", ledger)
.addv("travelPeriod", getTravelPeriod(ledger))
.addv("families", tourLedgerFamilyService.query(familyCnd))
.addv("directRelative", directRelative)
.addv("allowFamily", signupConfig.getBoolean("allowFamily", false))
.addv("fillBedInfo", signupConfig.getBoolean("fillBedInfo", true))
.addv("directFamilyUnitLine", signupConfig.getBoolean("directFamilyUnitLine", false)));
}
@At
@SaCheckPermission("tour.unionApproval")
public Result unionOptions(Boolean audit, Integer startYear, Integer endYear, String keyword, String lineId, String travelPeriod, String lineType) {
Cnd cnd = buildAuditCnd(audit, startYear, endYear, keyword, null, lineId, travelPeriod, lineType);
cnd.and("t.unionId", "IS NOT", null);
cnd.and("t.unionId", "<>", "");
Sql sql = Sqls.create("""
SELECT DISTINCT
t.unionId AS id,
COALESCE(NULLIF(u.name, ''), t.unionName) AS name,
u.unionCode
FROM wf_process_task task
LEFT JOIN wf_process_instance ins ON ins.id = task.processInstanceId
LEFT JOIN wf_process_define def ON def.id = ins.processDefineId
LEFT JOIN wf_process_task_actor ta ON ta.processTaskId = task.id
INNER JOIN tour_ledger t ON t.id = ins.businessNo
LEFT JOIN tour_matter m ON m.id = t.matterId AND m.delFlag = 0
LEFT JOIN tour_line l ON l.id = t.lineId AND l.delFlag = 0
LEFT JOIN sys_union u ON u.id = t.unionId
$condition
ORDER BY u.unionCode ASC, name ASC
""");
sql.setCondition(cnd);
sql.setCallback(Sqls.callback.maps());
tourLedgerService.dao().execute(sql);
return Result.success(sql.getList(NutMap.class));
}
@At
@SaCheckPermission("tour.unionApproval")
public Result lineOptions(Boolean audit, Integer startYear, Integer endYear, String travelPeriod, String lineType, String unionId, String keyword) {
Cnd cnd = buildAuditCnd(audit, startYear, endYear, keyword, unionId, null, travelPeriod, lineType);
cnd.and("t.lineName", "IS NOT", null);
cnd.and("t.lineName", "<>", "");
Sql sql = Sqls.create("""
SELECT DISTINCT
CASE
WHEN IFNULL(t.lineId, '') <> '' THEN CONCAT(t.lineId, '|', IFNULL(m.unionId, ''))
ELSE CONCAT('legacy:', t.lineName, '|', IFNULL(m.unionId, ''))
END AS lineId,
CASE
WHEN m.id IS NULL THEN COALESCE(NULLIF(l.lineName, ''), t.lineName)
ELSE CONCAT(
COALESCE(NULLIF(l.lineName, ''), t.lineName),
'',
CASE
WHEN IFNULL(m.unionId, '') = '' THEN '校工会'
ELSE IFNULL(mu.name, '')
END,
''
)
END AS lineName
FROM wf_process_task task
LEFT JOIN wf_process_instance ins ON ins.id = task.processInstanceId
LEFT JOIN wf_process_define def ON def.id = ins.processDefineId
LEFT JOIN wf_process_task_actor ta ON ta.processTaskId = task.id
INNER JOIN tour_ledger t ON t.id = ins.businessNo
LEFT JOIN tour_matter m ON m.id = t.matterId AND m.delFlag = 0
LEFT JOIN tour_line l ON l.id = t.lineId AND l.delFlag = 0
LEFT JOIN sys_union mu ON mu.id = m.unionId
$condition
ORDER BY lineName ASC
""");
sql.setCondition(cnd);
sql.setCallback(Sqls.callback.maps());
tourLedgerService.dao().execute(sql);
return Result.success(sql.getList(NutMap.class));
}
@At
@SaCheckPermission("tour.unionApproval")
public Result travelPeriodOptions(Boolean audit, Integer startYear, Integer endYear, String lineId, String lineType, String unionId, String keyword) {
Cnd cnd = buildAuditCnd(audit, startYear, endYear, keyword, unionId, lineId, null, lineType);
cnd.and("m.travelStartTime", "IS NOT", null);
cnd.and("m.travelStartTime", "<>", "");
cnd.and("m.travelEndTime", "IS NOT", null);
cnd.and("m.travelEndTime", "<>", "");
Sql sql = Sqls.create("""
SELECT DISTINCT CONCAT(m.travelStartTime, ' 至 ', m.travelEndTime) AS travelPeriod
FROM wf_process_task task
LEFT JOIN wf_process_instance ins ON ins.id = task.processInstanceId
LEFT JOIN wf_process_define def ON def.id = ins.processDefineId
LEFT JOIN wf_process_task_actor ta ON ta.processTaskId = task.id
INNER JOIN tour_ledger t ON t.id = ins.businessNo
LEFT JOIN tour_matter m ON m.id = t.matterId AND m.delFlag = 0
LEFT JOIN tour_line l ON l.id = t.lineId AND l.delFlag = 0
$condition
ORDER BY travelPeriod ASC
""");
sql.setCondition(cnd);
sql.setCallback(Sqls.callback.maps());
tourLedgerService.dao().execute(sql);
return Result.success(sql.getList(NutMap.class));
}
@At
@SaCheckPermission("tour.unionApproval")
public Result lineTypeOptions(Boolean audit, Integer startYear, Integer endYear, String lineId, String travelPeriod, String unionId, String keyword) {
Cnd cnd = buildAuditCnd(audit, startYear, endYear, keyword, unionId, lineId, travelPeriod, null);
cnd.and("t.lineType", "IS NOT", null);
cnd.and("t.lineType", "<>", "");
Sql sql = Sqls.create("""
SELECT DISTINCT t.lineType
FROM wf_process_task task
LEFT JOIN wf_process_instance ins ON ins.id = task.processInstanceId
LEFT JOIN wf_process_define def ON def.id = ins.processDefineId
LEFT JOIN wf_process_task_actor ta ON ta.processTaskId = task.id
INNER JOIN tour_ledger t ON t.id = ins.businessNo
LEFT JOIN tour_matter m ON m.id = t.matterId AND m.delFlag = 0
LEFT JOIN tour_line l ON l.id = t.lineId AND l.delFlag = 0
$condition
ORDER BY t.lineType ASC
""");
sql.setCondition(cnd);
sql.setCallback(Sqls.callback.maps());
tourLedgerService.dao().execute(sql);
return Result.success(sql.getList(NutMap.class));
}
private Cnd buildAuditCnd(Boolean audit, Integer startYear, Integer endYear, String keyword, String unionId, String lineId, String travelPeriod, String lineType) {
Cnd cnd = Cnd.NEW();
cnd.and("t.delFlag", "=", false);
cnd.and("def.name", "=", WORKFLOW_KEY);
cnd.and("task.displayName", "=", TASK_DISPLAY_NAME);
cnd.and("ta.actorId", "in", List.of(SecurityUtil.getUserId()));
appendAuditStateFilter(cnd, audit);
cnd.andEX("t.`year`", ">=", startYear);
cnd.andEX("t.`year`", "<=", endYear == null ? LocalDate.now().getYear() : endYear);
cnd.andEX("t.unionId", "=", unionId);
cnd.andEX("t.lineType", "=", lineType);
appendLineFilter(cnd, lineId);
appendTravelPeriodFilter(cnd, travelPeriod);
appendKeywordFilter(cnd, keyword);
return cnd;
}
private void appendAuditStateFilter(Cnd cnd, Boolean audit) {
if (Boolean.TRUE.equals(audit)) {
cnd.and("task.taskState", "in", List.of(
ProcessTaskStateEnum.FINISHED.getCode(),
ProcessTaskStateEnum.WITHDRAW.getCode(),
ProcessTaskStateEnum.INTERRUPT.getCode()));
return;
}
cnd.and("task.taskState", "=", ProcessTaskStateEnum.DOING.getCode());
}
private void appendKeywordFilter(Cnd cnd, String keyword) {
if (StrUtil.isBlank(keyword)) {
return;
}
SqlExpressionGroup group = new SqlExpressionGroup();
group.orLike("t.userName", keyword.trim());
group.orLike("t.jobNo", keyword.trim());
group.orLike("t.lineName", keyword.trim());
group.orLike("l.lineName", keyword.trim());
cnd.and(group);
}
private void appendTravelPeriodFilter(Cnd cnd, String travelPeriod) {
if (StrUtil.isBlank(travelPeriod)) {
return;
}
cnd.and("CONCAT(m.travelStartTime, ' 至 ', m.travelEndTime)", "=", travelPeriod);
}
private void appendLineFilter(Cnd cnd, String lineId) {
if (StrUtil.isBlank(lineId)) {
return;
}
LineFilter lineFilter = parseLineFilter(lineId);
if (lineFilter.getLineId().startsWith("legacy:")) {
SqlExpressionGroup legacyGroup = new SqlExpressionGroup();
legacyGroup.or("t.lineId", "IS", null);
legacyGroup.or("t.lineId", "=", "");
cnd.and(legacyGroup);
cnd.and("t.lineName", "=", lineFilter.getLineId().substring("legacy:".length()));
} else {
cnd.and("t.lineId", "=", lineFilter.getLineId());
}
appendMatterUnionFilter(cnd, lineFilter.getUnionId());
}
private LineFilter parseLineFilter(String value) {
int delimiterIndex = value.indexOf("|");
if (delimiterIndex < 0) {
return new LineFilter(value, null);
}
return new LineFilter(value.substring(0, delimiterIndex), value.substring(delimiterIndex + 1));
}
private void appendMatterUnionFilter(Cnd cnd, String unionId) {
if (unionId == null) {
return;
}
if (StrUtil.isBlank(unionId)) {
SqlExpressionGroup group = new SqlExpressionGroup();
group.or("m.unionId", "IS", null);
group.or("m.unionId", "=", "");
cnd.and(group);
return;
}
cnd.and("m.unionId", "=", unionId);
}
private String getOrderColumn(String orderName) {
if ("jobNo".equals(orderName)) {
return "t.jobNo";
}
if ("userName".equals(orderName)) {
return "t.userName";
}
if ("lineName".equals(orderName)) {
return """
CASE
WHEN m.id IS NULL THEN COALESCE(NULLIF(l.lineName, ''), t.lineName)
ELSE CONCAT(
COALESCE(NULLIF(l.lineName, ''), t.lineName),
'',
CASE
WHEN IFNULL(m.unionId, '') = '' THEN '校工会'
ELSE IFNULL(mu.name, '')
END,
''
)
END
""";
}
if ("travelPeriod".equals(orderName)) {
return "COALESCE(m.travelStartTime, tp.travelPeriod)";
}
if ("lineType".equals(orderName)) {
return "t.lineType";
}
if ("signupTime".equals(orderName)) {
return "t.signupTime";
}
if ("familyCount".equals(orderName)) {
return "familyCount";
}
if ("instanceState".equals(orderName)) {
return "ins.state";
}
return "task.createdAt";
}
private TourLedger fetchAuditLedger(String id) {
if (StrUtil.isBlank(id)) {
return null;
}
Sql sql = Sqls.create("""
SELECT COUNT(1)
FROM wf_process_task task
LEFT JOIN wf_process_instance ins ON ins.id = task.processInstanceId
LEFT JOIN wf_process_define def ON def.id = ins.processDefineId
LEFT JOIN wf_process_task_actor ta ON ta.processTaskId = task.id
INNER JOIN tour_ledger t ON t.id = ins.businessNo
WHERE t.delFlag = 0
AND t.id = @id
AND def.name = @workflowKey
AND task.displayName = @taskDisplayName
AND ta.actorId = @actorId
""");
sql.setParam("id", id);
sql.setParam("workflowKey", WORKFLOW_KEY);
sql.setParam("taskDisplayName", TASK_DISPLAY_NAME);
sql.setParam("actorId", SecurityUtil.getUserId());
sql.setCallback(Sqls.callback.integer());
tourLedgerService.dao().execute(sql);
return sql.getInt() > 0 ? tourLedgerService.fetch(id) : null;
}
private String getCurrentLineName(TourLedger ledger) {
if (ledger == null) {
return "";
}
Sql sql = Sqls.create("""
SELECT CASE
WHEN m.id IS NULL THEN COALESCE(NULLIF(l.lineName, ''), t.lineName)
ELSE CONCAT(
COALESCE(NULLIF(l.lineName, ''), t.lineName),
'',
CASE
WHEN IFNULL(m.unionId, '') = '' THEN '校工会'
ELSE IFNULL(mu.name, '')
END,
''
)
END AS lineName
FROM tour_ledger t
LEFT JOIN tour_matter m ON m.id = t.matterId AND m.delFlag = 0
LEFT JOIN tour_line l ON l.id = t.lineId AND l.delFlag = 0
LEFT JOIN sys_union mu ON mu.id = m.unionId
WHERE t.id = @ledgerId
""");
sql.setParam("ledgerId", ledger.getId());
sql.setCallback(Sqls.callback.str());
tourLedgerService.dao().execute(sql);
return StrUtil.blankToDefault(sql.getString(), ledger.getLineName());
}
private String getTravelPeriod(TourLedger ledger) {
if (ledger == null || ledger.getYear() == null || StrUtil.isBlank(ledger.getLineId())) {
return "";
}
if (StrUtil.isNotBlank(ledger.getMatterId())) {
Sql exactSql = Sqls.create("""
SELECT CASE
WHEN IFNULL(travelStartTime, '') <> '' AND IFNULL(travelEndTime, '') <> ''
THEN CONCAT(travelStartTime, ' 至 ', travelEndTime)
ELSE ''
END AS travelPeriod
FROM tour_matter
WHERE delFlag = 0
AND id = @matterId
""");
exactSql.setParam("matterId", ledger.getMatterId());
exactSql.setCallback(Sqls.callback.str());
tourLedgerService.dao().execute(exactSql);
return exactSql.getString();
}
Sql sql = Sqls.create("""
SELECT GROUP_CONCAT(
DISTINCT CASE
WHEN IFNULL(travelStartTime, '') <> '' AND IFNULL(travelEndTime, '') <> ''
THEN CONCAT(travelStartTime, ' 至 ', travelEndTime)
ELSE NULL
END
ORDER BY travelStartTime ASC
SEPARATOR ''
) AS travelPeriod
FROM tour_matter
WHERE delFlag = 0
AND `year` = @year
AND lineId = @lineId
""");
sql.setParam("year", ledger.getYear());
sql.setParam("lineId", ledger.getLineId());
sql.setCallback(Sqls.callback.str());
tourLedgerService.dao().execute(sql);
return sql.getString();
}
private NutMap getSignupConfig(TourLedger ledger) {
if (ledger == null || StrUtil.isBlank(ledger.getLineId())) {
return NutMap.NEW().addv("allowFamily", false).addv("fillBedInfo", true).addv("directFamilyUnitLine", false);
}
Sql sql = Sqls.create("""
SELECT
IFNULL(s.allowFamily, 0) AS allowFamily,
IFNULL(s.fillBedInfo, 1) AS fillBedInfo,
IFNULL(l.directFamilyUnitLine, 0) AS directFamilyUnitLine
FROM tour_ledger t
LEFT JOIN tour_matter m ON m.id = t.matterId AND m.delFlag = 0
LEFT JOIN tour_setting s ON s.id = m.settingId AND s.delFlag = 0
LEFT JOIN tour_line l ON l.id = t.lineId AND l.delFlag = 0
WHERE t.id = @ledgerId
""");
sql.setParam("ledgerId", ledger.getId());
sql.setCallback(Sqls.callback.map());
tourLedgerService.dao().execute(sql);
NutMap map = sql.getObject(NutMap.class);
if (map == null || map.isEmpty()) {
return NutMap.NEW().addv("allowFamily", false).addv("fillBedInfo", true).addv("directFamilyUnitLine", false);
}
return map;
}
private static class LineFilter {
private final String lineId;
private final String unionId;
private LineFilter(String lineId, String unionId) {
this.lineId = lineId;
this.unionId = unionId;
}
private String getLineId() {
return lineId;
}
private String getUnionId() {
return unionId;
}
}
}
@@ -0,0 +1,905 @@
package com.budwk.app.zhgh.dayofficework.tour.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.annotation.SLog;
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.sys.models.Sys_union;
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
import com.budwk.app.zhgh.dayofficework.tour.models.TourLedger;
import com.budwk.app.zhgh.dayofficework.tour.models.TourLedgerDirectRelative;
import com.budwk.app.zhgh.dayofficework.tour.models.TourLedgerFamily;
import com.budwk.app.zhgh.dayofficework.tour.service.TourLedgerDirectRelativeService;
import com.budwk.app.zhgh.dayofficework.tour.service.TourLedgerFamilyService;
import com.budwk.app.zhgh.dayofficework.tour.service.TourLedgerService;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.BorderStyle;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.Font;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.VerticalAlignment;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.util.CellRangeAddress;
import org.nutz.aop.interceptor.ioc.TransAop;
import org.nutz.dao.Cnd;
import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Sql;
import org.nutz.dao.util.cri.SqlExpressionGroup;
import org.nutz.ioc.aop.Aop;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.util.NutMap;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
import java.util.List;
@IocBean
@Ok("json:full")
@At("/platform/tour/unionledger")
public class TourUnionLedgerController {
@Inject
private TourLedgerService tourLedgerService;
@Inject
private TourLedgerFamilyService tourLedgerFamilyService;
@Inject
private TourLedgerDirectRelativeService tourLedgerDirectRelativeService;
@At("")
@Ok("beetl:/platform/zhgh/dayofficework/Tour/unionledger/index.html")
@SaCheckPermission("tour.unionledger")
public void index() {
}
@At
@SaCheckPermission("tour.unionledger")
public Result pageData(PageForm pageForm, Integer startYear, Integer endYear, String keyword, String unionId, String lineId, String travelPeriod, String lineType, String scopeType, Boolean overCostOnly) {
String currentUnionId = currentUnionId();
if (StrUtil.isBlank(currentUnionId)) {
return Result.error("当前登录人所属工会不能为空");
}
Cnd cnd = buildQueryCnd(currentUnionId, startYear, endYear, keyword, unionId, lineId, travelPeriod, lineType, scopeType, overCostOnly);
Sql countSql = Sqls.create("""
SELECT COUNT(DISTINCT t.id)
FROM tour_ledger t
LEFT JOIN wf_process_instance ins ON ins.businessNo = t.id
LEFT JOIN wf_process_task task ON task.processInstanceId = ins.id AND task.taskState = 10
LEFT JOIN tour_matter m ON m.id = t.matterId AND m.delFlag = 0
$condition
""");
countSql.setCondition(cnd);
countSql.setCallback(Sqls.callback.integer());
tourLedgerService.dao().execute(countSql);
int count = countSql.getInt();
Sql listSql = Sqls.create("""
SELECT
t.*,
ins.id AS instanceId,
ins.state AS instanceState,
ins.processDefineId AS instanceProcessDefineId,
IFNULL(l.directFamilyUnitLine, 0) AS directFamilyUnitLine,
IFNULL(GROUP_CONCAT(DISTINCT task.displayName), IF(ins.id IS NULL, '', '结束')) AS curTaskName,
CASE
WHEN m.id IS NULL THEN COALESCE(NULLIF(l.lineName, ''), t.lineName)
ELSE CONCAT(
COALESCE(NULLIF(l.lineName, ''), t.lineName),
'',
CASE
WHEN IFNULL(m.unionId, '') = '' THEN '校工会'
ELSE IFNULL(mu.name, '')
END,
''
)
END AS currentLineName,
IFNULL(f.familyCount, 0) AS familyCount,
CASE
WHEN IFNULL(m.travelStartTime, '') <> '' AND IFNULL(m.travelEndTime, '') <> ''
THEN CONCAT(m.travelStartTime, ' ', m.travelEndTime)
ELSE tp.travelPeriod
END AS travelPeriod
FROM tour_ledger t
LEFT JOIN wf_process_instance ins ON ins.businessNo = t.id
LEFT JOIN wf_process_task task ON task.processInstanceId = ins.id AND task.taskState = 10
LEFT JOIN (
SELECT ledgerId, COUNT(1) AS familyCount
FROM tour_ledger_family
WHERE delFlag = 0
GROUP BY ledgerId
) f ON f.ledgerId = t.id
LEFT JOIN tour_matter m ON m.id = t.matterId AND m.delFlag = 0
LEFT JOIN tour_line l ON l.id = t.lineId AND l.delFlag = 0
LEFT JOIN sys_union mu ON mu.id = m.unionId
LEFT JOIN (
SELECT
`year`,
lineId,
GROUP_CONCAT(
DISTINCT CASE
WHEN IFNULL(travelStartTime, '') <> '' AND IFNULL(travelEndTime, '') <> ''
THEN CONCAT(travelStartTime, ' ', travelEndTime)
ELSE NULL
END
ORDER BY travelStartTime ASC
SEPARATOR ''
) AS travelPeriod
FROM tour_matter
WHERE delFlag = 0
AND lineId IS NOT NULL
AND lineId <> ''
GROUP BY `year`, lineId
) tp ON (t.matterId IS NULL OR t.matterId = '') AND tp.`year` = t.`year` AND tp.lineId = t.lineId
$condition
GROUP BY t.id
ORDER BY $orderColumn $orderBy, t.`year` DESC, t.signupTime DESC, t.createdAt DESC
""");
listSql.setCondition(cnd);
listSql.setVar("orderColumn", getOrderColumn(pageForm.getPageOrderName()));
listSql.setVar("orderBy", "descending".equals(pageForm.getPageOrderBy()) ? "desc" : "asc");
listSql.setPager(tourLedgerService.dao().createPager(pageForm.getPageNumber(), pageForm.getPageSize()));
listSql.setCallback(Sqls.callback.maps());
tourLedgerService.dao().execute(listSql);
var list = listSql.getList(NutMap.class);
list.forEach(item -> item.put("lineName", item.getString("currentLineName")));
Pagination<NutMap> pagination = new Pagination<>(pageForm.getPageNumber(), pageForm.getPageSize(), count, list);
return Result.success(pagination);
}
@At
@Ok("void")
@SaCheckPermission("tour.unionledger")
public void exportOverCostSummary(PageForm pageForm, Integer startYear, Integer endYear, String keyword, String unionId, String lineId, String travelPeriod, String lineType, String scopeType, HttpServletResponse response) {
String currentUnionId = currentUnionId();
if (StrUtil.isBlank(currentUnionId)) {
return;
}
Cnd cnd = buildQueryCnd(currentUnionId, startYear, endYear, keyword, unionId, lineId, travelPeriod, lineType, scopeType, true);
List<NutMap> list = queryOverCostSummaryList(cnd, pageForm);
Workbook workbook = buildOverCostSummaryWorkbook(currentUnionName(), list);
CommonDownloadUtil.download("5天外超出部分疗休养费用由单位承担申请人员汇总表.xls", workbook, response);
}
@At
@SaCheckPermission("tour.unionledger")
public Result detail(String id) {
TourLedger ledger = fetchScopedLedger(id);
if (ledger == null) {
return Result.error("台账记录不存在或无权查看");
}
ledger.setLineName(getCurrentLineName(ledger));
Cnd familyCnd = Cnd.where(TourLedgerFamily::getLedgerId, "=", id)
.and(TourLedgerFamily::getDelFlag, "=", false);
familyCnd.asc(TourLedgerFamily::getCreatedAt);
TourLedgerDirectRelative directRelative = tourLedgerDirectRelativeService.fetch(
Cnd.where(TourLedgerDirectRelative::getDelFlag, "=", false)
.and(TourLedgerDirectRelative::getLedgerId, "=", ledger.getId()));
return Result.success(NutMap.NEW()
.addv("ledger", ledger)
.addv("travelPeriod", getTravelPeriod(ledger))
.addv("families", tourLedgerFamilyService.query(familyCnd))
.addv("directRelative", directRelative)
.addv("fillBedInfo", isFillBedInfo(ledger))
.addv("directFamilyUnitLine", isDirectFamilyUnitLine(ledger)));
}
@At
@Aop(TransAop.READ_COMMITTED)
@SaCheckPermission("tour.unionledger")
@SLog(type = "tour", tag = "分工会疗休养台账", msg = "删除分工会疗休养台账")
public Result doDelete(String id) {
TourLedger ledger = fetchScopedLedger(id);
if (ledger == null) {
return Result.error("台账记录不存在或无权删除");
}
tourLedgerFamilyService.clear(Cnd.where(TourLedgerFamily::getLedgerId, "=", ledger.getId()));
tourLedgerService.clear(Cnd.where(TourLedger::getId, "=", ledger.getId()));
return Result.success();
}
@At
@SaCheckPermission("tour.unionledger")
public Result unionOptions(Integer startYear, Integer endYear) {
String currentUnionId = currentUnionId();
if (StrUtil.isBlank(currentUnionId)) {
return Result.error("当前登录人所属工会不能为空");
}
Cnd cnd = Cnd.NEW();
cnd.and("t.delFlag", "=", false);
cnd.andEX("t.`year`", ">=", startYear);
cnd.andEX("t.`year`", "<=", endYear == null ? LocalDate.now().getYear() : endYear);
cnd.and("t.unionId", "IS NOT", null);
cnd.and("t.unionId", "<>", "");
applyUnionLedgerScope(cnd, currentUnionId);
Sql sql = Sqls.create("""
SELECT DISTINCT
t.unionId AS id,
COALESCE(NULLIF(u.name, ''), t.unionName) AS name,
u.unionCode
FROM tour_ledger t
LEFT JOIN tour_matter m ON m.id = t.matterId AND m.delFlag = 0
LEFT JOIN sys_union u ON u.id = t.unionId
$condition
ORDER BY
CASE WHEN t.unionId = @currentUnionId THEN 0 ELSE 1 END,
u.unionCode ASC,
name ASC
""");
sql.setCondition(cnd);
sql.setParam("currentUnionId", currentUnionId);
sql.setCallback(Sqls.callback.maps());
tourLedgerService.dao().execute(sql);
return Result.success(sql.getList(NutMap.class));
}
@At
@SaCheckPermission("tour.unionledger")
public Result lineOptions(Integer startYear, Integer endYear, String travelPeriod, String lineType, String unionId, String keyword, String scopeType) {
String currentUnionId = currentUnionId();
if (StrUtil.isBlank(currentUnionId)) {
return Result.error("当前登录人所属工会不能为空");
}
Cnd cnd = Cnd.NEW();
cnd.and("t.delFlag", "=", false);
cnd.andEX("t.`year`", ">=", startYear);
cnd.andEX("t.`year`", "<=", endYear == null ? LocalDate.now().getYear() : endYear);
cnd.and("t.lineName", "IS NOT", null);
cnd.and("t.lineName", "<>", "");
cnd.andEX("t.unionId", "=", unionId);
cnd.andEX("t.lineType", "=", lineType);
appendTravelPeriodFilter(cnd, travelPeriod);
appendScopeFilter(cnd, currentUnionId, scopeType);
appendKeywordFilter(cnd, keyword);
Sql sql = Sqls.create("""
SELECT DISTINCT
CASE
WHEN IFNULL(t.lineId, '') <> '' THEN CONCAT(t.lineId, '|', IFNULL(m.unionId, ''))
ELSE CONCAT('legacy:', t.lineName, '|', IFNULL(m.unionId, ''))
END AS lineId,
CASE
WHEN m.id IS NULL THEN COALESCE(NULLIF(l.lineName, ''), t.lineName)
ELSE CONCAT(
COALESCE(NULLIF(l.lineName, ''), t.lineName),
'',
CASE
WHEN IFNULL(m.unionId, '') = '' THEN '校工会'
ELSE IFNULL(mu.name, '')
END,
''
)
END AS lineName
FROM tour_ledger t
LEFT JOIN tour_matter m ON m.id = t.matterId AND m.delFlag = 0
LEFT JOIN tour_line l ON l.id = t.lineId AND l.delFlag = 0
LEFT JOIN sys_union mu ON mu.id = m.unionId
$condition
ORDER BY lineName ASC
""");
sql.setCondition(cnd);
sql.setCallback(Sqls.callback.maps());
tourLedgerService.dao().execute(sql);
return Result.success(sql.getList(NutMap.class));
}
@At
@SaCheckPermission("tour.unionledger")
public Result travelPeriodOptions(Integer startYear, Integer endYear, String lineId, String lineType, String unionId, String keyword, String scopeType) {
String currentUnionId = currentUnionId();
if (StrUtil.isBlank(currentUnionId)) {
return Result.error("当前登录人所属工会不能为空");
}
Cnd cnd = Cnd.NEW();
cnd.and("t.delFlag", "=", false);
cnd.andEX("t.`year`", ">=", startYear);
cnd.andEX("t.`year`", "<=", endYear == null ? LocalDate.now().getYear() : endYear);
cnd.and("m.travelStartTime", "IS NOT", null);
cnd.and("m.travelStartTime", "<>", "");
cnd.and("m.travelEndTime", "IS NOT", null);
cnd.and("m.travelEndTime", "<>", "");
cnd.andEX("t.unionId", "=", unionId);
cnd.andEX("t.lineType", "=", lineType);
appendLineFilter(cnd, lineId);
appendScopeFilter(cnd, currentUnionId, scopeType);
appendKeywordFilter(cnd, keyword);
Sql sql = Sqls.create("""
SELECT DISTINCT CONCAT(m.travelStartTime, ' ', m.travelEndTime) AS travelPeriod
FROM tour_ledger t
LEFT JOIN tour_matter m ON m.id = t.matterId AND m.delFlag = 0
$condition
ORDER BY travelPeriod ASC
""");
sql.setCondition(cnd);
sql.setCallback(Sqls.callback.maps());
tourLedgerService.dao().execute(sql);
return Result.success(sql.getList(NutMap.class));
}
@At
@SaCheckPermission("tour.unionledger")
public Result lineTypeOptions(Integer startYear, Integer endYear, String lineId, String travelPeriod, String unionId, String keyword, String scopeType) {
String currentUnionId = currentUnionId();
if (StrUtil.isBlank(currentUnionId)) {
return Result.error("当前登录人所属工会不能为空");
}
Cnd cnd = Cnd.NEW();
cnd.and("t.delFlag", "=", false);
cnd.andEX("t.`year`", ">=", startYear);
cnd.andEX("t.`year`", "<=", endYear == null ? LocalDate.now().getYear() : endYear);
cnd.and("t.lineType", "IS NOT", null);
cnd.and("t.lineType", "<>", "");
cnd.andEX("t.unionId", "=", unionId);
appendLineFilter(cnd, lineId);
appendTravelPeriodFilter(cnd, travelPeriod);
appendScopeFilter(cnd, currentUnionId, scopeType);
appendKeywordFilter(cnd, keyword);
Sql sql = Sqls.create("""
SELECT DISTINCT t.lineType
FROM tour_ledger t
LEFT JOIN tour_matter m ON m.id = t.matterId AND m.delFlag = 0
$condition
ORDER BY t.lineType ASC
""");
sql.setCondition(cnd);
sql.setCallback(Sqls.callback.maps());
tourLedgerService.dao().execute(sql);
return Result.success(sql.getList(NutMap.class));
}
@At
@SaCheckPermission("tour.unionledger")
public Result summaryStats(Integer startYear, Integer endYear, String unionId, String lineId, String travelPeriod, String lineType) {
String currentUnionId = currentUnionId();
if (StrUtil.isBlank(currentUnionId)) {
return Result.error("当前登录人所属工会不能为空");
}
Cnd ownCnd = buildSummaryCnd(currentUnionId, startYear, endYear, unionId, lineId, travelPeriod, lineType, "ownUnionJoined");
Cnd organizedCnd = buildSummaryCnd(currentUnionId, startYear, endYear, unionId, lineId, travelPeriod, lineType, "organizedLineJoined");
Sql ownSql = Sqls.create("""
SELECT COUNT(1)
FROM tour_ledger t
LEFT JOIN tour_matter m ON m.id = t.matterId AND m.delFlag = 0
$condition
""");
ownSql.setCondition(ownCnd);
ownSql.setCallback(Sqls.callback.integer());
tourLedgerService.dao().execute(ownSql);
Sql organizedSql = Sqls.create("""
SELECT CAST(IFNULL(SUM(1 + IFNULL(f.familyCount, 0)), 0) AS SIGNED)
FROM tour_ledger t
INNER JOIN tour_matter m ON m.id = t.matterId AND m.delFlag = 0
LEFT JOIN (
SELECT ledgerId, COUNT(1) AS familyCount
FROM tour_ledger_family
WHERE delFlag = 0
GROUP BY ledgerId
) f ON f.ledgerId = t.id
$condition
""");
organizedSql.setCondition(organizedCnd);
organizedSql.setCallback(Sqls.callback.integer());
tourLedgerService.dao().execute(organizedSql);
return Result.success(NutMap.NEW()
.addv("ownUnionJoinedStaffCount", ownSql.getInt())
.addv("organizedLineJoinedTotalCount", organizedSql.getInt()));
}
private List<NutMap> queryOverCostSummaryList(Cnd cnd, PageForm pageForm) {
Sql sql = Sqls.create("""
SELECT
t.id,
t.userName,
t.idCard,
t.unionName,
vu.mobile,
m.travelStartTime,
m.travelEndTime,
m.estimatedCost,
CASE
WHEN IFNULL(m.travelStartTime, '') <> '' AND IFNULL(m.travelEndTime, '') <> ''
THEN CONCAT(m.travelStartTime, ' ', m.travelEndTime)
ELSE tp.travelPeriod
END AS travelPeriod,
CASE
WHEN m.id IS NULL THEN COALESCE(NULLIF(l.lineName, ''), t.lineName)
ELSE CONCAT(
COALESCE(NULLIF(l.lineName, ''), t.lineName),
'',
CASE
WHEN IFNULL(m.unionId, '') = '' THEN '校工会'
ELSE IFNULL(mu.name, '')
END,
''
)
END AS currentLineName
FROM tour_ledger t
LEFT JOIN tour_matter m ON m.id = t.matterId AND m.delFlag = 0
LEFT JOIN tour_line l ON l.id = t.lineId AND l.delFlag = 0
LEFT JOIN sys_union mu ON mu.id = m.unionId
LEFT JOIN vw_user vu ON vu.loginname = t.jobNo
LEFT JOIN (
SELECT
`year`,
lineId,
GROUP_CONCAT(
DISTINCT CASE
WHEN IFNULL(travelStartTime, '') <> '' AND IFNULL(travelEndTime, '') <> ''
THEN CONCAT(travelStartTime, ' ', travelEndTime)
ELSE NULL
END
ORDER BY travelStartTime ASC
SEPARATOR ''
) AS travelPeriod
FROM tour_matter
WHERE delFlag = 0
AND lineId IS NOT NULL
AND lineId <> ''
GROUP BY `year`, lineId
) tp ON (t.matterId IS NULL OR t.matterId = '') AND tp.`year` = t.`year` AND tp.lineId = t.lineId
$condition
ORDER BY $orderColumn $orderBy, t.`year` DESC, t.signupTime DESC, t.createdAt DESC
""");
sql.setCondition(cnd);
sql.setVar("orderColumn", getOrderColumn(pageForm == null ? null : pageForm.getPageOrderName()));
sql.setVar("orderBy", pageForm != null && "ascending".equals(pageForm.getPageOrderBy()) ? "asc" : "desc");
sql.setCallback(Sqls.callback.maps());
tourLedgerService.dao().execute(sql);
List<NutMap> list = sql.getList(NutMap.class);
for (NutMap item : list) {
item.put("lineName", item.getString("currentLineName"));
item.put("totalDays", calcDays(item.getString("travelStartTime", ""), item.getString("travelEndTime", "")));
item.put("estimatedCostText", formatAmount(item.get("estimatedCost")));
}
return list;
}
private Workbook buildOverCostSummaryWorkbook(String unionName, List<NutMap> list) {
Workbook workbook = new HSSFWorkbook();
Sheet sheet = workbook.createSheet("汇总表");
sheet.addMergedRegion(new CellRangeAddress(0, 0, 0, 9));
sheet.addMergedRegion(new CellRangeAddress(1, 1, 0, 9));
sheet.addMergedRegion(new CellRangeAddress(2, 2, 0, 9));
double[] widths = {5.14, 14.14, 21.29, 15.71, 27, 40, 50.43, 13.71, 22.57, 15.43};
for (int i = 0; i < widths.length; i++) {
sheet.setColumnWidth(i, (int) (widths[i] * 256));
}
CellStyle attachStyle = createStyle(workbook, "宋体", (short) 12, false, HorizontalAlignment.LEFT, false, false);
CellStyle titleStyle = createStyle(workbook, "黑体", (short) 18, true, HorizontalAlignment.CENTER, false, false);
CellStyle unionStyle = createStyle(workbook, "仿宋_GB2312", (short) 14, true, HorizontalAlignment.LEFT, false, false);
unionStyle.setBorderBottom(BorderStyle.THIN);
CellStyle headerStyle = createStyle(workbook, "宋体", (short) 12, true, HorizontalAlignment.CENTER, true, true);
CellStyle bodyStyle = createStyle(workbook, "宋体", (short) 12, false, HorizontalAlignment.CENTER, false, true);
Row row1 = sheet.createRow(0);
row1.setHeightInPoints(32);
setCell(row1, 0, "附件12", attachStyle);
fillMergedCells(row1, 1, 9, attachStyle);
Row row2 = sheet.createRow(1);
row2.setHeightInPoints(32);
setCell(row2, 0, "5天外超出部分疗休养费用由单位承担申请人员汇总表", titleStyle);
fillMergedCells(row2, 1, 9, titleStyle);
Row row3 = sheet.createRow(2);
row3.setHeightInPoints(32);
setCell(row3, 0, "分工会:" + StrUtil.blankToDefault(unionName, ""), unionStyle);
fillMergedCells(row3, 1, 9, unionStyle);
String[] headers = {"序号", "姓名", "身份证号码", "电话号码", "所属分工会", "所选线路名称", "疗休养时间", "疗休养时长", "疗休养费用总额(元)", "备注"};
Row header = sheet.createRow(3);
header.setHeightInPoints(43);
for (int i = 0; i < headers.length; i++) {
setCell(header, i, headers[i], headerStyle);
}
int rowCount = Math.max(list == null ? 0 : list.size(), 20);
for (int i = 0; i < rowCount; i++) {
Row row = sheet.createRow(i + 4);
row.setHeightInPoints(25);
NutMap item = list != null && i < list.size() ? list.get(i) : null;
setCell(row, 0, String.valueOf(i + 1), bodyStyle);
setCell(row, 1, item == null ? "" : item.getString("userName", ""), bodyStyle);
setCell(row, 2, item == null ? "" : item.getString("idCard", ""), bodyStyle);
setCell(row, 3, item == null ? "" : item.getString("mobile", ""), bodyStyle);
setCell(row, 4, item == null ? "" : item.getString("unionName", ""), bodyStyle);
setCell(row, 5, item == null ? "" : item.getString("lineName", ""), bodyStyle);
setCell(row, 6, item == null ? "" : item.getString("travelPeriod", ""), bodyStyle);
setCell(row, 7, item == null ? "" : item.getString("totalDays", ""), bodyStyle);
setCell(row, 8, item == null ? "" : item.getString("estimatedCostText", ""), bodyStyle);
setCell(row, 9, "", bodyStyle);
}
return workbook;
}
private CellStyle createStyle(Workbook workbook, String fontName, short fontSize, boolean bold, HorizontalAlignment alignment, boolean wrap, boolean border) {
Font font = workbook.createFont();
font.setFontName(fontName);
font.setFontHeightInPoints(fontSize);
font.setBold(bold);
CellStyle style = workbook.createCellStyle();
style.setFont(font);
style.setAlignment(alignment);
style.setVerticalAlignment(VerticalAlignment.CENTER);
style.setWrapText(wrap);
if (border) {
style.setBorderLeft(BorderStyle.THIN);
style.setBorderTop(BorderStyle.THIN);
style.setBorderRight(BorderStyle.THIN);
style.setBorderBottom(BorderStyle.THIN);
}
return style;
}
private void setCell(Row row, int col, String value, CellStyle style) {
Cell cell = row.createCell(col);
cell.setCellValue(StrUtil.blankToDefault(value, ""));
cell.setCellStyle(style);
}
private void fillMergedCells(Row row, int startCol, int endCol, CellStyle style) {
for (int i = startCol; i <= endCol; i++) {
setCell(row, i, "", style);
}
}
private Integer calcDays(String start, String end) {
if (StrUtil.isBlank(start) || StrUtil.isBlank(end)) {
return null;
}
try {
LocalDate startDate = LocalDate.parse(start.substring(0, 10));
LocalDate endDate = LocalDate.parse(end.substring(0, 10));
return Math.toIntExact(ChronoUnit.DAYS.between(startDate, endDate) + 1);
} catch (Exception e) {
return null;
}
}
private String formatAmount(Object value) {
if (value == null) {
return "";
}
try {
BigDecimal amount = new BigDecimal(String.valueOf(value));
return amount.stripTrailingZeros().toPlainString();
} catch (Exception e) {
return String.valueOf(value);
}
}
private Cnd buildQueryCnd(String currentUnionId, Integer startYear, Integer endYear, String keyword, String unionId, String lineId, String travelPeriod, String lineType, String scopeType, Boolean overCostOnly) {
Cnd cnd = Cnd.NEW();
cnd.and("t.delFlag", "=", false);
cnd.andEX("t.`year`", ">=", startYear);
cnd.andEX("t.`year`", "<=", endYear == null ? LocalDate.now().getYear() : endYear);
cnd.andEX("t.unionId", "=", unionId);
cnd.andEX("t.lineType", "=", lineType);
if (Boolean.TRUE.equals(overCostOnly)) {
cnd.and("t.overCostReimbursed", "=", true);
}
appendLineFilter(cnd, lineId);
appendTravelPeriodFilter(cnd, travelPeriod);
appendScopeFilter(cnd, currentUnionId, scopeType);
appendKeywordFilter(cnd, keyword);
return cnd;
}
private Cnd buildSummaryCnd(String currentUnionId, Integer startYear, Integer endYear, String unionId, String lineId, String travelPeriod, String lineType, String scopeType) {
Cnd cnd = Cnd.NEW();
cnd.and("t.delFlag", "=", false);
cnd.andEX("t.`year`", ">=", startYear);
cnd.andEX("t.`year`", "<=", endYear == null ? LocalDate.now().getYear() : endYear);
cnd.andEX("t.unionId", "=", unionId);
cnd.andEX("t.lineType", "=", lineType);
appendLineFilter(cnd, lineId);
appendTravelPeriodFilter(cnd, travelPeriod);
appendScopeFilter(cnd, currentUnionId, scopeType);
return cnd;
}
private void appendKeywordFilter(Cnd cnd, String keyword) {
if (StrUtil.isNotBlank(keyword)) {
SqlExpressionGroup group = new SqlExpressionGroup();
group.orLike("t.userName", keyword.trim());
group.orLike("t.jobNo", keyword.trim());
cnd.and(group);
}
}
private void applyUnionLedgerScope(Cnd cnd, String currentUnionId) {
SqlExpressionGroup group = new SqlExpressionGroup();
group.or("t.unionId", "=", currentUnionId);
group.or("m.unionId", "=", currentUnionId);
cnd.and(group);
}
private void appendScopeFilter(Cnd cnd, String currentUnionId, String scopeType) {
if ("ownUnionJoined".equals(scopeType)) {
cnd.and("t.unionId", "=", currentUnionId);
return;
}
if ("organizedLineJoined".equals(scopeType)) {
cnd.and("m.unionId", "=", currentUnionId);
return;
}
applyUnionLedgerScope(cnd, currentUnionId);
}
private String buildYearCondition(Integer startYear, Integer endYear) {
StringBuilder builder = new StringBuilder();
if (startYear != null) {
builder.append(" AND t.`year` >= ").append(startYear);
}
if (endYear != null) {
builder.append(" AND t.`year` <= ").append(endYear);
}
return builder.toString();
}
private TourLedger fetchScopedLedger(String id) {
if (StrUtil.isBlank(id) || StrUtil.isBlank(currentUnionId())) {
return null;
}
Sql sql = Sqls.create("""
SELECT COUNT(1)
FROM tour_ledger t
LEFT JOIN tour_matter m ON m.id = t.matterId AND m.delFlag = 0
WHERE t.delFlag = 0
AND t.id = @id
AND (t.unionId = @unionId OR m.unionId = @unionId)
""");
sql.setParam("id", id);
sql.setParam("unionId", currentUnionId());
sql.setCallback(Sqls.callback.integer());
tourLedgerService.dao().execute(sql);
return sql.getInt() > 0 ? tourLedgerService.fetch(id) : null;
}
private String getTravelPeriod(TourLedger ledger) {
if (ledger == null || ledger.getYear() == null || StrUtil.isBlank(ledger.getLineId())) {
return "";
}
if (StrUtil.isNotBlank(ledger.getMatterId())) {
Sql exactSql = Sqls.create("""
SELECT CASE
WHEN IFNULL(travelStartTime, '') <> '' AND IFNULL(travelEndTime, '') <> ''
THEN CONCAT(travelStartTime, ' ', travelEndTime)
ELSE ''
END AS travelPeriod
FROM tour_matter
WHERE delFlag = 0
AND id = @matterId
""");
exactSql.setParam("matterId", ledger.getMatterId());
exactSql.setCallback(Sqls.callback.str());
tourLedgerService.dao().execute(exactSql);
return exactSql.getString();
}
Sql sql = Sqls.create("""
SELECT GROUP_CONCAT(
DISTINCT CASE
WHEN IFNULL(travelStartTime, '') <> '' AND IFNULL(travelEndTime, '') <> ''
THEN CONCAT(travelStartTime, ' ', travelEndTime)
ELSE NULL
END
ORDER BY travelStartTime ASC
SEPARATOR ''
) AS travelPeriod
FROM tour_matter
WHERE delFlag = 0
AND `year` = @year
AND lineId = @lineId
""");
sql.setParam("year", ledger.getYear());
sql.setParam("lineId", ledger.getLineId());
sql.setCallback(Sqls.callback.str());
tourLedgerService.dao().execute(sql);
return sql.getString();
}
private boolean isDirectFamilyUnitLine(TourLedger ledger) {
if (ledger == null || StrUtil.isBlank(ledger.getLineId())) {
return false;
}
Sql sql = Sqls.create("""
SELECT IFNULL(directFamilyUnitLine, 0)
FROM tour_line
WHERE delFlag = 0
AND id = @lineId
""");
sql.setParam("lineId", ledger.getLineId());
sql.setCallback(Sqls.callback.integer());
tourLedgerService.dao().execute(sql);
return sql.getInt() > 0;
}
private boolean isFillBedInfo(TourLedger ledger) {
if (ledger == null || StrUtil.isBlank(ledger.getMatterId())) {
return true;
}
Sql sql = Sqls.create("""
SELECT IFNULL(MAX(s.fillBedInfo), 1)
FROM tour_ledger t
LEFT JOIN tour_matter m ON m.id = t.matterId AND m.delFlag = 0
LEFT JOIN tour_setting s ON s.id = m.settingId AND s.delFlag = 0
WHERE t.id = @ledgerId
""");
sql.setParam("ledgerId", ledger.getId());
sql.setCallback(Sqls.callback.integer());
tourLedgerService.dao().execute(sql);
return sql.getInt() > 0;
}
private void appendTravelPeriodFilter(Cnd cnd, String travelPeriod) {
if (StrUtil.isBlank(travelPeriod)) {
return;
}
cnd.and("CONCAT(m.travelStartTime, ' 至 ', m.travelEndTime)", "=", travelPeriod);
}
private void appendLineFilter(Cnd cnd, String lineId) {
if (StrUtil.isBlank(lineId)) {
return;
}
LineFilter lineFilter = parseLineFilter(lineId);
if (lineFilter.getLineId().startsWith("legacy:")) {
SqlExpressionGroup legacyGroup = new SqlExpressionGroup();
legacyGroup.or("t.lineId", "IS", null);
legacyGroup.or("t.lineId", "=", "");
cnd.and(legacyGroup);
cnd.and("t.lineName", "=", lineFilter.getLineId().substring("legacy:".length()));
} else {
cnd.and("t.lineId", "=", lineFilter.getLineId());
}
appendMatterUnionFilter(cnd, lineFilter.getUnionId());
}
private LineFilter parseLineFilter(String value) {
int delimiterIndex = value.indexOf("|");
if (delimiterIndex < 0) {
return new LineFilter(value, null);
}
return new LineFilter(value.substring(0, delimiterIndex), value.substring(delimiterIndex + 1));
}
private void appendMatterUnionFilter(Cnd cnd, String unionId) {
if (unionId == null) {
return;
}
if (StrUtil.isBlank(unionId)) {
SqlExpressionGroup group = new SqlExpressionGroup();
group.or("m.unionId", "IS", null);
group.or("m.unionId", "=", "");
cnd.and(group);
return;
}
cnd.and("m.unionId", "=", unionId);
}
private String getCurrentLineName(TourLedger ledger) {
if (ledger == null) {
return "";
}
Sql sql = Sqls.create("""
SELECT CASE
WHEN m.id IS NULL THEN COALESCE(NULLIF(l.lineName, ''), t.lineName)
ELSE CONCAT(
COALESCE(NULLIF(l.lineName, ''), t.lineName),
'',
CASE
WHEN IFNULL(m.unionId, '') = '' THEN '校工会'
ELSE IFNULL(mu.name, '')
END,
''
)
END AS lineName
FROM tour_ledger t
LEFT JOIN tour_matter m ON m.id = t.matterId AND m.delFlag = 0
LEFT JOIN tour_line l ON l.id = t.lineId AND l.delFlag = 0
LEFT JOIN sys_union mu ON mu.id = m.unionId
WHERE t.id = @ledgerId
""");
sql.setParam("ledgerId", ledger.getId());
sql.setCallback(Sqls.callback.str());
tourLedgerService.dao().execute(sql);
return StrUtil.blankToDefault(sql.getString(), ledger.getLineName());
}
private static class LineFilter {
private final String lineId;
private final String unionId;
private LineFilter(String lineId, String unionId) {
this.lineId = lineId;
this.unionId = unionId;
}
private String getLineId() {
return lineId;
}
private String getUnionId() {
return unionId;
}
}
private String currentUnionId() {
return SecurityUtil.getUnionId();
}
private String currentUnionName() {
String unionId = currentUnionId();
if (StrUtil.isBlank(unionId)) {
return "";
}
Sys_union union = tourLedgerService.dao().fetch(Sys_union.class, unionId);
return union == null ? "" : StrUtil.blankToDefault(union.getName(), "");
}
private String getOrderColumn(String orderName) {
if ("year".equals(orderName)) {
return "t.`year`";
}
if ("jobNo".equals(orderName)) {
return "t.jobNo";
}
if ("userName".equals(orderName)) {
return "t.userName";
}
if ("lineName".equals(orderName)) {
return """
CASE
WHEN m.id IS NULL THEN COALESCE(NULLIF(l.lineName, ''), t.lineName)
ELSE CONCAT(
COALESCE(NULLIF(l.lineName, ''), t.lineName),
'',
CASE
WHEN IFNULL(m.unionId, '') = '' THEN '校工会'
ELSE IFNULL(mu.name, '')
END,
''
)
END
""";
}
if ("travelPeriod".equals(orderName)) {
return "COALESCE(m.travelStartTime, tp.travelPeriod)";
}
if ("lineType".equals(orderName)) {
return "t.lineType";
}
if ("signupTime".equals(orderName)) {
return "t.signupTime";
}
if ("familyCount".equals(orderName)) {
return "familyCount";
}
if ("joined".equals(orderName)) {
return "t.joined";
}
return "t.`year`";
}
}
@@ -0,0 +1,34 @@
package com.budwk.app.zhgh.dayofficework.tour.mode;
import cn.afterturn.easypoi.excel.annotation.Excel;
import com.budwk.app.base.model.ExcelImportError;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.Date;
@EqualsAndHashCode(callSuper = true)
@Data
public class TourLedgerImportExcelMode extends ExcelImportError {
@Excel(name = "年度")
private Integer year;
@Excel(name = "工号")
private String jobNo;
@Excel(name = "姓名")
private String userName;
@Excel(name = "报名时间", format = "yyyy-MM-dd HH:mm:ss")
private Date signupTime;
@Excel(name = "线路名称")
private String lineName;
@Excel(name = "线路类型")
private String lineType;
@Excel(name = "是否参加")
private String joined;
}
@@ -0,0 +1,152 @@
package com.budwk.app.zhgh.dayofficework.tour.models;
import com.budwk.app.base.model.BaseModel;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.nutz.dao.entity.annotation.*;
import org.nutz.dao.interceptor.annotation.PrevInsert;
import java.io.Serializable;
/**
* 疗休养教职工报名台账
* 后续报名模块完成后将已报名或已参加的教职工写入本表台账页负责跨年度查询和详情查看
*/
@Data
@EqualsAndHashCode(callSuper = true)
@Table("tour_ledger")
@TableMeta("{'mysql-charset':'utf8mb4'}")
@Comment("普惠疗休养台账")
public class TourLedger extends BaseModel implements Serializable {
@Column
@Name
@Comment("ID")
@ColDefine(type = ColType.VARCHAR, width = 32)
@PrevInsert(uu32 = true)
private String id;
@Column
@Comment("年度")
@ColDefine(type = ColType.INT, width = 4)
private Integer year;
@Column
@Comment("工号")
@ColDefine(type = ColType.VARCHAR, width = 50)
private String jobNo;
@Column
@Comment("姓名")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String userName;
@Column
@Comment("性别")
@ColDefine(type = ColType.VARCHAR, width = 20)
private String gender;
@Column
@Comment("身份证号")
@ColDefine(type = ColType.VARCHAR, width = 30)
private String idCard;
@Column
@Comment("所在单位ID")
@ColDefine(type = ColType.VARCHAR, width = 32)
private String unitId;
@Column
@Comment("所在单位")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String unitName;
@Column
@Comment("所在工会ID")
@ColDefine(type = ColType.VARCHAR, width = 32)
private String unionId;
@Column
@Comment("所在工会")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String unionName;
@Column
@Comment("报名时间")
@ColDefine(type = ColType.VARCHAR, width = 30)
private String signupTime;
@Column
@Comment("报名事项ID")
@ColDefine(type = ColType.VARCHAR, width = 32)
private String matterId;
@Column
@Comment("报名线路ID")
@ColDefine(type = ColType.VARCHAR, width = 32)
private String lineId;
@Column
@Comment("报名线路")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String lineName;
@Column
@Comment("线路类型")
@ColDefine(type = ColType.VARCHAR, width = 50)
private String lineType;
@Column
@Comment("报名酒店")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String hotelName;
@Column
@Comment("旅行社ID")
@ColDefine(type = ColType.VARCHAR, width = 32)
private String travelAgencyId;
@Column
@Comment("旅行社")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String travelAgencyName;
@Column
@Comment("是否携带家属")
@ColDefine(type = ColType.BOOLEAN)
@Default("0")
private Boolean hasFamily;
@Column
@Comment("意向拼床人")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String intendedRoommate;
@Column
@Comment("床型")
@ColDefine(type = ColType.VARCHAR, width = 50)
private String bedType;
@Column
@Comment("床位信息")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String bedInfo;
@Column
@Comment("是否参加")
@ColDefine(type = ColType.BOOLEAN)
@Default("0")
private Boolean joined;
@Column
@Comment("是否报销")
@ColDefine(type = ColType.BOOLEAN)
@Default("0")
private Boolean reimbursed;
@Column
@Comment("报销超出费用")
@ColDefine(type = ColType.BOOLEAN)
@Default("0")
private Boolean overCostReimbursed;
}
@@ -0,0 +1,73 @@
package com.budwk.app.zhgh.dayofficework.tour.models;
import com.budwk.app.base.model.BaseModel;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.nutz.dao.entity.annotation.*;
import org.nutz.dao.interceptor.annotation.PrevInsert;
import java.io.Serializable;
/**
* 直系亲属线路报名信息
* 与普通携带亲属信息分表存放用于直系亲属线路的专属申请信息
*/
@Data
@EqualsAndHashCode(callSuper = true)
@Table("tour_ledger_direct_relative")
@TableMeta("{'mysql-charset':'utf8mb4'}")
@Comment("普惠疗休养直系亲属线路报名信息")
public class TourLedgerDirectRelative extends BaseModel implements Serializable {
@Column
@Name
@Comment("ID")
@ColDefine(type = ColType.VARCHAR, width = 32)
@PrevInsert(uu32 = true)
private String id;
@Column
@Comment("台账ID")
@ColDefine(type = ColType.VARCHAR, width = 32)
private String ledgerId;
@Column
@Comment("亲属姓名")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String relativeName;
@Column
@Comment("所在单位")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String unitName;
@Column
@Comment("亲属关系编码")
@ColDefine(type = ColType.VARCHAR, width = 50)
private String relationshipCode;
@Column
@Comment("亲属关系")
@ColDefine(type = ColType.VARCHAR, width = 50)
private String relationshipName;
@Column
@Comment("线路ID")
@ColDefine(type = ColType.VARCHAR, width = 32)
private String lineId;
@Column
@Comment("线路名称")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String lineName;
@Column
@Comment("出行开始日期")
@ColDefine(type = ColType.VARCHAR, width = 20)
private String travelStartTime;
@Column
@Comment("出行结束日期")
@ColDefine(type = ColType.VARCHAR, width = 20)
private String travelEndTime;
}
@@ -0,0 +1,83 @@
package com.budwk.app.zhgh.dayofficework.tour.models;
import com.budwk.app.base.model.BaseModel;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.nutz.dao.entity.annotation.*;
import org.nutz.dao.interceptor.annotation.PrevInsert;
import java.io.Serializable;
/**
* 疗休养台账家属信息
* 与教职工台账通过 ledgerId 关联用于查看教职工携带家属的历史记录
*/
@Data
@EqualsAndHashCode(callSuper = true)
@Table("tour_ledger_family")
@TableMeta("{'mysql-charset':'utf8mb4'}")
@Comment("普惠疗休养台账家属信息")
public class TourLedgerFamily extends BaseModel implements Serializable {
@Column
@Name
@Comment("ID")
@ColDefine(type = ColType.VARCHAR, width = 32)
@PrevInsert(uu32 = true)
private String id;
@Column
@Comment("台账ID")
@ColDefine(type = ColType.VARCHAR, width = 32)
private String ledgerId;
@Column
@Comment("教职工工号")
@ColDefine(type = ColType.VARCHAR, width = 50)
private String staffJobNo;
@Column
@Comment("教职工姓名")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String staffName;
@Column
@Comment("家属姓名")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String familyName;
@Column
@Comment("性别")
@ColDefine(type = ColType.VARCHAR, width = 20)
private String gender;
@Column
@Comment("身份证号码")
@ColDefine(type = ColType.VARCHAR, width = 30)
private String idCard;
@Column
@Comment("年龄")
@ColDefine(type = ColType.INT, width = 3)
private Integer age;
@Column
@Comment("床型")
@ColDefine(type = ColType.VARCHAR, width = 50)
private String bedType;
@Column
@Comment("床位")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String bedInfo;
@Column
@Comment("意向拼床人")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String intendedRoommate;
@Column
@Comment("关系")
@ColDefine(type = ColType.VARCHAR, width = 50)
private String relationship;
}
@@ -0,0 +1,106 @@
package com.budwk.app.zhgh.dayofficework.tour.models;
import com.budwk.app.base.model.BaseModel;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.nutz.dao.entity.annotation.*;
import org.nutz.dao.interceptor.annotation.PrevInsert;
import java.io.Serializable;
/**
* 疗休养线路管理
* 当前阶段先维护线路基础信息后续报名台账等模块可通过线路ID继续关联
*/
@Data
@EqualsAndHashCode(callSuper = true)
@Table("tour_line")
@TableMeta("{'mysql-charset':'utf8mb4'}")
@Comment("普惠疗休养线路")
public class TourLine extends BaseModel implements Serializable {
@Column
@Name
@Comment("ID")
@ColDefine(type = ColType.VARCHAR, width = 32)
@PrevInsert(uu32 = true)
private String id;
@Column
@Comment("创建年度")
@ColDefine(type = ColType.INT, width = 4)
private Integer year;
@Column
@Comment("线路编号")
@ColDefine(type = ColType.VARCHAR, width = 50)
private String lineCode;
@Column
@Comment("线路名称")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String lineName;
@Column
@Comment("旅行社ID")
@ColDefine(type = ColType.VARCHAR, width = 32)
private String travelAgencyId;
@Column
@Comment("创建人ID")
@ColDefine(type = ColType.VARCHAR, width = 32)
private String creatorUserId;
@Column
@Comment("创建人")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String creatorName;
@Column
@Comment("所在单位ID")
@ColDefine(type = ColType.VARCHAR, width = 32)
private String unitId;
@Column
@Comment("所在单位")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String unitName;
@Column
@Comment("线路类型")
@ColDefine(type = ColType.VARCHAR, width = 50)
private String lineType;
@Column
@Comment("时间标段ID")
@ColDefine(type = ColType.VARCHAR, width = 32)
private String lotId;
@Column
@Comment("线路内容")
@ColDefine(type = ColType.TEXT)
private String lineContent;
@Column
@Comment("移动端缩略图")
@ColDefine(type = ColType.VARCHAR, width = 500)
private String mobileThumb;
@Column
@Comment("是否对外开放")
@ColDefine(type = ColType.BOOLEAN)
@Default("1")
private Boolean openFlag;
@Column
@Comment("是否直系亲属线路")
@ColDefine(type = ColType.BOOLEAN)
@Default("0")
private Boolean directFamilyUnitLine;
@Column
@Comment("激活状态")
@ColDefine(type = ColType.BOOLEAN)
@Default("1")
private Boolean enabled;
}
@@ -0,0 +1,124 @@
package com.budwk.app.zhgh.dayofficework.tour.models;
import com.budwk.app.base.model.BaseModel;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.nutz.dao.entity.annotation.*;
import org.nutz.dao.interceptor.annotation.PrevInsert;
import java.io.Serializable;
import java.math.BigDecimal;
/**
* 疗休养事项
*/
@Data
@EqualsAndHashCode(callSuper = true)
@Table("tour_matter")
@TableMeta("{'mysql-charset':'utf8mb4'}")
@Comment("普惠疗休养事项")
public class TourMatter extends BaseModel implements Serializable {
@Column
@Name
@Comment("ID")
@ColDefine(type = ColType.VARCHAR, width = 32)
@PrevInsert(uu32 = true)
private String id;
@Column
@Comment("年度")
@ColDefine(type = ColType.INT, width = 4)
private Integer year;
@Column
@Comment("事项名称")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String matterName;
@Column
@Comment("疗休养配置ID")
@ColDefine(type = ColType.VARCHAR, width = 32)
private String settingId;
@Column
@Comment("创建人ID")
@ColDefine(type = ColType.VARCHAR, width = 32)
private String creatorUserId;
@Column
@Comment("创建人")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String creatorName;
@Column
@Comment("所属工会ID")
@ColDefine(type = ColType.VARCHAR, width = 32)
private String unionId;
@Column
@Comment("组织形式")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String organizationType;
@Column
@Comment("移动端缩略图")
@ColDefine(type = ColType.VARCHAR, width = 500)
private String mobileThumb;
@Column
@Comment("线路ID")
@ColDefine(type = ColType.VARCHAR, width = 32)
private String lineId;
@Column
@Comment("报名开始时间")
@ColDefine(type = ColType.VARCHAR, width = 20)
private String signupStartTime;
@Column
@Comment("报名结束时间")
@ColDefine(type = ColType.VARCHAR, width = 20)
private String signupEndTime;
@Column
@Comment("出行开始时间")
@ColDefine(type = ColType.VARCHAR, width = 20)
private String travelStartTime;
@Column
@Comment("出行结束时间")
@ColDefine(type = ColType.VARCHAR, width = 20)
private String travelEndTime;
@Column
@Comment("联系人")
@ColDefine(type = ColType.VARCHAR, width = 30)
private String contactName;
@Column
@Comment("联系方式")
@ColDefine(type = ColType.VARCHAR, width = 30)
private String contactPhone;
@Column
@Comment("最少成团人数")
@ColDefine(type = ColType.INT)
private Integer minGroupPeople;
@Column
@Comment("最多成团人数")
@ColDefine(type = ColType.INT)
private Integer maxGroupPeople;
@Column
@Comment("预计费用")
@ColDefine(customType = "decimal(10,2)")
private BigDecimal estimatedCost;
@Column
@Comment("事项状态")
@ColDefine(type = ColType.BOOLEAN)
@Default("1")
private Boolean enabled;
}
@@ -0,0 +1,134 @@
package com.budwk.app.zhgh.dayofficework.tour.models;
import com.budwk.app.base.model.BaseModel;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.nutz.dao.entity.annotation.*;
import org.nutz.dao.interceptor.annotation.PrevInsert;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.List;
/**
* 疗休养基础配置
* 这里先沉淀创建事项会复用的基础字段后续线路报名等阶段可以继续通过配置ID关联扩展
*/
@Data
@EqualsAndHashCode(callSuper = true)
@Table("tour_setting")
@TableMeta("{'mysql-charset':'utf8mb4'}")
@Comment("普惠疗休养配置")
public class TourSetting extends BaseModel implements Serializable {
@Column
@Name
@Comment("ID")
@ColDefine(type = ColType.VARCHAR, width = 32)
@PrevInsert(uu32 = true)
private String id;
@Column
@Comment("年度")
@ColDefine(type = ColType.INT, width = 4)
private Integer year;
@Column
@Comment("疗休养配置名称")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String configName;
@Column
@Comment("疗休养类型")
@ColDefine(type = ColType.VARCHAR, width = 50)
private String tourType;
@Column
@Comment("可参加人员范围ID")
@ColDefine(type = ColType.VARCHAR, width = 32)
private String activityGroupId;
@Column
@Comment("排序编号")
@ColDefine(type = ColType.INT)
private Integer sortNo;
@Column
@Comment("最少成团人数")
@ColDefine(type = ColType.INT)
private Integer minGroupPeople;
@Column
@Comment("最多成团人数")
@ColDefine(type = ColType.INT)
private Integer maxGroupPeople;
@Column
@Comment("省外几年去一次")
@ColDefine(type = ColType.INT)
private Integer outProvinceYears;
@Column
@Comment("省外人数占比")
@ColDefine(customType = "decimal(10,2)")
private BigDecimal outProvinceRatio;
@Column
@Comment("省外人数占比类型")
@ColDefine(type = ColType.VARCHAR, width = 50)
private String outProvinceRatioType;
@Column
@Comment("省外固定人数")
@ColDefine(type = ColType.INT)
private Integer outProvinceFixedPeople;
@Column
@Comment("周期开始年度")
@ColDefine(type = ColType.INT, width = 4)
private Integer cycleStartYear;
@Column
@Comment("周期结束年度")
@ColDefine(type = ColType.INT, width = 4)
private Integer cycleEndYear;
@Column
@Comment("周期内总费用")
@ColDefine(type = ColType.INT)
private Integer cycleTotalCost;
@Column
@Comment("周期允许次数")
@ColDefine(type = ColType.INT)
private Integer cycleAllowedTimes;
@Column
@Comment("是否允许携带家属")
@ColDefine(type = ColType.BOOLEAN)
@Default("0")
private Boolean allowFamily;
@Column
@Comment("是否填报床位信息")
@ColDefine(type = ColType.BOOLEAN)
@Default("1")
private Boolean fillBedInfo;
@Column
@Comment("是否启用")
@ColDefine(type = ColType.BOOLEAN)
@Default("1")
private Boolean enabled;
/**
* 标段管理沿用老疗休养配置的子表设计后续线路旅行社等模块可通过标段ID继续关联
*/
@Many(field = "settingId")
private List<TourSettingLot> lots;
@Column
@Comment("服务须知")
@ColDefine(type = ColType.TEXT)
private String serviceNotice;
}
@@ -0,0 +1,53 @@
package com.budwk.app.zhgh.dayofficework.tour.models;
import com.budwk.app.base.model.BaseModel;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.nutz.dao.entity.annotation.*;
import org.nutz.dao.interceptor.annotation.PrevInsert;
import java.io.Serializable;
/**
* 疗休养配置标段
* 标段从基础配置中拆成子表便于后续线路目的地报名等模块复用同一标段口径
*/
@Data
@EqualsAndHashCode(callSuper = true)
@Table("tour_setting_lot")
@TableMeta("{'mysql-charset':'utf8mb4'}")
@Comment("普惠疗休养配置标段")
public class TourSettingLot extends BaseModel implements Serializable {
@Column
@Name
@Comment("ID")
@ColDefine(type = ColType.VARCHAR, width = 32)
@PrevInsert(uu32 = true)
private String id;
@Column
@Comment("所属配置ID")
@ColDefine(type = ColType.VARCHAR, width = 32)
private String settingId;
@Column
@Comment("标段名称")
@ColDefine(type = ColType.VARCHAR, width = 50)
private String lotName;
@Column
@Comment("标段值")
@ColDefine(type = ColType.VARCHAR, width = 50)
private String lotValue;
@Column
@Comment("标段费用")
@ColDefine(type = ColType.INT)
private Integer activityCost;
@Column
@Comment("允许超出报销")
@ColDefine(type = ColType.BOOLEAN)
private Boolean allowOverReimbursement;
}
@@ -0,0 +1,74 @@
package com.budwk.app.zhgh.dayofficework.tour.models;
import com.budwk.app.base.model.BaseModel;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.nutz.dao.entity.annotation.*;
import org.nutz.dao.interceptor.annotation.PrevInsert;
import java.io.Serializable;
/**
* 旅行社管理
* 先维护疗休养线路创建会复用的旅行社基础信息后续线路模块可通过旅行社ID关联
*/
@Data
@EqualsAndHashCode(callSuper = true)
@Table("tour_travel_agency")
@TableMeta("{'mysql-charset':'utf8mb4'}")
@Comment("普惠疗休养旅行社")
public class TourTravelAgency extends BaseModel implements Serializable {
@Column
@Name
@Comment("ID")
@ColDefine(type = ColType.VARCHAR, width = 32)
@PrevInsert(uu32 = true)
private String id;
@Column
@Comment("年度")
@ColDefine(type = ColType.INT, width = 4)
private Integer year;
@Column
@Comment("旅行社编号")
@ColDefine(type = ColType.VARCHAR, width = 50)
private String agencyCode;
@Column
@Comment("旅行社名称")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String agencyName;
@Column
@Comment("联系人")
@ColDefine(type = ColType.VARCHAR, width = 30)
private String contactName;
@Column
@Comment("联系人手机")
@ColDefine(type = ColType.VARCHAR, width = 30)
private String contactPhone;
@Column
@Comment("邮箱")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String email;
@Column
@Comment("备注")
@ColDefine(type = ColType.TEXT)
private String remark;
@Column
@Comment("移动端缩略图")
@ColDefine(type = ColType.VARCHAR, width = 500)
private String mobileThumb;
@Column
@Comment("激活状态")
@ColDefine(type = ColType.BOOLEAN)
@Default("1")
private Boolean enabled;
}
@@ -0,0 +1,7 @@
package com.budwk.app.zhgh.dayofficework.tour.service;
import com.budwk.app.base.service.BaseService;
import com.budwk.app.zhgh.dayofficework.tour.models.TourLedgerDirectRelative;
public interface TourLedgerDirectRelativeService extends BaseService<TourLedgerDirectRelative> {
}
@@ -0,0 +1,7 @@
package com.budwk.app.zhgh.dayofficework.tour.service;
import com.budwk.app.base.service.BaseService;
import com.budwk.app.zhgh.dayofficework.tour.models.TourLedgerFamily;
public interface TourLedgerFamilyService extends BaseService<TourLedgerFamily> {
}
@@ -0,0 +1,7 @@
package com.budwk.app.zhgh.dayofficework.tour.service;
import com.budwk.app.base.service.BaseService;
import com.budwk.app.zhgh.dayofficework.tour.models.TourLedger;
public interface TourLedgerService extends BaseService<TourLedger> {
}
@@ -0,0 +1,7 @@
package com.budwk.app.zhgh.dayofficework.tour.service;
import com.budwk.app.base.service.BaseService;
import com.budwk.app.zhgh.dayofficework.tour.models.TourLine;
public interface TourLineService extends BaseService<TourLine> {
}
@@ -0,0 +1,7 @@
package com.budwk.app.zhgh.dayofficework.tour.service;
import com.budwk.app.base.service.BaseService;
import com.budwk.app.zhgh.dayofficework.tour.models.TourMatter;
public interface TourMatterService extends BaseService<TourMatter> {
}
@@ -0,0 +1,7 @@
package com.budwk.app.zhgh.dayofficework.tour.service;
import com.budwk.app.base.service.BaseService;
import com.budwk.app.zhgh.dayofficework.tour.models.TourSetting;
public interface TourSettingService extends BaseService<TourSetting> {
}
@@ -0,0 +1,7 @@
package com.budwk.app.zhgh.dayofficework.tour.service;
import com.budwk.app.base.service.BaseService;
import com.budwk.app.zhgh.dayofficework.tour.models.TourTravelAgency;
public interface TourTravelAgencyService extends BaseService<TourTravelAgency> {
}
@@ -0,0 +1,15 @@
package com.budwk.app.zhgh.dayofficework.tour.service.impl;
import com.budwk.app.base.service.impl.BaseServiceImpl;
import com.budwk.app.zhgh.dayofficework.tour.models.TourLedgerDirectRelative;
import com.budwk.app.zhgh.dayofficework.tour.service.TourLedgerDirectRelativeService;
import org.nutz.dao.Dao;
import org.nutz.ioc.loader.annotation.IocBean;
@IocBean(args = {"refer:dao"})
public class TourLedgerDirectRelativeServiceImpl extends BaseServiceImpl<TourLedgerDirectRelative> implements TourLedgerDirectRelativeService {
public TourLedgerDirectRelativeServiceImpl(Dao dao) {
super(dao);
}
}
@@ -0,0 +1,15 @@
package com.budwk.app.zhgh.dayofficework.tour.service.impl;
import com.budwk.app.base.service.impl.BaseServiceImpl;
import com.budwk.app.zhgh.dayofficework.tour.models.TourLedgerFamily;
import com.budwk.app.zhgh.dayofficework.tour.service.TourLedgerFamilyService;
import org.nutz.dao.Dao;
import org.nutz.ioc.loader.annotation.IocBean;
@IocBean(args = {"refer:dao"})
public class TourLedgerFamilyServiceImpl extends BaseServiceImpl<TourLedgerFamily> implements TourLedgerFamilyService {
public TourLedgerFamilyServiceImpl(Dao dao) {
super(dao);
}
}
@@ -0,0 +1,15 @@
package com.budwk.app.zhgh.dayofficework.tour.service.impl;
import com.budwk.app.base.service.impl.BaseServiceImpl;
import com.budwk.app.zhgh.dayofficework.tour.models.TourLedger;
import com.budwk.app.zhgh.dayofficework.tour.service.TourLedgerService;
import org.nutz.dao.Dao;
import org.nutz.ioc.loader.annotation.IocBean;
@IocBean(args = {"refer:dao"})
public class TourLedgerServiceImpl extends BaseServiceImpl<TourLedger> implements TourLedgerService {
public TourLedgerServiceImpl(Dao dao) {
super(dao);
}
}
@@ -0,0 +1,15 @@
package com.budwk.app.zhgh.dayofficework.tour.service.impl;
import com.budwk.app.base.service.impl.BaseServiceImpl;
import com.budwk.app.zhgh.dayofficework.tour.models.TourLine;
import com.budwk.app.zhgh.dayofficework.tour.service.TourLineService;
import org.nutz.dao.Dao;
import org.nutz.ioc.loader.annotation.IocBean;
@IocBean(args = {"refer:dao"})
public class TourLineServiceImpl extends BaseServiceImpl<TourLine> implements TourLineService {
public TourLineServiceImpl(Dao dao) {
super(dao);
}
}
@@ -0,0 +1,15 @@
package com.budwk.app.zhgh.dayofficework.tour.service.impl;
import com.budwk.app.base.service.impl.BaseServiceImpl;
import com.budwk.app.zhgh.dayofficework.tour.models.TourMatter;
import com.budwk.app.zhgh.dayofficework.tour.service.TourMatterService;
import org.nutz.dao.Dao;
import org.nutz.ioc.loader.annotation.IocBean;
@IocBean(args = {"refer:dao"})
public class TourMatterServiceImpl extends BaseServiceImpl<TourMatter> implements TourMatterService {
public TourMatterServiceImpl(Dao dao) {
super(dao);
}
}
@@ -0,0 +1,15 @@
package com.budwk.app.zhgh.dayofficework.tour.service.impl;
import com.budwk.app.base.service.impl.BaseServiceImpl;
import com.budwk.app.zhgh.dayofficework.tour.models.TourSetting;
import com.budwk.app.zhgh.dayofficework.tour.service.TourSettingService;
import org.nutz.dao.Dao;
import org.nutz.ioc.loader.annotation.IocBean;
@IocBean(args = {"refer:dao"})
public class TourSettingServiceImpl extends BaseServiceImpl<TourSetting> implements TourSettingService {
public TourSettingServiceImpl(Dao dao) {
super(dao);
}
}
@@ -0,0 +1,15 @@
package com.budwk.app.zhgh.dayofficework.tour.service.impl;
import com.budwk.app.base.service.impl.BaseServiceImpl;
import com.budwk.app.zhgh.dayofficework.tour.models.TourTravelAgency;
import com.budwk.app.zhgh.dayofficework.tour.service.TourTravelAgencyService;
import org.nutz.dao.Dao;
import org.nutz.ioc.loader.annotation.IocBean;
@IocBean(args = {"refer:dao"})
public class TourTravelAgencyServiceImpl extends BaseServiceImpl<TourTravelAgency> implements TourTravelAgencyService {
public TourTravelAgencyServiceImpl(Dao dao) {
super(dao);
}
}
@@ -25,6 +25,7 @@ import org.nutz.dao.util.cri.SqlExpressionGroup;
import org.nutz.ioc.aop.Aop;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.Strings;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
@@ -68,11 +69,15 @@ public class TeacherCongressInstitutionController {
Teacher_congress_institution rootInstitution = new Teacher_congress_institution();
rootInstitution.setId("0");
rootInstitution.setParentId("");
rootInstitution.setName("代会机构");
rootInstitution.setName("代会组织机构");
rootInstitution.setLocation(0);
institutionList.add(rootInstitution);
List<TreeNode<String>> treeNodes = institutionList.stream().map(institution -> new TreeNode<>(institution.getId(), institution.getParentId(), institution.getName(), institution.getLocation())).toList();
List<TreeNode<String>> treeNodes = institutionList.stream().map(institution -> {
TreeNode<String> treeNode = new TreeNode<>(institution.getId(), institution.getParentId(), institution.getName(), institution.getLocation());
treeNode.setExtra(Map.of("code", Strings.sNull(institution.getCode())));
return treeNode;
}).toList();
List<Tree<String>> treeList = TreeUtil.build(treeNodes, "");
return Result.success(treeList);
}
@@ -97,6 +102,16 @@ public class TeacherCongressInstitutionController {
return Result.success(Map.of("treeFlat", children, "treeList", treeList));
}
@At
@SaCheckPermission("tc.institution")
public Result specialCommitteeRoleOptions() {
Sys_dict parent = sysDictService.fetch(Cnd.where(Sys_dict::getCode, "=", "SPECIAL_COMMITTEE_ROLES"));
if (ObjectUtil.isEmpty(parent)) {
return Result.success(List.of());
}
List<Sys_dict> children = sysDictService.query(Cnd.where(Sys_dict::getPath, "like", parent.getPath() + "%").and(Sys_dict::getId, "!=", parent.getId()).asc(Sys_dict::getLocation));
return Result.success(children);
}
/**
* 机构分页查询
@@ -113,6 +128,7 @@ public class TeacherCongressInstitutionController {
cnd.and("parentId", "=", parentId);
cnd.and("sessionId", "=", sessionId);
cnd.asc("location");
cnd.asc("code");
Pagination pagination = baseService.listPage(pageForm.getPageNumber(), pageForm.getPageSize(), "teacher_congress_institution", cnd);
return Result.success(pagination);
}
@@ -129,7 +145,7 @@ public class TeacherCongressInstitutionController {
if (dao.count(Teacher_congress_institution.class, Cnd.where(Teacher_congress_institution::getCode, "=", institution.getCode()).and(Teacher_congress_institution::getSessionId, "=", institution.getSessionId())) > 0) {
return Result.error("机构已存在");
}
if (StrUtil.isNotBlank(institution.getParentId()) && !institution.getParentId().equals("0")) {
if (StrUtil.isNotBlank(institution.getParentId()) && !institution.getParentId().equals("0") && dao.count(Teacher_congress_institution.class, Cnd.where(Teacher_congress_institution::getId, "=", institution.getParentId()).and(Teacher_congress_institution::getSessionId, "=", institution.getSessionId())) == 0) {
Sys_dict sysDict = sysDictService.fetch(Cnd.where(Sys_dict::getId, "=", institution.getParentId()));
Teacher_congress_institution parentInstitution = dao.fetch(Teacher_congress_institution.class,
Cnd.where(Teacher_congress_institution::getCode, "=", sysDict.getCode())
@@ -151,10 +167,34 @@ public class TeacherCongressInstitutionController {
* @param id
* @return
*/
@At
@SaCheckPermission("tc.institution")
public Result update(Teacher_congress_institution institution) {
Teacher_congress_institution dbInstitution = dao.fetch(Teacher_congress_institution.class, institution.getId());
if (ObjectUtil.isEmpty(dbInstitution)) {
return Result.error("机构不存在");
}
dbInstitution.setName(institution.getName());
dbInstitution.setIntroduce(institution.getIntroduce());
dbInstitution.setLocation(institution.getLocation());
dao.update(dbInstitution);
return Result.success();
}
@At
@SaCheckPermission("tc.institution")
@Aop(TransAop.READ_COMMITTED)
public Result delete(@Valid String id) {
Teacher_congress_institution institution = dao.fetch(Teacher_congress_institution.class, id);
if (ObjectUtil.isEmpty(institution)) {
return Result.error("机构不存在");
}
List<Teacher_congress_institution> siblingList = dao.query(Teacher_congress_institution.class, Cnd.where(Teacher_congress_institution::getParentId, "=", institution.getParentId()).and(Teacher_congress_institution::getSessionId, "=", institution.getSessionId()).asc(Teacher_congress_institution::getLocation).asc(Teacher_congress_institution::getCode));
for (int i = 0; i < siblingList.size() && i < 6; i++) {
if (id.equals(siblingList.get(i).getId())) {
return Result.error("你选择的组织机构是两代会基本机构,不允许删除。");
}
}
dao.delete(Teacher_congress_institution.class, id);
dao.clear(Teacher_congress_institution_user.class, Cnd.where("institutionId", "=", id));
return Result.success();
@@ -214,8 +254,8 @@ public class TeacherCongressInstitutionController {
@SaCheckPermission("tc.institution")
@SLog(tag = "教代会-机构设置", msg = "添加人员")
@Aop(TransAop.READ_COMMITTED)
public Result userInsert(@Valid String userId, @Valid String institutionId, @Valid String sessionId, @Valid String identity) {
teacherCongressInstitutionUserService.userInsert(userId, institutionId, sessionId, identity);
public Result userInsert(@Valid String userId, @Valid String institutionId, @Valid String sessionId, @Valid String identity, String roleCode) {
teacherCongressInstitutionUserService.userInsert(userId, institutionId, sessionId, identity, roleCode);
return Result.success();
}
@@ -14,7 +14,7 @@ public interface TeacherCongressInstitutionUserService extends BaseService<Teach
* @param sessionId 届次id
* @param identity 身份 主任|副主任|成员
*/
void userInsert(String userId, String institutionId, String sessionId, String identity);
void userInsert(String userId, String institutionId, String sessionId, String identity, String roleCode);
/**
* 删除机构成员
@@ -1,5 +1,6 @@
package com.budwk.app.zhgh.democratic.teachercongress.institution.service.impl;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.constant.RoleConstant;
import com.budwk.app.base.exception.BaseException;
import com.budwk.app.base.service.impl.BaseServiceImpl;
@@ -30,7 +31,7 @@ public class TeacherCongressInstitutionUserServiceImpl extends BaseServiceImpl<T
@Override
@Aop(TransAop.READ_COMMITTED)
public void userInsert(String userId, String institutionId, String sessionId, String identity) {
public void userInsert(String userId, String institutionId, String sessionId, String identity, String roleCode) {
int count = dao().count(Teacher_congress_institution_user.class,
Cnd.where(Teacher_congress_institution_user::getInstitutionId, "=", institutionId)
.and(Teacher_congress_institution_user::getUserId, "=", userId)
@@ -49,6 +50,28 @@ public class TeacherCongressInstitutionUserServiceImpl extends BaseServiceImpl<T
dao().insert(institutionUser);
Teacher_congress_institution institution = dao().fetch(Teacher_congress_institution.class, institutionId);
if ("ZWH001".equals(institution.getCode()) || "ZXWYH".equals(institution.getCode()) || "DBZGSCXZ".equals(institution.getCode())) {
if (StrUtil.isBlank(roleCode)) {
throw new BaseException("请选择角色");
}
Sys_role sysRole = sysRoleService.fetch(Cnd.where(Sys_role::getCode, "=", roleCode));
if (sysRole == null) {
throw new BaseException("无法找到" + roleCode + "对应编码的角色");
}
int existsRole = dao().count(Sys_user_role.class, Cnd.where(Sys_user_role::getUserId, "=", userId)
.and(Sys_user_role::getTcSessionId, "=", sessionId)
.and(Sys_user_role::getRoleId, "=", sysRole.getId())
);
if (existsRole == 0) {
Sys_user_role insertSysUserRole = new Sys_user_role();
insertSysUserRole.setRoleId(sysRole.getId());
insertSysUserRole.setUserId(userId);
insertSysUserRole.setTcSessionId(sessionId);
dao().insert(insertSysUserRole);
sysRoleService.clearCache();
sysUserService.clearCache();
}
}
if (institution.getCode().contains("TEACHER_CONGRESS_INSTITUTION_PROPOSAL_COMMITTEE") && identity.equals("主任")) {
Sys_role sysRole = sysRoleService.getByCode(RoleConstant.PROPOSAL_COMMITTEE_DIRECTOR);
int existsRole = dao().count(Sys_user_role.class, Cnd.where(Sys_user_role::getUserId, "=", userId)
@@ -0,0 +1,383 @@
package com.budwk.app.zhgh.learning.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.annotation.SLog;
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.zhgh.learning.models.LearningCourse;
import com.budwk.app.zhgh.learning.models.LearningCourseOutline;
import com.budwk.app.zhgh.learning.models.LearningCourseType;
import com.budwk.app.zhgh.learning.models.LearningOutlineResource;
import com.budwk.app.zhgh.learning.models.LearningStudyRule;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.nutz.dao.Chain;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.dao.pager.Pager;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.util.NutMap;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@IocBean
@Ok("json:full")
@Api("学习教育平台-章节内容管理")
@At("/platform/learning/chapter/content")
public class LearningChapterContentController {
@Inject
private Dao dao;
@At("")
@Ok("beetl:/platform/zhgh/Learning/chapterContent/index.html")
@SaCheckPermission("learning.chapter.content")
public void index() {
}
@At
@ApiOperation("课程列表")
@SaCheckPermission("learning.chapter.content")
public Result courses(String courseId, String courseName, String courseTypeId, String lecturerName, String status) {
Cnd cnd = Cnd.NEW();
cnd.andEX("id", "=", courseId);
cnd.and(Cnd.likeEX("courseName", courseName));
cnd.andEX("courseTypeId", "=", courseTypeId);
cnd.and(Cnd.likeEX("lecturerName", lecturerName));
cnd.andEX("status", "=", status);
cnd.desc("createdAt");
return Result.success(dao.query(LearningCourse.class, cnd, dao.createPager(1, 50)));
}
@At
@ApiOperation("课程类型")
@SaCheckPermission("learning.chapter.content")
public Result courseTypes() {
return Result.success(dao.query(LearningCourseType.class, Cnd.where("enabled", "=", true).asc("sortNum").asc("createdAt")));
}
@At
@ApiOperation("课程大纲树")
@SaCheckPermission("learning.chapter.content")
public Result tree(String courseId) {
if (StrUtil.isBlank(courseId)) {
return Result.success(List.of());
}
List<LearningCourseOutline> outlines = dao.query(LearningCourseOutline.class, Cnd.where("courseId", "=", courseId).asc("sortOrder").asc("createdAt"));
Map<String, List<NutMap>> childrenMap = new HashMap<>();
List<NutMap> roots = new ArrayList<>();
for (LearningCourseOutline outline : outlines) {
NutMap map = NutMap.NEW()
.addv("id", outline.getId())
.addv("courseId", outline.getCourseId())
.addv("courseName", outline.getCourseName())
.addv("parentId", outline.getParentId())
.addv("nodeType", outline.getNodeType())
.addv("title", outline.getTitle())
.addv("subtitle", outline.getSubtitle())
.addv("description", outline.getDescription())
.addv("sortOrder", outline.getSortOrder())
.addv("required", outline.getRequired())
.addv("status", outline.getStatus())
.addv("children", new ArrayList<>());
String parentId = StrUtil.blankToDefault(outline.getParentId(), "");
childrenMap.computeIfAbsent(parentId, k -> new ArrayList<>()).add(map);
}
roots.addAll(childrenMap.getOrDefault("", new ArrayList<>()));
appendChildren(roots, childrenMap);
return Result.success(roots);
}
private void appendChildren(List<NutMap> nodes, Map<String, List<NutMap>> childrenMap) {
for (NutMap node : nodes) {
List<NutMap> children = childrenMap.getOrDefault(node.getString("id"), new ArrayList<>());
node.put("children", children);
appendChildren(children, childrenMap);
}
}
@At
@ApiOperation("新增/编辑大纲节点")
@SaCheckPermission("learning.chapter.content")
@SLog(tag = "保存大纲节点", msg = "节点标题:${args[0].title}")
public Result saveNode(LearningCourseOutline outline) {
Result checkResult = checkNode(outline);
if (checkResult != null) {
return checkResult;
}
LearningCourse course = dao.fetch(LearningCourse.class, outline.getCourseId());
outline.setCourseName(course == null ? outline.getCourseName() : course.getCourseName());
if ("chapter".equals(outline.getNodeType())) {
outline.setParentId("");
}
if (outline.getRequired() == null) {
outline.setRequired(false);
}
if (StrUtil.isBlank(outline.getStatus())) {
outline.setStatus("enabled");
}
if (outline.getSortOrder() == null) {
outline.setSortOrder(nextNodeSort(outline.getCourseId(), outline.getParentId()));
}
if (StrUtil.isBlank(outline.getId())) {
dao.insert(outline);
} else {
dao.updateIgnoreNull(outline);
}
ensureRule(outline.getCourseId(), "outline", outline.getId(), outline.getRequired(), "enabled");
return Result.success();
}
@At
@ApiOperation("删除大纲节点")
@SaCheckPermission("learning.chapter.content")
@SLog(tag = "删除大纲节点", msg = "节点ID:${args[0]}")
public Result deleteNode(String id) {
if (StrUtil.isBlank(id)) {
return Result.error("请选择要删除的数据");
}
int childCount = dao.count(LearningCourseOutline.class, Cnd.where("parentId", "=", id));
int resourceCount = dao.count(LearningOutlineResource.class, Cnd.where("outlineId", "=", id));
if (childCount > 0 || resourceCount > 0) {
return Result.error("该节点下存在子节点或学习资料,请先删除后再操作");
}
dao.clear(LearningStudyRule.class, Cnd.where("targetId", "=", id).and("targetType", "=", "outline"));
dao.clear(LearningCourseOutline.class, Cnd.where("id", "=", id));
return Result.success();
}
@At
@ApiOperation("启用/禁用大纲节点")
@SaCheckPermission("learning.chapter.content")
public Result toggleNode(String id, String status) {
if (StrUtil.isBlank(id)) {
return Result.error("请选择要操作的数据");
}
dao.update(LearningCourseOutline.class, Chain.make("status", status), Cnd.where("id", "=", id));
dao.update(LearningStudyRule.class, Chain.make("status", status), Cnd.where("targetId", "=", id).and("targetType", "=", "outline"));
return Result.success();
}
@At
@ApiOperation("移动大纲节点")
@SaCheckPermission("learning.chapter.content")
public Result moveNode(String id, String direction) {
LearningCourseOutline current = dao.fetch(LearningCourseOutline.class, id);
if (current == null) {
return Result.error("节点不存在");
}
Cnd cnd = Cnd.where("courseId", "=", current.getCourseId()).and("parentId", "=", StrUtil.blankToDefault(current.getParentId(), ""));
if ("up".equals(direction)) {
cnd.and("sortOrder", "<", current.getSortOrder()).desc("sortOrder");
} else {
cnd.and("sortOrder", ">", current.getSortOrder()).asc("sortOrder");
}
LearningCourseOutline target = dao.fetch(LearningCourseOutline.class, cnd);
if (target == null) {
return Result.success();
}
Integer currentSort = current.getSortOrder();
dao.update(LearningCourseOutline.class, Chain.make("sortOrder", target.getSortOrder()), Cnd.where("id", "=", current.getId()));
dao.update(LearningCourseOutline.class, Chain.make("sortOrder", currentSort), Cnd.where("id", "=", target.getId()));
return Result.success();
}
@At
@ApiOperation("学习资料分页")
@SaCheckPermission("learning.chapter.content")
public Result resourcePage(PageForm pageForm, String outlineId) {
if (StrUtil.isBlank(outlineId)) {
return Result.success(new Pagination<>(pageForm.getPageNumber(), pageForm.getPageSize(), 0, List.of()));
}
Cnd cnd = Cnd.where("outlineId", "=", outlineId);
String orderColumn = getResourceOrderColumn(pageForm.getPageOrderName());
if (StrUtil.isBlank(orderColumn)) {
cnd.asc("sortOrder").asc("createdAt");
} else if ("descending".equals(pageForm.getPageOrderBy())) {
cnd.desc(orderColumn);
} else {
cnd.asc(orderColumn);
}
int count = dao.count(LearningOutlineResource.class, Cnd.where("outlineId", "=", outlineId));
Pager pager = dao.createPager(pageForm.getPageNumber(), pageForm.getPageSize());
return Result.success(new Pagination<>(pageForm.getPageNumber(), pageForm.getPageSize(), count, dao.query(LearningOutlineResource.class, cnd, pager)));
}
@At
@ApiOperation("新增/编辑学习资料")
@SaCheckPermission("learning.chapter.content")
@SLog(tag = "保存学习资料", msg = "资料标题:${args[0].resourceTitle}")
public Result saveResource(LearningOutlineResource resource) {
Result checkResult = checkResource(resource);
if (checkResult != null) {
return checkResult;
}
if (resource.getRequired() == null) {
resource.setRequired(false);
}
if (resource.getAllowPreview() == null) {
resource.setAllowPreview(true);
}
if (resource.getAllowDownload() == null) {
resource.setAllowDownload(false);
}
if (StrUtil.isBlank(resource.getStatus())) {
resource.setStatus("enabled");
}
if (resource.getSortOrder() == null) {
resource.setSortOrder(nextResourceSort(resource.getOutlineId()));
}
if (StrUtil.isBlank(resource.getId())) {
dao.insert(resource);
} else {
dao.updateIgnoreNull(resource);
}
ensureRule(resource.getCourseId(), "resource", resource.getId(), resource.getRequired(), resource.getStatus());
return Result.success();
}
@At
@ApiOperation("删除学习资料")
@SaCheckPermission("learning.chapter.content")
@SLog(tag = "删除学习资料", msg = "资料ID:${args[0]}")
public Result deleteResource(String id) {
if (StrUtil.isBlank(id)) {
return Result.error("请选择要删除的数据");
}
dao.clear(LearningStudyRule.class, Cnd.where("targetId", "=", id).and("targetType", "=", "resource"));
dao.clear(LearningOutlineResource.class, Cnd.where("id", "=", id));
return Result.success();
}
@At
@ApiOperation("查询学习规则")
@SaCheckPermission("learning.chapter.content")
public Result getRule(String courseId, String targetType, String targetId) {
LearningStudyRule rule = dao.fetch(LearningStudyRule.class, Cnd.where("courseId", "=", courseId).and("targetType", "=", targetType).and("targetId", "=", targetId));
if (rule == null) {
rule = defaultRule(courseId, targetType, targetId, false, "enabled");
}
return Result.success(rule);
}
@At
@ApiOperation("保存学习规则")
@SaCheckPermission("learning.chapter.content")
public Result saveRule(LearningStudyRule rule) {
if (StrUtil.isBlank(rule.getCourseId()) || StrUtil.isBlank(rule.getTargetType()) || StrUtil.isBlank(rule.getTargetId())) {
return Result.error("规则对象不能为空");
}
if (StrUtil.isBlank(rule.getStatus())) {
rule.setStatus("enabled");
}
LearningStudyRule old = dao.fetch(LearningStudyRule.class, Cnd.where("courseId", "=", rule.getCourseId()).and("targetType", "=", rule.getTargetType()).and("targetId", "=", rule.getTargetId()));
if (old == null) {
dao.insert(rule);
} else {
rule.setId(old.getId());
dao.updateIgnoreNull(rule);
}
if ("outline".equals(rule.getTargetType())) {
dao.update(LearningCourseOutline.class, Chain.make("required", rule.getRequired()).add("status", rule.getStatus()), Cnd.where("id", "=", rule.getTargetId()));
}
if ("resource".equals(rule.getTargetType())) {
dao.update(LearningOutlineResource.class, Chain.make("required", rule.getRequired()).add("status", rule.getStatus()), Cnd.where("id", "=", rule.getTargetId()));
}
return Result.success();
}
private Result checkNode(LearningCourseOutline outline) {
if (outline == null || StrUtil.isBlank(outline.getCourseId())) {
return Result.error("请选择课程");
}
if (StrUtil.isBlank(outline.getNodeType())) {
return Result.error("请选择节点类型");
}
if (StrUtil.isBlank(outline.getTitle())) {
return Result.error("标题不能为空");
}
if ("section".equals(outline.getNodeType()) && StrUtil.isBlank(outline.getParentId())) {
return Result.error("新增节需要选择所属章");
}
return null;
}
private Result checkResource(LearningOutlineResource resource) {
if (resource == null || StrUtil.isBlank(resource.getCourseId()) || StrUtil.isBlank(resource.getOutlineId())) {
return Result.error("请选择章/节节点");
}
if (StrUtil.isBlank(resource.getResourceTitle())) {
return Result.error("资料标题不能为空");
}
if (StrUtil.isBlank(resource.getResourceType())) {
return Result.error("请选择资料类型");
}
if (StrUtil.isBlank(resource.getFileData())) {
return Result.error("请上传附件");
}
return null;
}
private Integer nextNodeSort(String courseId, String parentId) {
LearningCourseOutline outline = dao.fetch(LearningCourseOutline.class, Cnd.where("courseId", "=", courseId).and("parentId", "=", StrUtil.blankToDefault(parentId, "")).desc("sortOrder"));
return outline == null || outline.getSortOrder() == null ? 1 : outline.getSortOrder() + 1;
}
private Integer nextResourceSort(String outlineId) {
LearningOutlineResource resource = dao.fetch(LearningOutlineResource.class, Cnd.where("outlineId", "=", outlineId).desc("sortOrder"));
return resource == null || resource.getSortOrder() == null ? 1 : resource.getSortOrder() + 1;
}
private void ensureRule(String courseId, String targetType, String targetId, Boolean required, String status) {
LearningStudyRule rule = dao.fetch(LearningStudyRule.class, Cnd.where("courseId", "=", courseId).and("targetType", "=", targetType).and("targetId", "=", targetId));
if (rule == null) {
dao.insert(defaultRule(courseId, targetType, targetId, required, status));
} else {
dao.update(LearningStudyRule.class,
Chain.make("required", required != null && required).add("status", StrUtil.blankToDefault(status, "enabled")),
Cnd.where("id", "=", rule.getId()));
}
}
private LearningStudyRule defaultRule(String courseId, String targetType, String targetId, Boolean required, String status) {
LearningStudyRule rule = new LearningStudyRule();
rule.setCourseId(courseId);
rule.setTargetType(targetType);
rule.setTargetId(targetId);
rule.setRequired(required != null && required);
rule.setStudyMode("mixed");
rule.setCompletionRule("all_required_resource");
rule.setCompletePercent(90);
rule.setMinStudySeconds(0);
rule.setUnlockRule("free");
rule.setAllowSkip(false);
rule.setAllowDrag(true);
rule.setPauseCountTime(false);
rule.setHiddenCountTime(false);
rule.setInactiveCountTime(false);
rule.setStatus(StrUtil.blankToDefault(status, "enabled"));
return rule;
}
private String getResourceOrderColumn(String prop) {
Map<String, String> columns = new HashMap<>();
columns.put("resourceTitle", "resourceTitle");
columns.put("resourceType", "resourceType");
columns.put("fileExt", "fileExt");
columns.put("durationSeconds", "durationSeconds");
columns.put("sortOrder", "sortOrder");
columns.put("required", "required");
columns.put("allowPreview", "allowPreview");
columns.put("allowDownload", "allowDownload");
columns.put("status", "status");
return columns.get(prop);
}
}
@@ -0,0 +1,297 @@
package com.budwk.app.zhgh.learning.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.StrUtil;
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.OfficePlusUtil;
import com.budwk.app.sys.enums.SysFileEngineTypeEnum;
import com.budwk.app.sys.models.Sys_file;
import com.budwk.app.sys.models.Sys_dict;
import com.budwk.app.sys.services.SysDictService;
import com.budwk.app.sys.utils.SysFileMinIoUtil;
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
import com.budwk.app.zhgh.learning.models.LearningCourseOutline;
import com.budwk.app.zhgh.learning.models.LearningCourseType;
import com.budwk.app.zhgh.learning.models.LearningOutlineResource;
import com.budwk.app.zhgh.learning.models.LearningStudyRecord;
import com.google.common.net.HttpHeaders;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Sql;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.Strings;
import org.nutz.lang.util.NutMap;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.nio.file.Files;
import java.nio.file.StandardOpenOption;
import java.util.List;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
@IocBean
@Ok("json:full")
@Api("学习教育平台-课程展示")
@At("/platform/learning/course/display")
public class LearningCourseDisplayController {
@Inject
private Dao dao;
@Inject
private SysDictService sysDictService;
@At("")
@Ok("beetl:/platform/zhgh/Learning/courseDisplay/index.html")
@SaCheckPermission("learning.course.display")
public void index() {
}
@At("/detail")
@Ok("beetl:/platform/zhgh/Learning/courseDisplay/detail.html")
@SaCheckPermission("learning.course.display")
public void detail() {
}
@At("/study")
@Ok("beetl:/platform/zhgh/Learning/courseDisplay/study.html")
@SaCheckPermission("learning.course.display")
public void study() {
}
@At
@ApiOperation("课程展示列表")
@SaCheckPermission("learning.course.display")
public Result pageData(PageForm pageForm, String keyword, String courseTypeId, String recommendFlag) {
Cnd cnd = Cnd.where("c.status", "=", "published");
if (StrUtil.isNotBlank(keyword)) {
cnd.and(Cnd.exps("c.courseName", "like", "%" + keyword + "%")
.or("c.courseIntro", "like", "%" + keyword + "%"));
}
cnd.andEX("c.courseTypeId", "=", courseTypeId);
if (StrUtil.isNotBlank(recommendFlag)) {
cnd.and("c.recommendFlags", "like", "%" + recommendFlag + "%");
}
Sql countSql = Sqls.create("""
SELECT COUNT(1)
FROM learning_course c
LEFT JOIN learning_course_type t ON t.id = c.courseTypeId
$condition
""");
countSql.setCondition(cnd);
countSql.setCallback(Sqls.callback.integer());
dao.execute(countSql);
int count = countSql.getInt();
Sql listSql = Sqls.create("""
SELECT c.id, c.courseName, c.courseIntro, c.startTime, c.endTime, c.openType, c.cover,
c.recommendFlags, c.courseTypeId, c.lecturerName, c.sortNum, t.typeName AS courseTypeName
FROM learning_course c
LEFT JOIN learning_course_type t ON t.id = c.courseTypeId
$condition
ORDER BY c.sortNum ASC, c.createdAt DESC
""");
listSql.setCondition(cnd);
listSql.setPager(dao.createPager(pageForm.getPageNumber(), pageForm.getPageSize()));
listSql.setCallback(Sqls.callback.maps());
dao.execute(listSql);
List<NutMap> rows = listSql.getList(NutMap.class);
rows.forEach(row -> {
Object startTime = row.get("startTime");
Object endTime = row.get("endTime");
row.put("startTimeText", startTime == null ? "" : DateUtil.formatDateTime(DateUtil.date((java.util.Date) startTime)));
row.put("endTimeText", endTime == null ? "" : DateUtil.formatDateTime(DateUtil.date((java.util.Date) endTime)));
});
return Result.success(new Pagination<>(pageForm.getPageNumber(), pageForm.getPageSize(), count, rows));
}
@At
@ApiOperation("课程学习基础信息")
@SaCheckPermission("learning.course.display")
public Result courseInfo(String id) {
if (StrUtil.isBlank(id)) {
return Result.error("请选择课程");
}
Sql sql = Sqls.create("""
SELECT c.id, c.courseName, c.courseIntro, c.startTime, c.endTime, c.openType, c.cover,
c.recommendFlags, c.lecturerName, t.typeName AS courseTypeName
FROM learning_course c
LEFT JOIN learning_course_type t ON t.id = c.courseTypeId
WHERE c.id = @id
""");
sql.params().set("id", id);
sql.setCallback(Sqls.callback.map());
dao.execute(sql);
NutMap course = sql.getObject(NutMap.class);
if (course == null) {
return Result.error("课程不存在");
}
Object startTime = course.get("startTime");
Object endTime = course.get("endTime");
course.put("startTimeText", startTime == null ? "" : DateUtil.formatDateTime(DateUtil.date((java.util.Date) startTime)));
course.put("endTimeText", endTime == null ? "" : DateUtil.formatDateTime(DateUtil.date((java.util.Date) endTime)));
return Result.success(course);
}
@At
@ApiOperation("课程学习安排")
@SaCheckPermission("learning.course.display")
public Result studyTree(String courseId) {
if (StrUtil.isBlank(courseId)) {
return Result.success(List.of());
}
List<LearningCourseOutline> outlines = dao.query(LearningCourseOutline.class, Cnd.where("courseId", "=", courseId).and("status", "=", "enabled").asc("sortOrder").asc("createdAt"));
List<LearningOutlineResource> resources = dao.query(LearningOutlineResource.class, Cnd.where("courseId", "=", courseId).and("status", "=", "enabled").asc("sortOrder").asc("createdAt"));
List<LearningStudyRecord> records = dao.query(LearningStudyRecord.class, Cnd.where("courseId", "=", courseId).and("userId", "=", SecurityUtil.getUserId()));
Map<String, List<NutMap>> childrenMap = new HashMap<>();
List<NutMap> roots = new ArrayList<>();
Map<String, LearningStudyRecord> recordMap = new HashMap<>();
for (LearningStudyRecord record : records) {
recordMap.put(record.getOutlineId(), record);
}
Map<String, NutMap> outlineNodeMap = new HashMap<>();
for (LearningCourseOutline outline : outlines) {
List<NutMap> children = new ArrayList<>();
NutMap node = NutMap.NEW()
.addv("id", outline.getId())
.addv("type", "outline")
.addv("nodeType", outline.getNodeType())
.addv("title", outline.getTitle())
.addv("required", outline.getRequired())
.addv("progressPercent", recordMap.containsKey(outline.getId()) ? recordMap.get(outline.getId()).getProgressPercent() : 0)
.addv("studySeconds", recordMap.containsKey(outline.getId()) ? recordMap.get(outline.getId()).getStudySeconds() : 0)
.addv("lastPositionSeconds", recordMap.containsKey(outline.getId()) ? value(recordMap.get(outline.getId()).getLastPositionSeconds()) : 0)
.addv("completeStatus", recordMap.containsKey(outline.getId()) ? recordMap.get(outline.getId()).getCompleteStatus() : "not_started")
.addv("children", children);
outlineNodeMap.put(outline.getId(), node);
childrenMap.put(outline.getId(), children);
}
for (LearningCourseOutline outline : outlines) {
NutMap node = outlineNodeMap.get(outline.getId());
if (StrUtil.isBlank(outline.getParentId())) {
roots.add(node);
} else {
childrenMap.computeIfAbsent(outline.getParentId(), key -> new ArrayList<>()).add(node);
}
}
for (LearningOutlineResource resource : resources) {
NutMap node = NutMap.NEW()
.addv("id", resource.getId())
.addv("type", "resource")
.addv("outlineId", resource.getOutlineId())
.addv("title", resource.getResourceTitle())
.addv("resourceType", resource.getResourceType())
.addv("fileExt", resource.getFileExt())
.addv("fileData", resource.getFileData())
.addv("durationSeconds", resource.getDurationSeconds())
.addv("required", resource.getRequired())
.addv("progressPercent", recordMap.containsKey(resource.getOutlineId()) ? recordMap.get(resource.getOutlineId()).getProgressPercent() : 0)
.addv("studySeconds", recordMap.containsKey(resource.getOutlineId()) ? recordMap.get(resource.getOutlineId()).getStudySeconds() : 0)
.addv("lastPositionSeconds", recordMap.containsKey(resource.getOutlineId()) ? value(recordMap.get(resource.getOutlineId()).getLastPositionSeconds()) : 0)
.addv("completeStatus", recordMap.containsKey(resource.getOutlineId()) ? recordMap.get(resource.getOutlineId()).getCompleteStatus() : "not_started");
childrenMap.computeIfAbsent(resource.getOutlineId(), key -> new ArrayList<>()).add(node);
}
return Result.success(roots);
}
@At
@Ok("void")
@ApiOperation("PDF内联预览")
@SaCheckPermission("learning.course.display")
public void pdfPreview(String id, HttpServletResponse response) throws IOException {
if (StrUtil.isBlank(id)) {
response.sendError(HttpServletResponse.SC_BAD_REQUEST, "文件ID不能为空");
return;
}
Sys_file file = dao.fetch(Sys_file.class, id);
if (file == null) {
response.sendError(HttpServletResponse.SC_NOT_FOUND, "文件不存在");
return;
}
byte[] bytes;
if (SysFileEngineTypeEnum.MINIO.getValue().equals(file.getEngine())) {
bytes = SysFileMinIoUtil.getFileBytes(file.getBucket(), file.getStoragePath());
} else {
File localFile = FileUtil.file(file.getStoragePath());
if (!FileUtil.exist(localFile)) {
response.sendError(HttpServletResponse.SC_NOT_FOUND, "文件不存在");
return;
}
bytes = IoUtil.readBytes(FileUtil.getInputStream(localFile));
}
if (!"pdf".equalsIgnoreCase(file.getSuffix())) {
File sourceFile = File.createTempFile("learning_preview_origin", "." + file.getSuffix());
File pdfFile = File.createTempFile("learning_preview", ".pdf");
try {
Files.write(sourceFile.toPath(), bytes, StandardOpenOption.WRITE);
OfficePlusUtil.convert(sourceFile.getPath(), pdfFile.getPath());
if (!FileUtil.exist(pdfFile) || pdfFile.length() == 0) {
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "文件转换PDF失败");
return;
}
bytes = IoUtil.readBytes(FileUtil.getInputStream(pdfFile));
} finally {
FileUtil.del(sourceFile);
FileUtil.del(pdfFile);
}
}
String fileName = StrUtil.blankToDefault(FileUtil.mainName(file.getName()), "preview") + ".pdf";
String encodedName = URLEncoder.encode(fileName, CharsetUtil.UTF_8).replace("+", "%20");
response.setContentType("application/pdf");
response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "inline; filename=\"preview.pdf\"; filename*=UTF-8''" + encodedName);
response.setHeader(HttpHeaders.CONTENT_LENGTH, String.valueOf(bytes.length));
try (OutputStream out = response.getOutputStream()) {
out.write(bytes);
}
}
@At
@ApiOperation("启用课程类型")
@SaCheckPermission("learning.course.display")
public Result courseTypes() {
return Result.success(dao.query(LearningCourseType.class, Cnd.where("enabled", "=", true).asc("sortNum").asc("createdAt")));
}
@At
@ApiOperation("推荐标识")
@SaCheckPermission("learning.course.display")
public Result recommendOptions() {
Sys_dict root = sysDictService.fetch(Cnd.where("code", "=", "学习教育").or("name", "=", "学习教育"));
if (root == null) {
return Result.success(List.of());
}
List<Sys_dict> firstLevel = sysDictService.query(Cnd.where("parentId", "=", Strings.sNull(root.getId())).and("disabled", "=", false).asc("location"));
Sys_dict group = firstLevel.stream()
.filter(dict -> "推荐标识".equals(dict.getName()) || "推荐标识".equals(dict.getCode()))
.findFirst()
.orElse(null);
if (group == null) {
return Result.success(firstLevel);
}
List<Sys_dict> children = sysDictService.query(Cnd.where("parentId", "=", Strings.sNull(group.getId())).and("disabled", "=", false).asc("location"));
return Result.success(children.isEmpty() ? firstLevel : children);
}
private int value(Integer value) {
return value == null ? 0 : value;
}
}
@@ -0,0 +1,232 @@
package com.budwk.app.zhgh.learning.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.annotation.SLog;
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.sys.models.Sys_dict;
import com.budwk.app.sys.services.SysDictService;
import com.budwk.app.zhgh.learning.models.LearningCourse;
import com.budwk.app.zhgh.learning.models.LearningCourseOutline;
import com.budwk.app.zhgh.learning.models.LearningCourseType;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Sql;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.Strings;
import org.nutz.lang.util.NutMap;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@IocBean
@Ok("json:full")
@Api("学习教育平台-课程管理")
@At("/platform/learning/course/manage")
public class LearningCourseManageController {
@Inject
private Dao dao;
@Inject
private SysDictService sysDictService;
@At("")
@Ok("beetl:/platform/zhgh/Learning/courseManage/index.html")
@SaCheckPermission("learning.course.manage")
public void index() {
}
@At
@ApiOperation("课程列表")
@SaCheckPermission("learning.course.manage")
public Result pageData(PageForm pageForm,
String courseName,
String courseTypeId,
String startTime,
String endTime,
String lecturerName,
String status,
String recommendFlag) {
Cnd cnd = Cnd.NEW();
cnd.and(Cnd.likeEX("c.courseName", courseName));
cnd.andEX("c.courseTypeId", "=", courseTypeId);
cnd.and(Cnd.likeEX("c.lecturerName", lecturerName));
cnd.andEX("c.status", "=", status);
if (StrUtil.isNotBlank(recommendFlag)) {
cnd.and("c.recommendFlags", "like", "%" + recommendFlag + "%");
}
if (StrUtil.isNotBlank(startTime)) {
cnd.and("c.startTime", ">=", DateUtil.parse(startTime));
}
if (StrUtil.isNotBlank(endTime)) {
cnd.and("c.endTime", "<=", DateUtil.parse(endTime));
}
String orderColumn = getOrderColumn(pageForm.getPageOrderName());
String orderBy = "descending".equals(pageForm.getPageOrderBy()) ? "desc" : "asc";
if (StrUtil.isBlank(orderColumn)) {
orderColumn = "c.sortNum";
orderBy = "asc";
}
Sql countSql = Sqls.create("""
SELECT COUNT(1)
FROM learning_course c
LEFT JOIN learning_course_type t ON t.id = c.courseTypeId
$condition
""");
countSql.setCondition(cnd);
countSql.setCallback(Sqls.callback.integer());
dao.execute(countSql);
int count = countSql.getInt();
Sql listSql = Sqls.create("""
SELECT c.*, t.typeName AS courseTypeName
FROM learning_course c
LEFT JOIN learning_course_type t ON t.id = c.courseTypeId
$condition
ORDER BY $orderColumn $orderBy
""");
listSql.setCondition(cnd);
listSql.setVar("orderColumn", orderColumn);
listSql.setVar("orderBy", orderBy);
listSql.setPager(dao.createPager(pageForm.getPageNumber(), pageForm.getPageSize()));
listSql.setCallback(Sqls.callback.maps());
dao.execute(listSql);
Pagination<NutMap> pagination = new Pagination<>(pageForm.getPageNumber(), pageForm.getPageSize(), count, listSql.getList(NutMap.class));
return Result.success(pagination);
}
@At
@ApiOperation("新增课程")
@SaCheckPermission("learning.course.manage")
@SLog(tag = "新增课程", msg = "课程名称:${args[0].courseName}")
public Result doAdd(LearningCourse course) {
Result checkResult = checkCourse(course, null);
if (checkResult != null) {
return checkResult;
}
if (StrUtil.isBlank(course.getStatus())) {
course.setStatus("draft");
}
dao.insert(course);
return Result.success();
}
@At
@ApiOperation("编辑课程")
@SaCheckPermission("learning.course.manage")
@SLog(tag = "编辑课程", msg = "课程ID:${args[0].id}")
public Result doEdit(LearningCourse course) {
if (StrUtil.isBlank(course.getId())) {
return Result.error("请选择要编辑的数据");
}
Result checkResult = checkCourse(course, course.getId());
if (checkResult != null) {
return checkResult;
}
dao.updateIgnoreNull(course);
return Result.success();
}
@At
@ApiOperation("删除课程")
@SaCheckPermission("learning.course.manage")
@SLog(tag = "删除课程", msg = "课程ID:${args[0]}")
public Result doDelete(String id) {
if (StrUtil.isBlank(id)) {
return Result.error("请选择要删除的数据");
}
int outlineCount = dao.count(LearningCourseOutline.class, Cnd.where("courseId", "=", id));
if (outlineCount > 0) {
return Result.error("该课程存在章节内容,请先删除章节内容后再删除课程");
}
dao.clear(LearningCourse.class, Cnd.where("id", "=", id));
return Result.success();
}
@At
@ApiOperation("启用课程类型")
@SaCheckPermission("learning.course.manage")
public Result courseTypes() {
return Result.success(dao.query(LearningCourseType.class, Cnd.where("enabled", "=", true).asc("sortNum").asc("createdAt")));
}
@At
@ApiOperation("学习教育数据字典")
@SaCheckPermission("learning.course.manage")
public Result learningDictOptions(String name) {
Sys_dict root = sysDictService.fetch(Cnd.where("code", "=", "学习教育").or("name", "=", "学习教育"));
if (root == null) {
return Result.success(List.of());
}
List<Sys_dict> firstLevel = sysDictService.query(Cnd.where("parentId", "=", Strings.sNull(root.getId())).and("disabled", "=", false).asc("location"));
if (StrUtil.isBlank(name)) {
return Result.success(firstLevel);
}
Sys_dict group = firstLevel.stream()
.filter(dict -> name.equals(dict.getName()) || name.equals(dict.getCode()))
.findFirst()
.orElse(null);
if (group == null) {
return Result.success(firstLevel);
}
List<Sys_dict> children = sysDictService.query(Cnd.where("parentId", "=", Strings.sNull(group.getId())).and("disabled", "=", false).asc("location"));
return Result.success(children.isEmpty() ? firstLevel : children);
}
private Result checkCourse(LearningCourse course, String excludeId) {
if (course == null || StrUtil.isBlank(course.getCourseName())) {
return Result.error("课程名称不能为空");
}
if (StrUtil.isBlank(course.getCourseTypeId())) {
return Result.error("请选择课程类型");
}
if (StrUtil.isBlank(course.getLecturerName())) {
return Result.error("授课讲师不能为空");
}
if (course.getSortNum() == null) {
return Result.error("排序编码不能为空");
}
if (!"long_term".equals(course.getOpenType()) && (course.getStartTime() == null || course.getEndTime() == null)) {
return Result.error("请选择开课时间");
}
Cnd cnd = Cnd.where("courseName", "=", course.getCourseName().trim());
if (StrUtil.isNotBlank(excludeId)) {
cnd.and("id", "!=", excludeId);
}
if (dao.count(LearningCourse.class, cnd) > 0) {
return Result.error("课程名称已存在");
}
course.setCourseName(course.getCourseName().trim());
if ("long_term".equals(course.getOpenType())) {
course.setStartTime(null);
course.setEndTime(null);
}
return null;
}
private String getOrderColumn(String prop) {
Map<String, String> columns = new HashMap<>();
columns.put("courseName", "c.courseName");
columns.put("courseTypeName", "t.typeName");
columns.put("lecturerName", "c.lecturerName");
columns.put("startTime", "c.startTime");
columns.put("status", "c.status");
columns.put("recommendFlags", "c.recommendFlags");
columns.put("sortNum", "c.sortNum");
return columns.get(prop);
}
}
@@ -0,0 +1,110 @@
package com.budwk.app.zhgh.learning.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.annotation.SLog;
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.zhgh.learning.models.LearningCourseType;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.dao.pager.Pager;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
import org.nutz.mvc.annotation.Param;
@IocBean
@Ok("json:full")
@Api("学习教育平台-课程类型设置")
@At("/platform/learning/course/type")
public class LearningCourseTypeController {
@Inject
private Dao dao;
@At("")
@Ok("beetl:/platform/zhgh/Learning/courseType/index.html")
@SaCheckPermission("learning.course.type")
public void index() {
}
@At
@ApiOperation("课程类型列表")
@SaCheckPermission("learning.course.type")
public Result pageData(PageForm pageForm, @Param("typeName") String typeName) {
Cnd cnd = Cnd.NEW();
cnd.and(Cnd.likeEX("typeName", typeName));
cnd.asc("sortNum").asc("createdAt");
int count = dao.count(LearningCourseType.class, cnd);
Pager pager = dao.createPager(pageForm.getPageNumber(), pageForm.getPageSize());
Pagination<LearningCourseType> pagination = new Pagination<>(pageForm.getPageNumber(), pageForm.getPageSize(), count, dao.query(LearningCourseType.class, cnd, pager));
return Result.success(pagination);
}
@At
@ApiOperation("新增课程类型")
@SaCheckPermission("learning.course.type")
@SLog(tag = "新增课程类型", msg = "课程类型名称:${args[0].typeName}")
public Result doAdd(LearningCourseType courseType) {
Result checkResult = checkCourseType(courseType, null);
if (checkResult != null) {
return checkResult;
}
if (courseType.getEnabled() == null) {
courseType.setEnabled(true);
}
dao.insert(courseType);
return Result.success();
}
@At
@ApiOperation("编辑课程类型")
@SaCheckPermission("learning.course.type")
@SLog(tag = "编辑课程类型", msg = "课程类型ID:${args[0].id}")
public Result doEdit(LearningCourseType courseType) {
if (StrUtil.isBlank(courseType.getId())) {
return Result.error("请选择要编辑的数据");
}
Result checkResult = checkCourseType(courseType, courseType.getId());
if (checkResult != null) {
return checkResult;
}
dao.updateIgnoreNull(courseType);
return Result.success();
}
@At
@ApiOperation("删除课程类型")
@SaCheckPermission("learning.course.type")
@SLog(tag = "删除课程类型", msg = "课程类型ID:${args[0]}")
public Result doDelete(String id) {
if (StrUtil.isBlank(id)) {
return Result.error("请选择要删除的数据");
}
dao.clear(LearningCourseType.class, Cnd.where("id", "=", id));
return Result.success();
}
private Result checkCourseType(LearningCourseType courseType, String excludeId) {
if (courseType == null || StrUtil.isBlank(courseType.getTypeName())) {
return Result.error("课程类型名称不能为空");
}
if (courseType.getSortNum() == null) {
return Result.error("排序编号不能为空");
}
Cnd cnd = Cnd.where("typeName", "=", courseType.getTypeName().trim());
if (StrUtil.isNotBlank(excludeId)) {
cnd.and("id", "!=", excludeId);
}
if (dao.count(LearningCourseType.class, cnd) > 0) {
return Result.error("课程类型名称已存在");
}
courseType.setTypeName(courseType.getTypeName().trim());
return null;
}
}
@@ -0,0 +1,47 @@
package com.budwk.app.zhgh.learning.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
@IocBean
@At("/platform/learning")
public class LearningPlatformController {
@At("/activity/manage")
@Ok("beetl:/platform/zhgh/Learning/activityManage/index.html")
@SaCheckPermission("learning.activity.manage")
public void activityManage() {
}
@At("/my/record")
@Ok("beetl:/platform/zhgh/Learning/myRecord/index.html")
@SaCheckPermission("learning.my.record")
public void myRecord() {
}
@At("/my/record/h5")
@Ok("beetl:/platform/zhghh5/learning/myRecord/index.html")
@SaCheckPermission("learning.my.record")
public void myRecordH5() {
}
@At("/statistics")
@Ok("beetl:/platform/zhgh/Learning/statistics/index.html")
@SaCheckPermission("learning.statistics")
public void statistics() {
}
@At("/course/h5")
@Ok("beetl:/platform/zhghh5/learning/course/index.html")
@SaCheckPermission("learning.course.display")
public void courseH5() {
}
@At("/course/h5/study")
@Ok("beetl:/platform/zhghh5/learning/course/study.html")
@SaCheckPermission("learning.course.display")
public void courseH5Study() {
}
}
@@ -0,0 +1,330 @@
package com.budwk.app.zhgh.learning.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.constant.RoleConstant;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.result.Result;
import com.budwk.app.base.utils.PageUtil;
import com.budwk.app.sys.models.Sys_union;
import com.budwk.app.web.commons.auth.utils.AuthUtil;
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
import com.budwk.app.zhgh.learning.models.LearningCourse;
import com.budwk.app.zhgh.learning.param.LearningStatisticsPageForm;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Sql;
import org.nutz.dao.util.cri.Static;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.util.NutMap;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@IocBean
@Ok("json:full")
@Api("学习教育平台-学习统计")
@At("/platform/learning/statistics")
public class LearningStatisticsController {
@Inject
private Dao dao;
@At("")
@Ok("beetl:/platform/zhgh/Learning/statistics/index.html")
@SaCheckPermission("learning.statistics")
public void index() {
}
@At
@ApiOperation("课程统计")
@SaCheckPermission("learning.statistics")
public Result coursePageData(LearningStatisticsPageForm pageForm) {
Cnd cnd = courseCondition(pageForm);
Sql countSql = Sqls.create("""
SELECT COUNT(1)
FROM learning_course c
$condition
""");
countSql.setCondition(cnd);
countSql.setCallback(Sqls.callback.integer());
dao.execute(countSql);
Sql listSql = Sqls.create("""
SELECT
c.id AS courseId,
c.courseName,
IFNULL(COUNT(uc.userId), 0) AS learnerCount,
IFNULL(SUM(CASE WHEN target.targetCount > 0 AND uc.completedOutlineCount >= target.targetCount THEN 1 ELSE 0 END), 0) AS completedCount,
IFNULL(ROUND(AVG(uc.studySeconds)), 0) AS avgStudySeconds
FROM learning_course c
LEFT JOIN (
SELECT
courseId,
CASE
WHEN SUM(CASE WHEN required = 1 THEN 1 ELSE 0 END) > 0
THEN SUM(CASE WHEN required = 1 THEN 1 ELSE 0 END)
ELSE COUNT(1)
END AS targetCount,
SUM(CASE WHEN required = 1 THEN 1 ELSE 0 END) AS requiredCount
FROM learning_course_outline
WHERE status = 'enabled'
GROUP BY courseId
) target ON target.courseId = c.id
LEFT JOIN (
SELECT
r.courseId,
r.userId,
SUM(IFNULL(r.studySeconds, 0)) AS studySeconds,
COUNT(DISTINCT CASE
WHEN r.completeStatus = 'completed'
AND ((target.requiredCount > 0 AND o.required = 1) OR target.requiredCount = 0)
THEN r.outlineId
ELSE NULL
END) AS completedOutlineCount
FROM learning_study_record r
JOIN learning_course_outline o ON o.id = r.outlineId AND o.courseId = r.courseId AND o.status = 'enabled'
JOIN (
SELECT
courseId,
CASE
WHEN SUM(CASE WHEN required = 1 THEN 1 ELSE 0 END) > 0
THEN SUM(CASE WHEN required = 1 THEN 1 ELSE 0 END)
ELSE COUNT(1)
END AS targetCount,
SUM(CASE WHEN required = 1 THEN 1 ELSE 0 END) AS requiredCount
FROM learning_course_outline
WHERE status = 'enabled'
GROUP BY courseId
) target ON target.courseId = r.courseId
$recordCondition
GROUP BY r.courseId, r.userId
) uc ON uc.courseId = c.id
$condition
GROUP BY c.id, c.courseName
ORDER BY $orderColumn $orderBy
""");
listSql.setCondition(cnd);
listSql.setVar("recordCondition", new Static(recordWhere(pageForm)));
setOrder(listSql, pageForm, "course");
listSql.setPager(dao.createPager(pageForm.getPageNumber(), pageForm.getPageSize()));
listSql.setCallback(Sqls.callback.maps());
dao.execute(listSql);
List<NutMap> rows = listSql.getList(NutMap.class);
rows.forEach(row -> row.put("avgStudyTimeText", formatStudySeconds(row.getInt("avgStudySeconds", 0))));
return Result.success(new Pagination<>(pageForm.getPageNumber(), pageForm.getPageSize(), countSql.getInt(), rows));
}
@At
@ApiOperation("分工会统计")
@SaCheckPermission("learning.statistics")
public Result unionPageData(LearningStatisticsPageForm pageForm) {
Cnd cnd = unionCondition(pageForm);
Sql countSql = Sqls.create("""
SELECT COUNT(1)
FROM learning_course c
JOIN (
SELECT r.courseId, r.unionId
FROM learning_study_record r
$recordCondition
GROUP BY r.courseId, r.unionId
) uc ON uc.courseId = c.id
LEFT JOIN sys_union un ON un.id = uc.unionId
$condition
""");
countSql.setCondition(cnd);
countSql.setVar("recordCondition", new Static(recordWhere(pageForm)));
countSql.setCallback(Sqls.callback.integer());
dao.execute(countSql);
Sql listSql = Sqls.create("""
SELECT
c.id AS courseId,
c.courseName,
un.id AS unionId,
IFNULL(un.name, uc.unionName) AS unionName,
COUNT(uc.userId) AS learnerCount,
IFNULL(SUM(uc.studySeconds), 0) AS studySeconds,
IFNULL(SUM(CASE WHEN target.targetCount > 0 AND uc.completedOutlineCount >= target.targetCount THEN 1 ELSE 0 END), 0) AS completedCount
FROM learning_course c
JOIN (
SELECT
r.courseId,
r.userId,
r.unionId,
MAX(r.unionName) AS unionName,
SUM(IFNULL(r.studySeconds, 0)) AS studySeconds,
COUNT(DISTINCT CASE
WHEN r.completeStatus = 'completed'
AND ((target.requiredCount > 0 AND o.required = 1) OR target.requiredCount = 0)
THEN r.outlineId
ELSE NULL
END) AS completedOutlineCount
FROM learning_study_record r
JOIN learning_course_outline o ON o.id = r.outlineId AND o.courseId = r.courseId AND o.status = 'enabled'
JOIN (
SELECT
courseId,
CASE
WHEN SUM(CASE WHEN required = 1 THEN 1 ELSE 0 END) > 0
THEN SUM(CASE WHEN required = 1 THEN 1 ELSE 0 END)
ELSE COUNT(1)
END AS targetCount,
SUM(CASE WHEN required = 1 THEN 1 ELSE 0 END) AS requiredCount
FROM learning_course_outline
WHERE status = 'enabled'
GROUP BY courseId
) target ON target.courseId = r.courseId
$recordCondition
GROUP BY r.courseId, r.userId, r.unionId
) uc ON uc.courseId = c.id
LEFT JOIN sys_union un ON un.id = uc.unionId
LEFT JOIN (
SELECT
courseId,
CASE
WHEN SUM(CASE WHEN required = 1 THEN 1 ELSE 0 END) > 0
THEN SUM(CASE WHEN required = 1 THEN 1 ELSE 0 END)
ELSE COUNT(1)
END AS targetCount,
SUM(CASE WHEN required = 1 THEN 1 ELSE 0 END) AS requiredCount
FROM learning_course_outline
WHERE status = 'enabled'
GROUP BY courseId
) target ON target.courseId = c.id
$condition
GROUP BY c.id, c.courseName, uc.unionId, un.id, un.name
ORDER BY $orderColumn $orderBy
""");
listSql.setCondition(cnd);
listSql.setVar("recordCondition", new Static(recordWhere(pageForm)));
setOrder(listSql, pageForm, "union");
listSql.setPager(dao.createPager(pageForm.getPageNumber(), pageForm.getPageSize()));
listSql.setCallback(Sqls.callback.maps());
dao.execute(listSql);
List<NutMap> rows = listSql.getList(NutMap.class);
rows.forEach(row -> {
int learnerCount = row.getInt("learnerCount", 0);
int completedCount = row.getInt("completedCount", 0);
row.put("studyTimeText", formatStudySeconds(row.getInt("studySeconds", 0)));
row.put("completeRate", learnerCount == 0 ? "0%" : BigDecimal.valueOf(completedCount * 100.0 / learnerCount).setScale(2, RoundingMode.HALF_UP).stripTrailingZeros().toPlainString() + "%");
});
return Result.success(new Pagination<>(pageForm.getPageNumber(), pageForm.getPageSize(), countSql.getInt(), rows));
}
@At
@ApiOperation("可选课程")
@SaCheckPermission("learning.statistics")
public Result courseOptions(String courseName) {
Cnd cnd = Cnd.NEW();
cnd.and(Cnd.likeEX("courseName", courseName));
cnd.desc("createdAt").asc("sortNum");
return Result.success(dao.query(LearningCourse.class, cnd, dao.createPager(1, 50)));
}
@At
@ApiOperation("可选分工会")
@SaCheckPermission("learning.statistics")
public Result unionOptions() {
if (hasSchoolScope()) {
return Result.success(dao.query(Sys_union.class, Cnd.NEW().asc("unionCode").asc("name")));
}
if (hasBranchScope()) {
return Result.success(dao.query(Sys_union.class, Cnd.where("id", "=", SecurityUtil.getUnionId())));
}
return Result.success(List.of());
}
private Cnd courseCondition(LearningStatisticsPageForm pageForm) {
Cnd cnd = Cnd.NEW();
cnd.andEX("c.id", "=", pageForm.getCourseId());
cnd.and(Cnd.likeEX("c.courseName", pageForm.getCourseName()));
return cnd;
}
private Cnd unionCondition(LearningStatisticsPageForm pageForm) {
Cnd cnd = courseCondition(pageForm);
cnd.andEX("uc.unionId", "=", pageForm.getUnionId());
return cnd;
}
private String recordWhere(LearningStatisticsPageForm pageForm) {
StringBuilder where = new StringBuilder("WHERE 1 = 1");
if (hasBranchScope()) {
where.append(" AND r.unionId = '").append(escapeSql(SecurityUtil.getUnionId())).append("'");
} else if (!hasSchoolScope()) {
where.append(" AND r.userId = '").append(escapeSql(SecurityUtil.getUserId())).append("'");
}
if (StrUtil.isNotBlank(pageForm.getUnionId())) {
where.append(" AND r.unionId = '").append(escapeSql(pageForm.getUnionId())).append("'");
}
return where.toString();
}
private void setOrder(Sql sql, LearningStatisticsPageForm pageForm, String type) {
String orderColumn = "union".equals(type) ? getUnionOrderColumn(pageForm.getPageOrderName()) : getCourseOrderColumn(pageForm.getPageOrderName());
String orderBy = PageUtil.getOrder(pageForm.getPageOrderBy());
if (StrUtil.isBlank(orderColumn)) {
orderColumn = "union".equals(type) ? "c.courseName ASC, un.unionCode" : "c.sortNum ASC, c.createdAt";
orderBy = "DESC";
} else if (StrUtil.isBlank(orderBy)) {
orderBy = "ASC";
}
sql.setVar("orderColumn", new Static(orderColumn));
sql.setVar("orderBy", new Static(orderBy));
}
private String getCourseOrderColumn(String prop) {
Map<String, String> columns = new HashMap<>();
columns.put("courseName", "c.courseName");
columns.put("learnerCount", "learnerCount");
columns.put("completedCount", "completedCount");
columns.put("avgStudySeconds", "avgStudySeconds");
return columns.get(prop);
}
private String getUnionOrderColumn(String prop) {
Map<String, String> columns = new HashMap<>();
columns.put("courseName", "c.courseName");
columns.put("unionName", "unionName");
columns.put("learnerCount", "learnerCount");
columns.put("studySeconds", "studySeconds");
columns.put("completeRate", "completedCount");
return columns.get(prop);
}
private boolean hasSchoolScope() {
return AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_ADMIN.name());
}
private boolean hasBranchScope() {
return AuthUtil.hasRoleOr(RoleConstant.BRANCH_UNION_ADMIN.name());
}
private String escapeSql(String value) {
return value == null ? "" : value.replace("'", "''");
}
private String formatStudySeconds(Integer seconds) {
int value = seconds == null ? 0 : seconds;
int hour = value / 3600;
int minute = value % 3600 / 60;
int second = value % 60;
if (hour > 0) {
return hour + "小时" + minute + "" + second + "";
}
if (minute > 0) {
return minute + "" + second + "";
}
return second + "";
}
}
@@ -0,0 +1,493 @@
package com.budwk.app.zhgh.learning.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
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.sys.models.Sys_union;
import com.budwk.app.web.commons.auth.utils.AuthUtil;
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
import com.budwk.app.zhgh.learning.models.LearningCourse;
import com.budwk.app.zhgh.learning.models.LearningCourseOutline;
import com.budwk.app.zhgh.learning.models.LearningOutlineResource;
import com.budwk.app.zhgh.learning.models.LearningStudyRecord;
import com.budwk.app.zhgh.learning.models.LearningStudyRule;
import com.budwk.app.zhgh.learning.models.LearningStudySegment;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.nutz.dao.Chain;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Sql;
import org.nutz.dao.util.cri.SqlExpressionGroup;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.util.NutMap;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
import org.nutz.mvc.annotation.Param;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@IocBean
@Ok("json:full")
@Api("学习教育平台-学习记录")
@At("/platform/learning/study/record")
public class LearningStudyRecordController {
private static final int MAX_HEARTBEAT_SECONDS = 30;
@Inject
private Dao dao;
@At
@ApiOperation("学习记录分页")
@SaCheckPermission("learning.my.record")
public Result pageData(PageForm pageForm,
@Param("keyword") String keyword,
@Param("unionId") String unionId,
@Param("courseId") String courseId,
@Param("completeStatus") String completeStatus) {
Cnd cnd = Cnd.NEW();
if (StrUtil.isNotBlank(keyword)) {
SqlExpressionGroup seg = new SqlExpressionGroup();
seg.or("r.loginName", "like", "%" + keyword + "%");
seg.or("r.userName", "like", "%" + keyword + "%");
cnd.and(seg);
}
cnd.andEX("r.unionId", "=", unionId);
cnd.andEX("r.courseId", "=", courseId);
cnd.andEX("r.completeStatus", "=", completeStatus);
appendScope(cnd);
String orderColumn = getOrderColumn(pageForm.getPageOrderName());
if (StrUtil.isNotBlank(orderColumn) && StrUtil.isNotBlank(pageForm.getPageOrderBy())) {
cnd.orderBy(orderColumn, PageUtil.getOrder(pageForm.getPageOrderBy()));
} else {
cnd.desc("r.latestStudyTime");
}
Sql countSql = Sqls.create("SELECT COUNT(1) FROM learning_study_record r $condition");
countSql.setCondition(cnd);
countSql.setCallback(Sqls.callback.integer());
dao.execute(countSql);
Sql listSql = Sqls.create("""
SELECT r.*
FROM learning_study_record r
$condition
""");
listSql.setCondition(cnd);
listSql.setPager(dao.createPager(pageForm.getPageNumber(), pageForm.getPageSize()));
listSql.setCallback(Sqls.callback.maps());
dao.execute(listSql);
List<NutMap> rows = listSql.getList(NutMap.class);
rows.forEach(row -> row.put("studyTimeText", formatStudySeconds(row.getInt("studySeconds", 0))));
return Result.success(new Pagination<>(pageForm.getPageNumber(), pageForm.getPageSize(), countSql.getInt(), rows));
}
@At
@ApiOperation("移动端我的学习汇总")
@SaCheckPermission("learning.my.record")
public Result h5Summary() {
String userId = SecurityUtil.getUserId();
Sql sql = Sqls.create("""
SELECT IFNULL(SUM(IFNULL(studySeconds, 0)), 0) AS studySeconds,
COUNT(DISTINCT courseId) AS courseCount,
COUNT(DISTINCT CASE WHEN completeStatus = 'completed' THEN courseId END) AS completedCourseCount,
COUNT(DISTINCT outlineId) AS outlineCount,
COUNT(DISTINCT CASE WHEN completeStatus = 'completed' THEN outlineId END) AS completedOutlineCount
FROM learning_study_record
WHERE userId = @userId
""");
sql.params().set("userId", userId);
sql.setCallback(Sqls.callback.map());
dao.execute(sql);
NutMap data = sql.getObject(NutMap.class);
if (data == null) {
data = NutMap.NEW();
}
int studySeconds = data.getInt("studySeconds", 0);
data.put("studyHour", studySeconds / 3600);
data.put("studyMinute", studySeconds % 3600 / 60);
return Result.success(data);
}
@At
@ApiOperation("移动端我的学习课程")
@SaCheckPermission("learning.my.record")
public Result h5Courses() {
String userId = SecurityUtil.getUserId();
Sql sql = Sqls.create("""
SELECT r.courseId,
MAX(r.courseName) AS courseName,
MAX(c.cover) AS cover,
COUNT(1) AS outlineCount,
SUM(CASE WHEN r.completeStatus = 'completed' THEN 1 ELSE 0 END) AS completedOutlineCount,
IFNULL(SUM(IFNULL(r.studySeconds, 0)), 0) AS studySeconds,
IFNULL(ROUND(AVG(IFNULL(r.progressPercent, 0))), 0) AS progressPercent,
MAX(r.latestStudyTime) AS latestStudyTime
FROM learning_study_record r
LEFT JOIN learning_course c ON c.id = r.courseId
WHERE r.userId = @userId
GROUP BY r.courseId
ORDER BY latestStudyTime DESC
""");
sql.params().set("userId", userId);
sql.setCallback(Sqls.callback.maps());
dao.execute(sql);
List<NutMap> rows = sql.getList(NutMap.class);
rows.forEach(row -> row.put("studyTimeText", formatStudySeconds(row.getInt("studySeconds", 0))));
return Result.success(rows);
}
@At
@ApiOperation("开始学习")
@SaCheckPermission("learning.course.display")
public Result start(@Param("courseId") String courseId,
@Param("resourceId") String resourceId,
@Param("positionSeconds") Integer positionSeconds) {
if (StrUtil.hasBlank(courseId, resourceId)) {
return Result.error("请选择课程资料后开始学习");
}
LearningCourse course = dao.fetch(LearningCourse.class, courseId);
LearningOutlineResource resource = dao.fetch(LearningOutlineResource.class, resourceId);
if (course == null || resource == null || !courseId.equals(resource.getCourseId())) {
return Result.error("课程资料不存在");
}
LearningCourseOutline outline = dao.fetch(LearningCourseOutline.class, resource.getOutlineId());
if (outline == null) {
return Result.error("章节不存在");
}
String userId = SecurityUtil.getUserId();
Date now = new Date();
closeUnfinishedSegments(userId, "interrupted");
LearningStudyRecord record = dao.fetch(LearningStudyRecord.class, Cnd.where("userId", "=", userId)
.and("courseId", "=", courseId)
.and("outlineId", "=", outline.getId()));
if (record == null) {
record = buildRecord(userId, course, outline, now);
record.setLastPositionSeconds(sanitizePosition(positionSeconds, 0));
dao.insert(record);
} else {
int requiredSeconds = requiredSeconds(outline.getId());
dao.update(LearningStudyRecord.class, Chain.make("latestStudyTime", now)
.add("courseName", course.getCourseName())
.add("outlineName", outline.getTitle())
.add("lastPositionSeconds", sanitizePosition(positionSeconds, record.getLastPositionSeconds()))
.add("requiredSeconds", requiredSeconds)
.add("progressPercent", progress(record.getStudySeconds(), requiredSeconds))
.add("completeStatus", "completed".equals(record.getCompleteStatus()) ? "completed" : "studying"),
Cnd.where("id", "=", record.getId()));
record = dao.fetch(LearningStudyRecord.class, record.getId());
}
LearningStudySegment segment = new LearningStudySegment();
segment.setRecordId(record.getId());
segment.setUserId(userId);
segment.setCourseId(courseId);
segment.setOutlineId(outline.getId());
segment.setResourceId(resourceId);
segment.setResourceName(resource.getResourceTitle());
segment.setStartTime(now);
segment.setLastHeartbeatTime(now);
segment.setActiveSeconds(0);
segment.setState("studying");
dao.insert(segment);
return Result.success(NutMap.NEW()
.addv("recordId", record.getId())
.addv("segmentId", segment.getId())
.addv("studySeconds", record.getStudySeconds())
.addv("lastPositionSeconds", record.getLastPositionSeconds())
.addv("requiredSeconds", record.getRequiredSeconds())
.addv("progressPercent", record.getProgressPercent())
.addv("completeStatus", record.getCompleteStatus()));
}
@At
@ApiOperation("学习心跳")
@SaCheckPermission("learning.course.display")
public Result heartbeat(@Param("segmentId") String segmentId,
@Param("activeSeconds") Integer activeSeconds,
@Param("positionSeconds") Integer positionSeconds) {
LearningStudySegment segment = fetchOwnStudyingSegment(segmentId);
if (segment == null) {
return Result.error("学习时段已结束,请重新开始学习");
}
int seconds = Math.max(0, Math.min(activeSeconds == null ? 0 : activeSeconds, MAX_HEARTBEAT_SECONDS));
return Result.success(addActiveSeconds(segment, seconds, false, positionSeconds));
}
@At
@ApiOperation("结束学习")
@SaCheckPermission("learning.course.display")
public Result finish(@Param("segmentId") String segmentId,
@Param("activeSeconds") Integer activeSeconds,
@Param("positionSeconds") Integer positionSeconds) {
LearningStudySegment segment = fetchOwnStudyingSegment(segmentId);
if (segment == null) {
return Result.success();
}
int seconds = Math.max(0, Math.min(activeSeconds == null ? 0 : activeSeconds, MAX_HEARTBEAT_SECONDS));
NutMap result = addActiveSeconds(segment, seconds, true, positionSeconds);
dao.update(LearningStudySegment.class, Chain.make("endTime", new Date()).add("state", "finished"), Cnd.where("id", "=", segmentId));
return Result.success(result);
}
@At
@ApiOperation("保存播放位置")
@SaCheckPermission("learning.course.display")
public Result position(@Param("courseId") String courseId,
@Param("resourceId") String resourceId,
@Param("positionSeconds") Integer positionSeconds) {
if (StrUtil.hasBlank(courseId, resourceId) || positionSeconds == null || positionSeconds < 0) {
return Result.success();
}
LearningOutlineResource resource = dao.fetch(LearningOutlineResource.class, resourceId);
if (resource == null || !courseId.equals(resource.getCourseId())) {
return Result.success();
}
dao.update(LearningStudyRecord.class,
Chain.make("lastPositionSeconds", positionSeconds).add("latestStudyTime", new Date()),
Cnd.where("userId", "=", SecurityUtil.getUserId())
.and("courseId", "=", courseId)
.and("outlineId", "=", resource.getOutlineId()));
return Result.success();
}
@At
@ApiOperation("可选课程")
@SaCheckPermission("learning.my.record")
public Result courseOptions() {
return Result.success(dao.query(LearningCourse.class, Cnd.NEW().asc("sortNum").desc("createdAt")));
}
@At
@ApiOperation("可选分工会")
@SaCheckPermission("learning.my.record")
public Result unionOptions() {
if (hasSchoolScope()) {
return Result.success(dao.query(Sys_union.class, Cnd.NEW().asc("unionCode").asc("name")));
}
if (hasBranchScope()) {
return Result.success(dao.query(Sys_union.class, Cnd.where("id", "=", SecurityUtil.getUnionId())));
}
return Result.success(List.of());
}
@At
@ApiOperation("删除学习记录")
@SaCheckPermission("learning.my.record")
public Result delete(@Param("id") String id) {
if (StrUtil.isBlank(id)) {
return Result.error("请选择要删除的学习记录");
}
LearningStudyRecord record = dao.fetch(LearningStudyRecord.class, id);
if (record == null) {
return Result.error("学习记录不存在");
}
if (!canOperate(record)) {
return Result.error("无权删除该学习记录");
}
dao.clear(LearningStudySegment.class, Cnd.where("recordId", "=", id));
dao.clear(LearningStudyRecord.class, Cnd.where("id", "=", id));
return Result.success();
}
private LearningStudyRecord buildRecord(String userId, LearningCourse course, LearningCourseOutline outline, Date now) {
NutMap user = currentUserInfo(userId);
int requiredSeconds = requiredSeconds(outline.getId());
LearningStudyRecord record = new LearningStudyRecord();
record.setUserId(userId);
record.setLoginName(user.getString("loginname", SecurityUtil.getUserLoginname()));
record.setUserName(user.getString("username", SecurityUtil.getUserUsername()));
record.setUnionId(user.getString("unionId", SecurityUtil.getUnionId()));
record.setUnionName(user.getString("unionName", ""));
record.setCourseId(course.getId());
record.setCourseName(course.getCourseName());
record.setOutlineId(outline.getId());
record.setOutlineName(outline.getTitle());
record.setFirstStudyTime(now);
record.setLatestStudyTime(now);
record.setStudySeconds(0);
record.setLastPositionSeconds(0);
record.setRequiredSeconds(requiredSeconds);
record.setProgressPercent(0);
record.setCompleteStatus("studying");
return record;
}
private NutMap addActiveSeconds(LearningStudySegment segment, int activeSeconds, boolean finish, Integer positionSeconds) {
Date now = new Date();
int segmentSeconds = value(segment.getActiveSeconds()) + activeSeconds;
dao.update(LearningStudySegment.class, Chain.make("activeSeconds", segmentSeconds).add("lastHeartbeatTime", now), Cnd.where("id", "=", segment.getId()));
LearningStudyRecord record = dao.fetch(LearningStudyRecord.class, segment.getRecordId());
int studySeconds = value(record.getStudySeconds()) + activeSeconds;
int requiredSeconds = requiredSeconds(record.getOutlineId());
int progressPercent = progress(studySeconds, requiredSeconds);
String completeStatus = completeStatus(studySeconds, requiredSeconds, finish);
if ("completed".equals(completeStatus)) {
progressPercent = 100;
}
Chain chain = Chain.make("latestStudyTime", now)
.add("studySeconds", studySeconds)
.add("requiredSeconds", requiredSeconds)
.add("progressPercent", progressPercent)
.add("completeStatus", completeStatus);
int lastPositionSeconds = value(record.getLastPositionSeconds());
if (positionSeconds != null && positionSeconds >= 0) {
lastPositionSeconds = positionSeconds;
chain.add("lastPositionSeconds", positionSeconds);
}
if ("completed".equals(completeStatus) && record.getCompletedAt() == null) {
chain.add("completedAt", now);
}
dao.update(LearningStudyRecord.class, chain, Cnd.where("id", "=", record.getId()));
return NutMap.NEW()
.addv("studySeconds", studySeconds)
.addv("studyTimeText", formatStudySeconds(studySeconds))
.addv("lastPositionSeconds", lastPositionSeconds)
.addv("requiredSeconds", requiredSeconds)
.addv("progressPercent", progressPercent)
.addv("completeStatus", completeStatus);
}
private LearningStudySegment fetchOwnStudyingSegment(String segmentId) {
if (StrUtil.isBlank(segmentId)) {
return null;
}
return dao.fetch(LearningStudySegment.class, Cnd.where("id", "=", segmentId)
.and("userId", "=", SecurityUtil.getUserId())
.and("state", "=", "studying"));
}
private void closeUnfinishedSegments(String userId, String state) {
dao.update(LearningStudySegment.class,
Chain.make("endTime", new Date()).add("state", state),
Cnd.where("userId", "=", userId).and("state", "=", "studying"));
}
private int requiredSeconds(String outlineId) {
LearningStudyRule rule = dao.fetch(LearningStudyRule.class, Cnd.where("targetType", "=", "outline")
.and("targetId", "=", outlineId)
.and("status", "=", "enabled"));
if (rule != null && value(rule.getMinStudySeconds()) > 0) {
return rule.getMinStudySeconds();
}
Sql sql = Sqls.create("""
SELECT IFNULL(SUM(IFNULL(durationSeconds, 0)), 0)
FROM learning_outline_resource
WHERE outlineId = @outlineId AND status = 'enabled'
""");
sql.params().set("outlineId", outlineId);
sql.setCallback(Sqls.callback.integer());
dao.execute(sql);
return Math.max(0, sql.getInt());
}
private int progress(Integer studySeconds, int requiredSeconds) {
if (requiredSeconds <= 0) {
return 0;
}
return Math.min(100, (int) Math.floor(value(studySeconds) * 100.0 / requiredSeconds));
}
private String completeStatus(Integer studySeconds, int requiredSeconds, boolean finish) {
if (requiredSeconds <= 0) {
return finish ? "completed" : "studying";
}
if (value(studySeconds) >= requiredSeconds) {
return "completed";
}
return value(studySeconds) > 0 || finish ? "studying" : "not_started";
}
private NutMap currentUserInfo(String userId) {
Sql sql = Sqls.create("SELECT id, loginname, username, unionId AS unionId, unionName AS unionName FROM vw_user WHERE id = @id");
sql.params().set("id", userId);
sql.setCallback(Sqls.callback.map());
dao.execute(sql);
NutMap user = sql.getObject(NutMap.class);
return user == null ? NutMap.NEW() : user;
}
private void appendScope(Cnd cnd) {
if (hasSchoolScope()) {
return;
}
if (hasBranchScope()) {
cnd.and("r.unionId", "=", SecurityUtil.getUnionId());
return;
}
cnd.and("r.userId", "=", SecurityUtil.getUserId());
}
private boolean hasSchoolScope() {
return AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_ADMIN.name());
}
private boolean hasBranchScope() {
return AuthUtil.hasRoleOr(RoleConstant.BRANCH_UNION_ADMIN.name());
}
private boolean canOperate(LearningStudyRecord record) {
if (hasSchoolScope()) {
return true;
}
if (hasBranchScope()) {
return StrUtil.equals(record.getUnionId(), SecurityUtil.getUnionId());
}
return StrUtil.equals(record.getUserId(), SecurityUtil.getUserId());
}
private String getOrderColumn(String prop) {
Map<String, String> columns = new HashMap<>();
columns.put("loginName", "r.loginName");
columns.put("userName", "r.userName");
columns.put("unionName", "r.unionName");
columns.put("courseName", "r.courseName");
columns.put("outlineName", "r.outlineName");
columns.put("firstStudyTime", "r.firstStudyTime");
columns.put("latestStudyTime", "r.latestStudyTime");
columns.put("studySeconds", "r.studySeconds");
columns.put("progressPercent", "r.progressPercent");
columns.put("completeStatus", "r.completeStatus");
return columns.get(prop);
}
private String formatStudySeconds(Integer seconds) {
int value = value(seconds);
int hour = value / 3600;
int minute = value % 3600 / 60;
int second = value % 60;
if (hour > 0) {
return hour + "小时" + minute + "" + second + "";
}
if (minute > 0) {
return minute + "" + second + "";
}
return second + "";
}
private int value(Integer value) {
return value == null ? 0 : value;
}
private int sanitizePosition(Integer positionSeconds, Integer defaultValue) {
if (positionSeconds == null || positionSeconds < 0) {
return value(defaultValue);
}
return positionSeconds;
}
}
@@ -0,0 +1,117 @@
package com.budwk.app.zhgh.learning.models;
import com.budwk.app.base.model.BaseModel;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.nutz.dao.entity.annotation.ColDefine;
import org.nutz.dao.entity.annotation.ColType;
import org.nutz.dao.entity.annotation.Column;
import org.nutz.dao.entity.annotation.Comment;
import org.nutz.dao.entity.annotation.Name;
import org.nutz.dao.entity.annotation.Table;
import org.nutz.dao.interceptor.annotation.PrevInsert;
import java.util.Date;
@Data
@Table
@EqualsAndHashCode(callSuper = true)
public class LearningCourse extends BaseModel {
@Column
@Name
@Comment("ID")
@ColDefine(type = ColType.VARCHAR, width = 32)
@PrevInsert(uu32 = true)
private String id;
@Column
@Comment("课程名称")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String courseName;
@Column
@Comment("课程类型")
@ColDefine(type = ColType.VARCHAR, width = 32)
private String courseTypeId;
@Column
@Comment("授课讲师")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String lecturerName;
@Column
@Comment("讲师基本信息")
@ColDefine(type = ColType.VARCHAR, width = 500)
private String lecturerInfo;
@Column
@Comment("课程简介")
@ColDefine(customType = "longtext")
private String courseIntro;
@Column
@Comment("适合人群")
@ColDefine(type = ColType.VARCHAR, width = 500)
private String suitablePeople;
@Column
@Comment("学习目标")
@ColDefine(type = ColType.VARCHAR, width = 500)
private String learningGoal;
@Column
@Comment("课程周期")
@ColDefine(type = ColType.VARCHAR, width = 50)
private String coursePeriod;
@Column
@Comment("开课类型")
@ColDefine(type = ColType.VARCHAR, width = 20)
private String openType;
@Column
@Comment("开始时间")
@ColDefine(type = ColType.DATETIME)
private Date startTime;
@Column
@Comment("结束时间")
@ColDefine(type = ColType.DATETIME)
private Date endTime;
@Column
@Comment("课程状态")
@ColDefine(type = ColType.VARCHAR, width = 20)
private String status;
@Column
@Comment("推荐标识")
@ColDefine(type = ColType.VARCHAR, width = 500)
private String recommendFlags;
@Column
@Comment("课程标签")
@ColDefine(type = ColType.VARCHAR, width = 500)
private String courseTags;
@Column
@Comment("学习对象")
@ColDefine(type = ColType.VARCHAR, width = 20)
private String targetType;
@Column
@Comment("指定组织")
@ColDefine(type = ColType.VARCHAR, width = 500)
private String targetOrgText;
@Column
@Comment("排序编码")
@ColDefine(type = ColType.INT)
private Integer sortNum;
@Column
@Comment("课程封面")
@ColDefine(type = ColType.VARCHAR, width = 500)
private String cover;
}
@@ -0,0 +1,75 @@
package com.budwk.app.zhgh.learning.models;
import com.budwk.app.base.model.BaseModel;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.nutz.dao.entity.annotation.ColDefine;
import org.nutz.dao.entity.annotation.ColType;
import org.nutz.dao.entity.annotation.Column;
import org.nutz.dao.entity.annotation.Comment;
import org.nutz.dao.entity.annotation.Name;
import org.nutz.dao.entity.annotation.Table;
import org.nutz.dao.interceptor.annotation.PrevInsert;
@Data
@Table
@EqualsAndHashCode(callSuper = true)
public class LearningCourseOutline extends BaseModel {
@Column
@Name
@Comment("ID")
@ColDefine(type = ColType.VARCHAR, width = 32)
@PrevInsert(uu32 = true)
private String id;
@Column
@Comment("所属课程ID")
@ColDefine(type = ColType.VARCHAR, width = 32)
private String courseId;
@Column
@Comment("所属课程名称")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String courseName;
@Column
@Comment("父节点ID")
@ColDefine(type = ColType.VARCHAR, width = 32)
private String parentId;
@Column
@Comment("节点类型")
@ColDefine(type = ColType.VARCHAR, width = 20)
private String nodeType;
@Column
@Comment("章/节标题")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String title;
@Column
@Comment("副标题")
@ColDefine(type = ColType.VARCHAR, width = 200)
private String subtitle;
@Column
@Comment("简介")
@ColDefine(customType = "longtext")
private String description;
@Column
@Comment("排序值")
@ColDefine(type = ColType.INT)
private Integer sortOrder;
@Column
@Comment("是否必学")
@ColDefine(type = ColType.BOOLEAN)
private Boolean required;
@Column
@Comment("状态")
@ColDefine(type = ColType.VARCHAR, width = 20)
private String status;
}
@@ -0,0 +1,45 @@
package com.budwk.app.zhgh.learning.models;
import com.budwk.app.base.model.BaseModel;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.nutz.dao.entity.annotation.ColDefine;
import org.nutz.dao.entity.annotation.ColType;
import org.nutz.dao.entity.annotation.Column;
import org.nutz.dao.entity.annotation.Comment;
import org.nutz.dao.entity.annotation.Name;
import org.nutz.dao.entity.annotation.Table;
import org.nutz.dao.interceptor.annotation.PrevInsert;
@Data
@Table
@EqualsAndHashCode(callSuper = true)
public class LearningCourseType extends BaseModel {
@Column
@Name
@Comment("ID")
@ColDefine(type = ColType.VARCHAR, width = 32)
@PrevInsert(uu32 = true)
private String id;
@Column
@Comment("课程类型名称")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String typeName;
@Column
@Comment("排序编号")
@ColDefine(type = ColType.INT)
private Integer sortNum;
@Column
@Comment("是否启用")
@ColDefine(type = ColType.BOOLEAN)
private Boolean enabled;
@Column
@Comment("备注")
@ColDefine(type = ColType.VARCHAR, width = 500)
private String remark;
}
@@ -0,0 +1,85 @@
package com.budwk.app.zhgh.learning.models;
import com.budwk.app.base.model.BaseModel;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.nutz.dao.entity.annotation.ColDefine;
import org.nutz.dao.entity.annotation.ColType;
import org.nutz.dao.entity.annotation.Column;
import org.nutz.dao.entity.annotation.Comment;
import org.nutz.dao.entity.annotation.Name;
import org.nutz.dao.entity.annotation.Table;
import org.nutz.dao.interceptor.annotation.PrevInsert;
@Data
@Table
@EqualsAndHashCode(callSuper = true)
public class LearningOutlineResource extends BaseModel {
@Column
@Name
@Comment("ID")
@ColDefine(type = ColType.VARCHAR, width = 32)
@PrevInsert(uu32 = true)
private String id;
@Column
@Comment("所属课程ID")
@ColDefine(type = ColType.VARCHAR, width = 32)
private String courseId;
@Column
@Comment("所属章/节ID")
@ColDefine(type = ColType.VARCHAR, width = 32)
private String outlineId;
@Column
@Comment("资料标题")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String resourceTitle;
@Column
@Comment("资料类型")
@ColDefine(type = ColType.VARCHAR, width = 20)
private String resourceType;
@Column
@Comment("文件扩展名")
@ColDefine(type = ColType.VARCHAR, width = 20)
private String fileExt;
@Column
@Comment("附件")
@ColDefine(customType = "longtext")
private String fileData;
@Column
@Comment("视频/音频时长")
@ColDefine(type = ColType.INT)
private Integer durationSeconds;
@Column
@Comment("排序值")
@ColDefine(type = ColType.INT)
private Integer sortOrder;
@Column
@Comment("是否必学")
@ColDefine(type = ColType.BOOLEAN)
private Boolean required;
@Column
@Comment("是否允许预览")
@ColDefine(type = ColType.BOOLEAN)
private Boolean allowPreview;
@Column
@Comment("是否允许下载")
@ColDefine(type = ColType.BOOLEAN)
private Boolean allowDownload;
@Column
@Comment("状态")
@ColDefine(type = ColType.VARCHAR, width = 20)
private String status;
}
@@ -0,0 +1,119 @@
package com.budwk.app.zhgh.learning.models;
import com.budwk.app.base.model.BaseModel;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.nutz.dao.entity.annotation.ColDefine;
import org.nutz.dao.entity.annotation.ColType;
import org.nutz.dao.entity.annotation.Column;
import org.nutz.dao.entity.annotation.Comment;
import org.nutz.dao.entity.annotation.Index;
import org.nutz.dao.entity.annotation.Name;
import org.nutz.dao.entity.annotation.Table;
import org.nutz.dao.entity.annotation.TableIndexes;
import org.nutz.dao.interceptor.annotation.PrevInsert;
import java.util.Date;
@Data
@Table("learning_study_record")
@TableIndexes({
@Index(name = "idx_learning_record_user_course_outline", fields = {"userId", "courseId", "outlineId"}, unique = true),
@Index(name = "idx_learning_record_union", fields = {"unionId"}, unique = false),
@Index(name = "idx_learning_record_course", fields = {"courseId"}, unique = false)
})
@EqualsAndHashCode(callSuper = true)
public class LearningStudyRecord extends BaseModel {
@Column
@Name
@Comment("ID")
@ColDefine(type = ColType.VARCHAR, width = 32)
@PrevInsert(uu32 = true)
private String id;
@Column
@Comment("学习人ID")
@ColDefine(type = ColType.VARCHAR, width = 32)
private String userId;
@Column
@Comment("工号")
@ColDefine(type = ColType.VARCHAR, width = 120)
private String loginName;
@Column
@Comment("姓名")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String userName;
@Column
@Comment("所属分工会ID")
@ColDefine(type = ColType.VARCHAR, width = 32)
private String unionId;
@Column
@Comment("所属分工会")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String unionName;
@Column
@Comment("课程ID")
@ColDefine(type = ColType.VARCHAR, width = 32)
private String courseId;
@Column
@Comment("课程名称")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String courseName;
@Column
@Comment("章节ID")
@ColDefine(type = ColType.VARCHAR, width = 32)
private String outlineId;
@Column
@Comment("章节名称")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String outlineName;
@Column
@Comment("第一次进入时间")
@ColDefine(type = ColType.DATETIME)
private Date firstStudyTime;
@Column
@Comment("最近学习时间")
@ColDefine(type = ColType.DATETIME)
private Date latestStudyTime;
@Column
@Comment("累计有效学习时长(秒)")
@ColDefine(type = ColType.INT)
private Integer studySeconds;
@Column
@Comment("最近播放位置(秒)")
@ColDefine(type = ColType.INT)
private Integer lastPositionSeconds;
@Column
@Comment("要求学习时长(秒)")
@ColDefine(type = ColType.INT)
private Integer requiredSeconds;
@Column
@Comment("学习进度")
@ColDefine(type = ColType.INT)
private Integer progressPercent;
@Column
@Comment("完成状态")
@ColDefine(type = ColType.VARCHAR, width = 20)
private String completeStatus;
@Column
@Comment("完成时间")
@ColDefine(type = ColType.DATETIME)
private Date completedAt;
}
@@ -0,0 +1,100 @@
package com.budwk.app.zhgh.learning.models;
import com.budwk.app.base.model.BaseModel;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.nutz.dao.entity.annotation.ColDefine;
import org.nutz.dao.entity.annotation.ColType;
import org.nutz.dao.entity.annotation.Column;
import org.nutz.dao.entity.annotation.Comment;
import org.nutz.dao.entity.annotation.Name;
import org.nutz.dao.entity.annotation.Table;
import org.nutz.dao.interceptor.annotation.PrevInsert;
@Data
@Table
@EqualsAndHashCode(callSuper = true)
public class LearningStudyRule extends BaseModel {
@Column
@Name
@Comment("ID")
@ColDefine(type = ColType.VARCHAR, width = 32)
@PrevInsert(uu32 = true)
private String id;
@Column
@Comment("所属课程ID")
@ColDefine(type = ColType.VARCHAR, width = 32)
private String courseId;
@Column
@Comment("规则对象类型")
@ColDefine(type = ColType.VARCHAR, width = 20)
private String targetType;
@Column
@Comment("规则对象ID")
@ColDefine(type = ColType.VARCHAR, width = 32)
private String targetId;
@Column
@Comment("是否必学")
@ColDefine(type = ColType.BOOLEAN)
private Boolean required;
@Column
@Comment("学习方式")
@ColDefine(type = ColType.VARCHAR, width = 20)
private String studyMode;
@Column
@Comment("完成规则")
@ColDefine(type = ColType.VARCHAR, width = 50)
private String completionRule;
@Column
@Comment("完成比例")
@ColDefine(type = ColType.INT)
private Integer completePercent;
@Column
@Comment("最少学习时长")
@ColDefine(type = ColType.INT)
private Integer minStudySeconds;
@Column
@Comment("解锁规则")
@ColDefine(type = ColType.VARCHAR, width = 20)
private String unlockRule;
@Column
@Comment("是否允许跳过")
@ColDefine(type = ColType.BOOLEAN)
private Boolean allowSkip;
@Column
@Comment("是否允许拖动")
@ColDefine(type = ColType.BOOLEAN)
private Boolean allowDrag;
@Column
@Comment("暂停是否计时")
@ColDefine(type = ColType.BOOLEAN)
private Boolean pauseCountTime;
@Column
@Comment("页面隐藏是否计时")
@ColDefine(type = ColType.BOOLEAN)
private Boolean hiddenCountTime;
@Column
@Comment("长时间无操作是否计时")
@ColDefine(type = ColType.BOOLEAN)
private Boolean inactiveCountTime;
@Column
@Comment("状态")
@ColDefine(type = ColType.VARCHAR, width = 20)
private String status;
}
@@ -0,0 +1,88 @@
package com.budwk.app.zhgh.learning.models;
import com.budwk.app.base.model.BaseModel;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.nutz.dao.entity.annotation.ColDefine;
import org.nutz.dao.entity.annotation.ColType;
import org.nutz.dao.entity.annotation.Column;
import org.nutz.dao.entity.annotation.Comment;
import org.nutz.dao.entity.annotation.Index;
import org.nutz.dao.entity.annotation.Name;
import org.nutz.dao.entity.annotation.Table;
import org.nutz.dao.entity.annotation.TableIndexes;
import org.nutz.dao.interceptor.annotation.PrevInsert;
import java.util.Date;
@Data
@Table("learning_study_segment")
@TableIndexes({
@Index(name = "idx_learning_segment_record", fields = {"recordId"}, unique = false),
@Index(name = "idx_learning_segment_user_state", fields = {"userId", "state"}, unique = false)
})
@EqualsAndHashCode(callSuper = true)
public class LearningStudySegment extends BaseModel {
@Column
@Name
@Comment("ID")
@ColDefine(type = ColType.VARCHAR, width = 32)
@PrevInsert(uu32 = true)
private String id;
@Column
@Comment("学习记录ID")
@ColDefine(type = ColType.VARCHAR, width = 32)
private String recordId;
@Column
@Comment("学习人ID")
@ColDefine(type = ColType.VARCHAR, width = 32)
private String userId;
@Column
@Comment("课程ID")
@ColDefine(type = ColType.VARCHAR, width = 32)
private String courseId;
@Column
@Comment("章节ID")
@ColDefine(type = ColType.VARCHAR, width = 32)
private String outlineId;
@Column
@Comment("资源ID")
@ColDefine(type = ColType.VARCHAR, width = 32)
private String resourceId;
@Column
@Comment("资源名称")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String resourceName;
@Column
@Comment("开始时间")
@ColDefine(type = ColType.DATETIME)
private Date startTime;
@Column
@Comment("结束时间")
@ColDefine(type = ColType.DATETIME)
private Date endTime;
@Column
@Comment("最近心跳时间")
@ColDefine(type = ColType.DATETIME)
private Date lastHeartbeatTime;
@Column
@Comment("本时段有效学习时长(秒)")
@ColDefine(type = ColType.INT)
private Integer activeSeconds;
@Column
@Comment("状态")
@ColDefine(type = ColType.VARCHAR, width = 20)
private String state;
}
@@ -0,0 +1,16 @@
package com.budwk.app.zhgh.learning.param;
import com.budwk.app.base.param.PageForm;
import lombok.Data;
import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = true)
@Data
public class LearningStatisticsPageForm extends PageForm {
private String courseId;
private String courseName;
private String unionId;
}