补全课时统计、课时费与课表源码,避免他人拉取后缺少包。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
liumengyu
2026-08-20 18:15:36 +08:00
parent 4a0dcbb341
commit 2a5bf443fe
12 changed files with 912 additions and 205 deletions
@@ -0,0 +1,25 @@
package com.roomroot.jwgl.dto.ks;
import lombok.Data;
/**
* 课时统计 / 课时费统计查询条件。
*/
@Data
public class HourStatisticsQuery {
/** 学期年度(对应课时统计表.年份、学期.年度;空则默认当前学期) */
private Integer nd;
/** 学期序号(与 nd 一起标识学期;课时统计表按年存储,查询按年度过滤) */
private Integer xq;
/** 教员姓名(模糊匹配) */
private String jyxm;
/** 教员编号(教员登录时强制为本人) */
private String jybh;
/** 课时费标准编号(课时费统计可选;空则取最新一条方案) */
private String ksfbzbh;
}
@@ -0,0 +1,12 @@
package com.roomroot.jwgl.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.roomroot.jwgl.entity.KB;
import org.apache.ibatis.annotations.Mapper;
/**
* 课表 Mapper
*/
@Mapper
public interface KBMapper extends BaseMapper<KB> {
}
@@ -0,0 +1,48 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.roomroot.jwgl.mapper.KBMapper">
<resultMap id="BaseResultMap" type="com.roomroot.jwgl.entity.KB">
<id column="课编号" property="kbh" />
<result column="课名称" property="kmc" />
<result column="教研室代号" property="jysdh" />
<result column="备注" property="bz" />
<result column="序号" property="xh" />
<result column="学分" property="xf" />
<result column="简称" property="jc" />
<result column="学时" property="xs" />
<result column="培训层次" property="pxcc" />
<result column="培训类型" property="pxlx" />
<result column="课程类型" property="kclx" />
<result column="考试课时" property="ksks" />
<result column="拼音" property="py" />
<result column="规范名称" property="gfmc" />
<result column="成绩分制" property="cjfz" />
<result column="不计入学员平均分" property="bjrxypjf" />
<result column="教学管理部门编号" property="jxglbmbh" />
<result column="理论学时" property="llxs" />
<result column="实践学时" property="sjxs" />
<result column="科目代码" property="kmdm" />
<result column="周课时" property="zks" />
<result column="课类型" property="klx" />
<result column="考试课时不显示" property="ksksbxs" />
<result column="课程统一编号" property="kctybh" />
<result column="适用对象" property="sydx" />
<result column="辅导答疑课时" property="fddyks" />
<result column="自修课时" property="zxks" />
<result column="版本" property="bb" />
<result column="修订日期" property="xdrq" />
<result column="序号标识" property="xhbs" />
<result column="节次优选级" property="jcyxj" />
<result column="课程支持方式" property="kczcfs" />
<result column="课程建设方式" property="kcjsfs" />
<result column="json字典" property="jsonzd" />
<result column="政治类课程" property="zzlkc" />
<result column="考核方式" property="khfs" />
<result column="形成性成绩分值" property="xcxcjfz" />
<result column="终结性成绩分值" property="zjxcjfz" />
<result column="密级" property="mj" />
<result column="评选情况" property="pxqk" />
</resultMap>
</mapper>
@@ -0,0 +1,49 @@
package com.roomroot.jwgl.service;
import com.roomroot.jwgl.entity.KB;
import com.roomroot.jwgl.unit.PageQuery;
import com.roomroot.jwgl.unit.PageResult;
/**
* 课表(课程科目)服务。
*/
public interface KBService {
/**
* 新增课表科目。
*
* @param entity 课表
*/
void add(KB entity);
/**
* 删除课表科目。
*
* @param kbh 课编号
*/
void delete(String kbh);
/**
* 更新课表科目。
*
* @param entity 课表(须含课编号)
*/
void update(KB entity);
/**
* 按课编号查询。
*
* @param kbh 课编号
* @return 课表
*/
KB getByKbh(String kbh);
/**
* 分页查询课表科目。
*
* @param query 分页参数
* @param cond 查询条件
* @return 分页结果
*/
PageResult<KB> pageList(PageQuery query, KB cond);
}
@@ -0,0 +1,52 @@
package com.roomroot.jwgl.service;
import com.roomroot.jwgl.entity.KSFBZ;
import com.roomroot.jwgl.unit.PageQuery;
import com.roomroot.jwgl.unit.PageResult;
/**
* 课时管理服务。
* <p>
* 当前提供课时费标准方案(表:课时费标准)的增删改查。
* </p>
*/
public interface KJService {
/**
* 新增课时费标准方案。
*
* @param entity 课时费标准
*/
void addFeeStandard(KSFBZ entity);
/**
* 删除课时费标准方案。
*
* @param bh 编号
*/
void deleteFeeStandard(String bh);
/**
* 更新课时费标准方案。
*
* @param entity 课时费标准(须含编号)
*/
void updateFeeStandard(KSFBZ entity);
/**
* 按编号查询课时费标准方案。
*
* @param bh 编号
* @return 课时费标准,不存在时返回 null
*/
KSFBZ getFeeStandardById(String bh);
/**
* 分页查询课时费标准方案。
*
* @param query 分页参数
* @param cond 查询条件(名称模糊匹配)
* @return 分页结果
*/
PageResult<KSFBZ> pageFeeStandard(PageQuery query, KSFBZ cond);
}
@@ -0,0 +1,35 @@
package com.roomroot.jwgl.service;
import com.roomroot.jwgl.dto.ks.HourStatisticsQuery;
import com.roomroot.jwgl.unit.PageQuery;
import com.roomroot.jwgl.unit.PageResult;
import com.roomroot.jwgl.vo.ks.HourStatisticsVO;
import jakarta.servlet.http.HttpServletResponse;
/**
* 课时统计、课时费统计。
*/
public interface KSService {
/**
* 分页查询课时统计。
* <p>规则见课时统计表联查 SQL:标准课时量 180,超课时及职称单价计算过程和结果。</p>
*/
PageResult<HourStatisticsVO> pageHourStatistics(PageQuery query, HourStatisticsQuery cond);
/**
* 导出课时统计。
*/
void exportHourStatistics(HourStatisticsQuery cond, HttpServletResponse response);
/**
* 分页查询课时费统计。
* <p>课时量同源,单价取课时费标准(默认最新方案)。</p>
*/
PageResult<HourStatisticsVO> pageFeeStatistics(PageQuery query, HourStatisticsQuery cond);
/**
* 导出课时费统计。
*/
void exportFeeStatistics(HourStatisticsQuery cond, HttpServletResponse response);
}
@@ -0,0 +1,180 @@
package com.roomroot.jwgl.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.roomroot.common.exception.ServiceException;
import com.roomroot.common.utils.StringUtils;
import com.roomroot.jwgl.entity.KB;
import com.roomroot.jwgl.entity.KCBZ;
import com.roomroot.jwgl.entity.XYDRWB;
import com.roomroot.jwgl.entity.ZYJXJHB;
import com.roomroot.jwgl.mapper.KBMapper;
import com.roomroot.jwgl.mapper.KCBZMapper;
import com.roomroot.jwgl.mapper.StudentTeamTaskMapper;
import com.roomroot.jwgl.mapper.ZYJXJHBMapper;
import com.roomroot.jwgl.service.KBService;
import com.roomroot.jwgl.unit.PageQuery;
import com.roomroot.jwgl.unit.PageResult;
import com.roomroot.jwgl.utils.UuidUtil;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import jakarta.annotation.Resource;
import java.time.LocalDateTime;
import static com.roomroot.jwgl.utils.BasicManagementConstants.BAD_REQUEST;
import static com.roomroot.jwgl.utils.BasicManagementConstants.CONFLICT;
import static com.roomroot.jwgl.utils.BasicManagementConstants.NOT_FOUND;
/**
* 课表(课程科目)服务实现。
*/
@Service
public class KBServiceImpl implements KBService {
@Resource
private KBMapper kbMapper;
@Resource
private KCBZMapper kcbzMapper;
@Resource
private StudentTeamTaskMapper studentTeamTaskMapper;
@Resource
private ZYJXJHBMapper zyjxjhbMapper;
@Override
@Transactional
public void add(KB entity) {
if (entity == null) {
throw new ServiceException("课表信息不能为空", BAD_REQUEST);
}
validate(entity, true);
if (StringUtils.isEmpty(entity.getKbh())) {
entity.setKbh(UuidUtil.getOriginalUUID());
}
assertKbhUnique(entity.getKbh());
if (StringUtils.isEmpty(entity.getGfmc())) {
entity.setGfmc(entity.getKmc());
}
if (entity.getXdrq() == null) {
entity.setXdrq(LocalDateTime.now());
}
kbMapper.insert(entity);
}
@Override
@Transactional
public void delete(String kbh) {
if (StringUtils.isEmpty(kbh)) {
throw new ServiceException("课编号不能为空", BAD_REQUEST);
}
KB existing = kbMapper.selectById(kbh);
if (existing == null) {
throw new ServiceException("课表科目不存在", NOT_FOUND);
}
Long standardCount = kcbzMapper.selectCount(
new LambdaQueryWrapper<KCBZ>().eq(KCBZ::getKbh, kbh));
if (standardCount != null && standardCount > 0) {
throw new ServiceException("该科目已被课程标准引用,无法删除", CONFLICT);
}
Long taskCount = studentTeamTaskMapper.selectCount(
new LambdaQueryWrapper<XYDRWB>().eq(XYDRWB::getKbh, kbh));
if (taskCount != null && taskCount > 0) {
throw new ServiceException("该科目已被学员队任务引用,无法删除", CONFLICT);
}
Long planCount = zyjxjhbMapper.selectCount(
new LambdaQueryWrapper<ZYJXJHB>().eq(ZYJXJHB::getKbh, kbh));
if (planCount != null && planCount > 0) {
throw new ServiceException("该科目已被专业教学计划引用,无法删除", CONFLICT);
}
kbMapper.deleteById(kbh);
}
@Override
@Transactional
public void update(KB entity) {
if (entity == null || StringUtils.isEmpty(entity.getKbh())) {
throw new ServiceException("课编号不能为空", BAD_REQUEST);
}
validate(entity, false);
KB existing = kbMapper.selectById(entity.getKbh());
if (existing == null) {
throw new ServiceException("课表科目不存在", NOT_FOUND);
}
entity.setXdrq(LocalDateTime.now());
kbMapper.updateById(entity);
}
@Override
public KB getByKbh(String kbh) {
if (StringUtils.isEmpty(kbh)) {
throw new ServiceException("课编号不能为空", BAD_REQUEST);
}
return kbMapper.selectById(kbh);
}
@Override
public PageResult<KB> pageList(PageQuery query, KB cond) {
int pageNum = query.getPageNum() == null ? 1 : query.getPageNum();
int pageSize = query.getPageSize() == null ? 20 : query.getPageSize();
Page<KB> page = new Page<>(pageNum, pageSize);
LambdaQueryWrapper<KB> wrapper = new LambdaQueryWrapper<>();
if (cond != null) {
wrapper.like(StringUtils.isNotEmpty(cond.getKmc()), KB::getKmc, cond.getKmc());
wrapper.eq(StringUtils.isNotEmpty(cond.getJysdh()), KB::getJysdh, cond.getJysdh());
wrapper.eq(StringUtils.isNotEmpty(cond.getKclx()), KB::getKclx, cond.getKclx());
wrapper.eq(StringUtils.isNotEmpty(cond.getPxlx()), KB::getPxlx, cond.getPxlx());
wrapper.eq(StringUtils.isNotEmpty(cond.getPxcc()), KB::getPxcc, cond.getPxcc());
wrapper.like(StringUtils.isNotEmpty(cond.getKmdm()), KB::getKmdm, cond.getKmdm());
wrapper.like(StringUtils.isNotEmpty(cond.getJc()), KB::getJc, cond.getJc());
}
wrapper.orderByAsc(KB::getXhbs);
Page<KB> result = kbMapper.selectPage(page, wrapper);
return new PageResult<>(result.getRecords(), result.getTotal(), pageNum, pageSize);
}
private void validate(KB entity, boolean creating) {
if (creating && StringUtils.isEmpty(entity.getKmc())) {
throw new ServiceException("课名称不能为空", BAD_REQUEST);
}
assertMaxLength(entity.getKbh(), 50, "课编号");
assertMaxLength(entity.getKmc(), 250, "课名称");
assertMaxLength(entity.getJysdh(), 50, "教研室代号");
assertMaxLength(entity.getBz(), 500, "备注");
assertMaxLength(entity.getXh(), 50, "序号");
assertMaxLength(entity.getJc(), 50, "简称");
assertMaxLength(entity.getPxcc(), 50, "培训层次");
assertMaxLength(entity.getPxlx(), 50, "培训类型");
assertMaxLength(entity.getKclx(), 50, "课程类型");
assertMaxLength(entity.getPy(), 50, "拼音");
assertMaxLength(entity.getGfmc(), 250, "规范名称");
assertMaxLength(entity.getCjfz(), 50, "成绩分制");
assertMaxLength(entity.getJxglbmbh(), 50, "教学管理部门编号");
assertMaxLength(entity.getKmdm(), 50, "科目代码");
assertMaxLength(entity.getKlx(), 50, "课类型");
assertMaxLength(entity.getKctybh(), 50, "课程统一编号");
assertMaxLength(entity.getSydx(), 50, "适用对象");
assertMaxLength(entity.getBb(), 50, "版本");
assertMaxLength(entity.getKczcfs(), 50, "课程支持方式");
assertMaxLength(entity.getKcjsfs(), 50, "课程建设方式");
assertMaxLength(entity.getJsonzd(), 4000, "json字典");
assertMaxLength(entity.getKhfs(), 50, "考核方式");
assertMaxLength(entity.getMj(), 50, "密级");
assertMaxLength(entity.getPxqk(), 50, "评选情况");
}
private void assertMaxLength(String value, int max, String fieldName) {
if (value != null && value.length() > max) {
throw new ServiceException(fieldName + "长度不能超过" + max + "个字符", BAD_REQUEST);
}
}
private void assertKbhUnique(String kbh) {
Long count = kbMapper.selectCount(new LambdaQueryWrapper<KB>().eq(KB::getKbh, kbh));
if (count != null && count > 0) {
throw new ServiceException("课编号已存在", CONFLICT);
}
}
}
@@ -0,0 +1,151 @@
package com.roomroot.jwgl.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.roomroot.common.exception.ServiceException;
import com.roomroot.common.utils.StringUtils;
import com.roomroot.jwgl.entity.JSZWKSFBZ;
import com.roomroot.jwgl.entity.KSFBZ;
import com.roomroot.jwgl.entity.NZKSF;
import com.roomroot.jwgl.mapper.JSZWKSFBZMapper;
import com.roomroot.jwgl.mapper.KSFBZMapper;
import com.roomroot.jwgl.mapper.NZKSFMapper;
import com.roomroot.jwgl.service.KJService;
import com.roomroot.jwgl.unit.PageQuery;
import com.roomroot.jwgl.unit.PageResult;
import com.roomroot.jwgl.utils.UuidUtil;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import jakarta.annotation.Resource;
import java.time.LocalDateTime;
import static com.roomroot.jwgl.utils.BasicManagementConstants.BAD_REQUEST;
import static com.roomroot.jwgl.utils.BasicManagementConstants.CONFLICT;
import static com.roomroot.jwgl.utils.BasicManagementConstants.NOT_FOUND;
/**
* 课时管理服务实现。
*/
@Service
public class KJServiceImpl implements KJService {
private static final int NAME_MAX_LENGTH = 50;
private static final int TEXT_MAX_LENGTH = 500;
@Resource
private KSFBZMapper ksfbzMapper;
@Resource
private JSZWKSFBZMapper jszwksfbzMapper;
@Resource
private NZKSFMapper nzksfMapper;
@Override
@Transactional
public void addFeeStandard(KSFBZ entity) {
if (entity == null) {
throw new ServiceException("课时费标准方案不能为空", BAD_REQUEST);
}
validateFeeStandard(entity, true);
if (StringUtils.isEmpty(entity.getBh())) {
entity.setBh(UuidUtil.getOriginalUUID());
}
assertNameUnique(entity.getMc(), null);
entity.setCjsj(LocalDateTime.now());
ksfbzMapper.insert(entity);
}
@Override
@Transactional
public void deleteFeeStandard(String bh) {
if (StringUtils.isEmpty(bh)) {
throw new ServiceException("编号不能为空", BAD_REQUEST);
}
KSFBZ existing = ksfbzMapper.selectById(bh);
if (existing == null) {
throw new ServiceException("课时费标准方案不存在", NOT_FOUND);
}
Long techPosCount = jszwksfbzMapper.selectCount(
new LambdaQueryWrapper<JSZWKSFBZ>().eq(JSZWKSFBZ::getKsfbzbh, bh));
if (techPosCount != null && techPosCount > 0) {
throw new ServiceException("该方案已被技术职务课时费标准引用,无法删除", CONFLICT);
}
Long yearEndCount = nzksfMapper.selectCount(
new LambdaQueryWrapper<NZKSF>().eq(NZKSF::getKsfbzbh, bh));
if (yearEndCount != null && yearEndCount > 0) {
throw new ServiceException("该方案已被年终课时费引用,无法删除", CONFLICT);
}
ksfbzMapper.deleteById(bh);
}
@Override
@Transactional
public void updateFeeStandard(KSFBZ entity) {
if (entity == null || StringUtils.isEmpty(entity.getBh())) {
throw new ServiceException("编号不能为空", BAD_REQUEST);
}
validateFeeStandard(entity, false);
KSFBZ existing = ksfbzMapper.selectById(entity.getBh());
if (existing == null) {
throw new ServiceException("课时费标准方案不存在", NOT_FOUND);
}
assertNameUnique(entity.getMc(), entity.getBh());
entity.setCjsj(existing.getCjsj());
entity.setXgsj(LocalDateTime.now());
ksfbzMapper.updateById(entity);
}
@Override
public KSFBZ getFeeStandardById(String bh) {
if (StringUtils.isEmpty(bh)) {
throw new ServiceException("编号不能为空", BAD_REQUEST);
}
return ksfbzMapper.selectById(bh);
}
@Override
public PageResult<KSFBZ> pageFeeStandard(PageQuery query, KSFBZ cond) {
int pageNum = query.getPageNum() == null ? 1 : query.getPageNum();
int pageSize = query.getPageSize() == null ? 20 : query.getPageSize();
Page<KSFBZ> page = new Page<>(pageNum, pageSize);
LambdaQueryWrapper<KSFBZ> wrapper = new LambdaQueryWrapper<>();
if (cond != null && StringUtils.isNotEmpty(cond.getMc())) {
wrapper.like(KSFBZ::getMc, cond.getMc());
}
wrapper.orderByDesc(KSFBZ::getCjsj);
Page<KSFBZ> result = ksfbzMapper.selectPage(page, wrapper);
return new PageResult<>(result.getRecords(), result.getTotal(), pageNum, pageSize);
}
private void validateFeeStandard(KSFBZ entity, boolean creating) {
if (creating && StringUtils.isEmpty(entity.getMc())) {
throw new ServiceException("名称不能为空", BAD_REQUEST);
}
if (entity.getMc() != null && entity.getMc().length() > NAME_MAX_LENGTH) {
throw new ServiceException("名称长度不能超过" + NAME_MAX_LENGTH + "个字符", BAD_REQUEST);
}
if (entity.getSm() != null && entity.getSm().length() > TEXT_MAX_LENGTH) {
throw new ServiceException("说明长度不能超过" + TEXT_MAX_LENGTH + "个字符", BAD_REQUEST);
}
if (entity.getBz() != null && entity.getBz().length() > TEXT_MAX_LENGTH) {
throw new ServiceException("备注长度不能超过" + TEXT_MAX_LENGTH + "个字符", BAD_REQUEST);
}
}
private void assertNameUnique(String mc, String excludeBh) {
if (StringUtils.isEmpty(mc)) {
return;
}
LambdaQueryWrapper<KSFBZ> wrapper = new LambdaQueryWrapper<KSFBZ>()
.eq(KSFBZ::getMc, mc);
if (StringUtils.isNotEmpty(excludeBh)) {
wrapper.ne(KSFBZ::getBh, excludeBh);
}
Long count = ksfbzMapper.selectCount(wrapper);
if (count != null && count > 0) {
throw new ServiceException("课时费标准方案名称已存在", CONFLICT);
}
}
}
@@ -0,0 +1,301 @@
package com.roomroot.jwgl.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.roomroot.common.constant.Constants;
import com.roomroot.common.core.domain.entity.SysRole;
import com.roomroot.common.core.domain.entity.SysUser;
import com.roomroot.common.core.domain.model.LoginUser;
import com.roomroot.common.exception.ServiceException;
import com.roomroot.common.utils.SecurityUtils;
import com.roomroot.common.utils.StringUtils;
import com.roomroot.common.utils.poi.ExcelUtil;
import com.roomroot.jwgl.dto.ks.HourStatisticsQuery;
import com.roomroot.jwgl.entity.JYB;
import com.roomroot.jwgl.entity.KSFBZ;
import com.roomroot.jwgl.mapper.JYBMapper;
import com.roomroot.jwgl.mapper.KSFBZMapper;
import com.roomroot.jwgl.mapper.KSTJBMapper;
import com.roomroot.jwgl.mapper.SystemInitializationMapper;
import com.roomroot.jwgl.service.KSService;
import com.roomroot.jwgl.unit.PageQuery;
import com.roomroot.jwgl.unit.PageResult;
import com.roomroot.jwgl.vo.ks.HourStatisticsVO;
import com.roomroot.jwgl.vo.systeminitialization.SemesterInitializationVO;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Service;
import jakarta.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.util.List;
import static com.roomroot.jwgl.utils.AccountManagementConstants.ROLE_DEPARTMENT_PERSONNEL;
import static com.roomroot.jwgl.utils.BasicManagementConstants.BAD_REQUEST;
import static com.roomroot.jwgl.utils.BasicManagementConstants.FORBIDDEN;
import static com.roomroot.jwgl.utils.BasicManagementConstants.NOT_FOUND;
/**
* 课时统计、课时费统计实现。
*/
@Service
public class KSServiceImpl implements KSService {
private static final double DEFAULT_STANDARD_HOURS = 180D;
private static final double DEFAULT_BASE_RATE = 13D;
@Resource
private KSTJBMapper kstjbMapper;
@Resource
private KSFBZMapper ksfbzMapper;
@Resource
private JYBMapper jybMapper;
@Resource
private SystemInitializationMapper systemInitializationMapper;
@Override
public PageResult<HourStatisticsVO> pageHourStatistics(PageQuery query, HourStatisticsQuery cond) {
Page<HourStatisticsVO> page = queryHours(query, cond);
for (HourStatisticsVO row : page.getRecords()) {
fillHourRule(row);
}
return new PageResult<>(page.getRecords(), page.getTotal(),
(int) page.getCurrent(), (int) page.getSize());
}
@Override
public void exportHourStatistics(HourStatisticsQuery cond, HttpServletResponse response) {
List<HourStatisticsVO> list = listHours(cond);
for (HourStatisticsVO row : list) {
fillHourRule(row);
}
ExcelUtil<HourStatisticsVO> util = new ExcelUtil<>(HourStatisticsVO.class);
util.exportExcel(response, list, "课时统计");
}
@Override
public PageResult<HourStatisticsVO> pageFeeStatistics(PageQuery query, HourStatisticsQuery cond) {
KSFBZ standard = resolveFeeStandard(cond);
Page<HourStatisticsVO> page = queryHours(query, cond);
for (HourStatisticsVO row : page.getRecords()) {
fillFeeRule(row, standard);
}
return new PageResult<>(page.getRecords(), page.getTotal(),
(int) page.getCurrent(), (int) page.getSize());
}
@Override
public void exportFeeStatistics(HourStatisticsQuery cond, HttpServletResponse response) {
KSFBZ standard = resolveFeeStandard(cond);
List<HourStatisticsVO> list = listHours(cond);
for (HourStatisticsVO row : list) {
fillFeeRule(row, standard);
}
ExcelUtil<HourStatisticsVO> util = new ExcelUtil<>(HourStatisticsVO.class);
util.exportExcel(response, list, "课时费统计");
}
private Page<HourStatisticsVO> queryHours(PageQuery query, HourStatisticsQuery cond) {
HourStatisticsQuery filter = prepareFilter(cond);
int pageNum = query.getPageNum() == null ? 1 : query.getPageNum();
int pageSize = query.getPageSize() == null ? 20 : query.getPageSize();
Page<HourStatisticsVO> page = new Page<>(pageNum, pageSize);
return kstjbMapper.selectHourStatistics(page, filter);
}
private List<HourStatisticsVO> listHours(HourStatisticsQuery cond) {
return kstjbMapper.selectHourStatisticsList(prepareFilter(cond));
}
private HourStatisticsQuery prepareFilter(HourStatisticsQuery cond) {
HourStatisticsQuery filter = cond == null ? new HourStatisticsQuery() : cond;
applyScopeAndDefaultSemester(filter);
return filter;
}
/**
* 未传学期年度时默认当前学期;非管理员强制只查登录教员本人。
*/
private void applyScopeAndDefaultSemester(HourStatisticsQuery filter) {
if (filter.getNd() == null) {
SemesterInitializationVO current = resolveCurrentSemester();
if (current != null && current.getYear() != null) {
filter.setNd(current.getYear());
if (filter.getXq() == null) {
filter.setXq(current.getSequenceNumber());
}
} else {
filter.setNd(LocalDate.now().getYear());
}
}
if (isAdminViewer()) {
return;
}
String teacherId = resolveLoginTeacherId();
filter.setJybh(teacherId);
filter.setJyxm(null);
}
private boolean isAdminViewer() {
try {
if (SecurityUtils.isAdmin()) {
return true;
}
LoginUser loginUser = SecurityUtils.getLoginUser();
if (loginUser.getPermissions() != null
&& loginUser.getPermissions().contains(Constants.ALL_PERMISSION)) {
return true;
}
SysUser user = loginUser.getUser();
if (user == null || user.getRoles() == null) {
return false;
}
for (SysRole role : user.getRoles()) {
if (role == null || StringUtils.isEmpty(role.getRoleKey())) {
continue;
}
String key = role.getRoleKey();
if (Constants.SUPER_ADMIN.equals(key)
|| ROLE_DEPARTMENT_PERSONNEL.equalsIgnoreCase(key)) {
return true;
}
}
return false;
} catch (Exception ex) {
return false;
}
}
private String resolveLoginTeacherId() {
LoginUser loginUser = SecurityUtils.getLoginUser();
SysUser user = loginUser.getUser();
String username = loginUser.getUsername();
if (StringUtils.isNotEmpty(username)) {
JYB byId = jybMapper.selectById(username);
if (byId != null) {
return byId.getJybh();
}
}
String nickName = user == null ? null : user.getNickName();
if (StringUtils.isNotEmpty(nickName)) {
List<JYB> matched = jybMapper.selectList(
new LambdaQueryWrapper<JYB>().eq(JYB::getJyxm, nickName));
if (matched != null && matched.size() == 1) {
return matched.get(0).getJybh();
}
}
throw new ServiceException("当前登录用户不是教员,无法查看课时统计", FORBIDDEN);
}
private SemesterInitializationVO resolveCurrentSemester() {
return systemInitializationMapper.selectCurrentSemester();
}
private KSFBZ resolveFeeStandard(HourStatisticsQuery cond) {
if (cond != null && StringUtils.isNotEmpty(cond.getKsfbzbh())) {
KSFBZ specified = ksfbzMapper.selectById(cond.getKsfbzbh());
if (specified == null) {
throw new ServiceException("课时费标准不存在", NOT_FOUND);
}
return specified;
}
Page<KSFBZ> page = new Page<>(1, 1);
LambdaQueryWrapper<KSFBZ> wrapper = new LambdaQueryWrapper<KSFBZ>()
.orderByDesc(KSFBZ::getCjsj);
Page<KSFBZ> result = ksfbzMapper.selectPage(page, wrapper);
if (result.getRecords() == null || result.getRecords().isEmpty()) {
throw new ServiceException("尚未配置课时费标准", BAD_REQUEST);
}
return result.getRecords().get(0);
}
/**
* 课时统计:标准课时量 180;未超课时按 13 元;超课时部分按职称 15/16/17/18。
*/
private void fillHourRule(HourStatisticsVO row) {
double total = nvl(row.getSbn()) + nvl(row.getXbn());
double standardHours = DEFAULT_STANDARD_HOURS;
double overtime = Math.max(0D, total - standardHours);
row.setZj(round(total));
row.setBzksl(standardHours);
row.setCks(round(overtime));
Double overtimeRate = overtimeRateByTitle(row.getZc());
if (overtimeRate == null) {
return;
}
fillFormula(row, total, standardHours, overtime, DEFAULT_BASE_RATE, overtimeRate);
}
/**
* 课时费统计:标准课时量、课时费、超量课时费取课时费标准;超量单价为空时回退职称规则。
*/
private void fillFeeRule(HourStatisticsVO row, KSFBZ standard) {
double total = nvl(row.getSbn()) + nvl(row.getXbn());
double standardHours = standard.getMrbzksl() != null
? standard.getMrbzksl() : DEFAULT_STANDARD_HOURS;
double baseRate = standard.getMrksfbz() != null
? standard.getMrksfbz() : DEFAULT_BASE_RATE;
Double overtimeRate = standard.getMrclksfbz();
if (overtimeRate == null) {
overtimeRate = overtimeRateByTitle(row.getZc());
}
if (overtimeRate == null) {
overtimeRate = baseRate;
}
double overtime = Math.max(0D, total - standardHours);
row.setZj(round(total));
row.setBzksl(round(standardHours));
row.setCks(round(overtime));
row.setKsfbzmc(standard.getMc());
row.setKsfbz(round(baseRate));
row.setClksfbz(round(overtimeRate));
fillFormula(row, total, standardHours, overtime, baseRate, overtimeRate);
}
private void fillFormula(HourStatisticsVO row, double total, double standardHours,
double overtime, double baseRate, double overtimeRate) {
if (overtime > 0D) {
row.setJsgs(formatNumber(standardHours) + "×" + formatNumber(baseRate)
+ "+" + formatNumber(overtime) + "×" + formatNumber(overtimeRate));
row.setJg(round(standardHours * baseRate + overtime * overtimeRate));
} else {
row.setJsgs(formatNumber(total) + "×" + formatNumber(baseRate));
row.setJg(round(total * baseRate));
}
}
private Double overtimeRateByTitle(String title) {
if (StringUtils.isEmpty(title)) {
return null;
}
switch (title.trim()) {
case "助教":
return 15D;
case "讲师":
return 16D;
case "副教授":
return 17D;
case "教授":
return 18D;
default:
return null;
}
}
private double nvl(Double value) {
return value == null ? 0D : value;
}
private double round(double value) {
return BigDecimal.valueOf(value).setScale(1, RoundingMode.HALF_UP).doubleValue();
}
private String formatNumber(double value) {
return BigDecimal.valueOf(value).setScale(1, RoundingMode.HALF_UP)
.stripTrailingZeros().toPlainString();
}
}
@@ -0,0 +1,59 @@
package com.roomroot.jwgl.vo.ks;
import com.roomroot.common.annotation.Excel;
import lombok.Data;
/**
* 课时统计 / 课时费统计行。
*/
@Data
public class HourStatisticsVO {
@Excel(name = "教研室代号", sort = 1)
private String jysdh;
@Excel(name = "教研室名称", sort = 2)
private String jysmc;
@Excel(name = "教员编号", sort = 3)
private String jybh;
@Excel(name = "教员姓名", sort = 4)
private String jyxm;
@Excel(name = "职称", sort = 5)
private String zc;
@Excel(name = "年份", sort = 6)
private Integer nf;
@Excel(name = "上半年", sort = 7)
private Double sbn;
@Excel(name = "下半年", sort = 8)
private Double xbn;
@Excel(name = "总计", sort = 9)
private Double zj;
@Excel(name = "标准课时量", sort = 10)
private Double bzksl;
@Excel(name = "超课时", sort = 11)
private Double cks;
@Excel(name = "计算过程", sort = 12, width = 28)
private String jsgs;
@Excel(name = "结果", sort = 13)
private Double jg;
@Excel(name = "课时费标准", sort = 14)
private String ksfbzmc;
@Excel(name = "课时费单价", sort = 15)
private Double ksfbz;
@Excel(name = "超量课时费单价", sort = 16)
private Double clksfbz;
}