1
This commit is contained in:
@@ -6,7 +6,7 @@ import cn.hutool.core.util.StrUtil;
|
||||
import com.budwk.app.base.result.Result;
|
||||
import com.budwk.app.sys.models.Sys_home_activity;
|
||||
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
|
||||
import com.budwk.app.zhgh.activity.basic.models.ActivityUserScope;
|
||||
import com.budwk.app.zhgh.activity.basic.service.ActivityBasicScopeService;
|
||||
import io.swagger.annotations.Api;
|
||||
import org.nutz.dao.Cnd;
|
||||
import org.nutz.dao.Dao;
|
||||
@@ -32,6 +32,8 @@ public class SysH5IndexController {
|
||||
|
||||
@Inject
|
||||
private Dao dao;
|
||||
@Inject
|
||||
private ActivityBasicScopeService activityBasicScopeService;
|
||||
|
||||
@At
|
||||
@Ok("beetl:/platform/zhghh5/sys/home/index.html")
|
||||
@@ -40,6 +42,26 @@ public class SysH5IndexController {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the mobile featured activity aggregation page.
|
||||
*/
|
||||
@At
|
||||
@Ok("beetl:/platform/zhghh5/sys/home/featuredActivity.html")
|
||||
@SaCheckLogin
|
||||
public void featuredActivity() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the mobile festival benefit aggregation page.
|
||||
*/
|
||||
@At
|
||||
@Ok("beetl:/platform/zhghh5/sys/home/festivalBenefit.html")
|
||||
@SaCheckLogin
|
||||
public void festivalBenefit() {
|
||||
|
||||
}
|
||||
|
||||
@At
|
||||
@Ok("beetl:/platform/zhghh5/index/mine/index.html")
|
||||
@SaCheckLogin
|
||||
@@ -88,8 +110,8 @@ public class SysH5IndexController {
|
||||
}
|
||||
|
||||
if (allowUserGroupId != null) {
|
||||
int count = dao.count(ActivityUserScope.class, Cnd.where("groupId", "=", allowUserGroupId).and("userId", "=", userId));
|
||||
if (count > 0) {
|
||||
// Delegate group membership checks to the service so SQL based groups and stored groups stay consistent.
|
||||
if (activityBasicScopeService.isUserInGroup(allowUserGroupId, userId)) {
|
||||
allowActivityList.add(activity);
|
||||
continue;
|
||||
}
|
||||
@@ -97,7 +119,10 @@ public class SysH5IndexController {
|
||||
|
||||
if (StrUtil.isNotBlank(allowUserSql)){
|
||||
Sql sql = Sqls.create(allowUserSql).setParam("userId", userId);
|
||||
NutMap nutMap = (NutMap) Daos.query(dao, sql.toString(), Sqls.callback.map());
|
||||
// Execute the configured visibility SQL with Nutz parameters to avoid rebuilding raw SQL text in controller logic.
|
||||
sql.setCallback(Sqls.callback.map());
|
||||
dao.execute(sql);
|
||||
NutMap nutMap = (NutMap) sql.getResult();
|
||||
if (Lang.isNotEmpty(nutMap) && StrUtil.isNotBlank(nutMap.getString("userId"))) {
|
||||
allowActivityList.add(activity);
|
||||
}
|
||||
|
||||
@@ -6,11 +6,14 @@ import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.http.useragent.UserAgent;
|
||||
import cn.hutool.http.useragent.UserAgentUtil;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import com.budwk.app.base.constant.RoleConstant;
|
||||
import com.budwk.app.base.result.Result;
|
||||
import com.budwk.app.sys.models.Sys_home_activity;
|
||||
import com.budwk.app.sys.models.Sys_home_template;
|
||||
import com.budwk.app.sys.models.Sys_menu;
|
||||
import com.budwk.app.sys.services.SysMenuService;
|
||||
import com.budwk.app.sys.services.SysUserService;
|
||||
import com.budwk.app.web.commons.auth.utils.AuthUtil;
|
||||
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
|
||||
import com.budwk.app.zhgh.activity.basic.models.ActivityUserScope;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
@@ -39,6 +42,7 @@ import org.nutz.mvc.annotation.Param;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpSession;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
@@ -255,6 +259,26 @@ public class SysHomeController {
|
||||
// return Result.success(hasPermisiionList);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询 PC 首页工作模板。
|
||||
* 仅系统管理员和校工会管理员可见,返回前隐藏模板 SQL 和后端类路径等敏感字段。
|
||||
*
|
||||
* @return 首页工作模板列表
|
||||
*/
|
||||
@At
|
||||
@SaCheckLogin
|
||||
@ApiOperation("首页工作模板")
|
||||
@Ok("json")
|
||||
public Result listHomeTemplate() {
|
||||
if (!AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_ADMIN.name())) {
|
||||
return Result.success(Collections.emptyList());
|
||||
}
|
||||
FieldFilter fieldFilter = FieldFilter.locked(Sys_home_template.class, "allowUserSql|classPath");
|
||||
List<Sys_home_template> list = Daos.ext(dao, fieldFilter).query(Sys_home_template.class,
|
||||
Cnd.where("enable", "=", 1).asc("sortNo"));
|
||||
return Result.success(list);
|
||||
}
|
||||
|
||||
|
||||
@At
|
||||
@SaCheckLogin
|
||||
|
||||
@@ -338,8 +338,12 @@ public class SysUserController {
|
||||
@At
|
||||
@Ok("json")
|
||||
@SaCheckLogin
|
||||
public Result subAppMenus(@Param("appId") String appId) {
|
||||
public Result subAppMenus(@Param("appId") String appId, @Param("platform") String platform) {
|
||||
List<Sys_menu> menus = sysUserService.getMenus(SecurityUtil.getUserId());
|
||||
if (StrUtil.isNotBlank(platform)) {
|
||||
// Keep sub-application menus aligned with the requested client platform.
|
||||
menus = menus.stream().filter(menu -> platform.equals(menu.getPlatform())).toList();
|
||||
}
|
||||
List<Sys_menu> list = SysMenuUtil.createTreeMenus(menus, appId);
|
||||
if (ObjectUtil.isEmpty(list)) {
|
||||
List<Sys_menu> self = menus.stream().filter(menu -> menu.getId().equals(appId)).toList();
|
||||
|
||||
@@ -75,6 +75,7 @@ public class SysV4AppsController {
|
||||
m.href,
|
||||
m.icon,
|
||||
m.picIcon,
|
||||
m.location,
|
||||
sm.`name` AS moduleName,
|
||||
CASE
|
||||
WHEN f.userId IS NOT NULL THEN
|
||||
@@ -97,6 +98,7 @@ public class SysV4AppsController {
|
||||
cnd.and("m.id","in", menus.stream().map(Sys_menu::getId).toArray());
|
||||
cnd.andEX("m.moduleId", "=", categoryId);
|
||||
cnd.asc("m.location");
|
||||
cnd.asc("m.id");
|
||||
if (StrUtil.isNotBlank(keyword)) {
|
||||
cnd.where().andLike("m.name", keyword);
|
||||
}
|
||||
@@ -143,7 +145,7 @@ public class SysV4AppsController {
|
||||
@Ok("json:full")
|
||||
@SaCheckLogin
|
||||
@ApiOperation("获取收藏的应用")
|
||||
public Result favorite(HttpServletRequest req) {
|
||||
public Result favorite(@Param(value = "platform", df = "PC") String platform, HttpServletRequest req) {
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
m.id,
|
||||
@@ -151,6 +153,7 @@ public class SysV4AppsController {
|
||||
m.href,
|
||||
m.icon,
|
||||
m.picIcon,
|
||||
m.location,
|
||||
sm.`name` AS moduleName,
|
||||
1 AS isFavorite
|
||||
FROM
|
||||
@@ -162,12 +165,15 @@ public class SysV4AppsController {
|
||||
LEFT JOIN sys_module sm ON sm.id = m.moduleId
|
||||
WHERE
|
||||
f.userId = @userId
|
||||
AND m.platform = 'PC'
|
||||
AND m.platform = @platform
|
||||
AND m.disabled = 0
|
||||
ORDER BY
|
||||
m.location ASC;
|
||||
m.location ASC,
|
||||
m.id ASC;
|
||||
""");
|
||||
sql.setParam("userId", SecurityUtil.getUserId());
|
||||
// The H5 home sends platform=H5; defaulting to PC keeps the existing desktop caller compatible.
|
||||
sql.setParam("platform", platform);
|
||||
List<NutMap> list = sysMenuService.listMap(sql);
|
||||
return Result.success(list);
|
||||
}
|
||||
|
||||
+43
@@ -55,6 +55,49 @@ public class HonorManageController {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the mobile honor display page used by the H5 home feature card.
|
||||
*/
|
||||
@At("/h5")
|
||||
@SaCheckLogin
|
||||
@Ok("beetl:/platform/zhghh5/dayofficework/honor/manage/index.html")
|
||||
public void h5() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Query honor records for the mobile honor display page.
|
||||
*/
|
||||
@At
|
||||
@SaCheckLogin
|
||||
public Result h5Data(HonorPageForm pageForm) {
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
h.id,
|
||||
h.userName,
|
||||
h.applyUnionName,
|
||||
h.unionName,
|
||||
h.grantDate,
|
||||
h.files AS photoFiles,
|
||||
prize.`name` AS prizeName,
|
||||
type.`name` AS typeName,
|
||||
type.queryTypeCode AS typeQueryTypeCode
|
||||
FROM
|
||||
`honor` h
|
||||
LEFT JOIN honor_basic_settings prize ON prize.id = h.prize
|
||||
LEFT JOIN honor_basic_settings type ON type.id = h.honorType
|
||||
$condition
|
||||
""");
|
||||
Cnd cnd = Cnd.NEW();
|
||||
Integer year = pageForm.getYear() == null ? Calendar.getInstance().get(Calendar.YEAR) : pageForm.getYear();
|
||||
cnd.andEX("YEAR(h.grantDate)", "=", year);
|
||||
cnd.andEX("h.honorType", "=", pageForm.getHonorType());
|
||||
cnd.desc("h.grantDate");
|
||||
sql.setCondition(cnd);
|
||||
Pagination pagination = honorViService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql);
|
||||
return Result.success(pagination);
|
||||
}
|
||||
|
||||
@At
|
||||
@SaCheckPermission("honor.manage")
|
||||
public Result pageData(HonorPageForm pageForm) {
|
||||
|
||||
+24
-1
@@ -642,8 +642,10 @@ public class TourUserAssignmentServiceImpl extends BaseServiceImpl<TourUserAssig
|
||||
throw new IllegalArgumentException("该人员已取消退出");
|
||||
}
|
||||
checkAssignmentCancelDeadline(assignment);
|
||||
// 取消退出时删除对应事项台账,再标记人员分配表状态,防止后续移动端再次报名。
|
||||
// 取消退出时先删除对应事项台账,并仅清空当前人员分配记录的线路快照,避免误处理同配置下其它分配记录。
|
||||
deleteAssignmentLedgers(assignment);
|
||||
clearAssignmentMatterSnapshot(assignment.getId());
|
||||
// 标记人员分配表取消状态,防止后续移动端再次报名。
|
||||
update(Chain.make("cancelled", true),
|
||||
Cnd.where(TourUserAssignment::getId, "=", assignment.getId()));
|
||||
}
|
||||
@@ -829,6 +831,27 @@ public class TourUserAssignmentServiceImpl extends BaseServiceImpl<TourUserAssig
|
||||
dao().clear(TourLedger.class, Cnd.where(TourLedger::getId, "in", ledgerIds));
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空当前人员分配记录中的线路相关快照,不删除人员分配表数据。
|
||||
* 出行时间由 matterId 关联事项表展示,清空 matterId 后列表不再展示原出行时间。
|
||||
*
|
||||
* @param assignmentId 人员分配记录ID
|
||||
*/
|
||||
private void clearAssignmentMatterSnapshot(String assignmentId) {
|
||||
if (StrUtil.isBlank(assignmentId)) {
|
||||
return;
|
||||
}
|
||||
update(Chain.make("matterId", null)
|
||||
.add("matterName", null)
|
||||
.add("lineId", null)
|
||||
.add("lineName", null)
|
||||
.add("travelAgencyId", null)
|
||||
.add("travelAgencyName", null)
|
||||
.add("boardingPlace", null),
|
||||
Cnd.where(TourUserAssignment::getId, "=", assignmentId)
|
||||
.and(TourUserAssignment::getDelFlag, "=", false));
|
||||
}
|
||||
|
||||
private void restoreCancelledAssignment(TourUserAssignment assignment) {
|
||||
if (!Boolean.TRUE.equals(assignment.getCancelled())) {
|
||||
throw new IllegalArgumentException("该人员未取消退出,无需恢复");
|
||||
|
||||
+383
@@ -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);
|
||||
}
|
||||
}
|
||||
+298
@@ -0,0 +1,298 @@
|
||||
package com.budwk.app.zhgh.learning.controller;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import cn.dev33.satoken.annotation.SaMode;
|
||||
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(value = {"learning.course.display", "h5.learning.course.display"}, mode = SaMode.OR)
|
||||
public void index() {
|
||||
}
|
||||
|
||||
@At("/detail")
|
||||
@Ok("beetl:/platform/zhgh/Learning/courseDisplay/detail.html")
|
||||
@SaCheckPermission(value = {"learning.course.display", "h5.learning.course.display"}, mode = SaMode.OR)
|
||||
public void detail() {
|
||||
}
|
||||
|
||||
@At("/study")
|
||||
@Ok("beetl:/platform/zhgh/Learning/courseDisplay/study.html")
|
||||
@SaCheckPermission(value = {"learning.course.display", "h5.learning.course.display"}, mode = SaMode.OR)
|
||||
public void study() {
|
||||
}
|
||||
|
||||
@At
|
||||
@ApiOperation("课程展示列表")
|
||||
@SaCheckPermission(value = {"learning.course.display", "h5.learning.course.display"}, mode = SaMode.OR)
|
||||
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(value = {"learning.course.display", "h5.learning.course.display"}, mode = SaMode.OR)
|
||||
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(value = {"learning.course.display", "h5.learning.course.display"}, mode = SaMode.OR)
|
||||
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(value = {"learning.course.display", "h5.learning.course.display"}, mode = SaMode.OR)
|
||||
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(value = {"learning.course.display", "h5.learning.course.display"}, mode = SaMode.OR)
|
||||
public Result courseTypes() {
|
||||
return Result.success(dao.query(LearningCourseType.class, Cnd.where("enabled", "=", true).asc("sortNum").asc("createdAt")));
|
||||
}
|
||||
|
||||
@At
|
||||
@ApiOperation("推荐标识")
|
||||
@SaCheckPermission(value = {"learning.course.display", "h5.learning.course.display"}, mode = SaMode.OR)
|
||||
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;
|
||||
}
|
||||
}
|
||||
+232
@@ -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);
|
||||
}
|
||||
}
|
||||
+110
@@ -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("h5.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("h5.learning.course.display")
|
||||
public void courseH5() {
|
||||
}
|
||||
|
||||
@At("/course/h5/study")
|
||||
@Ok("beetl:/platform/zhghh5/learning/course/study.html")
|
||||
@SaCheckPermission("h5.learning.course.display")
|
||||
public void courseH5Study() {
|
||||
}
|
||||
}
|
||||
+330
@@ -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 + "秒";
|
||||
}
|
||||
}
|
||||
+494
@@ -0,0 +1,494 @@
|
||||
package com.budwk.app.zhgh.learning.controller;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import cn.dev33.satoken.annotation.SaMode;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.budwk.app.base.constant.RoleConstant;
|
||||
import com.budwk.app.base.page.Pagination;
|
||||
import com.budwk.app.base.param.PageForm;
|
||||
import com.budwk.app.base.result.Result;
|
||||
import com.budwk.app.base.utils.PageUtil;
|
||||
import com.budwk.app.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(value = {"learning.my.record", "h5.learning.my.record"}, mode = SaMode.OR)
|
||||
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(value = {"learning.my.record", "h5.learning.my.record"}, mode = SaMode.OR)
|
||||
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(value = {"learning.my.record", "h5.learning.my.record"}, mode = SaMode.OR)
|
||||
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(value = {"learning.course.display", "h5.learning.course.display"}, mode = SaMode.OR)
|
||||
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(value = {"learning.course.display", "h5.learning.course.display"}, mode = SaMode.OR)
|
||||
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(value = {"learning.course.display", "h5.learning.course.display"}, mode = SaMode.OR)
|
||||
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(value = {"learning.course.display", "h5.learning.course.display"}, mode = SaMode.OR)
|
||||
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(value = {"learning.my.record", "h5.learning.my.record"}, mode = SaMode.OR)
|
||||
public Result courseOptions() {
|
||||
return Result.success(dao.query(LearningCourse.class, Cnd.NEW().asc("sortNum").desc("createdAt")));
|
||||
}
|
||||
|
||||
@At
|
||||
@ApiOperation("可选分工会")
|
||||
@SaCheckPermission(value = {"learning.my.record", "h5.learning.my.record"}, mode = SaMode.OR)
|
||||
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(value = {"learning.my.record", "h5.learning.my.record"}, mode = SaMode.OR)
|
||||
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;
|
||||
}
|
||||
Reference in New Issue
Block a user