Merge remote-tracking branch 'origin/main'

This commit is contained in:
2026-07-23 13:58:17 +08:00
132 changed files with 9540 additions and 506 deletions
@@ -1,6 +1,7 @@
package com.budwk.app.sys.listener;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.event.user.UserChangeEventListener;
import com.budwk.app.base.event.user.UserChangeMsg;
import com.budwk.app.sys.models.Sys_user_role;
@@ -31,9 +32,17 @@ public class SysUserChangeListener implements UserChangeEventListener {
@Aop(TransAop.READ_COMMITTED)
public void receive(UserChangeMsg message) {
if (ObjectUtil.equals(message.getOperationType(), UserChangeMsg.UNIT_CHANGE_OPERATION)) {
if (StrUtil.isBlank(message.getSourceUnitId())) {
return;
}
// 单位变动,删除原单位的角色
dao.clear(Sys_user_role.class, Cnd.where("userId", "in", message.getUserIds()).and("unitId", "=", message.getSourceUnitId()));
if (StrUtil.isBlank(message.getTargetUnitId())) {
return;
}
// 判断这两个单位是不是在一个工会,如果不是,删除原来工会的所有角色
Sql sql = Sqls.create("""
SELECT
@@ -51,8 +60,9 @@ public class SysUserChangeListener implements UserChangeEventListener {
dao.execute(sql);
NutMap result = (NutMap) sql.getResult();
if (!result.getBoolean("result")) {
dao.clear(Sys_user_role.class, Cnd.where("userId", "in", message.getUserIds()).and("unionId", "=", result.getString("sourceUnionId")));
String sourceUnionId = result.getString("sourceUnionId");
if (!result.getBoolean("result") && StrUtil.isNotBlank(sourceUnionId)) {
dao.clear(Sys_user_role.class, Cnd.where("userId", "in", message.getUserIds()).and("unionId", "=", sourceUnionId));
}
}
@@ -138,6 +138,7 @@ public class Sys_user extends BaseModel implements Serializable {
@Column
@Comment("职称")
@ColDefine(type = ColType.VARCHAR, width = 50)
@DataCenterColumn(name = "职称", key = "ZYJSZW")
private String professionalTitle;
@Column
@@ -161,7 +162,7 @@ public class Sys_user extends BaseModel implements Serializable {
private String positionSeries;
@Column
@Comment("岗级")
@Comment("岗级,职务级别")
@ColDefine(type = ColType.VARCHAR, width = 50)
@DataCenterColumn(name = "岗级", key = "ZYJSGWDJ")
private String positionLevel;
@@ -293,6 +294,11 @@ public class Sys_user extends BaseModel implements Serializable {
@Default(value = "0")
private Boolean aidFundMember;
@Column
@Comment("大病基金会员类别")
@ColDefine(type = ColType.VARCHAR, width = 50)
private String aidFundMemberType;
@Column
@Comment("家庭主要成员及其工作单位")
@ColDefine(type = ColType.MYSQL_JSON)
@@ -14,6 +14,7 @@ import com.budwk.app.sys.services.SysDataUnitPullService;
import com.budwk.app.sys.utils.DataCenterUtil;
import lombok.extern.slf4j.Slf4j;
import org.nutz.aop.interceptor.ioc.TransAop;
import org.nutz.dao.Chain;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.dao.Sqls;
@@ -66,24 +67,57 @@ public class SysDataUnitPullServiceImpl extends BaseServiceImpl<Sys_unit> implem
public void updateUnits(List<String> unitIds) {
// 中间表数据
List<Record> unitMiddleList = dao().query("unit_middle", Cnd.NEW());
List<Sys_unit> sysUnits = dao().query(Sys_unit.class, Cnd.NEW());
Map<String, Sys_unit> unitMapById = sysUnits.stream()
.filter(unit -> StrUtil.isNotBlank(unit.getId()))
.collect(Collectors.toMap(Sys_unit::getId, unit -> unit, (oldValue, newValue) -> oldValue));
Map<String, Sys_unit> unitMapByCode = sysUnits.stream()
.filter(unit -> StrUtil.isNotBlank(unit.getUnitcode()))
.collect(Collectors.toMap(Sys_unit::getUnitcode, unit -> unit, (oldValue, newValue) -> oldValue));
// 需要新增的单位
List<Sys_unit> insertList = new ArrayList<>();
// 需要更新的单位
List<Sys_unit> updateList = new ArrayList<>();
int skipCount = 0;
Map<String, String> api2db = Arrays.stream(Api2UnitFiledMap.values())
.collect(Collectors.toMap(e -> e.apiField, e -> e.dbColumn));
for (Record row : unitMiddleList) {
JSONObject jsonRow = JSONUtil.parseObj(JSONUtil.toJsonStr(row));
String unitCode = getDataCenterField(jsonRow, "dwdm");
String unitLevel = getDataCenterField(jsonRow, "dwdj");
if (StrUtil.isBlank(unitCode)) {
skipCount++;
log.warn("单位数据编码为空,已跳过: {}", row);
continue;
}
if ("1".equals(unitLevel)) {
skipCount++;
continue;
}
Sys_unit unit = DataCenterUtil.mapJsonToBean(jsonRow, Sys_unit.class, api2db);
unit.setUnitcode(unit.getId());
unit.setId(unitCode);
unit.setUnitcode(unitCode);
unit.setUnitTypeCode(1);
unit.setParentId("0000");
unit.setUnitLevel(2);
if ("3".equals(unitLevel)) {
String parentUnitCode = getDataCenterField(jsonRow, "sjdwmcdm");
String defaultParentUnitCode = unitCode.length() > 4 ? unitCode.substring(0, 4) : "1";
unit.setParentId(StrUtil.isNotBlank(parentUnitCode) ? parentUnitCode : defaultParentUnitCode);
unit.setUnitLevel(3);
} else {
unit.setParentId("1");
unit.setUnitLevel(2);
}
if (unitIds.contains(unit.getId())) {
Sys_unit existUnit = unitMapById.get(unitCode);
if (existUnit == null) {
existUnit = unitMapByCode.get(unitCode);
}
if (existUnit != null) {
// 存在则更新
unit.setId(existUnit.getId());
updateList.add(unit);
} else {
// 不存在则新增
@@ -91,7 +125,53 @@ public class SysDataUnitPullServiceImpl extends BaseServiceImpl<Sys_unit> implem
}
}
log.info("本次拉取单位数据,新增{}条,更新{}条", insertList.size(), updateList.size());
log.info("本次跳过一级或无编码单位{}条", skipCount);
dao().insert(insertList);
dao().update(updateList);
updateDataCenterUnitFields(updateList);
}
/**
* 只更新数据中心负责维护的单位字段,避免清空本地维护的工会、路径、排序等配置。
*
* @param updateList 需要更新的单位数据
*/
private void updateDataCenterUnitFields(List<Sys_unit> updateList) {
for (Sys_unit unit : updateList) {
Chain chain = Chain.make("unitcode", unit.getUnitcode());
addChainIfNotNull(chain, "name", unit.getName());
addChainIfNotNull(chain, "unitType", unit.getUnitType());
addChainIfNotNull(chain, "unitTypeCode", unit.getUnitTypeCode());
addChainIfNotNull(chain, "parentId", unit.getParentId());
addChainIfNotNull(chain, "unitLevel", unit.getUnitLevel());
dao().update(Sys_unit.class, chain, Cnd.where(Sys_unit::getId, "=", unit.getId()));
}
}
/**
* 读取数据中心字段,兼容中间表字段大小写差异。
*
* @param jsonRow 中间表行数据
* @param fieldName 字段名
* @return 去除首尾空格后的字段值
*/
private String getDataCenterField(JSONObject jsonRow, String fieldName) {
String value = jsonRow.getStr(fieldName);
if (value == null) {
value = jsonRow.getStr(fieldName.toUpperCase());
}
return value == null ? null : value.trim();
}
/**
* 数据中心字段为空时不加入更新链,避免覆盖本地已有字段值。
*
* @param chain 更新字段链
* @param fieldName 字段名
* @param value 字段值
*/
private void addChainIfNotNull(Chain chain, String fieldName, Object value) {
if (value != null) {
chain.add(fieldName, value);
}
}
}
@@ -5,6 +5,7 @@ import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HtmlUtil;
import com.budwk.app.base.constant.RoleConstant;
import com.budwk.app.base.event.role.RoleEventMsg;
@@ -24,16 +25,16 @@ import com.budwk.app.zhgh.staffmanage.member.constant.MemberChangeOrigin;
import com.budwk.app.zhgh.staffmanage.member.constant.MemberChangeType;
import lombok.extern.slf4j.Slf4j;
import org.nutz.aop.interceptor.ioc.TransAop;
import org.nutz.dao.Chain;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.dao.FieldFilter;
import org.nutz.dao.util.Daos;
import org.nutz.ioc.aop.Aop;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.Lang;
import org.nutz.lang.random.R;
import org.nutz.lang.util.NutMap;
import org.springframework.beans.BeanUtils;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.lang.reflect.Field;
@@ -65,6 +66,11 @@ public class SysDataUserAllUpdateServiceImpl implements SysDataUserUpdateService
private static Map<String, String> unitIdNameMap;
/**
* 数据中心允许更新的用户字段表达式。
*/
private static String dataCenterUpdateFields;
/**
* 字段映射类,用于缓存反射结果
*/
@@ -111,6 +117,24 @@ public class SysDataUserAllUpdateServiceImpl implements SysDataUserUpdateService
return fieldMappings;
}
/**
* 获取数据中心字段白名单表达式,供批量更新时过滤本地业务字段。
*
* @return 字段过滤表达式
*/
private static String getDataCenterUpdateFields() {
if (dataCenterUpdateFields == null) {
synchronized (SysDataUserAllUpdateServiceImpl.class) {
if (dataCenterUpdateFields == null) {
dataCenterUpdateFields = getFieldMappings().stream()
.map(mapping -> mapping.field.getName())
.collect(Collectors.joining("|", "^(id|", ")$"));
}
}
}
return dataCenterUpdateFields;
}
/**
* 检查用户是否符合会员条件
*
@@ -178,21 +202,13 @@ public class SysDataUserAllUpdateServiceImpl implements SysDataUserUpdateService
List<Sys_user> needDoUpdateList = new CopyOnWriteArrayList<>();
List<Sys_user> needInitUserList = new CopyOnWriteArrayList<>();
List<Sys_user_history> histories = new CopyOnWriteArrayList<>();
List<String> addMemberUserIds = Collections.synchronizedList(new ArrayList<>());
List<String> removeMemberUserIds = Collections.synchronizedList(new ArrayList<>());
// 处理每条数据
for (Sys_user_source source : sources) {
Sys_user user = userMap.get(source.getLoginname());
// 创建用户对象
Sys_user u = new Sys_user();
BeanUtils.copyProperties(source, u);
//单位id取前四位数(二级单位)
if (u.getUnitId() != null && u.getUnitId().length() > 4) {
u.setUnitId(u.getUnitId().substring(0, 4));
}
// 创建只包含数据中心字段的用户对象,避免覆盖本地维护的会员、登录、积分等业务字段。
Sys_user u = buildDataCenterUser(source);
if (user == null) {
// 新增用户,初始化数据
@@ -200,23 +216,11 @@ public class SysDataUserAllUpdateServiceImpl implements SysDataUserUpdateService
u.setSalt(salt);
u.setPassword(PwdUtil.getPassword(PwdUtil.generate(12), salt));
if (u.getMember()) {
addMemberUserIds.add(u.getId());
}
needInitUserList.add(u);
} else {
// 修改现有用户
u.setId(user.getId());
// 更新会员状态
boolean currentIsMember = user.getMember() != null && user.getMember();
if (!currentIsMember) {
addMemberUserIds.add(user.getId());
} else{
removeMemberUserIds.add(user.getId());
}
needDoUpdateList.add(u);
}
@@ -249,36 +253,6 @@ public class SysDataUserAllUpdateServiceImpl implements SysDataUserUpdateService
});
}, executorService);
updateTasks.add(insertTask);
// 异步处理公共角色分配
if (!needInitUserList.isEmpty()) {
CompletableFuture<Void> roleTask = CompletableFuture.runAsync(() -> {
try {
log.info("为新用户分配公共角色");
List<Sys_user_role> roleList = needInitUserList.stream().map(item -> {
Sys_user_role userRole = new Sys_user_role();
userRole.setRoleId(publicRole.getId());
userRole.setUserId(item.getId());
return userRole;
}).collect(Collectors.toList());
if (!roleList.isEmpty()) {
// 分批处理角色分配
List<List<Sys_user_role>> roleBatches = ListUtil.split(roleList, 500);
roleBatches.forEach(batch -> {
try {
dao.fastInsert(batch);
} catch (Exception e) {
log.error("批量分配角色异常", e);
}
});
}
} catch (Exception e) {
log.error("分配公共角色异常", e);
}
}, executorService);
// 不等待角色分配完成
}
}
// 2. 更新现有用户 - 使用批量处理
@@ -289,7 +263,8 @@ public class SysDataUserAllUpdateServiceImpl implements SysDataUserUpdateService
List<List<Sys_user>> batches = ListUtil.split(needDoUpdateList, 500);
batches.forEach(batch -> {
try {
dao.updateIgnoreNull(batch);
Dao extDao = Daos.ext(dao, FieldFilter.create(Sys_user.class, getDataCenterUpdateFields()));
extDao.updateIgnoreNull(batch);
} catch (Exception e) {
log.error("批量更新用户异常", e);
}
@@ -311,7 +286,18 @@ public class SysDataUserAllUpdateServiceImpl implements SysDataUserUpdateService
return "更新失败: " + e.getMessage();
}
// 3. 异步添加历史记录 - 不等待完成
// 3. 全量同步不变更会员状态,只按当前用户会员标记补齐基础角色。
List<Sys_user> roleUsers = dao.query(Sys_user.class, Cnd.NEW());
List<String> publicRoleUserIds = roleUsers.stream().map(Sys_user::getId).toList();
List<String> memberRoleUserIds = roleUsers.stream()
.filter(user -> Boolean.TRUE.equals(user.getMember()))
.map(Sys_user::getId)
.toList();
int addPublicRoleCount = addMissingUserRole(publicRole.getId(), publicRoleUserIds, "公共角色");
int addMemberRoleCount = addMissingUserRole(memberRole.getId(), memberRoleUserIds, "会员角色");
sysRoleService.clearCache();
// 4. 异步添加历史记录 - 不等待完成
if (Lang.isNotEmpty(histories)) {
executorService.execute(() -> {
log.info("添加历史记录: {} 条", histories.size());
@@ -327,58 +313,8 @@ public class SysDataUserAllUpdateServiceImpl implements SysDataUserUpdateService
});
}
// 4. 异步添加会员角色 - 不等待完成
if (Lang.isNotEmpty(addMemberUserIds)) {
executorService.execute(() -> {
try {
log.info("添加会员角色: {} 个", addMemberUserIds.size());
List<Sys_user_role> roleList = addMemberUserIds.stream().map(id -> {
Sys_user_role userRole = new Sys_user_role();
userRole.setRoleId(memberRole.getId());
userRole.setUserId(id);
return userRole;
}).collect(Collectors.toList());
if (!roleList.isEmpty()) {
// 分批处理角色分配
List<List<Sys_user_role>> batches = ListUtil.split(roleList, 500);
batches.forEach(batch -> {
try {
dao.fastInsert(batch);
} catch (Exception e) {
log.error("批量添加会员角色异常", e);
}
});
}
} catch (Exception e) {
log.error("添加会员角色异常", e);
}
});
}
// 5. 异步移除会员角色 - 不等待完成
if (Lang.isNotEmpty(removeMemberUserIds)) {
executorService.execute(() -> {
try {
log.info("移除会员角色: {} 个", removeMemberUserIds.size());
// 批量处理,避免IN子句过长
List<List<String>> batches = ListUtil.split(removeMemberUserIds, 500);
for (List<String> batch : batches) {
try {
dao.update(Sys_user.class, Chain.make("member", false), Cnd.where("id", "in", batch));
dao.clear(Sys_user_role.class, Cnd.where("userId", "in", batch).and("roleId", "=", memberRole.getId()));
} catch (Exception e) {
log.error("批量移除会员角色异常", e);
}
}
} catch (Exception e) {
log.error("移除会员角色异常", e);
}
});
}
// 6. 处理其他任务
// 6.1 更新提案的校领导角色,发送订阅
// 5. 处理其他任务
// 5.1 更新提案的校领导角色,发送订阅
ProposalConfig config = dao.fetch(ProposalConfig.class, Cnd.where("delFlag", "=", false).desc("updatedAt"));
for (String unitId : config.getSchoolLeaderUnitIds()) {
RoleEventPublisher.broadcast(new RoleEventMsg(unitId, RoleConstant.PROPOSAL_BRANCH_SCHOOL_LEADER.name(), RoleEventMsg.RENEW_ROLE));
@@ -388,7 +324,116 @@ public class SysDataUserAllUpdateServiceImpl implements SysDataUserUpdateService
log.info("全量更新用户数据完成,耗时: {} 毫秒", (endTime - startTime));
return "更新完成: 新增用户 " + needInitUserList.size() + " 个, 更新用户 " + needDoUpdateList.size()
+ " 个, 待添加会员 " + addMemberUserIds.size() + " 个, 待移除会员 " + removeMemberUserIds.size() + " 个";
+ " 个, 补齐公共角色 " + addPublicRoleCount + " 个, 补齐会员角色 " + addMemberRoleCount + " 个";
}
/**
* 补齐用户基础角色,只新增缺失的用户角色关系,避免全量更新误改会员状态或重复插入角色。
*
* @param roleId 角色ID
* @param userIds 需要拥有该角色的用户ID
* @param roleName 日志中的角色名称
* @return 本次新增的角色关系数量
*/
private int addMissingUserRole(String roleId, List<String> userIds, String roleName) {
List<String> distinctUserIds = userIds.stream()
.filter(Objects::nonNull)
.distinct()
.toList();
if (Lang.isEmpty(distinctUserIds)) {
return 0;
}
List<Sys_user_role> existingRoles = new ArrayList<>();
List<List<String>> userIdBatches = ListUtil.split(distinctUserIds, 500);
for (List<String> batch : userIdBatches) {
existingRoles.addAll(dao.query(Sys_user_role.class,
Cnd.where(Sys_user_role::getRoleId, "=", roleId)
.and(Sys_user_role::getUserId, "in", batch)));
}
Set<String> existingUserIds = existingRoles.stream()
.map(Sys_user_role::getUserId)
.collect(Collectors.toSet());
List<Sys_user_role> addRoles = distinctUserIds.stream()
.filter(userId -> !existingUserIds.contains(userId))
.map(userId -> {
Sys_user_role userRole = new Sys_user_role();
userRole.setRoleId(roleId);
userRole.setUserId(userId);
return userRole;
})
.toList();
if (Lang.isEmpty(addRoles)) {
return 0;
}
log.info("补齐{}: {} 个", roleName, addRoles.size());
List<List<Sys_user_role>> roleBatches = ListUtil.split(addRoles, 500);
for (List<Sys_user_role> batch : roleBatches) {
dao.fastInsert(batch);
}
return addRoles.size();
}
/**
* 将数据中心单位编码统一归一到系统使用的二级单位编码。
*
* @param unitId 数据中心或系统中的单位编码
* @return 二级单位编码
*/
private String normalizeSecondLevelUnitId(String unitId) {
if (unitId != null && unitId.length() > 4) {
return unitId.substring(0, 4);
}
return unitId;
}
/**
* 构建数据中心同步对象,只写入带有DataCenterColumn注解且来源非空的字段。
*
* @param source 数据中心来源用户
* @return 待新增或更新的系统用户
*/
private Sys_user buildDataCenterUser(Sys_user_source source) {
Sys_user user = new Sys_user();
for (FieldMapping mapping : getFieldMappings()) {
try {
Object sourceValue = mapping.field.get(source);
Object updateValue = normalizeDataCenterValue(mapping.field.getName(), sourceValue);
if (updateValue != null) {
mapping.field.set(user, updateValue);
}
} catch (IllegalAccessException e) {
log.error("数据中心字段赋值失败: {}", mapping.field.getName(), e);
}
}
return user;
}
/**
* 数据中心字段更新值归一化,空字符串和空值不参与更新,单位编码统一归一到二级单位。
*
* @param fieldName 字段名
* @param value 来源值
* @return 可更新值
*/
private Object normalizeDataCenterValue(String fieldName, Object value) {
if (value == null) {
return null;
}
if (value instanceof String) {
String strValue = (String) value;
String trimmedValue = StrUtil.trimToNull(strValue);
if (trimmedValue == null) {
return null;
}
if ("unitId".equals(fieldName)) {
return normalizeSecondLevelUnitId(trimmedValue);
}
return trimmedValue;
}
return value;
}
/**
@@ -419,16 +464,24 @@ public class SysDataUserAllUpdateServiceImpl implements SysDataUserUpdateService
// 遍历带有DataCenterColumn注解的字段进行比较
for (FieldMapping mapping : getFieldMappings()) {
try {
// 单位编码需要先归一到二级单位,统一放到单位变更逻辑中比较。
if ("unitId".equals(mapping.field.getName())) {
continue;
}
Object sourceValue = mapping.field.get(source);
Object userValue = mapping.field.get(user);
Object updateValue = normalizeDataCenterValue(mapping.field.getName(), sourceValue);
if (updateValue == null) {
continue;
}
// 如果值不相等,记录变更
if (!ObjectUtil.equals(sourceValue, userValue)) {
if (!ObjectUtil.equals(updateValue, userValue)) {
NutMap change = NutMap.NEW();
change.put("fieldName", mapping.name);
change.put("field", mapping.field.getName());
change.put("sourceValue", userValue == null ? "" : userValue.toString());
change.put("newValue", sourceValue == null ? "" : sourceValue.toString());
change.put("newValue", updateValue.toString());
changeList.add(change);
}
} catch (IllegalAccessException e) {
@@ -452,18 +505,23 @@ public class SysDataUserAllUpdateServiceImpl implements SysDataUserUpdateService
// }
// 2. 单位变更
if (!ObjectUtil.equals(user.getUnitId(), source.getUnitId())) {
String oldUnitId = normalizeSecondLevelUnitId(user.getUnitId());
String newUnitId = normalizeSecondLevelUnitId(source.getUnitId());
if (StrUtil.isNotBlank(newUnitId) && !ObjectUtil.equals(oldUnitId, newUnitId)) {
changeTypes.add(MemberChangeType.UNIT_CHANGE.name());
NutMap change = NutMap.NEW();
change.put("fieldName", "单位");
change.put("field", "unitId");
change.put("sourceValue", unitIdNameMap.get(user.getUnitId()));
change.put("newValue", unitIdNameMap.get(user.getUnitId()));
change.put("sourceValue", getUnitChangeDisplayValue(oldUnitId));
change.put("newValue", getUnitChangeDisplayValue(newUnitId));
changeList.add(change);
// 单位异动发送订阅消息,清空原单位的所有角色
UserChangePublisher.broadcast(new UserChangeMsg(List.of(user.getId()),
UserChangeMsg.UNIT_CHANGE_OPERATION, user.getUnitId(), source.getUnitId()));
// 旧单位为空时没有明确的来源单位角色范围,避免清理到全局角色。
if (StrUtil.isNotBlank(oldUnitId)) {
// 单位异动发送订阅消息,清空原单位的所有角色
UserChangePublisher.broadcast(new UserChangeMsg(List.of(user.getId()),
UserChangeMsg.UNIT_CHANGE_OPERATION, oldUnitId, newUnitId));
}
}
// 如果没有任何变更,返回null
@@ -474,8 +532,8 @@ public class SysDataUserAllUpdateServiceImpl implements SysDataUserUpdateService
// 生成变更信息描述
String changeInfos = changeList.stream()
.map(v -> v.getString("fieldName") + ":" +
HtmlUtil.cleanHtmlTag(v.getString("sourceValue")) + "→" +
HtmlUtil.cleanHtmlTag(v.getString("newValue")))
cleanChangeValue(v.get("sourceValue")) + "→" +
cleanChangeValue(v.get("newValue")))
.collect(Collectors.joining(";"));
// 设置历史记录信息
@@ -485,4 +543,31 @@ public class SysDataUserAllUpdateServiceImpl implements SysDataUserUpdateService
return history;
}
/**
* 单位变更历史展示值,优先显示单位名称,单位表缺失时显示单位编码,空单位显示空字符串。
*
* @param unitId 单位编码
* @return 历史记录展示值
*/
private String getUnitChangeDisplayValue(String unitId) {
if (StrUtil.isBlank(unitId)) {
return "";
}
String unitName = unitIdNameMap.get(unitId);
return StrUtil.blankToDefault(unitName, unitId);
}
/**
* 生成变更描述前统一处理空值和HTML标签,避免来源数据为空时生成历史记录失败。
*
* @param value 变更前后值
* @return 清洗后的变更展示值
*/
private String cleanChangeValue(Object value) {
if (value == null) {
return "";
}
return HtmlUtil.cleanHtmlTag(value.toString());
}
}
@@ -210,4 +210,7 @@ public class View_user {
@Column
private Boolean aidFundMember;
@Column
private String aidFundMemberType;
}
@@ -94,6 +94,29 @@ public class EnrollmentRegistrationSummaryController {
return Result.success();
}
@At
@ApiOperation("设置解决状态")
@Aop(TransAop.READ_COMMITTED)
@SaCheckPermission("enrollmentRegistration.summary")
@SLog(tag = "子女入学-查询统计", type = "enrollmentRegistration", msg = "设置解决状态id: ${args[0]}")
public Result doUpdateSolved(String id, Boolean solved) {
if (StrUtil.isBlank(id) || solved == null) {
return Result.error("参数错误");
}
enrollmentRegistrationSummaryService.updateSolved(id, solved);
return Result.success();
}
@At
@ApiOperation("一键设置已解决")
@Aop(TransAop.READ_COMMITTED)
@SaCheckPermission("enrollmentRegistration.summary")
@SLog(tag = "子女入学-查询统计", type = "enrollmentRegistration", msg = "一键设置已解决")
public Result doBatchSetSolved(@Param("data") EnrollmentRegistrationPageForm pageForm) {
int count = enrollmentRegistrationSummaryService.batchSetSolved(pageForm);
return Result.success("已设置" + count + "条数据");
}
@At
@Ok("void")
@@ -108,6 +131,7 @@ public class EnrollmentRegistrationSummaryController {
map.forEach(item -> {
Sys_dict dict = dictList.stream().filter(sys_dict -> sys_dict.getCode().equals(item.getString("registrationType"))).findFirst().orElse(null);
item.put("registrationTypeName", dict.getName());
item.put("solvedName", item.getBoolean("solved", false) ? "已解决" : "未解决");
});
List<ExcelExportEntity> entityList = new ArrayList<>();
@@ -123,6 +147,7 @@ public class EnrollmentRegistrationSummaryController {
entityList.add(new ExcelExportEntity("手机号码", "mobile", 20));
entityList.add(new ExcelExportEntity("登记类型", "registrationTypeName", 20));
entityList.add(new ExcelExportEntity("填报时间", "applyTime", 20));
entityList.add(new ExcelExportEntity("是否解决", "solvedName", 20));
response.setContentType("application/octet-stream");
response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode("子女信息登记汇总.xlsx", "UTF-8"));
@@ -49,6 +49,11 @@ public class EnrollmentRegistration extends BaseModel implements Serializable {
@Comment("工号")
private String loginName;
@Column
@ColDefine(type = ColType.VARCHAR,width = 100)
@Comment("职称")
private String professionalTitle;
@Column
@ColDefine(type = ColType.VARCHAR,width = 32)
@Comment("userId")
@@ -139,6 +144,12 @@ public class EnrollmentRegistration extends BaseModel implements Serializable {
@Comment("子女出生证照片")
private List<JSONObject> birthCertificateFiles;
@Column
@ColDefine(type = ColType.BOOLEAN)
@Default("0")
@Comment("是否解决")
private Boolean solved;
}
@@ -9,4 +9,20 @@ public interface EnrollmentRegistrationSummaryService extends BaseService {
Sql getSql(EnrollmentRegistrationPageForm pageForm,String searchKeyword);
/**
* 更新单条登记记录的解决状态。
*
* @param id 登记记录id
* @param solved 是否解决
*/
void updateSolved(String id, Boolean solved);
/**
* 将当前筛选条件下的登记记录批量设置为已解决。
*
* @param pageForm 当前汇总列表筛选条件
* @return 实际更新条数
*/
int batchSetSolved(EnrollmentRegistrationPageForm pageForm);
}
@@ -3,14 +3,19 @@ package com.budwk.app.zhgh.dayofficework.enrollmentRegistration.service.impl;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.service.impl.BaseServiceImpl;
import com.budwk.app.flow.enums.ProcessTaskStateEnum;
import com.budwk.app.zhgh.dayofficework.enrollmentRegistration.model.EnrollmentRegistration;
import com.budwk.app.zhgh.dayofficework.enrollmentRegistration.service.EnrollmentRegistrationSummaryService;
import com.budwk.app.zhgh.dayofficework.enrollmentRegistration.vo.EnrollmentRegistrationPageForm;
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.IocBean;
import org.nutz.lang.util.NutMap;
import java.util.List;
/**
* @author zhf
@@ -53,19 +58,49 @@ public class EnrollmentRegistrationSummaryServiceImpl extends BaseServiceImpl im
$condition
""");
Cnd cnd = Cnd.NEW();
// 姓名和工号查询条件
if (StrUtil.isNotBlank(searchKeyword)) {
// 统一处理列表、导出和批量设置的姓名/工号查询条件,保证各入口筛选范围一致。
String keyword = StrUtil.blankToDefault(searchKeyword, pageForm.getSearchKeyword());
if (StrUtil.isNotBlank(keyword)) {
SqlExpressionGroup seg = new SqlExpressionGroup();
seg.orLike("info.userName", "%" + searchKeyword + "%");
seg.orLike("info.loginName", "%" + searchKeyword + "%");
seg.orLike("info.userName", "%" + keyword + "%");
seg.orLike("info.loginName", "%" + keyword + "%");
cnd.and(seg);
}
cnd.andEX("YEAR(info.applyTime)", "=", pageForm.getYear());
cnd.and("ins.state", "=", ProcessTaskStateEnum.FINISHED.getCode());
cnd.andEX("info.unionId","=",pageForm.getUnionId());
cnd.andEX("info.unitId","=",pageForm.getUnitId());
cnd.andEX("info.professionalTitle","=",pageForm.getProfessionalTitle());
cnd.andEX("info.registrationType","=",pageForm.getRegistrationType());
if (pageForm.getSolved() != null) {
if (pageForm.getSolved()) {
cnd.and("info.solved", "=", true);
} else {
// 历史数据的解决状态可能为空,按业务默认值一并视为未解决。
cnd.and(Cnd.exps("info.solved", "=", false).or("info.solved", "is", null));
}
}
sql.setCondition(cnd);
return sql;
}
@Override
public void updateSolved(String id, Boolean solved) {
if (StrUtil.isBlank(id) || solved == null) {
return;
}
dao().update(EnrollmentRegistration.class, Chain.make("solved", solved), Cnd.where("id", "=", id));
}
@Override
public int batchSetSolved(EnrollmentRegistrationPageForm pageForm) {
// 复用汇总列表查询条件,保证“一键设置已解决”的范围与当前列表筛选结果一致。
Sql sql = getSql(pageForm, null);
List<NutMap> list = listMap(sql);
List<String> idList = list.stream().map(item -> item.getString("id")).toList();
if (idList.isEmpty()) {
return 0;
}
return dao().update(EnrollmentRegistration.class, Chain.make("solved", true), Cnd.where("id", "in", idList));
}
}
@@ -15,5 +15,7 @@ public class EnrollmentRegistrationPageForm extends PageForm {
private Integer year;
private String unionId;
private String unitId;
private String professionalTitle;
private String registrationType;
private Boolean solved;
}
@@ -1,6 +1,7 @@
package com.budwk.app.zhgh.dayofficework.fund.member.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;
@@ -39,8 +40,14 @@ public class FundMemberBranchUnionApprovalController {
}
@At("/h5")
@Ok("beetl:/platform/zhghh5/dayofficework/fund/member/branchUnionApproval/index.html")
@SaCheckPermission("h5.fundMember.branchUnionApproval")
public void h5() {
}
@At
@SaCheckPermission("fundMember.branchUnionApproval")
@SaCheckPermission(value = {"fundMember.branchUnionApproval", "h5.fundMember.branchUnionApproval"}, mode = SaMode.OR)
public Result pageData(PageForm pageForm, String taskId, String searchKeyword, Integer year, boolean approval) {
Sql sql = Sqls.create("""
SELECT
@@ -4,6 +4,7 @@ import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import com.budwk.app.base.result.Result;
import com.budwk.app.base.utils.CommonDownloadUtil;
import com.budwk.app.base.utils.SysOfficeTemplateUtil;
@@ -31,9 +32,10 @@ import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.io.ByteArrayOutputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@IocBean
@At("/platform/fundMember/common")
@@ -72,9 +74,12 @@ public class FundMemberCommonController {
FundMemberApply fundMember = dao.fetch(FundMemberApply.class, id);
Map<String, Object> docData = BeanUtil.beanToMap(fundMember);
docData.put("birthday", DateUtil.format(fundMember.getBirthday(), "yyyy年MM月dd日"));
docData.put("joinWorkTime", DateUtil.format(fundMember.getJoinWorkTime(), "yyyy年MM月dd日"));
docData.put("retireTime", DateUtil.format(fundMember.getRetireTime(), "yyyy年MM月dd日"));
docData.put("birthday", formatDate(fundMember.getBirthday(), "yyyy年MM月"));
docData.put("joinWorkTime", formatDate(fundMember.getJoinWorkTime(), "yyyy年MM月dd日"));
docData.put("retireTime", formatDate(fundMember.getRetireTime(), "yyyy年MM月dd日"));
docData.put("submitTime", formatDate(fundMember.getSubmitTime(), "yyyy年MM月dd日"));
docData.put("unionName", StrUtil.nullToEmpty(fundMember.getUnionName()));
docData.put("familyMembers", buildFamilyMembers(fundMember.getFamilies()));
if (StrUtil.isNotBlank(fundMember.getAvatar())) {
docData.put("avatar", sysOfficeTemplateUtil.createPictureRenderData(fundMember.getAvatar()));
}
@@ -88,19 +93,87 @@ public class FundMemberCommonController {
doneTaskVos.add(taskVO);
}
// 按任务节点分组
Map<String, List<ProcessTaskVO>> taskGroups = doneTaskVos.stream().collect(Collectors.groupingBy(ProcessTaskVO::getDisplayName));
docData.putAll(taskGroups);
// 按模板约定输出分工会、校工会审核信息,fgh、xgh 可在模板中直接使用 date 和 opinion 子字段。
putApprovalData(docData, doneTaskVos, List.of("fgh", "ltx"), "fgh");
putApprovalData(docData, doneTaskVos, List.of("xgh"), "xgh");
Configure configure = Configure.builder().build();
try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {
XWPFTemplate.compile(sysOfficeTemplateUtil.getTemplate("fundMember"), configure).render(docData).writeAndClose(byteArrayOutputStream);
XWPFTemplate.compile(sysOfficeTemplateUtil.getTemplate("fundMemberApply"), configure).render(docData).writeAndClose(byteArrayOutputStream);
CommonDownloadUtil.download(fundMember.getUserName() + "的医疗互助“爱心”基金入会申请表.docx", byteArrayOutputStream.toByteArray(), response);
} catch (Exception e) {
log.error("导出word异常", e);
}
}
/**
* 格式化导出日期,空日期以空字符串输出,避免 Word 模板显示 null。
*
* @param date 日期
* @param pattern Word 模板展示格式
* @return 格式化后的日期文本
*/
private String formatDate(Date date, String pattern) {
return date == null ? "" : DateUtil.format(date, pattern);
}
/**
* 将家庭成员列表转换为 Word 模板单元格可直接展示的多行文本。
*
* @param families 家庭成员列表
* @return 序号、姓名、年龄、与本人关系组成的多行文本
*/
private String buildFamilyMembers(List<JSONObject> families) {
if (families == null || families.isEmpty()) {
return "";
}
StringBuilder content = new StringBuilder();
for (int index = 0; index < families.size(); index++) {
JSONObject family = families.get(index);
if (family == null) {
continue;
}
if (content.length() > 0) {
content.append(System.lineSeparator());
}
String sequence = StrUtil.blankToDefault(family.getStr("sequence"), String.valueOf(index + 1));
content.append(sequence).append("、")
.append(StrUtil.nullToEmpty(family.getStr("name"))).append(",")
.append(StrUtil.nullToEmpty(family.getStr("age"))).append("岁,")
.append(StrUtil.nullToEmpty(family.getStr("relation")));
}
return content.toString();
}
/**
* 从指定流程节点中取最后一次已办记录,组装 Word 模板所需的审核日期与意见。
*
* @param docData 模板数据
* @param doneTasks 已办流程任务
* @param taskNames 对应的流程任务名称
* @param templateKey 模板对象名称,例如 fgh、xgh
*/
private void putApprovalData(Map<String, Object> docData, List<ProcessTaskVO> doneTasks, List<String> taskNames, String templateKey) {
Map<String, Object> approval = new HashMap<>();
approval.put("date", "");
approval.put("opinion", "");
ProcessTaskVO latestTask = null;
for (ProcessTaskVO task : doneTasks) {
if (!taskNames.contains(task.getTaskName())) {
continue;
}
if (latestTask == null || (task.getCreatedAt() != null
&& (latestTask.getCreatedAt() == null || task.getCreatedAt() > latestTask.getCreatedAt()))) {
latestTask = task;
}
}
if (latestTask != null) {
approval.put("date", formatDate(latestTask.getFinishTime(), "yyyy年MM月dd日"));
approval.put("opinion", StrUtil.nullToEmpty(latestTask.getTaskFormData().getStr("tf_opinion")));
}
docData.put(templateKey, approval);
}
}
@@ -1,6 +1,7 @@
package com.budwk.app.zhgh.dayofficework.fund.member.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.annotation.SaMode;
import com.budwk.app.base.annotation.SLog;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.result.Result;
@@ -44,8 +45,14 @@ public class FundMemberMineController {
}
@At("/h5")
@Ok("beetl:/platform/zhghh5/dayofficework/fund/member/mine/index.html")
@SaCheckPermission("h5.fundMember.mine")
public void h5() {
}
@At
@SaCheckPermission("fundMember.mine")
@SaCheckPermission(value = {"fundMember.mine", "h5.fundMember.mine"}, mode = SaMode.OR)
public Result pageData(Integer pageNumber, Integer pageSize, String searchKeyword, Integer year) {
Sql sql = Sqls.create("""
SELECT
@@ -76,13 +83,15 @@ public class FundMemberMineController {
Cnd cnd = Cnd.NEW();
cnd.andEX("YEAR(info.submitTime)", "=", year);
cnd.and("info.createdBy", "=", SecurityUtil.getUserId());
// 同一流程任务存在多个参与人时,按申请主表主键合并,避免我的申请列表出现重复记录。
cnd.groupBy("info.id");
sql.setCondition(cnd);
Pagination<NutMap> pagination = fundMemberService.listPageMap(pageNumber, pageSize, sql);
return Result.success(pagination);
}
@At
@SaCheckPermission("fundMember.mine")
@SaCheckPermission(value = {"fundMember.mine", "h5.fundMember.mine"}, mode = SaMode.OR)
@Aop(TransAop.READ_COMMITTED)
@ApiOperation("删除")
@SLog(tag = "基金会员-我的申请", msg = "删除")
@@ -39,6 +39,7 @@ 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 javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
@@ -78,9 +79,16 @@ public class FundMemberQueryController {
u.userState,
u.preparedBy,
u.personType,
COALESCE(NULLIF(u.aidFundMemberType, ''),
(SELECT applyInfo.aidFundMemberType
FROM fund_member_apply applyInfo
WHERE applyInfo.loginName = u.loginName
AND applyInfo.isJoin = 1
ORDER BY applyInfo.submitTime DESC
LIMIT 1)) AS aidFundMemberType,
u.unitName,
u.unionName,
(SELECT changeTime FROM fund_member_change_record WHERE loginName = u.loginName AND isJoin = 1 ORDER BY changeTime DESC limit 1) joinTime
(SELECT changeTime FROM fund_member_change_record WHERE loginName = u.loginName AND isJoin = 1 ORDER BY changeTime DESC LIMIT 1) AS joinTime
FROM
vw_user u
$condition
@@ -93,6 +101,14 @@ public class FundMemberQueryController {
return Result.success(pagination);
}
@At
@SaCheckPermission("fundMember.query")
@ApiOperation("修改大病基金会员类别")
public Result updateAidFundMemberType(@Param("loginName") String loginName, @Param("aidFundMemberType") String aidFundMemberType) {
fundMemberService.updateAidFundMemberType(loginName, aidFundMemberType);
return Result.success();
}
@At
@SaCheckPermission("fundMember.query")
@Ok("void")
@@ -1,6 +1,7 @@
package com.budwk.app.zhgh.dayofficework.fund.member.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;
@@ -39,8 +40,14 @@ public class FundMemberSchoolUnionApprovalController {
}
@At("/h5")
@Ok("beetl:/platform/zhghh5/dayofficework/fund/member/schoolUnionApproval/index.html")
@SaCheckPermission("h5.fundMember.schoolUnionApproval")
public void h5() {
}
@At
@SaCheckPermission("fundMember.schoolUnionApproval")
@SaCheckPermission(value = {"fundMember.schoolUnionApproval", "h5.fundMember.schoolUnionApproval"}, mode = SaMode.OR)
public Result pageData(PageForm pageForm, String unionId, Integer year, boolean approval) {
Sql sql = Sqls.create("""
SELECT
@@ -1,6 +1,7 @@
package com.budwk.app.zhgh.dayofficework.fund.member.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.annotation.SaMode;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.annotation.SLog;
@@ -50,8 +51,14 @@ public class FundMemberWriteController {
}
@At("/h5")
@Ok("beetl:/platform/zhghh5/dayofficework/fund/member/write/index.html")
@SaCheckPermission("h5.fundMember.write")
public void h5() {
}
@At
@SaCheckPermission("fundMember.write")
@SaCheckPermission(value = {"fundMember.write", "h5.fundMember.write"}, mode = SaMode.OR)
@ApiOperation("保存申请")
@SLog(tag = "建言献策-填写申请", msg = "保存申请")
public Result save(@Param("data") FundMemberApply fundMember) {
@@ -64,11 +71,12 @@ public class FundMemberWriteController {
@At
@SaCheckPermission("fundMember.write")
@SaCheckPermission(value = {"fundMember.write", "h5.fundMember.write"}, mode = SaMode.OR)
@ApiOperation("提交申请")
@Aop(TransAop.READ_COMMITTED)
@SLog(tag = "基金会员-填写申请", msg = "提交申请")
public Result submit(@Param("data") FundMemberApply fundMember) {
fundMemberService.validateCurrentUserNotFundMember();
if (StrUtil.isBlank(fundMember.getId())) {
fundMemberService.validateApply();
}
@@ -80,7 +88,7 @@ public class FundMemberWriteController {
args.set(FlowConst.SUBMIT_TYPE, ProcessSubmitTypeEnum.APPLY.getCode());
args.set(FlowConst.FORM_DATA, fundMember);
args.set(FlowConst.TASK_FORM_DATA_PREFIX + "userState", fundMember.getUserState());
ProcessInstance instance = flowEngine.startProcessInstanceByKey("FUND_MEMBER", fundMember.getId(), SecurityUtil.getUserId(), args);
ProcessInstance instance = flowEngine.startProcessInstanceByKey("JJHY", fundMember.getId(), SecurityUtil.getUserId(), args);
// 自动完成第一个申请任务
List<ProcessTask> doingTaskList = flowEngine.processTaskService().getDoingTaskList(instance.getId(), null);
@@ -91,11 +99,12 @@ public class FundMemberWriteController {
}
@At
@SaCheckPermission("fundMember.write")
@SaCheckPermission(value = {"fundMember.write", "h5.fundMember.write"}, mode = SaMode.OR)
@ApiOperation("重新提交申请")
@Aop(TransAop.READ_COMMITTED)
@SLog(tag = "基金会员-填写申请", msg = "重新提交申请")
public Result submitAgain(@Param("data") FundMemberApply fundMember, @Param("taskId") Long taskId) {
fundMemberService.validateCurrentUserNotFundMember();
if (StrUtil.isBlank(fundMember.getId())) {
fundMemberService.validateApply();
}
@@ -109,7 +118,7 @@ public class FundMemberWriteController {
}
@At
@SaCheckPermission("fundMember.write")
@SaCheckPermission(value = {"fundMember.write", "fundMember.mine", "fundMember.branchUnionApproval", "fundMember.schoolUnionApproval", "h5.fundMember.write", "h5.fundMember.mine", "h5.fundMember.branchUnionApproval", "h5.fundMember.schoolUnionApproval"}, mode = SaMode.OR)
@ApiOperation("获取申请信息")
public Result info(@Param("id") String id) {
FundMemberApply box = dao.fetch(FundMemberApply.class, id);
@@ -118,10 +127,20 @@ public class FundMemberWriteController {
@At
@SaCheckPermission("fundMember.write")
@SaCheckPermission(value = {"fundMember.write", "h5.fundMember.write"}, mode = SaMode.OR)
@ApiOperation("获取当前用户基金会员状态")
public Result memberStatus() {
return Result.success(fundMemberService.isCurrentUserFundMember());
}
@At
@SaCheckPermission(value = {"fundMember.write", "h5.fundMember.write"}, mode = SaMode.OR)
@ApiOperation("校验是否有申请在流程中")
public Result validateApply(){
fundMemberService.validateApply();
return Result.success();
boolean isFundMember = fundMemberService.isCurrentUserFundMember();
if (!isFundMember) {
fundMemberService.validateApply();
}
return Result.success(isFundMember);
}
}
@@ -25,11 +25,15 @@ public class FundMemberPassInterceptor implements FlowInterceptor {
public void intercept(Execution execution) {
Dao dao = ServiceContext.find(Dao.class);
FundMemberApply fundMemberApply = Json.fromJson(FundMemberApply.class, execution.getArgs().getStr("f_data"));
// 修改基金会员状态
dao.update(Sys_user.class, Chain.make("aidFundMember", 1), Cnd.where(Sys_user::getLoginname, "=", fundMemberApply.getLoginName()));
// 修改基金会员状态,并同步申请时选择的大病基金会员类别。
dao.update(Sys_user.class,
Chain.make("aidFundMember", 1).add("aidFundMemberType", fundMemberApply.getAidFundMemberType()),
Cnd.where(Sys_user::getLoginname, "=", fundMemberApply.getLoginName()));
// 添加到变更记录
FundMemberChangeRecord record = BeanUtil.copyProperties(fundMemberApply, FundMemberChangeRecord.class, "id");
record.setId(null);
// 后置拦截器只在入会申请审核通过后执行,明确标记为加入记录,供台账展示加入时间。
record.setIsJoin(true);
record.setChangeOrigin("流程申请");
record.setChangeTime(new Date());
dao.insert(record);
@@ -1,6 +1,7 @@
package com.budwk.app.zhgh.dayofficework.fund.member.models;
import cn.afterturn.easypoi.excel.annotation.Excel;
import cn.hutool.json.JSONObject;
import com.budwk.app.base.model.BaseModel;
import lombok.Data;
import lombok.EqualsAndHashCode;
@@ -8,6 +9,7 @@ import org.nutz.dao.entity.annotation.*;
import org.nutz.dao.interceptor.annotation.PrevInsert;
import java.util.Date;
import java.util.List;
@Data
@Table("fund_member_apply")
@@ -48,7 +50,7 @@ public class FundMemberApply extends BaseModel {
private String idCard;
@Column
@Comment("参加工作时间")
@Comment("入职时间")
@ColDefine(type = ColType.DATE)
private Date joinWorkTime;
@@ -57,11 +59,31 @@ public class FundMemberApply extends BaseModel {
@ColDefine(type = ColType.DATE)
private Date retireTime;
@Column
@Comment("大病基金会员类别")
@ColDefine(type = ColType.VARCHAR, width = 50)
private String aidFundMemberType;
@Column
@Comment("家庭住址")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String address;
@Column
@Comment("籍贯")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String nativePlace;
@Column
@Comment("学历")
@ColDefine(type = ColType.VARCHAR, width = 50)
private String education;
@Column
@Comment("健康状况")
@ColDefine(type = ColType.VARCHAR, width = 200)
private String healthStatus;
@Column
@Comment("手机号码")
@ColDefine(type = ColType.VARCHAR, width = 20)
@@ -72,6 +94,16 @@ public class FundMemberApply extends BaseModel {
@ColDefine(type = ColType.VARCHAR, width = 20)
private String homePhone;
@Column
@Comment("家庭主要成员")
@ColDefine(type = ColType.MYSQL_JSON)
private List<JSONObject> families;
@Column
@Comment("备注")
@ColDefine(type = ColType.VARCHAR, customType = "text")
private String remark;
@Column
@Comment("照片")
@ColDefine(type = ColType.VARCHAR, width = 100)
@@ -9,4 +9,25 @@ public interface FundMemberService extends BaseService<FundMemberApply> {
* 验证是否已经申请了
*/
void validateApply();
/**
* 判断当前登录用户是否为基金会员。
*
* @return 当前用户是基金会员时返回 true
*/
boolean isCurrentUserFundMember();
/**
* 校验当前登录用户不是基金会员,避免重复发起入会申请。
*/
void validateCurrentUserNotFundMember();
/**
* 修改大病基金会员类别。
*
* @param loginName 工号
* @param aidFundMemberType 大病基金会员类别
*/
void updateAidFundMemberType(String loginName, String aidFundMemberType);
}
@@ -4,9 +4,11 @@ import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.service.impl.BaseServiceImpl;
import com.budwk.app.flow.entity.ProcessInstance;
import com.budwk.app.flow.enums.ProcessTaskStateEnum;
import com.budwk.app.sys.models.Sys_user;
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
import com.budwk.app.zhgh.dayofficework.fund.member.models.FundMemberApply;
import com.budwk.app.zhgh.dayofficework.fund.member.service.FundMemberService;
import org.nutz.dao.Chain;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.ioc.loader.annotation.IocBean;
@@ -21,6 +23,7 @@ public class FundMemberServiceImpl extends BaseServiceImpl<FundMemberApply> impl
@Override
public void validateApply() {
validateCurrentUserNotFundMember();
FundMemberApply apply = dao().fetch(FundMemberApply.class,
Cnd.where(FundMemberApply::getLoginName, "=", SecurityUtil.getUserLoginname())
.and("isJoin", "=", 1)
@@ -36,4 +39,32 @@ public class FundMemberServiceImpl extends BaseServiceImpl<FundMemberApply> impl
throw new RuntimeException("您有申请流程正在进行中,请等待流程结束后再次操作");
}
}
@Override
public boolean isCurrentUserFundMember() {
Sys_user user = dao().fetch(Sys_user.class,
Cnd.where(Sys_user::getLoginname, "=", SecurityUtil.getUserLoginname()));
return user != null && Boolean.TRUE.equals(user.getAidFundMember());
}
@Override
public void validateCurrentUserNotFundMember() {
if (isCurrentUserFundMember()) {
throw new RuntimeException("您已是基金会员,无需重复申请");
}
}
@Override
public void updateAidFundMemberType(String loginName, String aidFundMemberType) {
if (StrUtil.isBlank(loginName)) {
throw new RuntimeException("工号不能为空");
}
if (StrUtil.isBlank(aidFundMemberType)) {
throw new RuntimeException("会员类别不能为空");
}
dao().update(Sys_user.class,
Chain.make("aidFundMemberType", aidFundMemberType),
Cnd.where(Sys_user::getLoginname, "=", loginName).and(Sys_user::getAidFundMember, "=", 1));
}
}
@@ -43,6 +43,10 @@ public class FundMemberCommonPageForm extends PageForm {
@ApiModelProperty("教职工类别")
private List<String> personTypes;
// 大病基金会员类别
@ApiModelProperty("大病基金会员类别")
private List<String> aidFundMemberTypes;
public void buildSearch(Cnd cnd, String prefix){
if (cnd == null) {
cnd = Cnd.NEW();
@@ -60,5 +64,6 @@ public class FundMemberCommonPageForm extends PageForm {
cnd.andEX(prefix + "userState", "in", this.getUserStates());
cnd.andEX(prefix + "preparedBy", "in", this.getPreparedBys());
cnd.andEX(prefix + "personType", "in", this.getPersonTypes());
cnd.andEX(prefix + "aidFundMemberType", "in", this.getAidFundMemberTypes());
}
}
@@ -59,17 +59,11 @@ public class QsvActivityController {
//保存问卷基础信息
@At
@SaCheckPermission("qsv.activity")
@Aop(TransAop.READ_COMMITTED)
@ApiOperation("保存问卷基础信息")
@SLog(type = "qsv.activity", tag = "保存问卷基础信息", msg = "保存问卷基础信息")
public Result save(QsvActivity qsvActivity) {
if (qsvActivity.getCategory().equals("QUIZ")) {
if (qsvActivity.getMode().equals("SCHEDULED")) {
qsvActivity.setRepeatMode("DAILY");
} else if (qsvActivity.getMode().equals("REGULAR")) {
qsvActivity.setRepeatMode("TOTAL");
}
}
dao.insertOrUpdate(qsvActivity);
qsvActivityService.saveActivity(qsvActivity);
return Result.success();
}
@@ -83,13 +77,25 @@ public class QsvActivityController {
// 删除问卷
@At
@SaCheckPermission("qsv.activity")
@Aop(TransAop.READ_COMMITTED)
@ApiOperation("删除问卷")
@SLog(type = "qsv.activity", tag = "删除问卷", msg = "删除问卷")
public Result delete(@Valid String id) {
dao.delete(QsvActivity.class, id);
qsvActivityService.deleteActivity(id);
return Result.success();
}
// 更新活动开启状态
@At
@SaCheckPermission("qsv.activity")
@Aop(TransAop.READ_COMMITTED)
@ApiOperation("更新活动开启状态")
@SLog(type = "qsv.activity", tag = "更新活动开启状态", msg = "更新活动开启状态")
public Result updateEnabled(@Valid String id, Boolean enabled) {
qsvActivityService.updateEnabled(id, enabled);
return Result.success(enabled ? "开启成功" : "关闭成功");
}
// 保存问卷题目
@At
@@ -1,6 +1,7 @@
package com.budwk.app.zhgh.dayofficework.qsv.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.json.JSONArray;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONObject;
import com.budwk.app.base.annotation.SLog;
@@ -77,8 +78,34 @@ public class QsvSurveyController {
@SaCheckPermission("qsv.survey")
@ApiOperation("选项选择详情")
public Result selectOptionUsers(@Valid String activityId, @Valid String subjectId, @Valid String optionId) {
List<QsvUserAnswerRecord> answerRecords = dao.query(QsvUserAnswerRecord.class, Cnd.where("activityId", "=", activityId));
List<QsvUserAnswerRecord> selectOptionUsers = answerRecords.stream().filter(ext -> ObjectUtil.isNotNull(ext.getExtJson().get(subjectId, JSONObject.class)) && ext.getExtJson().get(subjectId, JSONObject.class).getJSONArray("optionIds").contains(optionId)).toList();
List<QsvUserAnswerRecord> answerRecords = dao.query(QsvUserAnswerRecord.class, Cnd.where("activityId", "=", activityId)
.and("isFinish", "=", true));
List<NutMap> selectOptionUsers = answerRecords.stream().map(record -> {
JSONObject extJson = record.getExtJson();
if (ObjectUtil.isEmpty(extJson)) {
return null;
}
JSONObject subjectAnswer = extJson.get(subjectId, JSONObject.class);
if (ObjectUtil.isEmpty(subjectAnswer)) {
return null;
}
JSONArray optionIds = subjectAnswer.getJSONArray("optionIds");
if (ObjectUtil.isEmpty(optionIds) || !optionIds.contains(optionId)) {
return null;
}
JSONObject optionFillContents = subjectAnswer.getJSONObject("optionFillContents");
String fillContent = ObjectUtil.isEmpty(optionFillContents) ? "" : optionFillContents.getStr(optionId);
return NutMap.NEW()
.addv("userName", record.getUserName())
.addv("loginName", record.getLoginName())
.addv("unionName", record.getUnionName())
.addv("unitName", record.getUnitName())
.addv("attemptDate", record.getAttemptDate())
.addv("submitTime", record.getSubmitTime())
.addv("fillContent", fillContent);
})
.filter(ObjectUtil::isNotNull)
.toList();
return Result.success(selectOptionUsers);
}
@@ -36,6 +36,7 @@ public class H5QsvController {
public Result pageData(@Valid PageForm pageForm, @Valid String category) {
Cnd cnd = Cnd.NEW();
cnd.andEX("category", "=", category);
cnd.and(Cnd.exps("enabled", "=", true).or("enabled", "is", null));
cnd.desc("startTime");
Pagination pagination = qsvActivityService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), cnd);
return Result.success(pagination);
@@ -1,5 +1,6 @@
package com.budwk.app.zhgh.dayofficework.qsv.h5controller;
import cn.dev33.satoken.annotation.SaCheckLogin;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONObject;
import com.budwk.app.base.result.Result;
@@ -12,6 +13,8 @@ import com.budwk.app.zhgh.dayofficework.qsv.param.QsvAnswerParam;
import com.budwk.app.zhgh.dayofficework.qsv.service.QsvUserAnswerRecordService;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.aop.interceptor.ioc.TransAop;
import org.nutz.ioc.aop.Aop;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.util.NutMap;
@@ -23,6 +26,7 @@ import javax.validation.Valid;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
@IocBean
@At("/platform/h5/qsv/survey")
@@ -35,13 +39,20 @@ public class H5QsvSurveyController {
private QsvUserAnswerRecordService qsvUserAnswerRecordService;
@At("")
@SaCheckLogin
@Ok("beetl:/platform/zhghh5/dayofficework/qsv/survey/index.html")
public void index() {
}
@At
@SaCheckLogin
@Aop(TransAop.READ_COMMITTED)
public Result subjects(String activityId) {
QsvActivity activity = dao.fetch(QsvActivity.class, activityId);
Result activityStatusResult = checkActivityStatus(activity);
if (activityStatusResult != null) {
return activityStatusResult;
}
int count = dao.count(ActivityUserScope.class, Cnd.where("groupId", "=", activity.getGroupId())
.and("userId", "=", SecurityUtil.getUserId()));
@@ -51,16 +62,17 @@ public class H5QsvSurveyController {
String answerRecordId = null;
List<QsvSubject> activitySubjects = dao.query(QsvSubject.class, Cnd.where("activityId", "=", activityId).asc("sortNum"));
QsvUserAnswerRecord answerRecord = dao.fetch(QsvUserAnswerRecord.class, Cnd.where("activityId", "=", activityId)
.and("userId", "=", SecurityUtil.getUserId()));
if (answerRecord == null) {
List<QsvSubject> subjects = dao.query(QsvSubject.class, Cnd.where("activityId", "=", activityId).asc("sortNum"));
qsvUserAnswerRecordService.insertRecord(activityId, subjects.stream().map(QsvSubject::getId).toList());
qsvUserAnswerRecordService.insertRecord(activityId, activitySubjects.stream().map(QsvSubject::getId).toList());
}
QsvUserAnswerRecord answerRecord2 = dao.fetch(QsvUserAnswerRecord.class,
Cnd.where("activityId", "=", activityId)
.and("userId", "=", SecurityUtil.getUserId()));
syncAnswerRecordSubjects(answerRecord2, activitySubjects);
answerRecordId = answerRecord2.getId();
@@ -77,14 +89,89 @@ public class H5QsvSurveyController {
return Result.success(result);
}
/**
* 校验调查活动是否处于可答题状态。activity 为空表示活动不存在;
* enabled 为 false 表示活动已关闭,当前时间早于 startTime 表示未开始,当前时间晚于 endTime 表示已结束。
*
* @param activity 当前调查活动,包含开启状态、开始时间和结束时间
* @return 状态或时间不允许答题时返回错误结果,允许答题时返回 null
*/
private Result checkActivityStatus(QsvActivity activity) {
if (activity == null) {
return Result.error("调查不存在");
}
if (Boolean.FALSE.equals(activity.getEnabled())) {
return Result.error("活动已关闭,暂不能参与");
}
Date now = new Date();
if (activity.getStartTime() != null && activity.getStartTime().after(now)) {
return Result.error("调查尚未开始,请在开始后再参与");
}
if (activity.getEndTime() != null && activity.getEndTime().before(now)) {
return Result.error("调查已结束");
}
return null;
}
/**
* 同步问卷当前题目到未完成答题记录。后台新增题目后,已进入过问卷的用户记录里没有新题ID,
* 这里补齐 subjectIds 和 extJson,保证手机端能拿到最新题目并正常提交答案。
*
* @param answerRecord 用户当前问卷答题记录
* @param activitySubjects 当前活动下按排序查询出的最新题目列表
*/
private void syncAnswerRecordSubjects(QsvUserAnswerRecord answerRecord, List<QsvSubject> activitySubjects) {
if (answerRecord == null || Boolean.TRUE.equals(answerRecord.getIsFinish())) {
return;
}
List<String> currentSubjectIds = activitySubjects.stream().map(QsvSubject::getId).collect(Collectors.toList());
List<String> recordSubjectIds = answerRecord.getSubjectIds();
if (recordSubjectIds == null) {
recordSubjectIds = new ArrayList<>();
}
JSONObject extJson = answerRecord.getExtJson();
if (extJson == null) {
extJson = new JSONObject();
}
boolean changed = !recordSubjectIds.equals(currentSubjectIds);
for (String subjectId : currentSubjectIds) {
if (ObjectUtil.isEmpty(extJson.get(subjectId, JSONObject.class))) {
JSONObject entry = new JSONObject();
entry.set("optionIds", new ArrayList<>());
entry.set("text", null);
entry.set("optionFillContents", new JSONObject());
extJson.set(subjectId, entry);
changed = true;
}
}
if (changed) {
answerRecord.setSubjectIds(currentSubjectIds);
answerRecord.setExtJson(extJson);
dao.update(answerRecord);
}
}
@At
@SaCheckLogin
public Result answerRecord(@Valid String answerRecordId) {
QsvUserAnswerRecord record = dao.fetch(QsvUserAnswerRecord.class, answerRecordId);
return Result.success(record);
}
@At
@SaCheckLogin
@Aop(TransAop.READ_COMMITTED)
public Result submitAnswer(@Valid @Param("answer") QsvAnswerParam qsvAnswerParam) {
QsvActivity activity = dao.fetch(QsvActivity.class, qsvAnswerParam.getActivityId());
Result activityStatusResult = checkActivityStatus(activity);
if (activityStatusResult != null) {
return activityStatusResult;
}
QsvUserAnswerRecord answerRecord = dao.fetch(QsvUserAnswerRecord.class, qsvAnswerParam.getAnswerRecordId());
JSONObject extJson = answerRecord.getExtJson();
@@ -93,6 +180,7 @@ public class H5QsvSurveyController {
if (ObjectUtil.isNotEmpty(entries)) {
entries.set("optionIds", subject.getUserSelectOptionIds());
entries.set("text", subject.getUserFillContent());
entries.set("optionFillContents", subject.getOptionFillContents());
}
extJson.set(subject.getId(), entries);
}
@@ -1,6 +1,8 @@
package com.budwk.app.zhgh.dayofficework.qsv.models;
import com.budwk.app.base.model.BaseModel;
import com.budwk.app.sys.models.Sys_home_activity;
import com.budwk.app.sys.services.SysHomeConvert;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.nutz.dao.entity.annotation.*;
@@ -14,7 +16,7 @@ import java.util.Date;
@Table("qsv_activity")
@TableMeta("{'mysql-charset':'utf8mb4'}")
@Comment("问卷调查投票活动表")
public class QsvActivity extends BaseModel implements Serializable {
public class QsvActivity extends BaseModel implements Serializable, SysHomeConvert {
@Name
@Comment("ID")
@ColDefine(type = ColType.VARCHAR, width = 32)
@@ -46,6 +48,12 @@ public class QsvActivity extends BaseModel implements Serializable {
@ColDefine(type = ColType.DATETIME)
private Date endTime;
@Column
@Comment("是否开启")
@ColDefine(type = ColType.BOOLEAN)
@Default("1")
private Boolean enabled;
@Column
@Comment("活动分组ID")
@ColDefine(type = ColType.INT)
@@ -107,4 +115,35 @@ public class QsvActivity extends BaseModel implements Serializable {
@Comment("封面图片")
@ColDefine(type = ColType.VARCHAR, width = 255)
private String cover;
@Override
public Sys_home_activity covertToSysHomeActivity() {
Sys_home_activity sysHomeActivity = new Sys_home_activity();
sysHomeActivity.setId(this.getId());
sysHomeActivity.setName(this.getTitle());
sysHomeActivity.setCover(this.getCover());
sysHomeActivity.setContent(this.getDescription());
sysHomeActivity.setUrl("/platform/qsv/activity");
sysHomeActivity.setH5Url(getH5Url());
sysHomeActivity.setStartDate(this.getStartTime());
sysHomeActivity.setEndDate(this.getEndTime());
sysHomeActivity.setAllowUserGroupId(this.getGroupId());
sysHomeActivity.setEnable(!Boolean.FALSE.equals(this.getEnabled()));
sysHomeActivity.setClassPath(this.getClass().getPackageName() + this.getClass().getName());
return sysHomeActivity;
}
/**
* 根据活动类型生成移动端首页跳转地址。
* QUIZ 跳转答题页,SURVEY 跳转调查页,VOTE 预留投票页地址,返回值为移动端路由字符串。
*/
private String getH5Url() {
if ("SURVEY".equals(this.getCategory())) {
return "/platform/h5/qsv/survey?id=" + this.getId();
}
if ("VOTE".equals(this.getCategory())) {
return "/platform/h5/qsv/vote?id=" + this.getId();
}
return "/platform/h5/qsv/quiz?id=" + this.getId();
}
}
@@ -51,6 +51,16 @@ public class QsvOption extends BaseModel implements Serializable {
@ColDefine(customType = "longtext")
private String description;
@Column
@Comment("选中后是否需要填写补充内容")
@ColDefine(type = ColType.BOOLEAN)
private Boolean fillRequired;
@Column
@Comment("补充内容输入提示")
@ColDefine(type = ColType.VARCHAR, width = 255)
private String fillPlaceholder;
@Column
@Comment("排序")
@ColDefine(type = ColType.INT)
@@ -63,6 +63,26 @@ public class QsvSubject extends BaseModel implements Serializable {
@ColDefine(type = ColType.INT, width = 1)
private Integer sortNum;
@Column
@Comment("是否启用题目隐显逻辑")
@ColDefine(type = ColType.BOOLEAN)
private Boolean visibleRuleEnabled;
@Column
@Comment("隐显逻辑处理方式(show显示,hide隐藏)")
@ColDefine(type = ColType.VARCHAR, width = 20)
private String visibleRuleAction;
@Column
@Comment("隐显逻辑条件关系(AND并且,OR或者)")
@ColDefine(type = ColType.VARCHAR, width = 20)
private String visibleRuleLogic;
@Column
@Comment("隐显逻辑条件列表")
@ColDefine(type = ColType.MYSQL_JSON)
private List<VisibleRuleCondition> visibleRuleConditions;
/**
* 选项
*/
@@ -79,4 +99,22 @@ public class QsvSubject extends BaseModel implements Serializable {
*/
private String userFillContent;
/**
* 题目隐显逻辑条件,一条条件表示选择指定题目的指定选项后参与显示或隐藏判断。
*/
@Data
public static class VisibleRuleCondition implements Serializable {
/**
* 作为触发条件的题目ID。
*/
private String subjectId;
/**
* 作为触发条件的选项ID。
*/
private String optionId;
}
}
@@ -3,6 +3,7 @@ package com.budwk.app.zhgh.dayofficework.qsv.param;
import lombok.Data;
import java.util.List;
import java.util.Map;
@Data
//答题参数
@@ -21,6 +22,7 @@ public class QsvAnswerParam {
private String id;
private List<String> userSelectOptionIds;
private String userFillContent;
private Map<String, String> optionFillContents;
}
}
@@ -5,4 +5,10 @@ import com.budwk.app.base.service.BaseService;
import com.budwk.app.zhgh.dayofficework.qsv.models.QsvActivity;
public interface QsvActivityService extends BaseService<QsvActivity> {
void saveActivity(QsvActivity qsvActivity);
void deleteActivity(String id);
void updateEnabled(String id, Boolean enabled);
}
@@ -1,9 +1,13 @@
package com.budwk.app.zhgh.dayofficework.qsv.service.impl;
import com.budwk.app.base.service.impl.BaseServiceImpl;
import com.budwk.app.sys.models.Sys_home_activity;
import com.budwk.app.zhgh.dayofficework.qsv.models.QsvActivity;
import com.budwk.app.zhgh.dayofficework.qsv.service.QsvActivityService;
import org.nutz.aop.interceptor.ioc.TransAop;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.ioc.aop.Aop;
import org.nutz.ioc.loader.annotation.IocBean;
@IocBean(args = {"refer:dao"})
@@ -12,4 +16,53 @@ public class QsvActivityServiceImpl extends BaseServiceImpl<QsvActivity> impleme
public QsvActivityServiceImpl(Dao dao) {
super(dao);
}
@Override
@Aop(TransAop.READ_COMMITTED)
public void saveActivity(QsvActivity qsvActivity) {
fillQuizRepeatMode(qsvActivity);
dao().insertOrUpdate(qsvActivity);
syncHomeActivity(qsvActivity);
}
@Override
@Aop(TransAop.READ_COMMITTED)
public void deleteActivity(String id) {
dao().delete(QsvActivity.class, id);
dao().delete(Sys_home_activity.class, id);
}
@Override
@Aop(TransAop.READ_COMMITTED)
public void updateEnabled(String id, Boolean enabled) {
QsvActivity activity = dao().fetch(QsvActivity.class, id);
if (activity == null) {
return;
}
activity.setEnabled(enabled);
dao().update(activity);
syncHomeActivity(activity);
}
/**
* 答题活动的重复模式由出题模式决定。定时定题按天统计,常规模式按活动总次数统计。
*/
private void fillQuizRepeatMode(QsvActivity qsvActivity) {
if (!"QUIZ".equals(qsvActivity.getCategory())) {
return;
}
if ("SCHEDULED".equals(qsvActivity.getMode())) {
qsvActivity.setRepeatMode("DAILY");
} else if ("REGULAR".equals(qsvActivity.getMode())) {
qsvActivity.setRepeatMode("TOTAL");
}
}
/**
* 同步移动端首页活动。问卷活动保存或启停后,都以活动自身 enabled、时间、人员范围生成首页记录。
*/
private void syncHomeActivity(QsvActivity activity) {
Sys_home_activity sysHomeActivity = activity.covertToSysHomeActivity();
dao().insertOrUpdate(sysHomeActivity);
}
}
@@ -68,47 +68,47 @@ public class QsvSurveyServiceImpl extends BaseServiceImpl<QsvUserAnswerRecord> i
// 处理每个题目
for (NutMap subject : subjects) {
// 获取当前题目的选项列表
List<NutMap> subjectOptions = Lang.collection2list(optionsGroup.get(subject.getString("id")), NutMap.class);
List<QsvOption> currentOptions = optionsGroup.get(subject.getString("id"));
List<NutMap> subjectOptions = ObjectUtil.isEmpty(currentOptions) ? new ArrayList<>() : Lang.collection2list(currentOptions, NutMap.class);
// 获取题目类型
String subjectType = subject.getString("type");
// 处理文本类型题目
if ("text".equals(subjectType)) {
List<String> texts = answerExtList.stream()
.filter(ext -> ObjectUtil.isNull(ext.get(subject.getString("id"), JSONObject.class)))
.map(ext -> ext.get(subject.getString("id"), JSONObject.class).getStr("text"))
List<NutMap> textAnswers = buildTextAnswerDetails(answerRecords, subject.getString("id"));
List<String> texts = textAnswers.stream()
.map(textAnswer -> textAnswer.getString("text"))
.toList();
subject.put("texts", texts);
subject.put("textAnswers", textAnswers);
}
// 处理单选类型题目 处理多选类型题目
else if ("radio".equals(subjectType) || "checkbox".equals(subjectType)) {
long selectTotal = answerExtList.stream()
.filter(ext -> ext.containsKey(subject.getString("id")))
.filter(ext -> ObjectUtil.isNotEmpty(ext) && ext.containsKey(subject.getString("id")))
.count();
subjectOptions.forEach(subjectOption -> {
long selectCount = answerExtList.stream()
.filter(ext ->
{
if (ObjectUtil.isEmpty(ext)) {
return false;
}
JSONObject jsonObject = ext.get(subject.getString("id"), JSONObject.class);
return jsonObject != null
&& jsonObject.getJSONArray("optionIds") != null
&& jsonObject.getJSONArray("optionIds").contains(subjectOption.getString("id"));
})
.count();
long round = Math.round((double) selectCount / selectTotal * 100);
long round = selectTotal == 0 ? 0 : Math.round((double) selectCount / selectTotal * 100);
subjectOption.put("selectPercent", round + "%");
subjectOption.put("selectCount", selectCount);
});
subjectOptions.sort((o1, o2) -> {
int count1 = o1.getInt("selectCount");
int count2 = o2.getInt("selectCount");
if (count1 == count2) {
return Integer.compare(o1.getInt("sortNum"), o2.getInt("sortNum"));
}
return Integer.compare(count2, count1);
return Integer.compare(o1.getInt("sortNum"), o2.getInt("sortNum"));
});
subject.put("selectTotal", selectTotal);
@@ -134,10 +134,8 @@ public class QsvSurveyServiceImpl extends BaseServiceImpl<QsvUserAnswerRecord> i
exportEntities.add(new ExcelExportEntity("选择人数", "selectCount", 20));
exportEntities.add(new ExcelExportEntity("选择比例", "selectPercent", 20));
Map<String, List<NutMap>> listMap = report.stream().collect(Collectors.groupingBy(v -> v.getString("id")));
Workbook workbook = new XSSFWorkbook();
listMap.forEach((k, v) -> {
NutMap nutMap = v.get(0);
report.forEach(nutMap -> {
ExcelExportService service = new ExcelExportService();
ExportParams exportParams = new ExportParams();
exportParams.setTitle(nutMap.getString("title"));
@@ -171,7 +169,7 @@ public class QsvSurveyServiceImpl extends BaseServiceImpl<QsvUserAnswerRecord> i
List<JSONObject> answerExtList = answerRecords.stream().map(QsvUserAnswerRecord::getExtJson).toList();
// 查询活动题目列表
List<QsvSubject> subjects = dao().query(QsvSubject.class, Cnd.where("activityId", "=", activityId));
List<QsvSubject> subjects = dao().query(QsvSubject.class, Cnd.where("activityId", "=", activityId).asc("sortNum"));
// 将题目列表转换为Map
Map<String, QsvSubject> subjectMap = subjects.stream().collect(Collectors.toMap(QsvSubject::getId, v -> v));
// 获取题目ID列表
@@ -185,7 +183,7 @@ public class QsvSurveyServiceImpl extends BaseServiceImpl<QsvUserAnswerRecord> i
// 构建Excel导出实体
List<ExcelExportEntity> excelExportEntities = buildExcelExportEntities(subjects);
// 构建答题记录列表
List<NutMap> list = buildAnswerRecords(answerRecords, subjectMap, options);
List<NutMap> list = buildAnswerRecords(answerRecords, subjectMap, optionMap);
// 设置导出参数
ExportParams exportParams = new ExportParams();
@@ -223,7 +221,7 @@ public class QsvSurveyServiceImpl extends BaseServiceImpl<QsvUserAnswerRecord> i
// List<JSONObject> answerExtList = answerRecords.stream().map(QsvUserAnswerRecord::getExtJson).toList();
// 查询活动题目列表
List<QsvSubject> subjects = dao().query(QsvSubject.class, Cnd.where("activityId", "=", activityId));
List<QsvSubject> subjects = dao().query(QsvSubject.class, Cnd.where("activityId", "=", activityId).asc("sortNum"));
// 将题目列表转换为Map
Map<String, QsvSubject> subjectMap = subjects.stream().collect(Collectors.toMap(QsvSubject::getId, v -> v));
// 获取题目ID列表
@@ -245,18 +243,15 @@ public class QsvSurveyServiceImpl extends BaseServiceImpl<QsvUserAnswerRecord> i
.addv("unitName", record.getString("unitName"))
.addv("unionName", record.getString("unionName"));
JSONObject extJson = record.getAs("extJson", JSONObject.class);
if (ObjectUtil.isEmpty(extJson)) {
return map;
}
extJson.forEach((k, v) -> {
JSONObject jsonVal = (JSONObject) v;
String type = subjectMap.get(k).getType();
if (type.equals("text")) {
map.addv(k, jsonVal.getStr("text"));
} else if (type.equals("radio") || type.equals("checkbox")) {
JSONArray optionIds = jsonVal.getJSONArray("optionIds");
String selectOptionTexts = options.stream().filter(option -> optionIds.contains(option.getId()))
.map(QsvOption::getText).collect(Collectors.joining(";"));
map.addv(k, selectOptionTexts);
QsvSubject subject = subjectMap.get(k);
if (subject == null || !(v instanceof JSONObject jsonVal)) {
return;
}
map.addv(k, buildAnswerText(jsonVal, subject, optionMap));
});
return map;
}).toList();
@@ -309,7 +304,37 @@ public class QsvSurveyServiceImpl extends BaseServiceImpl<QsvUserAnswerRecord> i
return excelExportEntities;
}
private List<NutMap> buildAnswerRecords(List<QsvUserAnswerRecord> answerRecords, Map<String, QsvSubject> subjectMap, List<QsvOption> options) {
/**
* 构建填空题填写详情。分析弹窗需要同时展示填写内容和答题人信息,
* 因此这里从已完成答题记录中提取当前题目的文本答案,并携带姓名、工号、所属工会和单位。
*
* @param answerRecords 当前活动已完成的答题记录
* @param subjectId 当前填空题ID,用于从 extJson 中取出该题填写内容
* @return 填空题填写详情列表,每条包含 loginName、userName、unionName、unitName、submitTime、text
*/
private List<NutMap> buildTextAnswerDetails(List<QsvUserAnswerRecord> answerRecords, String subjectId) {
List<NutMap> textAnswers = new ArrayList<>();
for (QsvUserAnswerRecord answerRecord : answerRecords) {
JSONObject extJson = answerRecord.getExtJson();
if (ObjectUtil.isEmpty(extJson)) {
continue;
}
JSONObject subjectAnswer = extJson.get(subjectId, JSONObject.class);
if (ObjectUtil.isEmpty(subjectAnswer) || ObjectUtil.isEmpty(subjectAnswer.getStr("text"))) {
continue;
}
textAnswers.add(NutMap.NEW()
.addv("loginName", answerRecord.getLoginName())
.addv("userName", answerRecord.getUserName())
.addv("unionName", answerRecord.getUnionName())
.addv("unitName", answerRecord.getUnitName())
.addv("submitTime", answerRecord.getSubmitTime())
.addv("text", subjectAnswer.getStr("text")));
}
return textAnswers;
}
private List<NutMap> buildAnswerRecords(List<QsvUserAnswerRecord> answerRecords, Map<String, QsvSubject> subjectMap, Map<String, QsvOption> optionMap) {
return answerRecords.stream().map(record -> {
NutMap map = NutMap.NEW()
.addv("id", record.getId())
@@ -318,20 +343,61 @@ public class QsvSurveyServiceImpl extends BaseServiceImpl<QsvUserAnswerRecord> i
.addv("unitName", record.getUnitName())
.addv("unionName", record.getUnionName());
JSONObject extJson = record.getExtJson();
if (ObjectUtil.isEmpty(extJson)) {
return map;
}
extJson.forEach((k, v) -> {
JSONObject jsonVal = (JSONObject) v;
String type = subjectMap.get(k).getType();
if (type.equals("text")) {
map.addv(k, jsonVal.getStr("text"));
} else if (type.equals("radio") || type.equals("checkbox")) {
JSONArray optionIds = jsonVal.getJSONArray("optionIds");
String selectOptionTexts = options.stream().filter(option -> optionIds.contains(option.getId()))
.map(QsvOption::getText).collect(Collectors.joining(";"));
map.addv(k, selectOptionTexts);
QsvSubject subject = subjectMap.get(k);
if (subject == null || !(v instanceof JSONObject jsonVal)) {
return;
}
map.addv(k, buildAnswerText(jsonVal, subject, optionMap));
});
return map;
}).collect(Collectors.toList());
}
/**
* 组装单题答案展示内容。填空题返回填写文本;选择题返回选项文本,若选项配置了补充填写,
* 则追加对应补充内容,保证后台查看答卷和导出能看到用户填写的说明。
*
* @param jsonVal 答题记录 extJson 中当前题目的答案对象,包含 optionIds、text、optionFillContents
* @param subject 当前题目,用于判断题目类型
* @param optionMap 当前活动所有选项,key 为选项ID,value 为选项实体
* @return 当前题目的答案展示文本
*/
private String buildAnswerText(JSONObject jsonVal, QsvSubject subject, Map<String, QsvOption> optionMap) {
String type = subject.getType();
if ("text".equals(type)) {
return jsonVal.getStr("text");
}
if (!"radio".equals(type) && !"checkbox".equals(type)) {
return "";
}
JSONArray optionIds = jsonVal.getJSONArray("optionIds");
if (ObjectUtil.isEmpty(optionIds)) {
return "";
}
JSONObject optionFillContents = jsonVal.getJSONObject("optionFillContents");
List<String> answerTexts = new ArrayList<>();
for (Object optionIdObj : optionIds) {
String optionId = String.valueOf(optionIdObj);
QsvOption option = optionMap.get(optionId);
if (option == null) {
continue;
}
String answerText = option.getText();
if (ObjectUtil.isNotEmpty(optionFillContents)) {
String fillContent = optionFillContents.getStr(optionId);
if (ObjectUtil.isNotEmpty(fillContent)) {
answerText = answerText + ":" + fillContent;
}
}
answerTexts.add(answerText);
}
return String.join(";", answerTexts);
}
}
@@ -1,6 +1,7 @@
package com.budwk.app.zhgh.democratic.appeal.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.annotation.SaMode;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.lang.Dict;
@@ -49,8 +50,14 @@ public class AppealAcceptController {
public void index() {
}
@At("/h5")
@Ok("beetl:/platform/zhghh5/democratic/appeal/accept/index.html")
@SaCheckPermission("h5.appeal.accept")
public void h5Index() {
}
@At
@SaCheckPermission("appeal.accept")
@SaCheckPermission(value = {"appeal.accept", "h5.appeal.accept"}, mode = SaMode.OR)
public Result pageData(Integer pageNumber, Integer pageSize, String searchKeyword, String appealTypeId,
Integer year, boolean approval) {
Sql sql = listSql();
@@ -68,7 +75,7 @@ public class AppealAcceptController {
}
@At
@SaCheckPermission("appeal.accept")
@SaCheckPermission(value = {"appeal.accept", "h5.appeal.accept"}, mode = SaMode.OR)
@ApiOperation("查询受理任务")
public Result queryTask(@Param("taskId") Long taskId) {
Sql sql = listSql();
@@ -77,7 +84,7 @@ public class AppealAcceptController {
}
@At
@SaCheckPermission("appeal.accept")
@SaCheckPermission(value = {"appeal.accept", "h5.appeal.accept"}, mode = SaMode.OR)
@ApiOperation("受理申诉")
@Aop(TransAop.READ_COMMITTED)
@SLog(tag = "申诉管理-受理", msg = "受理申诉")
@@ -1,6 +1,7 @@
package com.budwk.app.zhgh.democratic.appeal.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.annotation.SaMode;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.lang.Dict;
@@ -49,8 +50,14 @@ public class AppealExecuteController {
public void index() {
}
@At("/h5")
@Ok("beetl:/platform/zhghh5/democratic/appeal/execute/index.html")
@SaCheckPermission("h5.appeal.execute")
public void h5Index() {
}
@At
@SaCheckPermission("appeal.execute")
@SaCheckPermission(value = {"appeal.execute", "h5.appeal.execute"}, mode = SaMode.OR)
public Result pageData(Integer pageNumber, Integer pageSize, String searchKeyword, String appealTypeId,
Integer year, boolean approval) {
Sql sql = listSql();
@@ -68,7 +75,7 @@ public class AppealExecuteController {
}
@At
@SaCheckPermission("appeal.execute")
@SaCheckPermission(value = {"appeal.execute", "h5.appeal.execute"}, mode = SaMode.OR)
@ApiOperation("查询执行任务")
public Result queryTask(@Param("taskId") Long taskId) {
Sql sql = listSql();
@@ -77,7 +84,7 @@ public class AppealExecuteController {
}
@At
@SaCheckPermission("appeal.execute")
@SaCheckPermission(value = {"appeal.execute", "h5.appeal.execute"}, mode = SaMode.OR)
@ApiOperation("提交执行情况")
@Aop(TransAop.READ_COMMITTED)
@SLog(tag = "申诉管理-执行", msg = "提交执行情况")
@@ -0,0 +1,19 @@
package com.budwk.app.zhgh.democratic.appeal.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import io.swagger.annotations.Api;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
@IocBean
@At("/platform/appeal")
@Api("申诉管理-H5入口")
public class AppealH5Controller {
@At("/h5")
@Ok("beetl:/platform/zhghh5/democratic/appeal/index.html")
@SaCheckPermission("h5.appeal")
public void h5Index() {
}
}
@@ -1,6 +1,7 @@
package com.budwk.app.zhgh.democratic.appeal.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.annotation.SaMode;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.result.Result;
@@ -38,8 +39,14 @@ public class AppealMineController {
public void index() {
}
@At("/h5")
@Ok("beetl:/platform/zhghh5/democratic/appeal/mine/index.html")
@SaCheckPermission("h5.appeal.mine")
public void h5Index() {
}
@At
@SaCheckPermission("appeal.mine")
@SaCheckPermission(value = {"appeal.mine", "h5.appeal.mine"}, mode = SaMode.OR)
public Result pageData(Integer pageNumber, Integer pageSize, String searchKeyword, String appealTypeId, Integer year) {
Sql sql = Sqls.create("""
SELECT
@@ -72,7 +79,7 @@ public class AppealMineController {
}
@At
@SaCheckPermission("appeal.mine")
@SaCheckPermission(value = {"appeal.mine", "h5.appeal.mine"}, mode = SaMode.OR)
@Aop(TransAop.READ_COMMITTED)
@ApiOperation("删除申诉")
public Result delete(@Param("id") String id) {
@@ -4,17 +4,23 @@ import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.result.Result;
import com.budwk.app.flow.engine.FlowEngine;
import com.budwk.app.flow.entity.ProcessInstance;
import com.budwk.app.flow.enums.ProcessInstanceStateEnum;
import com.budwk.app.zhgh.democratic.appeal.service.AppealInfoService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.nutz.aop.interceptor.ioc.TransAop;
import org.nutz.dao.Cnd;
import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Sql;
import org.nutz.ioc.aop.Aop;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.util.NutMap;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
import org.nutz.mvc.annotation.Param;
@IocBean
@At("/platform/appeal/query")
@@ -24,6 +30,8 @@ public class AppealQueryController {
@Inject
private AppealInfoService appealInfoService;
@Inject
private FlowEngine flowEngine;
@At("")
@Ok("beetl:/platform/zhgh/democratic/appeal/query/index.html")
@@ -72,6 +80,20 @@ public class AppealQueryController {
return Result.success(data);
}
@At
@SaCheckPermission("appeal.query")
@Aop(TransAop.READ_COMMITTED)
@ApiOperation("删除申诉")
public Result delete(@Param("id") String id) {
ProcessInstance instance = flowEngine.processInstanceService().fetch(Cnd.where(ProcessInstance::getBusinessNo, "=", id));
if (instance == null || !ProcessInstanceStateEnum.FINISHED.getCode().equals(instance.getState())) {
return Result.error("仅可删除已完成的申诉记录");
}
appealInfoService.delete(id);
flowEngine.processInstanceService().deleteProcessInstanceByBusinessKey(id);
return Result.success();
}
private Cnd buildCondition(String searchKeyword, String appealTypeId, String state, String unitId,
String defendantUnitId, Integer year) {
Cnd cnd = Cnd.NEW();
@@ -1,6 +1,7 @@
package com.budwk.app.zhgh.democratic.appeal.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.annotation.SaMode;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.annotation.SLog;
@@ -57,8 +58,14 @@ public class AppealWriteController {
public void index() {
}
@At("/h5")
@Ok("beetl:/platform/zhghh5/democratic/appeal/write/index.html")
@SaCheckPermission("h5.appeal.write")
public void h5Index() {
}
@At
@SaCheckPermission("appeal.write")
@SaCheckPermission(value = {"appeal.write", "h5.appeal.write"}, mode = SaMode.OR)
@ApiOperation("当前用户信息")
public Result currentUserInfo() {
View_user user = dao.fetch(View_user.class, Cnd.where("id", "=", SecurityUtil.getUserId()));
@@ -85,14 +92,14 @@ public class AppealWriteController {
}
@At
@SaCheckPermission("appeal.write")
@SaCheckPermission(value = {"appeal.write", "h5.appeal.write"}, mode = SaMode.OR)
@ApiOperation("获取申诉信息")
public Result info(@Param("id") String id) {
return Result.success(appealInfoService.fetch(id));
}
@At
@SaCheckPermission("appeal.write")
@SaCheckPermission(value = {"appeal.write", "h5.appeal.write"}, mode = SaMode.OR)
@ApiOperation("保存申诉")
@SLog(tag = "申诉管理-提交申诉", msg = "保存申诉")
public Result save(@Param("data") AppealInfo appealInfo) {
@@ -102,7 +109,7 @@ public class AppealWriteController {
}
@At
@SaCheckPermission("appeal.write")
@SaCheckPermission(value = {"appeal.write", "h5.appeal.write"}, mode = SaMode.OR)
@ApiOperation("提交申诉")
@Aop(TransAop.READ_COMMITTED)
@SLog(tag = "申诉管理-提交申诉", msg = "提交申诉")
@@ -130,7 +137,7 @@ public class AppealWriteController {
}
@At
@SaCheckPermission("appeal.write")
@SaCheckPermission(value = {"appeal.write", "h5.appeal.write"}, mode = SaMode.OR)
@ApiOperation("重新提交申诉")
@Aop(TransAop.READ_COMMITTED)
@SLog(tag = "申诉管理-提交申诉", msg = "重新提交申诉")
@@ -141,7 +141,7 @@ public class ProposalUnderTakeReplyController {
List<NutMap> list = pagination.getList();
for (NutMap row : list) {
if (row.getInt("taskIsMaster") == 1) {
boolean b = Stream.of("master_reply", "slave_reply", "school_leader")
boolean b = Stream.of("master_reply", "slave_reply", "feedback")
.anyMatch(a -> row.getString("curTaskKey").contains(a));
row.setv("canRevoke", b && row.getInt("taskState") == ProcessTaskStateEnum.FINISHED.getCode());
} else {
@@ -115,6 +115,7 @@ public class LegalApplyController {
Cnd.where(LegalApply::getDoctorUser, "=", nutMap.getString("userId"))
.and(LegalApply::getStartTime, ">=", DateUtil.now())
.and(LegalApply::getUserId, "is", null)
.asc(LegalApply::getStartTimeTs)
);
nutMap.put("times", applyList);
}
@@ -114,6 +114,7 @@ public class PsychologyApplyController {
Cnd.where(PsychologyApply::getDoctorUser, "=", nutMap.getString("userId"))
.and(PsychologyApply::getStartTime, ">=", DateUtil.now())
.and(PsychologyApply::getUserId, "is", null)
.asc(PsychologyApply::getStartTimeTs)
);
nutMap.put("times", applyList);
}
@@ -59,7 +59,7 @@ public class ChildManageManageController {
public void index() {
}
@At("/h5")
@Ok("beetl:/platform/zhghh5/staffmanage/childManage/manage/index.html")
@Ok("beetl:/platform/zhghh5/staffmanage/childmanage/manage/index.html")
@SaCheckPermission("h5.childManage.manage")
public void h5Index() {
}
@@ -39,7 +39,7 @@ public class ChildManageMineController {
public void index() {
}
@At("/h5")
@Ok("beetl:/platform/zhghh5/staffmanage/childManage/mine/index.html")
@Ok("beetl:/platform/zhghh5/staffmanage/childmanage/mine/index.html")
@SaCheckPermission("h5.childManage.mine")
public void h5Index() {
}
@@ -50,7 +50,7 @@ public class ChildManageWriteController {
public void index() {
}
@At("/h5")
@Ok("beetl:/platform/zhghh5/staffmanage/childManage/write/index.html")
@Ok("beetl:/platform/zhghh5/staffmanage/childmanage/write/index.html")
@SaCheckPermission("h5.childManage.write")
public void h5Index() {
}
@@ -0,0 +1,110 @@
package com.budwk.app.zhgh.staffmanage.retired.group.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import com.budwk.app.base.param.PageForm;
import com.budwk.app.base.result.Result;
import com.budwk.app.zhgh.staffmanage.retired.group.models.RetiredGroup;
import com.budwk.app.zhgh.staffmanage.retired.group.service.RetiredGroupService;
import lombok.extern.slf4j.Slf4j;
import org.nutz.aop.interceptor.ioc.TransAop;
import org.nutz.ioc.aop.Aop;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
import org.nutz.mvc.annotation.Param;
@Slf4j
@IocBean
@Ok("json:full")
@At("/platform/retired/group/manage")
public class RetiredGroupManageController {
@Inject
private RetiredGroupService retiredGroupService;
@At("")
@Ok("beetl:/platform/zhgh/staffmanage/retired/group/manage/index.html")
@SaCheckPermission("retired.group.manage")
public void index() {
}
@At
@SaCheckPermission("retired.group.manage")
public Result getTree() {
return Result.success(retiredGroupService.getTree());
}
@At
@SaCheckPermission("retired.group.manage")
public Result doSetGroup(@Param("..") RetiredGroup retiredGroup) {
retiredGroupService.saveGroup(retiredGroup);
return Result.success();
}
@At
@SaCheckPermission("retired.group.manage")
public Result groupPageData(PageForm pageForm) {
return Result.success(retiredGroupService.groupPageData(pageForm));
}
@At
@Aop(TransAop.READ_COMMITTED)
@SaCheckPermission("retired.group.manage")
public Result deleteGroup(String id) {
retiredGroupService.deleteGroup(id);
return Result.success();
}
@At
@SaCheckPermission("retired.group.manage")
public Result componentUnitPageData(PageForm pageForm, String groupId) {
return Result.success(retiredGroupService.componentUnitPageData(pageForm, groupId));
}
@At
@SaCheckPermission("retired.group.manage")
public Result groupComponentUnit(String groupId) {
return Result.success(retiredGroupService.groupComponentUnit(groupId));
}
@At
@SaCheckPermission("retired.group.manage")
public Result notSetUnit(String groupId) {
return Result.success(retiredGroupService.notSetUnit(groupId));
}
@At
@Aop(TransAop.READ_COMMITTED)
@SaCheckPermission("retired.group.manage")
public Result doSetComponentUnit(String groupId, @Param("units") String[] units) {
retiredGroupService.setComponentUnit(groupId, units);
return Result.success();
}
@At
@SaCheckPermission("retired.group.manage")
public Result groupUser(PageForm pageForm, String groupId) {
return Result.success(retiredGroupService.groupUser(pageForm, groupId));
}
@At
@Aop(TransAop.READ_COMMITTED)
@SaCheckPermission("retired.group.manage")
public Result setGroupLeader(String userId, String groupId) {
retiredGroupService.setGroupLeader(userId, groupId);
return Result.success();
}
@At
@Aop(TransAop.READ_COMMITTED)
@SaCheckPermission("retired.group.manage")
public Result removeGroupLeader(String userId, String groupId) {
retiredGroupService.removeGroupLeader(userId, groupId);
return Result.success();
}
}
@@ -0,0 +1,43 @@
package com.budwk.app.zhgh.staffmanage.retired.group.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import com.budwk.app.base.result.Result;
import com.budwk.app.zhgh.staffmanage.retired.group.service.RetiredGroupService;
import lombok.extern.slf4j.Slf4j;
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;
@Slf4j
@IocBean
@Ok("json:full")
@At("/platform/retired/group/statistics")
public class RetiredGroupStatisticsController {
@Inject
private RetiredGroupService retiredGroupService;
@At("")
@Ok("beetl:/platform/zhgh/staffmanage/retired/group/statistics/index.html")
@SaCheckPermission("retired.group.statistics")
public void index() {
}
@At
@SaCheckPermission("retired.group.statistics")
public Result data(@Param("retiredGroupId") String retiredGroupId) {
return Result.success(retiredGroupService.statisticsData(retiredGroupId));
}
@At
@SaCheckPermission("retired.group.statistics")
public Result retiredGroup() {
return Result.success(retiredGroupService.options());
}
}
@@ -0,0 +1,42 @@
package com.budwk.app.zhgh.staffmanage.retired.group.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.Many;
import org.nutz.dao.entity.annotation.Name;
import org.nutz.dao.entity.annotation.Table;
import org.nutz.dao.interceptor.annotation.PrevInsert;
import java.util.List;
@Data
@EqualsAndHashCode(callSuper = true)
@Table("retired_group")
@Comment("离退休小组")
public class RetiredGroup extends BaseModel {
@Name
@Comment("ID")
@ColDefine(type = ColType.VARCHAR, width = 32)
@PrevInsert(uu32 = true)
private String id;
@Column
@Comment("小组名称")
@ColDefine(type = ColType.VARCHAR, width = 50)
private String groupName;
@Column
@Comment("小组代码")
@ColDefine(type = ColType.INT)
private Integer groupCode;
@Many(target = RetiredGroupUnit.class, field = "retiredGroupId", key = "id")
private List<RetiredGroupUnit> retiredGroupUnitList;
}
@@ -0,0 +1,36 @@
package com.budwk.app.zhgh.staffmanage.retired.group.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
@EqualsAndHashCode(callSuper = true)
@Table("retired_group_unit")
@Comment("离退休小组组成单位")
public class RetiredGroupUnit extends BaseModel {
@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 retiredGroupId;
@Column
@Comment("单位ID")
@ColDefine(type = ColType.VARCHAR, width = 32)
private String unitId;
}
@@ -0,0 +1,52 @@
package com.budwk.app.zhgh.staffmanage.retired.group.service;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.param.PageForm;
import com.budwk.app.base.service.BaseService;
import com.budwk.app.zhgh.staffmanage.retired.group.models.RetiredGroup;
import org.nutz.lang.util.NutMap;
import java.util.List;
public interface RetiredGroupService extends BaseService<RetiredGroup> {
/** 查询离退休小组树。 */
List<NutMap> getTree();
/** 新增或修改离退休小组。 */
void saveGroup(RetiredGroup retiredGroup);
/** 分页查询离退休小组。 */
Pagination groupPageData(PageForm pageForm);
/** 删除离退休小组及其单位关联。 */
void deleteGroup(String id);
/** 分页查询小组组成单位。 */
Pagination componentUnitPageData(PageForm pageForm, String groupId);
/** 查询小组已配置单位 ID。 */
List<String> groupComponentUnit(String groupId);
/** 查询可配置到当前小组的单位。 */
List<NutMap> notSetUnit(String groupId);
/** 保存小组组成单位。 */
void setComponentUnit(String groupId, String[] units);
/** 分页查询小组覆盖的离退休人员。 */
Pagination groupUser(PageForm pageForm, String groupId);
/** 设置小组负责人。 */
void setGroupLeader(String userId, String groupId);
/** 取消小组负责人。 */
void removeGroupLeader(String userId, String groupId);
/** 查询小组统计数据。 */
List<NutMap> statisticsData(String retiredGroupId);
/** 查询全部小组选项。 */
List<RetiredGroup> options();
}
@@ -0,0 +1,222 @@
package com.budwk.app.zhgh.staffmanage.retired.group.service.impl;
import cn.dev33.satoken.stp.StpUtil;
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.service.impl.BaseServiceImpl;
import com.budwk.app.sys.models.Sys_user_role;
import com.budwk.app.web.commons.auth.utils.AuthUtil;
import com.budwk.app.zhgh.staffmanage.retired.group.models.RetiredGroup;
import com.budwk.app.zhgh.staffmanage.retired.group.models.RetiredGroupUnit;
import com.budwk.app.zhgh.staffmanage.retired.group.service.RetiredGroupService;
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.Daos;
import org.nutz.dao.util.cri.SqlExpressionGroup;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.Lang;
import org.nutz.lang.util.NutMap;
import java.util.Arrays;
import java.util.List;
@IocBean(args = {"refer:dao"})
public class RetiredGroupServiceImpl extends BaseServiceImpl<RetiredGroup> implements RetiredGroupService {
private static final String GROUP_LEADER_ROLE_CODE = "BRANCH_UNION_GROUP_LEADER";
private static final List<String> RETIRED_STATES = List.of("退休", "离休", "离退休");
public RetiredGroupServiceImpl(Dao dao) {
super(dao);
}
@Override
public List<NutMap> getTree() {
NutMap topMap = NutMap.NEW().addv("label", "离退休小组").addv("id", "1");
Sql sql = Sqls.create("select id, groupName as label from retired_group $condition");
Cnd cnd = Cnd.NEW();
cnd.asc("groupCode");
appendGroupScope(cnd, "id");
sql.setCondition(cnd);
topMap.put("children", listMap(sql));
return List.of(topMap);
}
@Override
public void saveGroup(RetiredGroup retiredGroup) {
dao().insertOrUpdate(retiredGroup);
}
@Override
public Pagination groupPageData(PageForm pageForm) {
Sql sql = Sqls.create("select * from retired_group $condition");
Cnd cnd = Cnd.NEW();
appendGroupScope(cnd, "id");
if (StrUtil.isNotBlank(pageForm.getSearchKeyword())) {
SqlExpressionGroup seg = new SqlExpressionGroup();
seg.orLike("groupName", pageForm.getSearchKeyword());
seg.orLike("groupCode", pageForm.getSearchKeyword());
cnd.and(seg);
}
cnd.asc("groupCode");
sql.setCondition(cnd);
return listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql);
}
@Override
public void deleteGroup(String id) {
dao().clear(RetiredGroup.class, Cnd.where("id", "=", id));
dao().clear(RetiredGroupUnit.class, Cnd.where("retiredGroupId", "=", id));
}
@Override
public Pagination componentUnitPageData(PageForm pageForm, String groupId) {
Sql sql = Sqls.create("select * from sys_unit $condition");
Cnd cnd = Cnd.NEW();
cnd.and("id", "in", Sqls.create("select unitId from retired_group_unit where retiredGroupId = @retiredGroupId").setParam("retiredGroupId", groupId));
if (StrUtil.isNotBlank(pageForm.getSearchKeyword())) {
cnd.and(Cnd.likeEX("name", pageForm.getSearchKeyword()));
}
sql.setCondition(cnd);
return listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql);
}
@Override
public List<String> groupComponentUnit(String groupId) {
Sql sql = Sqls.create("select unitId from retired_group_unit where retiredGroupId = @groupId").setParam("groupId", groupId);
return (List<String>) Daos.query(dao(), sql.toString(), Sqls.callback.strs());
}
@Override
public List<NutMap> notSetUnit(String groupId) {
Sql sql = Sqls.create("""
select id, name, unitcode
from sys_unit
$condition
""");
Cnd cnd = Cnd.NEW();
cnd.or("id", "in", Sqls.create("select unitId from retired_group_unit where retiredGroupId = @retiredGroupId").setParam("retiredGroupId", groupId));
cnd.or("id", "not in", Sqls.create("select unitId from retired_group_unit where retiredGroupId != @retiredGroupId").setParam("retiredGroupId", groupId));
cnd.and("unitLevel", "=", 2);
cnd.asc("unitcode");
sql.setCondition(cnd);
return listMap(sql);
}
@Override
public void setComponentUnit(String groupId, String[] units) {
dao().clear(RetiredGroupUnit.class, Cnd.where("retiredGroupId", "=", groupId));
if (units == null || units.length == 0) {
return;
}
List<RetiredGroupUnit> insertGroupUnits = Arrays.stream(units).map(v -> {
RetiredGroupUnit groupUnit = new RetiredGroupUnit();
groupUnit.setRetiredGroupId(groupId);
groupUnit.setUnitId(v);
return groupUnit;
}).toList();
dao().insert(insertGroupUnits);
}
@Override
public Pagination groupUser(PageForm pageForm, String groupId) {
Sql sql = Sqls.create("""
SELECT
u.id,
u.loginname,
u.username,
u.sex,
DATE(u.birthday) AS birthday,
u.mobile,
u.personType,
u.userState,
u.unitName as unitname,
u.unionName as unionname,
GROUP_CONCAT(DISTINCT r.code) as roleCode,
IFNULL(GROUP_CONCAT(DISTINCT r.name), '组员') as roleName
FROM vw_user u
LEFT JOIN sys_user_role sur ON sur.userId = u.id AND sur.unionGroupId = @groupId
LEFT JOIN sys_role r ON r.id = sur.roleId
$condition
""");
sql.setParam("groupId", groupId);
Cnd cnd = Cnd.NEW();
cnd.and("u.unitId", "in", Sqls.create("select unitId from retired_group_unit where retiredGroupId = @retiredGroupId").setParam("retiredGroupId", groupId));
cnd.and("u.userState", "in", RETIRED_STATES);
if (StrUtil.isNotBlank(pageForm.getSearchKeyword())) {
SqlExpressionGroup seg = new SqlExpressionGroup();
seg.orLike("u.username", pageForm.getSearchKeyword());
seg.orLike("u.loginname", pageForm.getSearchKeyword());
cnd.and(seg);
}
cnd.groupBy("u.id");
cnd.desc("r.name");
sql.setCondition(cnd);
return listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql);
}
@Override
public void setGroupLeader(String userId, String groupId) {
String roleId = groupLeaderRoleId();
dao().clear(Sys_user_role.class, Cnd.where("userId", "=", userId).and("roleId", "=", roleId));
Sys_user_role role = new Sys_user_role();
role.setUserId(userId);
role.setRoleId(roleId);
role.setUnionGroupId(groupId);
dao().insert(role);
}
@Override
public void removeGroupLeader(String userId, String groupId) {
String roleId = groupLeaderRoleId();
dao().clear(Sys_user_role.class, Cnd.where("userId", "=", userId)
.and("roleId", "=", roleId)
.and("unionGroupId", "=", groupId));
}
@Override
public List<NutMap> statisticsData(String retiredGroupId) {
Sql sql = Sqls.create("""
SELECT
g.id,
g.groupName,
g.groupCode,
COUNT(u.userState) AS retireCount,
GROUP_CONCAT(DISTINCT n.name) as componentUnit
FROM retired_group g
LEFT JOIN retired_group_unit rgu ON rgu.retiredGroupId = g.id
LEFT JOIN sys_unit n on n.id = rgu.unitId
LEFT JOIN vw_user u ON u.unitId = rgu.unitId AND u.userState IN ('退休', '离休', '离退休')
$condition
""");
Cnd cnd = Cnd.NEW();
cnd.andEX("g.id", "=", retiredGroupId);
appendGroupScope(cnd, "g.id");
cnd.groupBy("g.id", "g.groupName", "g.groupCode");
cnd.asc("g.groupCode");
sql.setCondition(cnd);
return listMap(sql);
}
@Override
public List<RetiredGroup> options() {
return query(Cnd.NEW().asc("groupCode"));
}
private void appendGroupScope(Cnd cnd, String groupIdColumn) {
if (!AuthUtil.hasRoleOr("ltbzr", "ltbfzr", "ltbms", "sysadmin", "SYSADMIN") && StpUtil.hasRole("ltbxzzz")) {
cnd.and(groupIdColumn, "in", Sqls.create("select unionGroupId from sys_user_role where userId = @userId and roleId = @roleId")
.setParam("userId", StpUtil.getLoginIdAsString())
.setParam("roleId", groupLeaderRoleId()));
}
}
private String groupLeaderRoleId() {
// 当前库角色 ID 可能和参考项目不同,按角色编码取 ID,避免硬编码旧数据导致组长无法回显。
return dao().fetch("sys_role", Cnd.where("code", "=", GROUP_LEADER_ROLE_CODE)).getString("id");
}
}
@@ -0,0 +1,117 @@
package com.budwk.app.zhgh.staffmanage.retired.partyBranch.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import com.budwk.app.base.param.PageForm;
import com.budwk.app.base.result.Result;
import com.budwk.app.zhgh.staffmanage.retired.partyBranch.models.RetiredPartyBranch;
import com.budwk.app.zhgh.staffmanage.retired.partyBranch.service.RetiredPartyBranchService;
import lombok.extern.slf4j.Slf4j;
import org.nutz.aop.interceptor.ioc.TransAop;
import org.nutz.ioc.aop.Aop;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
import org.nutz.mvc.annotation.Param;
@Slf4j
@IocBean
@Ok("json:full")
@At("/platform/retired/partyBranch/manage")
public class RetiredPartyBranchManageController {
@Inject
private RetiredPartyBranchService retiredPartyBranchService;
@At("")
@Ok("beetl:/platform/zhgh/staffmanage/retired/partyBranch/manage/index.html")
@SaCheckPermission("retired.partyBranch.manage")
public void index() {
}
@At
@SaCheckPermission("retired.partyBranch.manage")
public Result getTree() {
return Result.success(retiredPartyBranchService.getTree());
}
@At
@SaCheckPermission("retired.partyBranch.manage")
public Result doSetPartyBranch(@Param("..") RetiredPartyBranch retiredPartyBranch) {
retiredPartyBranchService.savePartyBranch(retiredPartyBranch);
return Result.success();
}
@At
@Aop(TransAop.READ_COMMITTED)
@SaCheckPermission("retired.partyBranch.manage")
public Result deletePartyBranch(String id) {
retiredPartyBranchService.deletePartyBranch(id);
return Result.success();
}
@At
@SaCheckPermission("retired.partyBranch.manage")
public Result partyBranchPageData(PageForm pageForm) {
return Result.success(retiredPartyBranchService.partyBranchPageData(pageForm));
}
@At
@SaCheckPermission("retired.partyBranch.manage")
public Result partyBranchUserPageData(PageForm pageForm, String partyBranchId) {
return Result.success(retiredPartyBranchService.partyBranchUserPageData(pageForm, partyBranchId));
}
@At
@SaCheckPermission("retired.partyBranch.manage")
public Result queryNonSetUser(String val) {
return Result.success(retiredPartyBranchService.queryNonSetUser(val));
}
@At
@Aop(TransAop.READ_COMMITTED)
@SaCheckPermission("retired.partyBranch.manage")
public Result doAddBranchPartyUser(String partyBranchId, @Param("userIds") String[] userIds) {
retiredPartyBranchService.addBranchPartyUser(partyBranchId, userIds);
return Result.success();
}
@At
@Aop(TransAop.READ_COMMITTED)
@SaCheckPermission("retired.partyBranch.manage")
public Result deleteUser(String userId, String partyBranchId) {
retiredPartyBranchService.deleteUser(userId, partyBranchId);
return Result.success();
}
@At
@SaCheckPermission("retired.partyBranch.manage")
public Result queryUserByPartyBranchId(String partyBranchId, String val) {
return Result.success(retiredPartyBranchService.queryUserByPartyBranchId(partyBranchId, val));
}
@At
@Aop(TransAop.READ_COMMITTED)
@SaCheckPermission("retired.partyBranch.manage")
public Result doAddCommitteeUser(String partyBranchId, String userId, String roleCode) {
retiredPartyBranchService.addCommitteeUser(partyBranchId, userId, roleCode);
return Result.success();
}
@At
@SaCheckPermission("retired.partyBranch.manage")
public Result partyBranchCommitteePageData(PageForm pageForm, String partyBranchId) {
return Result.success(retiredPartyBranchService.partyBranchCommitteePageData(pageForm, partyBranchId));
}
@At
@Aop(TransAop.READ_COMMITTED)
@SaCheckPermission("retired.partyBranch.manage")
public Result deleteCommittee(String userId, String partyBranchId) {
retiredPartyBranchService.deleteCommittee(userId, partyBranchId);
return Result.success();
}
}
@@ -0,0 +1,43 @@
package com.budwk.app.zhgh.staffmanage.retired.partyBranch.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import com.budwk.app.base.result.Result;
import com.budwk.app.zhgh.staffmanage.retired.partyBranch.service.RetiredPartyBranchService;
import lombok.extern.slf4j.Slf4j;
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;
@Slf4j
@IocBean
@Ok("json:full")
@At("/platform/retired/partyBranch/statistics")
public class RetiredPartyBranchStatisticsController {
@Inject
private RetiredPartyBranchService retiredPartyBranchService;
@At("")
@Ok("beetl:/platform/zhgh/staffmanage/retired/partyBranch/statistics/index.html")
@SaCheckPermission("retired.partyBranch.statistics")
public void index() {
}
@At
@SaCheckPermission("retired.partyBranch.statistics")
public Result data(@Param("retiredPartyBranchId") String retiredPartyBranchId) {
return Result.success(retiredPartyBranchService.statisticsData(retiredPartyBranchId));
}
@At
@SaCheckPermission("retired.partyBranch.statistics")
public Result retiredPartyBranch() {
return Result.success(retiredPartyBranchService.options());
}
}
@@ -0,0 +1,36 @@
package com.budwk.app.zhgh.staffmanage.retired.partyBranch.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
@EqualsAndHashCode(callSuper = true)
@Table("retired_party_branch")
@Comment("离退休党支部")
public class RetiredPartyBranch extends BaseModel {
@Name
@Comment("ID")
@ColDefine(type = ColType.VARCHAR, width = 32)
@PrevInsert(uu32 = true)
private String id;
@Column
@Comment("党支部名称")
@ColDefine(type = ColType.VARCHAR, width = 50)
private String partyBranchName;
@Column
@Comment("党支部代码")
@ColDefine(type = ColType.INT)
private Integer partyBranchCode;
}
@@ -0,0 +1,41 @@
package com.budwk.app.zhgh.staffmanage.retired.partyBranch.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
@EqualsAndHashCode(callSuper = true)
@Table("retired_party_branch_user")
@Comment("离退休党支部人员")
public class RetiredPartyBranchUser extends BaseModel {
@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 partyBranchId;
@Column
@Comment("用户ID")
@ColDefine(type = ColType.VARCHAR, width = 32)
private String userId;
@Column
@Comment("党支部委员角色编码")
@ColDefine(type = ColType.VARCHAR, width = 50)
private String roleCode;
}
@@ -0,0 +1,55 @@
package com.budwk.app.zhgh.staffmanage.retired.partyBranch.service;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.param.PageForm;
import com.budwk.app.base.service.BaseService;
import com.budwk.app.zhgh.staffmanage.retired.partyBranch.models.RetiredPartyBranch;
import org.nutz.lang.util.NutMap;
import java.util.List;
public interface RetiredPartyBranchService extends BaseService<RetiredPartyBranch> {
/** 查询离退休党支部树。 */
List<NutMap> getTree();
/** 新增或修改离退休党支部。 */
void savePartyBranch(RetiredPartyBranch retiredPartyBranch);
/** 删除离退休党支部及其人员关联。 */
void deletePartyBranch(String id);
/** 分页查询离退休党支部。 */
Pagination partyBranchPageData(PageForm pageForm);
/** 分页查询党支部人员。 */
Pagination partyBranchUserPageData(PageForm pageForm, String partyBranchId);
/** 查询未设置党支部的离退休人员。 */
List<NutMap> queryNonSetUser(String val);
/** 批量添加党支部人员。 */
void addBranchPartyUser(String partyBranchId, String[] userIds);
/** 删除党支部人员。 */
void deleteUser(String userId, String partyBranchId);
/** 查询党支部下可设为委员的人员。 */
List<NutMap> queryUserByPartyBranchId(String partyBranchId, String val);
/** 设置党支部委员角色。 */
void addCommitteeUser(String partyBranchId, String userId, String roleCode);
/** 分页查询党支部委员。 */
Pagination partyBranchCommitteePageData(PageForm pageForm, String partyBranchId);
/** 删除党支部委员角色。 */
void deleteCommittee(String userId, String partyBranchId);
/** 查询党支部统计数据。 */
List<NutMap> statisticsData(String retiredPartyBranchId);
/** 查询全部党支部选项。 */
List<RetiredPartyBranch> options();
}
@@ -0,0 +1,232 @@
package com.budwk.app.zhgh.staffmanage.retired.partyBranch.service.impl;
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.service.impl.BaseServiceImpl;
import com.budwk.app.zhgh.staffmanage.retired.partyBranch.models.RetiredPartyBranch;
import com.budwk.app.zhgh.staffmanage.retired.partyBranch.models.RetiredPartyBranchUser;
import com.budwk.app.zhgh.staffmanage.retired.partyBranch.service.RetiredPartyBranchService;
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.IocBean;
import org.nutz.lang.util.NutMap;
import java.util.Arrays;
import java.util.List;
@IocBean(args = {"refer:dao"})
public class RetiredPartyBranchServiceImpl extends BaseServiceImpl<RetiredPartyBranch> implements RetiredPartyBranchService {
private static final List<String> RETIRED_STATES = List.of("退休", "离休", "离退休");
public RetiredPartyBranchServiceImpl(Dao dao) {
super(dao);
}
@Override
public List<NutMap> getTree() {
NutMap topMap = NutMap.NEW().addv("label", "离退休党支部").addv("id", "1");
Sql sql = Sqls.create("select id, partyBranchName as label from retired_party_branch order by partyBranchCode");
topMap.put("children", listMap(sql));
return List.of(topMap);
}
@Override
public void savePartyBranch(RetiredPartyBranch retiredPartyBranch) {
dao().insertOrUpdate(retiredPartyBranch);
}
@Override
public void deletePartyBranch(String id) {
dao().delete(RetiredPartyBranch.class, id);
dao().clear(RetiredPartyBranchUser.class, Cnd.where("partyBranchId", "=", id));
}
@Override
public Pagination partyBranchPageData(PageForm pageForm) {
Sql sql = Sqls.create("select * from retired_party_branch $condition");
Cnd cnd = Cnd.NEW();
if (StrUtil.isNotBlank(pageForm.getSearchKeyword())) {
SqlExpressionGroup seg = new SqlExpressionGroup();
seg.orLike("partyBranchName", pageForm.getSearchKeyword());
seg.orLike("partyBranchCode", pageForm.getSearchKeyword());
cnd.and(seg);
}
cnd.asc("partyBranchCode");
sql.setCondition(cnd);
return listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql);
}
@Override
public Pagination partyBranchUserPageData(PageForm pageForm, String partyBranchId) {
Sql sql = Sqls.create("""
SELECT
u.id,
u.loginname,
u.username,
u.sex,
u.birthday,
u.mobile,
u.personType,
u.userState,
u.unitName as unitname,
u.unionName as unionname
FROM retired_party_branch_user rpbu
LEFT JOIN vw_user u ON u.id = rpbu.userId
$condition
""");
Cnd cnd = Cnd.NEW();
cnd.and("rpbu.partyBranchId", "=", partyBranchId);
if (StrUtil.isNotBlank(pageForm.getSearchKeyword())) {
SqlExpressionGroup seg = new SqlExpressionGroup();
seg.orLike("u.username", pageForm.getSearchKeyword());
seg.orLike("u.loginname", pageForm.getSearchKeyword());
cnd.and(seg);
}
sql.setCondition(cnd);
return listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql);
}
@Override
public List<NutMap> queryNonSetUser(String val) {
Sql sql = Sqls.create("""
SELECT
u.id,
u.username,
u.loginname,
u.unitName as unitname,
u.userState,
u.political
FROM vw_user u
$condition
""");
Cnd cnd = Cnd.NEW();
cnd.and("u.userState", "in", RETIRED_STATES);
cnd.and("u.id", "not in", Sqls.create("select userId from retired_party_branch_user"));
if (StrUtil.isNotBlank(val)) {
SqlExpressionGroup seg = new SqlExpressionGroup();
seg.orLike("u.username", val);
seg.orLike("u.loginname", val);
cnd.and(seg);
}
sql.setCondition(cnd);
return listPageMap(1, 10, sql).getList();
}
@Override
public void addBranchPartyUser(String partyBranchId, String[] userIds) {
if (userIds == null || userIds.length == 0) {
return;
}
List<RetiredPartyBranchUser> users = Arrays.stream(userIds).map(v -> {
RetiredPartyBranchUser partyBranchUser = new RetiredPartyBranchUser();
partyBranchUser.setPartyBranchId(partyBranchId);
partyBranchUser.setUserId(v);
return partyBranchUser;
}).toList();
dao().insert(users);
}
@Override
public void deleteUser(String userId, String partyBranchId) {
dao().clear(RetiredPartyBranchUser.class, Cnd.where("userId", "=", userId).and("partyBranchId", "=", partyBranchId));
}
@Override
public List<NutMap> queryUserByPartyBranchId(String partyBranchId, String val) {
Sql sql = Sqls.create("""
SELECT
u.id,
u.username,
u.loginname,
u.unitName as unitname
FROM retired_party_branch_user rpbu
LEFT JOIN vw_user u ON u.id = rpbu.userId
$condition
""");
Cnd cnd = Cnd.NEW();
cnd.and("u.userState", "in", RETIRED_STATES);
cnd.and("rpbu.partyBranchId", "=", partyBranchId);
if (StrUtil.isNotBlank(val)) {
SqlExpressionGroup seg = new SqlExpressionGroup();
seg.orLike("u.username", val);
seg.orLike("u.loginname", val);
cnd.and(seg);
}
sql.setCondition(cnd);
return listMap(sql);
}
@Override
public void addCommitteeUser(String partyBranchId, String userId, String roleCode) {
dao().update(RetiredPartyBranchUser.class, Chain.make("roleCode", roleCode), Cnd.where("partyBranchId", "=", partyBranchId).and("userId", "=", userId));
}
@Override
public Pagination partyBranchCommitteePageData(PageForm pageForm, String partyBranchId) {
Sql sql = Sqls.create("""
SELECT
u.id,
u.loginname,
u.username,
u.sex,
u.birthday,
u.mobile,
u.personType,
u.userState,
u.unitName as unitname,
u.unionName as unionname,
dict.name as roleName
FROM retired_party_branch_user rpbu
LEFT JOIN sys_dict dict ON dict.code = rpbu.roleCode
LEFT JOIN vw_user u ON u.id = rpbu.userId
$condition
""");
Cnd cnd = Cnd.NEW();
cnd.and("rpbu.roleCode", "is not", null);
cnd.and("rpbu.partyBranchId", "=", partyBranchId);
if (StrUtil.isNotBlank(pageForm.getSearchKeyword())) {
SqlExpressionGroup seg = new SqlExpressionGroup();
seg.orLike("u.username", pageForm.getSearchKeyword());
seg.orLike("u.loginname", pageForm.getSearchKeyword());
cnd.and(seg);
}
sql.setCondition(cnd);
return listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql);
}
@Override
public void deleteCommittee(String userId, String partyBranchId) {
dao().update(RetiredPartyBranchUser.class, Chain.make("roleCode", null), Cnd.where("userId", "=", userId).and("partyBranchId", "=", partyBranchId));
}
@Override
public List<NutMap> statisticsData(String retiredPartyBranchId) {
Sql sql = Sqls.create("""
SELECT
rpb.id,
rpb.partyBranchName,
rpb.partyBranchCode,
count(rpbu.userId) AS userCount
FROM retired_party_branch rpb
LEFT JOIN retired_party_branch_user rpbu ON rpbu.partyBranchId = rpb.id
$condition
""");
Cnd cnd = Cnd.NEW();
cnd.andEX("rpb.id", "=", retiredPartyBranchId);
cnd.groupBy("rpb.id");
cnd.asc("rpb.partyBranchCode");
sql.setCondition(cnd);
return listMap(sql);
}
@Override
public List<RetiredPartyBranch> options() {
return query(Cnd.NEW().asc("partyBranchCode"));
}
}
@@ -0,0 +1,16 @@
package com.budwk.app.zhgh.staffmanage.retired.user.constant;
import lombok.AllArgsConstructor;
import lombok.Getter;
@AllArgsConstructor
@Getter
public enum RetiredUserChangeType {
JOB_TO_RETIRED("在职转离退休", 1),
RETIRED_TO_DEAD("离退休转去世", 2);
private final String desc;
private final int code;
}
@@ -0,0 +1,42 @@
package com.budwk.app.zhgh.staffmanage.retired.user.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import com.budwk.app.base.param.PageForm;
import com.budwk.app.base.result.Result;
import com.budwk.app.zhgh.staffmanage.retired.user.service.RetiredUserService;
import lombok.extern.slf4j.Slf4j;
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;
@Slf4j
@IocBean
@Ok("json:full")
@At("/platform/retired/change")
public class RetiredChangeController {
@Inject
private RetiredUserService retiredUserService;
@At("")
@Ok("beetl:/platform/zhgh/staffmanage/retired/change/index.html")
@SaCheckPermission("retired.change")
public void index() {
}
@At
@SaCheckPermission("retired.change")
public Result pageData(PageForm pageForm,
Integer year,
@Param("unitId") String unitId,
@Param("unionId") String unionId,
@Param("changeType") Integer changeType) {
return Result.success(retiredUserService.changePageData(pageForm, year, unitId, unionId, changeType));
}
}
@@ -0,0 +1,61 @@
package com.budwk.app.zhgh.staffmanage.retired.user.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import com.budwk.app.base.param.PageForm;
import com.budwk.app.base.result.Result;
import com.budwk.app.zhgh.staffmanage.retired.group.service.RetiredGroupService;
import com.budwk.app.zhgh.staffmanage.retired.partyBranch.service.RetiredPartyBranchService;
import com.budwk.app.zhgh.staffmanage.retired.user.service.RetiredUserService;
import lombok.extern.slf4j.Slf4j;
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;
@Slf4j
@IocBean
@Ok("json:full")
@At("/platform/retired/history")
public class RetiredHistoryController {
@Inject
private RetiredUserService retiredUserService;
@Inject
private RetiredGroupService retiredGroupService;
@Inject
private RetiredPartyBranchService retiredPartyBranchService;
@At("")
@Ok("beetl:/platform/zhgh/staffmanage/retired/history/index.html")
@SaCheckPermission("retired.history")
public void index() {
}
@At
@SaCheckPermission("retired.history")
public Result pageData(PageForm pageForm,
@Param("year") Integer year,
@Param("unitId") String unitId,
@Param("unionId") String unionId,
@Param("retiredGroupId") String retiredGroupId,
@Param("retiredPartyBranchId") String retiredPartyBranchId) {
return Result.success(retiredUserService.historyPageData(pageForm, year, unitId, unionId, retiredGroupId, retiredPartyBranchId));
}
@At
@SaCheckPermission("retired.history")
public Result retiredGroup() {
return Result.success(retiredGroupService.options());
}
@At
@SaCheckPermission("retired.history")
public Result retiredPartyBranch() {
return Result.success(retiredPartyBranchService.options());
}
}
@@ -0,0 +1,128 @@
package com.budwk.app.zhgh.staffmanage.retired.user.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.annotation.SaMode;
import com.budwk.app.base.param.PageForm;
import com.budwk.app.base.result.Result;
import com.budwk.app.zhgh.staffmanage.retired.group.service.RetiredGroupService;
import com.budwk.app.zhgh.staffmanage.retired.partyBranch.service.RetiredPartyBranchService;
import com.budwk.app.zhgh.staffmanage.retired.user.service.RetiredUserService;
import lombok.extern.slf4j.Slf4j;
import org.nutz.aop.interceptor.ioc.TransAop;
import org.nutz.ioc.aop.Aop;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
import org.nutz.mvc.annotation.Param;
import java.util.Date;
@Slf4j
@IocBean
@Ok("json:full")
@At("/platform/retired/list")
public class RetiredListController {
@Inject
private RetiredUserService retiredUserService;
@Inject
private RetiredGroupService retiredGroupService;
@Inject
private RetiredPartyBranchService retiredPartyBranchService;
@At("")
@Ok("beetl:/platform/zhgh/staffmanage/retired/list/index.html")
@SaCheckPermission("retired.list")
public void index() {
}
@At("/h5")
@Ok("beetl:/platform/zhghh5/staffmanage/retired/list/index.html")
@SaCheckPermission("h5.retired.list")
public void h5Index() {
}
@At
@SaCheckPermission(value = {"retired.list", "h5.retired.list"}, mode = SaMode.OR)
public Result pageData(PageForm pageForm,
@Param("unitId") String unitId,
@Param("unionId") String unionId,
@Param("retiredGroupId") String retiredGroupId,
@Param("retiredPartyBranchId") String retiredPartyBranchId) {
return Result.success(retiredUserService.pageData(pageForm, unitId, unionId, retiredGroupId, retiredPartyBranchId));
}
@At
@Aop(TransAop.READ_COMMITTED)
@SaCheckPermission(value = {"retired.list", "h5.retired.list"}, mode = SaMode.OR)
public Result changeToDie(String userId, Date deathDate) {
retiredUserService.changeToDie(userId, deathDate);
return Result.success();
}
@At
@SaCheckPermission(value = {"retired.list", "h5.retired.list"}, mode = SaMode.OR)
public Result nonRetiredUserPageData(PageForm pageForm) {
return Result.success(retiredUserService.nonRetiredUserPageData(pageForm));
}
@At
@Aop(TransAop.READ_COMMITTED)
@SaCheckPermission(value = {"retired.list", "h5.retired.list"}, mode = SaMode.OR)
public Result doBatchJoinRetire(@Param("userIds") String[] userIds) {
retiredUserService.batchJoinRetire(userIds);
return Result.success();
}
@At
@SaCheckPermission(value = {"retired.list", "h5.retired.list"}, mode = SaMode.OR)
public Result retiredGroup() {
return Result.success(retiredGroupService.options());
}
@At
@SaCheckPermission(value = {"retired.list", "h5.retired.list"}, mode = SaMode.OR)
public Result retiredPartyBranch() {
return Result.success(retiredPartyBranchService.options());
}
@At
@SaCheckPermission("retired.list")
public Result backUp() {
retiredUserService.backUp();
return Result.success();
}
@At
@SaCheckPermission(value = {"retired.list", "h5.retired.list"}, mode = SaMode.OR)
public Result userInfo(String userId) {
return Result.success(retiredUserService.userInfo(userId));
}
@At
@Aop(TransAop.READ_COMMITTED)
@SaCheckPermission(value = {"retired.list", "h5.retired.list"}, mode = SaMode.OR)
public Result doSubmit(@Param("user") String user, @Param("userId") String userId) {
retiredUserService.submit(user, userId);
return Result.success();
}
@At
@SaCheckPermission(value = {"retired.list", "h5.retired.list"}, mode = SaMode.OR)
public Result getInfo(String id) {
return Result.success(retiredUserService.getInfo(id));
}
@At
@Aop(TransAop.READ_COMMITTED)
@SaCheckPermission(value = {"retired.list", "h5.retired.list"}, mode = SaMode.OR)
public Result doDelete(String id) {
retiredUserService.deleteRetired(id);
return Result.success();
}
}
@@ -0,0 +1,60 @@
package com.budwk.app.zhgh.staffmanage.retired.user.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import com.budwk.app.base.param.PageForm;
import com.budwk.app.base.result.Result;
import com.budwk.app.zhgh.staffmanage.retired.group.service.RetiredGroupService;
import com.budwk.app.zhgh.staffmanage.retired.partyBranch.service.RetiredPartyBranchService;
import com.budwk.app.zhgh.staffmanage.retired.user.service.RetiredUserService;
import lombok.extern.slf4j.Slf4j;
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;
@Slf4j
@IocBean
@Ok("json:full")
@At("/platform/retired/statistics")
public class RetiredStatisticsController {
@Inject
private RetiredUserService retiredUserService;
@Inject
private RetiredGroupService retiredGroupService;
@Inject
private RetiredPartyBranchService retiredPartyBranchService;
@At("")
@Ok("beetl:/platform/zhgh/staffmanage/retired/statistics/index.html")
@SaCheckPermission("retired.statistics")
public void index() {
}
@At
@SaCheckPermission("retired.statistics")
public Result pageData(PageForm pageForm,
@Param("unitId") String unitId,
@Param("unionId") String unionId,
@Param("retiredGroupId") String retiredGroupId,
@Param("retiredPartyBranchId") String retiredPartyBranchId) {
return Result.success(retiredUserService.statisticsPageData(pageForm, unitId, unionId, retiredGroupId, retiredPartyBranchId));
}
@At
@SaCheckPermission("retired.statistics")
public Result retiredGroup() {
return Result.success(retiredGroupService.options());
}
@At
@SaCheckPermission("retired.statistics")
public Result retiredPartyBranch() {
return Result.success(retiredPartyBranchService.options());
}
}
@@ -0,0 +1,43 @@
package com.budwk.app.zhgh.staffmanage.retired.user.models;
import com.budwk.app.base.model.BaseModel;
import com.budwk.app.zhgh.staffmanage.retired.user.constant.RetiredUserChangeType;
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
@EqualsAndHashCode(callSuper = true)
@Table("retired_user_change")
@Comment("离退休人员变动记录")
public class RetiredUserChange extends BaseModel {
@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.DATETIME)
private Date changeDate;
@Column
@Comment("变动类型")
@ColDefine(type = ColType.INT)
private Integer changeType;
}
@@ -0,0 +1,33 @@
package com.budwk.app.zhgh.staffmanage.retired.user.models;
import com.budwk.app.sys.models.Sys_user;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
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
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = true)
@Table("retired_user_history")
@Comment("离退休人员年度历史快照")
public class RetiredUserHistory extends Sys_user {
@Name
@Comment("ID")
@ColDefine(type = ColType.VARCHAR, width = 32)
@PrevInsert(uu32 = true)
private String id;
@Column
@Comment("年度")
@ColDefine(type = ColType.INT, width = 4)
private Integer year;
}
@@ -0,0 +1,49 @@
package com.budwk.app.zhgh.staffmanage.retired.user.service;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.param.PageForm;
import com.budwk.app.base.service.BaseService;
import com.budwk.app.sys.models.Sys_user;
import org.nutz.lang.util.NutMap;
import java.util.Date;
public interface RetiredUserService extends BaseService<Sys_user> {
/** 分页查询离退休人员名单。 */
Pagination pageData(PageForm pageForm, String unitId, String unionId, String retiredGroupId, String retiredPartyBranchId);
/** 将离退休人员设置为去世并写入变动记录。 */
void changeToDie(String userId, Date deathDate);
/** 分页查询可加入离退休名单的人员。 */
Pagination nonRetiredUserPageData(PageForm pageForm);
/** 批量将人员设置为离退休。 */
void batchJoinRetire(String[] userIds);
/** 按当前年度备份离退休人员快照。 */
void backUp();
/** 查询人员详情展示数据。 */
NutMap userInfo(String userId);
/** 新增或修改离退休人员,userJson 为前端表单 JSON。 */
void submit(String userJson, String userId);
/** 查询人员编辑数据。 */
Sys_user getInfo(String id);
/** 将人员移出离退休名单。 */
void deleteRetired(String id);
/** 分页查询离退休人员变动记录。 */
Pagination changePageData(PageForm pageForm, Integer year, String unitId, String unionId, Integer changeType);
/** 分页查询离退休人员历史快照。 */
Pagination historyPageData(PageForm pageForm, Integer year, String unitId, String unionId, String retiredGroupId, String retiredPartyBranchId);
/** 分页查询离退休统计明细。 */
Pagination statisticsPageData(PageForm pageForm, String unitId, String unionId, String retiredGroupId, String retiredPartyBranchId);
}
@@ -0,0 +1,393 @@
package com.budwk.app.zhgh.staffmanage.retired.user.service.impl;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.date.DateUtil;
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.service.impl.BaseServiceImpl;
import com.budwk.app.sys.models.Sys_user;
import com.budwk.app.web.commons.auth.utils.AuthUtil;
import com.budwk.app.zhgh.staffmanage.retired.user.constant.RetiredUserChangeType;
import com.budwk.app.zhgh.staffmanage.retired.user.models.RetiredUserChange;
import com.budwk.app.zhgh.staffmanage.retired.user.models.RetiredUserHistory;
import com.budwk.app.zhgh.staffmanage.retired.user.service.RetiredUserService;
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.Daos;
import org.nutz.dao.util.cri.SqlExpressionGroup;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.json.Json;
import org.nutz.lang.Lang;
import org.nutz.lang.random.R;
import org.nutz.lang.util.NutMap;
import org.springframework.beans.BeanUtils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
@IocBean(args = {"refer:dao"})
public class RetiredUserServiceImpl extends BaseServiceImpl<Sys_user> implements RetiredUserService {
private static final String GROUP_LEADER_ROLE_CODE = "BRANCH_UNION_GROUP_LEADER";
private static final List<String> RETIRED_STATES = List.of("退休", "离休", "离退休");
public RetiredUserServiceImpl(Dao dao) {
super(dao);
}
@Override
public Pagination pageData(PageForm pageForm, String unitId, String unionId, String retiredGroupId, String retiredPartyBranchId) {
Sql sql = retiredUserSql();
Cnd cnd = retiredUserCnd(pageForm, unitId, unionId, retiredGroupId, retiredPartyBranchId);
sql.setCondition(cnd);
return listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql);
}
@Override
public void changeToDie(String userId, Date deathDate) {
Date changeDate = deathDate == null ? new Date() : deathDate;
update(Chain.make("userState", "死亡").add("personType", "去世人员"), Cnd.where("id", "=", userId));
insertChange(userId, RetiredUserChangeType.RETIRED_TO_DEAD, changeDate);
}
@Override
public Pagination nonRetiredUserPageData(PageForm pageForm) {
Sql sql = Sqls.create("""
SELECT
u.id,
u.loginname,
u.username,
u.sex,
u.birthday,
u.mobile,
u.idCard,
u.position,
u.professionalTitle,
u.preparedBy,
u.education,
u.academicDegree,
u.nation,
null as campusName,
u.unionGroupName,
u.political,
u.personType,
u.userState,
u.unitName as unitname,
u.unionName as unionname,
null as threeUnitName
FROM vw_user u
$condition
""");
Cnd cnd = Cnd.NEW();
cnd.and("u.userState", "not in", RETIRED_STATES);
cnd.and("u.userState", "!=", "死亡");
appendSearch(cnd, pageForm, "u");
appendUserScope(cnd, "u.unitId");
sql.setCondition(cnd);
return listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql);
}
@Override
public void batchJoinRetire(String[] userIds) {
if (userIds == null || userIds.length == 0) {
return;
}
update(Chain.make("userState", "退休").add("personType", "离退休人员").add("retireDate", new Date()), Cnd.where("id", "in", userIds));
List<RetiredUserChange> changes = Arrays.stream(userIds).map(userId -> buildChange(userId, RetiredUserChangeType.JOB_TO_RETIRED, new Date())).toList();
dao().insert(changes);
}
@Override
public void backUp() {
dao().clear(RetiredUserHistory.class, Cnd.where("year", "=", DateUtil.thisYear()));
List<Sys_user> users = query(Cnd.where("userState", "in", RETIRED_STATES));
if (Lang.isEmpty(users)) {
return;
}
List<RetiredUserHistory> histories = new ArrayList<>();
for (Sys_user user : users) {
RetiredUserHistory history = new RetiredUserHistory();
BeanUtils.copyProperties(user, history, "password", "salt");
history.setId(R.UU32());
history.setYear(DateUtil.thisYear());
histories.add(history);
}
dao().fastInsert(histories);
}
@Override
public NutMap userInfo(String userId) {
Sql sql = Sqls.create("""
SELECT
u.id,
u.loginname,
u.username,
u.sex,
u.birthday,
u.political,
u.joinPartyDate,
u.nation,
u.nativePlace,
u.idCardType,
u.idCard,
u.mobile,
u.education,
u.marriage,
u.academicDegree,
u.position,
u.professionalTitle,
u.preparedBy,
u.arrivalAtSchoolDate,
u.teachingTime,
u.userState,
u.personType,
u.retireDate,
u.email,
u.unitId,
u.unitName,
u.unionId,
u.unionName,
u.unionGroupId,
u.unionGroupName,
u.memberTime,
u.families,
u.personalData,
null as campusName,
null as threeUnitName,
rg.groupName as retiredGroupName,
rpb.partyBranchName as retiredPartyBranchName
FROM vw_user u
LEFT JOIN retired_group_unit rgu ON rgu.unitId = u.unitId
LEFT JOIN retired_group rg ON rg.id = rgu.retiredGroupId
LEFT JOIN retired_party_branch_user rpbu ON rpbu.userId = u.id
LEFT JOIN retired_party_branch rpb ON rpb.id = rpbu.partyBranchId
WHERE u.id = @id
""").setParam("id", userId);
return (NutMap) Daos.query(dao(), sql.toString(), Sqls.callback.map());
}
@Override
public void submit(String userJson, String userId) {
if (StrUtil.isBlank(userJson)) {
throw new RuntimeException("人员信息不能为空");
}
// 前端按 JSON 字符串提交表单,统一在 service 层解析,避免 controller 承担公共参数处理。
Sys_user user = Json.fromJson(Sys_user.class, userJson);
if (StrUtil.isBlank(userId)) {
if (StrUtil.isBlank(user.getUserState())) {
user.setUserState("退休");
}
if (StrUtil.isBlank(user.getPersonType())) {
user.setPersonType("离退休人员");
}
if (user.getRetireDate() == null) {
user.setRetireDate(new Date());
}
dao().insert(user);
insertChange(user.getId(), RetiredUserChangeType.JOB_TO_RETIRED, new Date());
} else {
if (StrUtil.isBlank(user.getId())) {
user.setId(userId);
}
dao().updateIgnoreNull(user);
}
}
@Override
public Sys_user getInfo(String id) {
return dao().fetch(Sys_user.class, id);
}
@Override
public void deleteRetired(String id) {
update(Chain.make("userState", "在职").add("personType", null).add("retireDate", null), Cnd.where("id", "=", id));
dao().clear(RetiredUserChange.class, Cnd.where("userId", "=", id));
}
@Override
public Pagination changePageData(PageForm pageForm, Integer year, String unitId, String unionId, Integer changeType) {
Sql sql = Sqls.create("""
SELECT
ruc.id,
ruc.changeDate,
ruc.changeType,
u.loginname,
u.username,
u.sex,
u.birthday,
u.mobile,
u.idCard,
u.position,
u.professionalTitle,
u.preparedBy,
u.education,
u.academicDegree,
u.nation,
null as campusName,
u.unionGroupName,
u.personType,
u.userState,
u.unitName as unitname,
u.unionName as unionname
FROM retired_user_change ruc
LEFT JOIN vw_user u ON u.id = ruc.userId
$condition
""");
Cnd cnd = Cnd.NEW();
cnd.andEX("u.unitId", "=", unitId);
cnd.andEX("u.unionId", "=", unionId);
cnd.andEX("YEAR(ruc.changeDate)", "=", year);
cnd.andEX("ruc.changeType", "=", changeType);
appendUserScope(cnd, "u.unitId");
appendSearch(cnd, pageForm, "u");
sql.setCondition(cnd);
return listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql);
}
@Override
public Pagination historyPageData(PageForm pageForm, Integer year, String unitId, String unionId, String retiredGroupId, String retiredPartyBranchId) {
Sql sql = Sqls.create("""
SELECT
u.id,
u.loginname,
u.username,
u.sex,
u.birthday,
u.mobile,
u.idCard,
u.position,
u.professionalTitle,
u.preparedBy,
u.education,
u.academicDegree,
u.nation,
null as campusName,
unionGroup.name as unionGroupName,
u.personType,
u.userState,
n.name AS unitname,
gh.name AS unionname,
u.political,
null as threeUnitName,
rg.groupName AS retiredGroupName,
rpb.partyBranchName AS retiredPartyBranchName
FROM retired_user_history u
LEFT JOIN sys_unit n ON n.id = u.unitId
LEFT JOIN sys_union gh ON gh.id = n.unionId
LEFT JOIN sys_union_group unionGroup ON unionGroup.id = n.unionGroupId
LEFT JOIN retired_group_unit rgu ON rgu.unitId = u.unitId
LEFT JOIN retired_group rg ON rg.id = rgu.retiredGroupId
LEFT JOIN retired_party_branch_user rpbu ON rpbu.userId = u.id
LEFT JOIN retired_party_branch rpb ON rpb.id = rpbu.partyBranchId
$condition
""");
Cnd cnd = Cnd.NEW();
cnd.andEX("u.unitId", "=", unitId);
cnd.andEX("gh.id", "=", unionId);
cnd.andEX("u.year", "=", year);
cnd.andEX("rg.id", "=", retiredGroupId);
cnd.andEX("rpb.id", "=", retiredPartyBranchId);
appendSearch(cnd, pageForm, "u");
appendUserScope(cnd, "u.unitId");
cnd.groupBy("u.id");
sql.setCondition(cnd);
return listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql);
}
@Override
public Pagination statisticsPageData(PageForm pageForm, String unitId, String unionId, String retiredGroupId, String retiredPartyBranchId) {
Sql sql = retiredUserSql();
Cnd cnd = retiredUserCnd(pageForm, unitId, unionId, retiredGroupId, retiredPartyBranchId);
sql.setCondition(cnd);
return listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql);
}
private Sql retiredUserSql() {
return Sqls.create("""
SELECT
u.id,
u.loginname,
u.username,
u.sex,
u.birthday,
u.mobile,
u.idCard,
u.position,
u.professionalTitle,
u.preparedBy,
u.education,
u.academicDegree,
u.nation,
null as campusName,
u.unionGroupName,
u.personType,
u.userState,
u.unitName as unitname,
u.unionName as unionname,
u.political,
null as threeUnitName,
rg.groupName as retiredGroupName,
rpb.partyBranchName as retiredPartyBranchName
FROM vw_user u
LEFT JOIN retired_group_unit rgu ON rgu.unitId = u.unitId
LEFT JOIN retired_group rg ON rg.id = rgu.retiredGroupId
LEFT JOIN retired_party_branch_user rpbu ON rpbu.userId = u.id
LEFT JOIN retired_party_branch rpb ON rpb.id = rpbu.partyBranchId
$condition
""");
}
private Cnd retiredUserCnd(PageForm pageForm, String unitId, String unionId, String retiredGroupId, String retiredPartyBranchId) {
Cnd cnd = Cnd.NEW();
cnd.and("u.userState", "in", RETIRED_STATES);
cnd.andEX("u.unitId", "=", unitId);
cnd.andEX("u.unionId", "=", unionId);
cnd.andEX("rg.id", "=", retiredGroupId);
cnd.andEX("rpb.id", "=", retiredPartyBranchId);
appendSearch(cnd, pageForm, "u");
appendUserScope(cnd, "u.unitId");
cnd.groupBy("u.id");
return cnd;
}
private void appendSearch(Cnd cnd, PageForm pageForm, String alias) {
if (pageForm != null && StrUtil.isNotBlank(pageForm.getSearchKeyword())) {
SqlExpressionGroup seg = new SqlExpressionGroup();
seg.orLike(alias + ".loginname", pageForm.getSearchKeyword());
seg.orLike(alias + ".username", pageForm.getSearchKeyword());
cnd.and(seg);
}
}
private void appendUserScope(Cnd cnd, String unitColumn) {
if (!AuthUtil.hasRoleOr("ltbzr", "ltbfzr", "ltbms", "sysadmin", "SYSADMIN") && StpUtil.hasRole("ltbxzzz")) {
cnd.and(unitColumn, "in", Sqls.create("""
select rgu.unitId
from retired_group_unit rgu
inner join sys_user_role sur on rgu.retiredGroupId = sur.unionGroupId
where sur.userId = @userId and sur.roleId = @roleId
""").setParam("userId", StpUtil.getLoginIdAsString()).setParam("roleId", groupLeaderRoleId()));
}
}
private String groupLeaderRoleId() {
// 当前库角色 ID 可能和参考项目不同,按角色编码取 ID,保证离退休小组长数据范围正确。
return dao().fetch("sys_role", Cnd.where("code", "=", GROUP_LEADER_ROLE_CODE)).getString("id");
}
private void insertChange(String userId, RetiredUserChangeType type, Date changeDate) {
dao().insert(buildChange(userId, type, changeDate));
}
private RetiredUserChange buildChange(String userId, RetiredUserChangeType type, Date changeDate) {
RetiredUserChange change = new RetiredUserChange();
change.setUserId(userId);
change.setChangeDate(changeDate);
change.setChangeType(type.getCode());
return change;
}
}
@@ -1,6 +1,7 @@
package com.budwk.app.zhgh.staffmanage.singleTeacher.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.annotation.SaMode;
import com.budwk.app.base.annotation.SLog;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.result.Result;
@@ -38,8 +39,14 @@ public class SingleTeacherLedgerController {
public void index() {
}
@At("/h5")
@Ok("beetl:/platform/zhghh5/staffmanage/singleTeacher/ledger/index.html")
@SaCheckPermission("h5.singleTeacher.ledger")
public void h5Index() {
}
@At
@SaCheckPermission("singleTeacher.ledger")
@SaCheckPermission(value = {"singleTeacher.ledger", "h5.singleTeacher.ledger"}, mode = SaMode.OR)
@ApiOperation("分页查询")
@Ok("json:{dateFormat:'yyyy-MM-dd'}")
public Result pageData(@Param("pageForm") SingleTeacherPageForm pageForm) {
@@ -48,7 +55,7 @@ public class SingleTeacherLedgerController {
}
@At
@SaCheckPermission("singleTeacher.ledger")
@SaCheckPermission(value = {"singleTeacher.ledger", "h5.singleTeacher.ledger"}, mode = SaMode.OR)
@ApiOperation("新增")
@SLog(tag = "单身教工台账", msg = "新增了一条记录,用户ID:${args[0].userId}")
public Result insert(@Param("singleTeacher") SingleTeacher singleTeacher) {
@@ -57,7 +64,7 @@ public class SingleTeacherLedgerController {
}
@At
@SaCheckPermission("singleTeacher.ledger")
@SaCheckPermission(value = {"singleTeacher.ledger", "h5.singleTeacher.ledger"}, mode = SaMode.OR)
@ApiOperation("编辑")
@SLog(tag = "单身教工台账", msg = "编辑了一条记录,用户ID:${args[0].userId}")
public Result update(@Param("singleTeacher") SingleTeacher singleTeacher) {
@@ -66,7 +73,7 @@ public class SingleTeacherLedgerController {
}
@At
@SaCheckPermission("singleTeacher.ledger")
@SaCheckPermission(value = {"singleTeacher.ledger", "h5.singleTeacher.ledger"}, mode = SaMode.OR)
@ApiOperation("删除")
@SLog(tag = "单身教工台账", msg = "删除了一条记录,用户ID:${args[0].userId}")
public Result delete(@Param("id") String id) {
@@ -75,7 +82,7 @@ public class SingleTeacherLedgerController {
}
@At
@SaCheckPermission("singleTeacher.ledger")
@SaCheckPermission(value = {"singleTeacher.ledger", "h5.singleTeacher.ledger"}, mode = SaMode.OR)
@ApiOperation("详情")
public Result detail(@Param("id") String id) {
SingleTeacher singleTeacher = dao.fetch(SingleTeacher.class, id);
@@ -83,7 +90,7 @@ public class SingleTeacherLedgerController {
}
@At
@SaCheckPermission("singleTeacher.ledger")
@SaCheckPermission(value = {"singleTeacher.ledger", "h5.singleTeacher.ledger"}, mode = SaMode.OR)
@ApiOperation("查询非单身教工人员")
public Result queryNotSingleTeacher(@Param("key") String key) {
Sql sql = Sqls.create("select id,username,loginname,mobile,education,birthday from sys_user $condition");
@@ -5,6 +5,7 @@ import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.enmus.ExcelType;
import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.annotation.SaMode;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.excel.EasyExcel;
@@ -70,9 +71,15 @@ public class ThirtyTeachController {
public void index() {
}
@At("/h5")
@Ok("beetl:/platform/zhghh5/staffmanage/thirtyTeach/manage/index.html")
@SaCheckPermission("h5.thirtyTeach.manage")
public void h5Index() {
}
@At
@ApiOperation("分页查询")
@SaCheckPermission("thirtyTeach.manage")
@SaCheckPermission(value = {"thirtyTeach.manage", "h5.thirtyTeach.manage"}, mode = SaMode.OR)
public Result pageData(ThirtyTeachPageForm pageForm) {
Sql sql = thirtyTeachService.getSql(pageForm);
Pagination pagination = thirtyTeachService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql);
@@ -98,7 +105,7 @@ public class ThirtyTeachController {
@At
@ApiOperation("批量删除三十龄教工")
@SaCheckPermission("thirtyTeach.manage")
@SaCheckPermission(value = {"thirtyTeach.manage", "h5.thirtyTeach.manage"}, mode = SaMode.OR)
@SLog(type = "30龄教工-教职工管理", tag = "批量删除三十龄教工",msg = "批量删除三十龄教工${args[0]}")
public Result doBatchDelete(@Param("userIds") String[] userIds) {
List<String> userIdList = Arrays.asList(userIds);
@@ -156,7 +163,7 @@ public class ThirtyTeachController {
@At
@ApiOperation("修改荣誉证办理年月")
@SaCheckPermission("thirtyTeach.manage")
@SaCheckPermission(value = {"thirtyTeach.manage", "h5.thirtyTeach.manage"}, mode = SaMode.OR)
public Result doEditThirtyCertificateProcessingTime(String id,String thirtyCertificateProcessingTime){
sysUserService.update(Chain.make("thirtyCertificateProcessingTime", thirtyCertificateProcessingTime), Cnd.where("id", "=", id));
return Result.success();
@@ -0,0 +1,330 @@
-- 申诉管理初始化脚本
-- 说明:本脚本依赖系统基础表 sys_menu、sys_role 已存在;如 sys_role_menu 不存在会自动创建。
SET @now := CAST(UNIX_TIMESTAMP(NOW()) * 1000 AS UNSIGNED);
CREATE TABLE IF NOT EXISTS `appeal_type` (
`id` varchar(32) NOT NULL COMMENT 'ID',
`name` varchar(100) DEFAULT NULL COMMENT '类型名称',
`sort` int DEFAULT 0 COMMENT '排序',
`enabled` tinyint(1) DEFAULT 1 COMMENT '是否启用',
`remark` varchar(500) DEFAULT NULL COMMENT '备注',
`createdBy` varchar(32) DEFAULT NULL COMMENT '创建人',
`createdAt` bigint DEFAULT NULL COMMENT '创建时间',
`updatedBy` varchar(32) DEFAULT NULL COMMENT '修改人',
`updatedAt` bigint DEFAULT NULL COMMENT '修改时间',
`delFlag` tinyint(1) DEFAULT 0 COMMENT '删除标记',
PRIMARY KEY (`id`),
KEY `INDEX_APPEAL_TYPE_ENABLED` (`enabled`),
KEY `INDEX_APPEAL_TYPE_SORT` (`sort`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='申诉类型';
CREATE TABLE IF NOT EXISTS `appeal_info` (
`id` varchar(32) NOT NULL COMMENT 'ID',
`userId` varchar(32) DEFAULT NULL COMMENT '申诉人ID',
`userName` varchar(50) DEFAULT NULL COMMENT '申诉人姓名',
`loginName` varchar(50) DEFAULT NULL COMMENT '申诉人工号',
`sex` varchar(20) DEFAULT NULL COMMENT '性别',
`birthday` date DEFAULT NULL COMMENT '出生年月',
`political` varchar(100) DEFAULT NULL COMMENT '政治面貌',
`unitId` varchar(32) DEFAULT NULL COMMENT '所在单位ID',
`unitName` varchar(100) DEFAULT NULL COMMENT '所在单位',
`positionName` varchar(100) DEFAULT NULL COMMENT '岗位',
`address` varchar(300) DEFAULT NULL COMMENT '住址',
`mobile` varchar(100) DEFAULT NULL COMMENT '联系方式',
`appealTypeId` varchar(32) DEFAULT NULL COMMENT '申诉类型ID',
`appealTypeName` varchar(100) DEFAULT NULL COMMENT '申诉类型名称',
`defendantUnitId` varchar(32) DEFAULT NULL COMMENT '被申诉单位ID',
`defendantUnitName` varchar(100) DEFAULT NULL COMMENT '被申诉单位',
`defendantAddress` varchar(300) DEFAULT NULL COMMENT '被申诉单位地址',
`defendantContact` varchar(100) DEFAULT NULL COMMENT '被申诉单位联系方式',
`title` varchar(200) DEFAULT NULL COMMENT '申诉标题',
`appealMatter` text COMMENT '申诉事项',
`reason` text COMMENT '申诉理由',
`request` text COMMENT '申诉要求',
`applyDate` date DEFAULT NULL COMMENT '申请日期',
`signature` varchar(255) DEFAULT NULL COMMENT '申诉人签名',
`files` json COMMENT '附件',
`state` varchar(50) DEFAULT NULL COMMENT '业务状态',
`acceptUserId` varchar(32) DEFAULT NULL COMMENT '受理人ID',
`acceptUserName` varchar(50) DEFAULT NULL COMMENT '受理人姓名',
`acceptTime` datetime DEFAULT NULL COMMENT '受理时间',
`decisionContent` text COMMENT '处理决定',
`legalBasis` text COMMENT '事实理由及适用依据',
`decisionDate` date DEFAULT NULL COMMENT '作出决定日期',
`decisionFiles` json COMMENT '决定书附件',
`executeUserId` varchar(32) DEFAULT NULL COMMENT '执行人ID',
`executeUserName` varchar(50) DEFAULT NULL COMMENT '执行人姓名',
`executeTime` datetime DEFAULT NULL COMMENT '执行时间',
`executeContent` text COMMENT '执行情况',
`executeFiles` json COMMENT '执行附件',
`createdBy` varchar(32) DEFAULT NULL COMMENT '创建人',
`createdAt` bigint DEFAULT NULL COMMENT '创建时间',
`updatedBy` varchar(32) DEFAULT NULL COMMENT '修改人',
`updatedAt` bigint DEFAULT NULL COMMENT '修改时间',
`delFlag` tinyint(1) DEFAULT 0 COMMENT '删除标记',
PRIMARY KEY (`id`),
KEY `INDEX_APPEAL_INFO_USER` (`userId`),
KEY `INDEX_APPEAL_INFO_UNIT` (`unitId`),
KEY `INDEX_APPEAL_INFO_TYPE` (`appealTypeId`),
KEY `INDEX_APPEAL_INFO_DEFENDANT_UNIT` (`defendantUnitId`),
KEY `INDEX_APPEAL_INFO_STATE` (`state`),
KEY `INDEX_APPEAL_INFO_APPLY_DATE` (`applyDate`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='教职工申诉';
CREATE TABLE IF NOT EXISTS `sys_role_menu` (
`roleId` varchar(32) NOT NULL,
`menuId` varchar(32) NOT NULL,
KEY `INDEX_SYS_ROLE_MENU_ROLE` (`roleId`),
KEY `INDEX_SYS_ROLE_MENU_MENU` (`menuId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='角色菜单关系';
INSERT INTO `sys_role` (`id`, `name`, `code`, `aliasName`, `disabled`, `unitid`, `note`, `sort`, `createdBy`, `createdAt`, `updatedBy`, `updatedAt`, `delFlag`)
SELECT 'a0e9c002000000000000000000000001', '申诉处理委员会成员', 'APPEAL_COMMITTEE_MEMBER', 'AppealCommitteeMember', 0, NULL, '申诉受理环节处理角色', 60, '', @now, '', @now, 0
FROM DUAL
WHERE NOT EXISTS (SELECT 1 FROM `sys_role` WHERE `code` = 'APPEAL_COMMITTEE_MEMBER');
INSERT INTO `sys_menu` (`id`, `parentId`, `path`, `name`, `aliasName`, `type`, `href`, `target`, `icon`, `showit`, `disabled`, `permission`, `note`, `location`, `hasChildren`, `createdBy`, `createdAt`, `updatedBy`, `updatedAt`, `delFlag`, `platform`)
SELECT 'a0e9c001000000000000000000000001', '', '9901', '申诉管理', 'Appeal', 'menu', '', '', 'ti-comments', 1, 0, 'appeal', NULL, 9901, 1, '', @now, '', @now, 0, 'PC'
FROM DUAL
WHERE NOT EXISTS (SELECT 1 FROM `sys_menu` WHERE `permission` = 'appeal' OR `path` = '9901');
INSERT INTO `sys_menu` (`id`, `parentId`, `path`, `name`, `aliasName`, `type`, `href`, `target`, `icon`, `showit`, `disabled`, `permission`, `note`, `location`, `hasChildren`, `createdBy`, `createdAt`, `updatedBy`, `updatedAt`, `delFlag`, `platform`)
SELECT 'a0e9c001000000000000000000000002', parent.`id`, '99010001', '申诉类型', 'AppealType', 'menu', '/platform/appeal/type', 'data-pjax', '', 1, 0, 'appeal.type', NULL, 990101, 0, '', @now, '', @now, 0, 'PC'
FROM `sys_menu` parent
WHERE parent.`permission` = 'appeal'
AND NOT EXISTS (SELECT 1 FROM `sys_menu` WHERE `permission` = 'appeal.type' OR `path` = '99010001');
INSERT INTO `sys_menu` (`id`, `parentId`, `path`, `name`, `aliasName`, `type`, `href`, `target`, `icon`, `showit`, `disabled`, `permission`, `note`, `location`, `hasChildren`, `createdBy`, `createdAt`, `updatedBy`, `updatedAt`, `delFlag`, `platform`)
SELECT 'a0e9c001000000000000000000000003', parent.`id`, '99010002', '提交申诉', 'AppealWrite', 'menu', '/platform/appeal/write', 'data-pjax', '', 1, 0, 'appeal.write', NULL, 990102, 0, '', @now, '', @now, 0, 'PC'
FROM `sys_menu` parent
WHERE parent.`permission` = 'appeal'
AND NOT EXISTS (SELECT 1 FROM `sys_menu` WHERE `permission` = 'appeal.write' OR `path` = '99010002');
INSERT INTO `sys_menu` (`id`, `parentId`, `path`, `name`, `aliasName`, `type`, `href`, `target`, `icon`, `showit`, `disabled`, `permission`, `note`, `location`, `hasChildren`, `createdBy`, `createdAt`, `updatedBy`, `updatedAt`, `delFlag`, `platform`)
SELECT 'a0e9c001000000000000000000000004', parent.`id`, '99010003', '我的申诉', 'AppealMine', 'menu', '/platform/appeal/mine', 'data-pjax', '', 1, 0, 'appeal.mine', NULL, 990103, 0, '', @now, '', @now, 0, 'PC'
FROM `sys_menu` parent
WHERE parent.`permission` = 'appeal'
AND NOT EXISTS (SELECT 1 FROM `sys_menu` WHERE `permission` = 'appeal.mine' OR `path` = '99010003');
INSERT INTO `sys_menu` (`id`, `parentId`, `path`, `name`, `aliasName`, `type`, `href`, `target`, `icon`, `showit`, `disabled`, `permission`, `note`, `location`, `hasChildren`, `createdBy`, `createdAt`, `updatedBy`, `updatedAt`, `delFlag`, `platform`)
SELECT 'a0e9c001000000000000000000000005', parent.`id`, '99010004', '申诉受理', 'AppealAccept', 'menu', '/platform/appeal/accept', 'data-pjax', '', 1, 0, 'appeal.accept', NULL, 990104, 0, '', @now, '', @now, 0, 'PC'
FROM `sys_menu` parent
WHERE parent.`permission` = 'appeal'
AND NOT EXISTS (SELECT 1 FROM `sys_menu` WHERE `permission` = 'appeal.accept' OR `path` = '99010004');
INSERT INTO `sys_menu` (`id`, `parentId`, `path`, `name`, `aliasName`, `type`, `href`, `target`, `icon`, `showit`, `disabled`, `permission`, `note`, `location`, `hasChildren`, `createdBy`, `createdAt`, `updatedBy`, `updatedAt`, `delFlag`, `platform`)
SELECT 'a0e9c001000000000000000000000006', parent.`id`, '99010005', '申诉执行', 'AppealExecute', 'menu', '/platform/appeal/execute', 'data-pjax', '', 1, 0, 'appeal.execute', NULL, 990105, 0, '', @now, '', @now, 0, 'PC'
FROM `sys_menu` parent
WHERE parent.`permission` = 'appeal'
AND NOT EXISTS (SELECT 1 FROM `sys_menu` WHERE `permission` = 'appeal.execute' OR `path` = '99010005');
INSERT INTO `sys_menu` (`id`, `parentId`, `path`, `name`, `aliasName`, `type`, `href`, `target`, `icon`, `showit`, `disabled`, `permission`, `note`, `location`, `hasChildren`, `createdBy`, `createdAt`, `updatedBy`, `updatedAt`, `delFlag`, `platform`)
SELECT 'a0e9c001000000000000000000000007', parent.`id`, '99010006', '查询统计', 'AppealQuery', 'menu', '/platform/appeal/query', 'data-pjax', '', 1, 0, 'appeal.query', NULL, 990106, 0, '', @now, '', @now, 0, 'PC'
FROM `sys_menu` parent
WHERE parent.`permission` = 'appeal'
AND NOT EXISTS (SELECT 1 FROM `sys_menu` WHERE `permission` = 'appeal.query' OR `path` = '99010006');
UPDATE `sys_menu`
SET `platform` = 'PC'
WHERE `permission` IN ('appeal', 'appeal.type', 'appeal.write', 'appeal.mine', 'appeal.accept', 'appeal.execute', 'appeal.query')
AND (`platform` IS NULL OR `platform` = '');
INSERT INTO `appeal_type` (`id`, `name`, `sort`, `enabled`, `remark`, `createdBy`, `createdAt`, `updatedBy`, `updatedAt`, `delFlag`)
SELECT 'a0e9c003000000000000000000000001', '处分', 1, 1, NULL, '', @now, '', @now, 0
FROM DUAL
WHERE NOT EXISTS (SELECT 1 FROM `appeal_type` WHERE `name` = '处分');
INSERT INTO `appeal_type` (`id`, `name`, `sort`, `enabled`, `remark`, `createdBy`, `createdAt`, `updatedBy`, `updatedAt`, `delFlag`)
SELECT 'a0e9c003000000000000000000000002', '岗位聘用', 2, 1, NULL, '', @now, '', @now, 0
FROM DUAL
WHERE NOT EXISTS (SELECT 1 FROM `appeal_type` WHERE `name` = '岗位聘用');
INSERT INTO `appeal_type` (`id`, `name`, `sort`, `enabled`, `remark`, `createdBy`, `createdAt`, `updatedBy`, `updatedAt`, `delFlag`)
SELECT 'a0e9c003000000000000000000000003', '撤销奖励', 3, 1, NULL, '', @now, '', @now, 0
FROM DUAL
WHERE NOT EXISTS (SELECT 1 FROM `appeal_type` WHERE `name` = '撤销奖励');
INSERT INTO `appeal_type` (`id`, `name`, `sort`, `enabled`, `remark`, `createdBy`, `createdAt`, `updatedBy`, `updatedAt`, `delFlag`)
SELECT 'a0e9c003000000000000000000000004', '考核结果定为基本合格或不合格', 4, 1, NULL, '', @now, '', @now, 0
FROM DUAL
WHERE NOT EXISTS (SELECT 1 FROM `appeal_type` WHERE `name` = '考核结果定为基本合格或不合格');
INSERT INTO `appeal_type` (`id`, `name`, `sort`, `enabled`, `remark`, `createdBy`, `createdAt`, `updatedBy`, `updatedAt`, `delFlag`)
SELECT 'a0e9c003000000000000000000000005', '未按国家规定确定或扣减工资福利待遇', 5, 1, NULL, '', @now, '', @now, 0
FROM DUAL
WHERE NOT EXISTS (SELECT 1 FROM `appeal_type` WHERE `name` = '未按国家规定确定或扣减工资福利待遇');
INSERT INTO `appeal_type` (`id`, `name`, `sort`, `enabled`, `remark`, `createdBy`, `createdAt`, `updatedBy`, `updatedAt`, `delFlag`)
SELECT 'a0e9c003000000000000000000000006', '法律法规和规章制度规定可以提出申诉的其他事项', 6, 1, NULL, '', @now, '', @now, 0
FROM DUAL
WHERE NOT EXISTS (SELECT 1 FROM `appeal_type` WHERE `name` = '法律法规和规章制度规定可以提出申诉的其他事项');
/* todo 系统上线时手动录入工作流程
INSERT INTO `wf_process_category` (`id`, `name`, `picIcon`, `icon`, `createdBy`, `createdAt`, `updatedBy`, `updatedAt`, `delFlag`)
SELECT 'a0e9c004000000000000000000000001', '申诉管理', NULL, 'ti-comments', '', @now, '', @now, 0
FROM DUAL
WHERE NOT EXISTS (SELECT 1 FROM `wf_process_category` WHERE `id` = 'a0e9c004000000000000000000000001' OR `name` = '申诉管理');
SET @appeal_flow_json := '{
"name": "SSGL",
"displayName": "申诉管理",
"type": "appeal",
"category": "a0e9c004000000000000000000000001",
"description": "教职工申诉审核流程",
"instanceUrl": "/platform/appeal/write",
"instanceViewUrl": "/platform/appeal/mine",
"nodes": [
{
"id": "startTask",
"type": "snaker:start",
"x": 120,
"y": 160,
"text": {"value": "提交申诉"},
"properties": {"width": 90, "height": 50}
},
{
"id": "appealAccept",
"type": "snaker:task",
"x": 330,
"y": 160,
"text": {"value": "受理"},
"properties": {
"width": 120,
"height": 60,
"form": "/platform/appeal/accept",
"taskType": 0,
"performType": 0,
"assignmentHandler": "com.budwk.app.flow.handler.FlowAppealCommitteeAssignmentHandler"
}
},
{
"id": "appealExecute",
"type": "snaker:task",
"x": 560,
"y": 160,
"text": {"value": "执行"},
"properties": {
"width": 120,
"height": 60,
"form": "/platform/appeal/execute",
"taskType": 0,
"performType": 0,
"assignmentHandler": "com.budwk.app.flow.handler.FlowAppealDefendantUnitAssignmentHandler"
}
},
{
"id": "endTask",
"type": "snaker:end",
"x": 780,
"y": 160,
"text": {"value": "结束"},
"properties": {"width": 90, "height": 50}
}
],
"edges": [
{
"id": "edge_start_accept",
"type": "polyline",
"sourceNodeId": "startTask",
"targetNodeId": "appealAccept",
"properties": {},
"text": {"value": ""},
"startPoint": {"x": 165, "y": 160},
"endPoint": {"x": 270, "y": 160},
"pointsList": [{"x": 165, "y": 160}, {"x": 270, "y": 160}]
},
{
"id": "edge_accept_execute",
"type": "polyline",
"sourceNodeId": "appealAccept",
"targetNodeId": "appealExecute",
"properties": {"expr": "#submitType==1"},
"text": {"value": "受理"},
"startPoint": {"x": 390, "y": 160},
"endPoint": {"x": 500, "y": 160},
"pointsList": [{"x": 390, "y": 160}, {"x": 500, "y": 160}]
},
{
"id": "edge_accept_end",
"type": "polyline",
"sourceNodeId": "appealAccept",
"targetNodeId": "endTask",
"properties": {"expr": "#submitType==2"},
"text": {"value": "不予受理"},
"startPoint": {"x": 330, "y": 220},
"endPoint": {"x": 780, "y": 220},
"pointsList": [{"x": 330, "y": 220}, {"x": 780, "y": 220}, {"x": 780, "y": 185}]
},
{
"id": "edge_execute_end",
"type": "polyline",
"sourceNodeId": "appealExecute",
"targetNodeId": "endTask",
"properties": {},
"text": {"value": ""},
"startPoint": {"x": 620, "y": 160},
"endPoint": {"x": 735, "y": 160},
"pointsList": [{"x": 620, "y": 160}, {"x": 735, "y": 160}]
}
]
}';
INSERT INTO `wf_process_define` (`name`, `displayName`, `type`, `category`, `instanceUrl`, `instanceViewUrl`, `icon`, `state`, `content`, `version`, `pinyinName`, `description`, `createdBy`, `createdAt`, `updatedBy`, `updatedAt`, `delFlag`)
SELECT 'SSGL', '申诉管理', 'appeal', 'a0e9c004000000000000000000000001', '/platform/appeal/write', '/platform/appeal/mine', 'ti-comments', 1, @appeal_flow_json, 1, 'S', '教职工申诉审核流程', '', @now, '', @now, 0
FROM DUAL
WHERE NOT EXISTS (SELECT 1 FROM `wf_process_define` WHERE `name` = 'SSGL');
UPDATE `wf_process_define`
SET `state` = 1,
`type` = 'appeal',
`category` = 'a0e9c004000000000000000000000001',
`pinyinName` = 'S',
`icon` = 'ti-comments',
`updatedAt` = @now
WHERE `name` = 'SSGL'
AND (`state` IS NULL OR `state` <> 1 OR IFNULL(`type`, '') <> 'appeal' OR IFNULL(`category`, '') <> 'a0e9c004000000000000000000000001' OR IFNULL(`pinyinName`, '') <> 'S' OR IFNULL(`icon`, '') <> 'ti-comments');
UPDATE `wf_process_define`
SET `content` = @appeal_flow_json,
`displayName` = IFNULL(`displayName`, '申诉管理'),
`instanceUrl` = IFNULL(`instanceUrl`, '/platform/appeal/write'),
`instanceViewUrl` = IFNULL(`instanceViewUrl`, '/platform/appeal/mine'),
`updatedAt` = @now
WHERE `name` = 'SSGL'
AND (`content` IS NULL OR `content` = '');*/
/*
INSERT INTO `sys_role_menu` (`roleId`, `menuId`)
SELECT r.`id`, m.`id`
FROM `sys_role` r
JOIN `sys_menu` m ON m.`permission` IN ('appeal', 'appeal.write', 'appeal.mine')
WHERE r.`code` = 'MEMBER'
AND NOT EXISTS (SELECT 1 FROM `sys_role_menu` rm WHERE rm.`roleId` = r.`id` AND rm.`menuId` = m.`id`);
INSERT INTO `sys_role_menu` (`roleId`, `menuId`)
SELECT r.`id`, m.`id`
FROM `sys_role` r
JOIN `sys_menu` m ON m.`permission` IN ('appeal', 'appeal.accept', 'appeal.query')
WHERE r.`code` = 'APPEAL_COMMITTEE_MEMBER'
AND NOT EXISTS (SELECT 1 FROM `sys_role_menu` rm WHERE rm.`roleId` = r.`id` AND rm.`menuId` = m.`id`);
INSERT INTO `sys_role_menu` (`roleId`, `menuId`)
SELECT r.`id`, m.`id`
FROM `sys_role` r
JOIN `sys_menu` m ON m.`permission` IN ('appeal', 'appeal.execute', 'appeal.query')
WHERE r.`code` = 'BRANCH_UNION_ADMIN'
AND NOT EXISTS (SELECT 1 FROM `sys_role_menu` rm WHERE rm.`roleId` = r.`id` AND rm.`menuId` = m.`id`);
INSERT INTO `sys_role_menu` (`roleId`, `menuId`)
SELECT r.`id`, m.`id`
FROM `sys_role` r
JOIN `sys_menu` m ON m.`permission` IN ('appeal', 'appeal.type', 'appeal.write', 'appeal.mine', 'appeal.accept', 'appeal.execute', 'appeal.query')
WHERE r.`code` IN ('SYSADMIN', 'SCHOOL_UNION_ADMIN', 'SCHOOL_UNION_DC_ADMIN')
AND NOT EXISTS (SELECT 1 FROM `sys_role_menu` rm WHERE rm.`roleId` = r.`id` AND rm.`menuId` = m.`id`);
*/
INSERT INTO `sys_role_menu` (`roleId`, `menuId`)
SELECT r.`id`, m.`id`
FROM `sys_role` r
JOIN `sys_menu` m ON m.`permission` IN ('appeal', 'appeal.type', 'appeal.write', 'appeal.mine', 'appeal.accept', 'appeal.execute', 'appeal.query')
WHERE r.`code` IN ('SYSADMIN')
AND NOT EXISTS (SELECT 1 FROM `sys_role_menu` rm WHERE rm.`roleId` = r.`id` AND rm.`menuId` = m.`id`);
@@ -0,0 +1,146 @@
-- 申诉管理手机端初始化脚本
-- 说明:PC端申诉模块已部署时,只需要执行本脚本新增手机端入口和权限。
SET @now := CAST(UNIX_TIMESTAMP(NOW()) * 1000 AS UNSIGNED);
INSERT INTO `sys_module` (`id`, `platform`, `name`, `icon`, `faIcon`, `sortNum`, `createdBy`, `createdAt`, `updatedBy`, `updatedAt`, `delFlag`)
SELECT 'a0e9c005000000000000000000000001', 'H5', '民主管理', NULL, 'ti-comments', 9901, '', @now, '', @now, 0
FROM DUAL
WHERE NOT EXISTS (SELECT 1 FROM `sys_module` WHERE `id` = 'a0e9c005000000000000000000000001' OR (`platform` = 'H5' AND `name` = '民主管理'));
INSERT INTO `sys_menu` (`id`, `parentId`, `path`, `name`, `initialPinyinName`, `aliasName`, `type`, `href`, `target`, `icon`, `picIcon`, `showit`, `disabled`, `permission`, `note`, `location`, `hasChildren`, `createdBy`, `createdAt`, `updatedBy`, `updatedAt`, `delFlag`, `platform`, `moduleId`, `isRecommendApp`, `isRecommendService`)
SELECT 'a0e9c006000000000000000000000001', '', 'H9901', '申诉管理', 'S', '', 'menu', '/platform/appeal/h5', 'data-pjax', 'comment-o', NULL, 1, 0, 'h5.appeal', NULL, 9901, 1, '', @now, '', @now, 0, 'H5', module.`id`, 0, 0
FROM `sys_module` module
WHERE module.`platform` = 'H5'
AND module.`name` = '民主管理'
AND NOT EXISTS (SELECT 1 FROM `sys_menu` WHERE `permission` = 'h5.appeal' OR `path` = 'H9901');
INSERT INTO `sys_menu` (`id`, `parentId`, `path`, `name`, `initialPinyinName`, `aliasName`, `type`, `href`, `target`, `icon`, `picIcon`, `showit`, `disabled`, `permission`, `note`, `location`, `hasChildren`, `createdBy`, `createdAt`, `updatedBy`, `updatedAt`, `delFlag`, `platform`, `moduleId`, `isRecommendApp`, `isRecommendService`)
SELECT 'a0e9c006000000000000000000000002', 'a0e9c006000000000000000000000001', 'H9901H9902', '提交申诉', 'T', '', 'menu', '/platform/appeal/write/h5', 'data-pjax', 'edit', NULL, 1, 0, 'h5.appeal.write', NULL, 9902, 0, '', @now, '', @now, 0, 'H5', module.`id`, 0, 0
FROM `sys_module` module
WHERE module.`platform` = 'H5'
AND module.`name` = '民主管理'
AND NOT EXISTS (SELECT 1 FROM `sys_menu` WHERE `permission` = 'h5.appeal.write' OR `path` = 'H9902');
INSERT INTO `sys_menu` (`id`, `parentId`, `path`, `name`, `initialPinyinName`, `aliasName`, `type`, `href`, `target`, `icon`, `picIcon`, `showit`, `disabled`, `permission`, `note`, `location`, `hasChildren`, `createdBy`, `createdAt`, `updatedBy`, `updatedAt`, `delFlag`, `platform`, `moduleId`, `isRecommendApp`, `isRecommendService`)
SELECT 'a0e9c006000000000000000000000003', 'a0e9c006000000000000000000000001', 'H9901H9903', '我的申诉', 'W', '', 'menu', '/platform/appeal/mine/h5', 'data-pjax', 'records', NULL, 1, 0, 'h5.appeal.mine', NULL, 9903, 0, '', @now, '', @now, 0, 'H5', module.`id`, 0, 0
FROM `sys_module` module
WHERE module.`platform` = 'H5'
AND module.`name` = '民主管理'
AND NOT EXISTS (SELECT 1 FROM `sys_menu` WHERE `permission` = 'h5.appeal.mine' OR `path` = 'H9903');
INSERT INTO `sys_menu` (`id`, `parentId`, `path`, `name`, `initialPinyinName`, `aliasName`, `type`, `href`, `target`, `icon`, `picIcon`, `showit`, `disabled`, `permission`, `note`, `location`, `hasChildren`, `createdBy`, `createdAt`, `updatedBy`, `updatedAt`, `delFlag`, `platform`, `moduleId`, `isRecommendApp`, `isRecommendService`)
SELECT 'a0e9c006000000000000000000000004', 'a0e9c006000000000000000000000001', 'H9901H9904', '申诉受理', 'S', '', 'menu', '/platform/appeal/accept/h5', 'data-pjax', 'todo-list-o', NULL, 1, 0, 'h5.appeal.accept', NULL, 9904, 0, '', @now, '', @now, 0, 'H5', module.`id`, 0, 0
FROM `sys_module` module
WHERE module.`platform` = 'H5'
AND module.`name` = '民主管理'
AND NOT EXISTS (SELECT 1 FROM `sys_menu` WHERE `permission` = 'h5.appeal.accept' OR `path` = 'H9904');
INSERT INTO `sys_menu` (`id`, `parentId`, `path`, `name`, `initialPinyinName`, `aliasName`, `type`, `href`, `target`, `icon`, `picIcon`, `showit`, `disabled`, `permission`, `note`, `location`, `hasChildren`, `createdBy`, `createdAt`, `updatedBy`, `updatedAt`, `delFlag`, `platform`, `moduleId`, `isRecommendApp`, `isRecommendService`)
SELECT 'a0e9c006000000000000000000000005', 'a0e9c006000000000000000000000001', 'H9901H9905', '申诉执行', 'S', '', 'menu', '/platform/appeal/execute/h5', 'data-pjax', 'passed', NULL, 1, 0, 'h5.appeal.execute', NULL, 9905, 0, '', @now, '', @now, 0, 'H5', module.`id`, 0, 0
FROM `sys_module` module
WHERE module.`platform` = 'H5'
AND module.`name` = '民主管理'
AND NOT EXISTS (SELECT 1 FROM `sys_menu` WHERE `permission` = 'h5.appeal.execute' OR `path` = 'H9905');
UPDATE `sys_menu`
SET `href` = '/platform/appeal/h5',
`hasChildren` = 1,
`isRecommendService` = 1,
`updatedAt` = @now
WHERE `permission` = 'h5.appeal';
UPDATE `sys_menu`
SET `parentId` = 'a0e9c006000000000000000000000001',
`path` = CONCAT('H9901', RIGHT(`path`, 5)),
`isRecommendService` = 0,
`updatedAt` = @now
WHERE `permission` IN ('h5.appeal.write', 'h5.appeal.mine', 'h5.appeal.accept', 'h5.appeal.execute');
INSERT INTO `sys_role_menu` (`roleId`, `menuId`)
SELECT r.`id`, m.`id`
FROM `sys_role` r
JOIN `sys_menu` m ON m.`permission` IN ('h5.appeal', 'h5.appeal.write', 'h5.appeal.mine', 'h5.appeal.accept', 'h5.appeal.execute')
WHERE r.`code` = 'SYSADMIN'
AND NOT EXISTS (SELECT 1 FROM `sys_role_menu` rm WHERE rm.`roleId` = r.`id` AND rm.`menuId` = m.`id`);
INSERT INTO `sys_role_menu` (`roleId`, `menuId`)
SELECT r.`id`, m.`id`
FROM `sys_role` r
JOIN `sys_menu` m ON m.`permission` IN ('h5.appeal', 'h5.appeal.write', 'h5.appeal.mine')
WHERE r.`code` IN ('SYSADMIN', 'MEMBER')
AND NOT EXISTS (SELECT 1 FROM `sys_role_menu` rm WHERE rm.`roleId` = r.`id` AND rm.`menuId` = m.`id`);
INSERT INTO `sys_role_menu` (`roleId`, `menuId`)
SELECT r.`id`, m.`id`
FROM `sys_role` r
JOIN `sys_menu` m ON m.`permission` IN ('h5.appeal', 'h5.appeal.accept', 'h5.appeal.execute')
WHERE r.`code` = 'APPEAL_COMMITTEE_MEMBER'
AND NOT EXISTS (SELECT 1 FROM `sys_role_menu` rm WHERE rm.`roleId` = r.`id` AND rm.`menuId` = m.`id`);
/* 分工会管理员不需要权限
INSERT INTO `sys_role_menu` (`roleId`, `menuId`)
SELECT r.`id`, m.`id`
FROM `sys_role` r
JOIN `sys_menu` m ON m.`permission` IN ('h5.appeal', 'h5.appeal.execute')
WHERE r.`code` = 'BRANCH_UNION_ADMIN'
AND NOT EXISTS (SELECT 1 FROM `sys_role_menu` rm WHERE rm.`roleId` = r.`id` AND rm.`menuId` = m.`id`);*/
UPDATE `wf_process_define`
SET `content` = REPLACE(`content`, '"form": "/platform/appeal/accept",', '"form": "/platform/appeal/accept", "h5form": "/platform/appeal/accept/h5",'),
`updatedAt` = @now
WHERE `name` = 'SSGL'
AND `content` LIKE '%"id": "appealAccept"%'
AND `content` LIKE '%"form": "/platform/appeal/accept"%'
AND `content` NOT LIKE '%"h5form": "/platform/appeal/accept/h5"%';
UPDATE `wf_process_define`
SET `content` = REPLACE(`content`, '"form":"/platform/appeal/accept",', '"form":"/platform/appeal/accept","h5form":"/platform/appeal/accept/h5",'),
`updatedAt` = @now
WHERE `name` = 'SSGL'
AND `content` LIKE '%"id":"appealAccept"%'
AND `content` LIKE '%"form":"/platform/appeal/accept"%'
AND `content` NOT LIKE '%"h5form":"/platform/appeal/accept/h5"%'
AND `content` NOT LIKE '%"h5form": "/platform/appeal/accept/h5"%';
UPDATE `wf_process_define`
SET `content` = REPLACE(`content`, '"form": "/platform/appeal/execute",', '"form": "/platform/appeal/execute", "h5form": "/platform/appeal/execute/h5",'),
`updatedAt` = @now
WHERE `name` = 'SSGL'
AND `content` LIKE '%"id": "appealExecute"%'
AND `content` LIKE '%"form": "/platform/appeal/execute"%'
AND `content` NOT LIKE '%"h5form": "/platform/appeal/execute/h5"%';
UPDATE `wf_process_define`
SET `content` = REPLACE(`content`, '"form":"/platform/appeal/execute",', '"form":"/platform/appeal/execute","h5form":"/platform/appeal/execute/h5",'),
`updatedAt` = @now
WHERE `name` = 'SSGL'
AND `content` LIKE '%"id":"appealExecute"%'
AND `content` LIKE '%"form":"/platform/appeal/execute"%'
AND `content` NOT LIKE '%"h5form":"/platform/appeal/execute/h5"%'
AND `content` NOT LIKE '%"h5form": "/platform/appeal/execute/h5"%';
UPDATE `wf_process_define`
SET `h5InstanceUrl` = IFNULL(NULLIF(`h5InstanceUrl`, ''), '/platform/appeal/write/h5'),
`h5InstanceViewUrl` = IFNULL(NULLIF(`h5InstanceViewUrl`, ''), '/platform/appeal/mine/h5'),
`updatedAt` = @now
WHERE `name` = 'SSGL'
AND (IFNULL(`h5InstanceUrl`, '') = '' OR IFNULL(`h5InstanceViewUrl`, '') = '');
UPDATE `wf_process_task` t
JOIN `wf_process_instance` ins ON ins.`id` = t.`processInstanceId`
JOIN `wf_process_define` def ON def.`id` = ins.`processDefineId`
SET t.`h5FormKey` = '/platform/appeal/accept/h5',
t.`updatedAt` = @now
WHERE def.`name` = 'SSGL'
AND t.`taskName` = 'appealAccept'
AND IFNULL(t.`h5FormKey`, '') = '';
UPDATE `wf_process_task` t
JOIN `wf_process_instance` ins ON ins.`id` = t.`processInstanceId`
JOIN `wf_process_define` def ON def.`id` = ins.`processDefineId`
SET t.`h5FormKey` = '/platform/appeal/execute/h5',
t.`updatedAt` = @now
WHERE def.`name` = 'SSGL'
AND t.`taskName` = 'appealExecute'
AND IFNULL(t.`h5FormKey`, '') = '';
@@ -0,0 +1,18 @@
-- 离退休人员档案手机端初始化脚本
-- 说明:PC端离退休人员档案已部署时,只需要执行本脚本新增手机端入口和权限,菜单挂载到H5端已有的人员管理模块。
SET @now := CAST(UNIX_TIMESTAMP(NOW()) * 1000 AS UNSIGNED);
INSERT INTO `sys_menu` (`id`, `parentId`, `path`, `name`, `initialPinyinName`, `aliasName`, `type`, `href`, `target`, `icon`, `picIcon`, `showit`, `disabled`, `permission`, `note`, `location`, `hasChildren`, `createdBy`, `createdAt`, `updatedBy`, `updatedAt`, `delFlag`, `platform`, `moduleId`, `isRecommendApp`, `isRecommendService`)
SELECT 'b1f00006000000000000000000000003', '', 'H9913', '离退休人员档案', 'L', NULL, 'menu', '/platform/retired/list/h5', 'data-pjax', 'manager-o', NULL, 1, 0, 'h5.retired.list', NULL, 9913, 0, '', @now, '', @now, 0, 'H5', module.`id`, 0, 0
FROM `sys_module` module
WHERE module.`platform` = 'H5'
AND module.`name` = '人员管理'
AND NOT EXISTS (SELECT 1 FROM `sys_menu` WHERE `permission` = 'h5.retired.list' OR `path` = 'H9913');
INSERT INTO `sys_role_menu` (`roleId`, `menuId`)
SELECT r.`id`, m.`id`
FROM `sys_role` r
JOIN `sys_menu` m ON m.`permission` = 'h5.retired.list'
WHERE r.`code` IN ('SYSADMIN', 'SCHOOL_UNION_ADMIN')
AND NOT EXISTS (SELECT 1 FROM `sys_role_menu` rm WHERE rm.`roleId` = r.`id` AND rm.`menuId` = m.`id`);
@@ -0,0 +1,18 @@
-- 单身教职工管理手机端初始化脚本
-- 说明:PC端单身教职工管理已部署时,只需要执行本脚本新增手机端入口和权限,菜单挂载到H5端已有的人员管理模块。
SET @now := CAST(UNIX_TIMESTAMP(NOW()) * 1000 AS UNSIGNED);
INSERT INTO `sys_menu` (`id`, `parentId`, `path`, `name`, `initialPinyinName`, `aliasName`, `type`, `href`, `target`, `icon`, `picIcon`, `showit`, `disabled`, `permission`, `note`, `location`, `hasChildren`, `createdBy`, `createdAt`, `updatedBy`, `updatedAt`, `delFlag`, `platform`, `moduleId`, `isRecommendApp`, `isRecommendService`)
SELECT 'b1f00006000000000000000000000001', '', 'H9911', '单身教职工管理', 'D', NULL, 'menu', '/platform/singleTeacher/ledger/h5', 'data-pjax', 'friends-o', NULL, 1, 0, 'h5.singleTeacher.ledger', NULL, 9911, 0, '', @now, '', @now, 0, 'H5', module.`id`, 0, 0
FROM `sys_module` module
WHERE module.`platform` = 'H5'
AND module.`name` = '人员管理'
AND NOT EXISTS (SELECT 1 FROM `sys_menu` WHERE `permission` = 'h5.singleTeacher.ledger' OR `path` = 'H9911');
INSERT INTO `sys_role_menu` (`roleId`, `menuId`)
SELECT r.`id`, m.`id`
FROM `sys_role` r
JOIN `sys_menu` m ON m.`permission` = 'h5.singleTeacher.ledger'
WHERE r.`code` IN ('SYSADMIN', 'SCHOOL_UNION_ADMIN')
AND NOT EXISTS (SELECT 1 FROM `sys_role_menu` rm WHERE rm.`roleId` = r.`id` AND rm.`menuId` = m.`id`);
@@ -0,0 +1,18 @@
-- 30教龄教职工管理手机端初始化脚本
-- 说明:PC端30教龄教职工管理已部署时,只需要执行本脚本新增手机端入口和权限,菜单挂载到H5端已有的人员管理模块。
SET @now := CAST(UNIX_TIMESTAMP(NOW()) * 1000 AS UNSIGNED);
INSERT INTO `sys_menu` (`id`, `parentId`, `path`, `name`, `initialPinyinName`, `aliasName`, `type`, `href`, `target`, `icon`, `picIcon`, `showit`, `disabled`, `permission`, `note`, `location`, `hasChildren`, `createdBy`, `createdAt`, `updatedBy`, `updatedAt`, `delFlag`, `platform`, `moduleId`, `isRecommendApp`, `isRecommendService`)
SELECT 'b1f00006000000000000000000000002', '', 'H9912', '30教龄教职工管理', 'S', NULL, 'menu', '/platform/thirtyTeach/manage/h5', 'data-pjax', 'medal-o', NULL, 1, 0, 'h5.thirtyTeach.manage', NULL, 9912, 0, '', @now, '', @now, 0, 'H5', module.`id`, 0, 0
FROM `sys_module` module
WHERE module.`platform` = 'H5'
AND module.`name` = '人员管理'
AND NOT EXISTS (SELECT 1 FROM `sys_menu` WHERE `permission` = 'h5.thirtyTeach.manage' OR `path` = 'H9912');
INSERT INTO `sys_role_menu` (`roleId`, `menuId`)
SELECT r.`id`, m.`id`
FROM `sys_role` r
JOIN `sys_menu` m ON m.`permission` = 'h5.thirtyTeach.manage'
WHERE r.`code` IN ('SYSADMIN', 'SCHOOL_UNION_ADMIN')
AND NOT EXISTS (SELECT 1 FROM `sys_role_menu` rm WHERE rm.`roleId` = r.`id` AND rm.`menuId` = m.`id`);
+89 -9
View File
@@ -1,17 +1,85 @@
<?xml version="1.0" encoding="UTF-8" ?>
<configuration scan="false" scanPeriod="60000" debug="false">
<!-- 定义日志文件的存储路径 -->
<!-- 日志存储路径 -->
<property name="LOG_HOME" value="./logs"/>
<!-- 控制台输出配置 -->
<!-- 控制台输出 -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<!-- INFO级别日志文件配置 -->
<!--
#################################################################################
# #
# 全文输出日志(一个文件存储)start #
# #
#################################################################################
-->
<!-- 所有级别日志写入同一个文件 -->
<appender name="ALL_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_HOME}/app.log</file>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_HOME}/app-%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>300</maxHistory>
</rollingPolicy>
</appender>
<!-- 框架日志级别(按需调整) -->
<logger name="org.eclipse.jetty" level="INFO"/>
<logger name="org.quartz" level="INFO"/>
<logger name="org.nutz" level="DEBUG"/>
<!-- root logger:DEBUG 级别,输出到控制台和全量文件 -->
<root level="DEBUG">
<appender-ref ref="STDOUT"/>
<appender-ref ref="ALL_FILE"/>
</root>
<!--
#################################################################################
# #
# 全文输出日志(一个文件存储)end #
# #
#################################################################################
-->
<!--
#################################################################################
# #
# debug、info、error分文件输出 start #
# #
#################################################################################
-->
<!-- DEBUG级别日志文件配置 -->
<!--<appender name="DEBUG_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_HOME}/debug.log</file>
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>DEBUG</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_HOME}/debug-%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>300</maxHistory>
</rollingPolicy>
</appender>
&lt;!&ndash; INFO级别日志文件配置 &ndash;&gt;
<appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_HOME}/info.log</file>
<encoder>
@@ -24,11 +92,11 @@
</filter>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_HOME}/info-%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>30</maxHistory>
<maxHistory>300</maxHistory>
</rollingPolicy>
</appender>
<!-- ERROR级别日志文件配置 -->
&lt;!&ndash; ERROR级别日志文件配置 &ndash;&gt;
<appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_HOME}/error.log</file>
<encoder>
@@ -41,19 +109,31 @@
</filter>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_HOME}/error-%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>30</maxHistory>
<maxHistory>300</maxHistory>
</rollingPolicy>
</appender>
<logger name="java" additivity="false" />
&lt;!&ndash; 框架日志级别 &ndash;&gt;
&lt;!&ndash; <logger name="java" additivity="false" />&ndash;&gt;
<logger name="org.eclipse.jetty" level="INFO"/>
<logger name="org.quartz" level="INFO"/>
<logger name="org.nutz" level="DEBUG"/>
<!-- 日志级别和appender的关联 -->
&lt;!&ndash; 日志级别和appender的关联 &ndash;&gt;
<root level="DEBUG">
<appender-ref ref="STDOUT"/>
<appender-ref ref="DEBUG_FILE"/>
<appender-ref ref="INFO_FILE"/>
<appender-ref ref="ERROR_FILE"/>
</root>
</root>-->
<!--
#################################################################################
# #
# debug、info、error分文件输出 end #
# #
#################################################################################
-->
</configuration>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

After

Width:  |  Height:  |  Size: 2.1 MiB

@@ -0,0 +1,296 @@
/**
*Desc:
*Create by: jug
*Create time:2023/11/20/16:49
*/
<template>
<div>
<el-form :model="formData" :rules="formRules" label-width="0" ref="form" class="flow-task-form">
<el-descriptions :column="3" border>
<el-descriptions-item label="工号">
<el-form-item prop="loginname">
<el-input v-model="formData.loginname" maxlength="120" size="small"></el-input>
</el-form-item>
</el-descriptions-item>
<el-descriptions-item label="姓名">
<el-form-item prop="username">
<el-input v-model="formData.username" maxlength="100" size="small"></el-input>
</el-form-item>
</el-descriptions-item>
<el-descriptions-item label="籍贯">
<el-form-item prop="nativePlace">
<el-input v-model="formData.nativePlace" maxlength="100" placeholder="请输入籍贯"></el-input>
</el-form-item>
</el-descriptions-item>
<el-descriptions-item label="性别">
<el-form-item prop="sex">
<el-radio-group v-model="formData.sex" size="small">
<el-radio border label="男">男</el-radio>
<el-radio border label="女">女</el-radio>
</el-radio-group>
</el-form-item>
</el-descriptions-item>
<el-descriptions-item label="出生日期">
<el-form-item prop="birthday">
<el-date-picker v-model="formData.birthday" type="date"
placeholder="请选择出生日期"
value-format="yyyy-MM-dd"
style="width: 100%"></el-date-picker>
</el-form-item>
</el-descriptions-item>
<el-descriptions-item label="身份证号码">
<el-form-item prop="idCard">
<el-input v-model="formData.idCard" maxlength="30" placeholder="请输入身份证号码"></el-input>
</el-form-item>
</el-descriptions-item>
<el-descriptions-item label="政治面貌">
<el-form-item prop="political">
<dict-select style="width: 100%" placeholder="请选择政治面貌" v-model="formData.political"
code="USER_POLITICAL"></dict-select>
</el-form-item>
</el-descriptions-item>
<el-descriptions-item label="参加工作时间">
<el-form-item prop="arrivalAtSchoolDate">
<el-input v-model="formData.arrivalAtSchoolDate" maxlength="20" placeholder="请输入参加工作时间"></el-input>
</el-form-item>
</el-descriptions-item>
<el-descriptions-item label="单位">
<el-form-item prop="unitId">
<el-select clearable filterable placeholder="请选择单位" style="width: 100%"
v-model="formData.unitId" @change="handleUnitChange">
<el-option :key="item.id" :label="item.name" :value="item.id"
v-for="item in units"></el-option>
</el-select>
</el-form-item>
</el-descriptions-item>
<el-descriptions-item label="工会">
<el-form-item prop="unionName">
<el-input v-model="formData.unionName" readonly placeholder="系统自动显示"></el-input>
</el-form-item>
</el-descriptions-item>
<el-descriptions-item label="职务">
<el-form-item prop="position">
<el-input v-model="formData.position" maxlength="50" placeholder="请输入职务"></el-input>
</el-form-item>
</el-descriptions-item>
<el-descriptions-item label="职称">
<el-form-item prop="professionalTitle">
<el-input v-model="formData.professionalTitle" maxlength="50" placeholder="请输入职称"></el-input>
</el-form-item>
</el-descriptions-item>
<el-descriptions-item label="退休时间">
<el-form-item prop="retireDate">
<el-date-picker v-model="formData.retireDate" type="date"
placeholder="请选择退休时间"
value-format="yyyy-MM-dd"
style="width: 100%"></el-date-picker>
</el-form-item>
</el-descriptions-item>
<el-descriptions-item label="电话">
<el-form-item prop="mobile">
<el-input v-model="formData.mobile" maxlength="32" placeholder="请输入电话"></el-input>
</el-form-item>
</el-descriptions-item>
<el-descriptions-item label="工龄">
<el-form-item>
<el-input :value="workAgeText(formData.arrivalAtSchoolDate)" readonly placeholder="系统自动计算"></el-input>
</el-form-item>
</el-descriptions-item>
<el-descriptions-item label="在职状态">
<el-form-item prop="userState">
<dict-select v-model="formData.userState" code="USER_STATE" style="width: 100%"></dict-select>
</el-form-item>
</el-descriptions-item>
<el-descriptions-item label="人员类型">
<el-form-item prop="personType">
<dict-select v-model="formData.personType" code="USER_PERSON_TYPE" style="width: 100%"></dict-select>
</el-form-item>
</el-descriptions-item>
</el-descriptions>
</el-form>
<el-row :gutter="10" class="mt10" type="flex" justify="end">
<el-button @click="doSubmit" type="primary">确定</el-button>
</el-row>
</div>
</template>
<script>
module.exports = {
name: "userEdit",
data() {
return {
userId: null,
units: [],
formData: this.defaultFormData(),
formRules: {
loginname: [{required: true, message: "必填", trigger: ["change", "blur"]}],
username: [{required: true, message: "必填", trigger: ["change", "blur"]}],
sex: [{required: true, message: "必填", trigger: ["change", "blur"]}],
userState: [{required: true, message: "必填", trigger: ["change", "blur"]}],
personType: [{required: true, message: "必填", trigger: ["change", "blur"]}]
}
}
},
components: {
'dict-select': httpVueLoader('/components/plugins/sysDict/DictSelect.vue?v=1.0.0')
},
methods: {
// 新增和重置共用默认值,避免页面直接修改同一个对象引用。
defaultFormData() {
return {
userState: '退休',
personType: '离退休人员',
families: []
}
},
doSubmit() {
this.$refs.form.validate(async valid => {
if (valid) {
const {code, msg} = await $.post('/platform/retired/list/doSubmit', {
user: JSON.stringify(this.formData),
userId: this.userId
})
if (code === 0) {
this.$message.success(msg)
this.$emit('finish')
} else {
this.$message.warning(msg)
}
}
})
},
reset() {
this.$set(this, "userId", null)
this.$set(this, "formData", this.defaultFormData())
this.$nextTick(() => {
if (this.$refs.form) {
this.$refs.form.clearValidate()
}
})
},
async getInfo(id) {
this.$set(this, "userId", id)
const resp = await $.post('/platform/retired/list/userInfo', {userId: this.userId})
const data = this.normalizeUserData(resp.data || {})
this.$set(this, "formData", data)
this.$nextTick(() => {
if (this.$refs.form) {
this.$refs.form.clearValidate()
}
})
},
handleUnitChange(unitId) {
const unit = this.units.find(item => item.id === unitId)
this.$set(this.formData, "unitName", unit ? unit.name : null)
const unionId = unit ? (unit.unionId || unit.unionid || null) : null
const unionName = unit ? (unit.unionName || unit.unionname || null) : null
this.$set(this.formData, "unionId", unionId)
this.$set(this.formData, "unionName", unionName)
},
addFamily() {
const families = (this.formData.families || []).concat([{}])
this.$set(this.formData, "families", families)
},
removeFamily(index) {
const families = (this.formData.families || []).slice()
families.splice(index, 1)
this.$set(this.formData, "families", families)
},
async loadUnits() {
const units = await this.$businessTool.listUnit()
this.$set(this, "units", units || [])
},
// 工龄不落库,根据参加工作时间实时计算,兼容 yyyy、yyyy-MM、yyyy-MM-dd 三种输入。
workAgeText(val) {
const start = this.parseWorkDate(val)
if (!start) {
return ""
}
const today = new Date()
let years = today.getFullYear() - start.getFullYear()
if (today.getMonth() < start.getMonth() ||
(today.getMonth() === start.getMonth() && today.getDate() < start.getDate())) {
years = years - 1
}
if (years < 0) {
years = 0
}
return years + "年"
},
parseWorkDate(val) {
if (!val) {
return null
}
const parts = String(val).split("-")
const year = Number(parts[0])
const month = parts.length > 1 ? Number(parts[1]) : 1
const day = parts.length > 2 ? Number(parts[2]) : 1
if (!year || !month || !day) {
return null
}
const date = new Date(year, month - 1, day)
if (isNaN(date.getTime())) {
return null
}
return date
},
normalizeUserData(user) {
const data = Object.assign(this.defaultFormData(), user)
// 详情数据可能来自视图或实体,统一把大小写差异字段整理成页面绑定的驼峰字段。
const fieldMap = {
idcard: "idCard",
idcardtype: "idCardType",
unitid: "unitId",
unitname: "unitName",
unionid: "unionId",
unionname: "unionName",
uniongroupid: "unionGroupId",
uniongroupname: "unionGroupName",
joinpartydate: "joinPartyDate",
nativeplace: "nativePlace",
academicdegree: "academicDegree",
professionaltitle: "professionalTitle",
arrivalatschooldate: "arrivalAtSchoolDate",
teachingtime: "teachingTime",
userstate: "userState",
persontype: "personType",
retiredate: "retireDate",
membertime: "memberTime",
personaldata: "personalData",
campusname: "campusName",
threeunitname: "threeUnitName",
retiredgroupname: "retiredGroupName",
retiredpartybranchname: "retiredPartyBranchName"
}
Object.keys(fieldMap).forEach(key => {
if ((data[fieldMap[key]] === undefined || data[fieldMap[key]] === null) && user[key] !== undefined) {
this.$set(data, fieldMap[key], user[key])
}
})
if (typeof data.families === "string") {
try {
this.$set(data, "families", JSON.parse(data.families))
} catch (e) {
this.$set(data, "families", [])
}
} else if (!data.families) {
this.$set(data, "families", [])
}
return data
}
},
created() {
this.loadUnits()
}
}
</script>
<style scoped>
</style>
@@ -0,0 +1,173 @@
/**
*Desc:
*Create by: jug
*Create time:2023/7/25/9:14
*/
<template>
<div>
<el-descriptions :column="3" border>
<el-descriptions-item label="工号">{{ emptyText(formData.loginname) }}</el-descriptions-item>
<el-descriptions-item label="姓名">{{ emptyText(formData.username) }}</el-descriptions-item>
<el-descriptions-item label="籍贯">{{ emptyText(formData.nativePlace) }}</el-descriptions-item>
<el-descriptions-item label="性别">{{ emptyText(formData.sex) }}</el-descriptions-item>
<el-descriptions-item label="出生日期">{{ formatDate(formData.birthday) }}</el-descriptions-item>
<el-descriptions-item label="身份证号码">{{ emptyText(formData.idCard) }}</el-descriptions-item>
<el-descriptions-item label="政治面貌">{{ emptyText(formData.political) }}</el-descriptions-item>
<el-descriptions-item label="参加工作时间">{{ displayDate(formData.arrivalAtSchoolDate) }}</el-descriptions-item>
<el-descriptions-item label="单位">{{ emptyText(formData.unitName) }}</el-descriptions-item>
<el-descriptions-item label="工会">{{ emptyText(formData.unionName) }}</el-descriptions-item>
<el-descriptions-item label="职务">{{ emptyText(formData.position) }}</el-descriptions-item>
<el-descriptions-item label="职称">{{ emptyText(formData.professionalTitle) }}</el-descriptions-item>
<el-descriptions-item label="退休时间">{{ formatDate(formData.retireDate) }}</el-descriptions-item>
<el-descriptions-item label="电话">{{ emptyText(formData.mobile) }}</el-descriptions-item>
<el-descriptions-item label="工龄">{{ workAgeText(formData.arrivalAtSchoolDate) || '暂无' }}</el-descriptions-item>
<el-descriptions-item label="在职状态">{{ emptyText(formData.userState) }}</el-descriptions-item>
<el-descriptions-item label="人员类型">{{ emptyText(formData.personType) }}</el-descriptions-item>
</el-descriptions>
<slot></slot>
</div>
</template>
<script>
module.exports = {
name: "userInfo",
props: {
id: {
type: String,
required: true
}
},
data() {
return {
formData: {}
}
},
methods: {
formatDate(val) {
if (val) {
return moment(val).format('YYYY-MM-DD')
}
return '暂无'
},
displayDate(val) {
if (!val) {
return '暂无'
}
return String(val)
},
emptyText(val) {
if (val === undefined || val === null || val === '') {
return '暂无'
}
return val
},
// 工龄不落库,根据参加工作时间实时计算,兼容 yyyy、yyyy-MM、yyyy-MM-dd 三种输入。
workAgeText(val) {
const start = this.parseWorkDate(val)
if (!start) {
return ""
}
const today = new Date()
let years = today.getFullYear() - start.getFullYear()
if (today.getMonth() < start.getMonth() ||
(today.getMonth() === start.getMonth() && today.getDate() < start.getDate())) {
years = years - 1
}
if (years < 0) {
years = 0
}
return years + "年"
},
parseWorkDate(val) {
if (!val) {
return null
}
const parts = String(val).split("-")
const year = Number(parts[0])
const month = parts.length > 1 ? Number(parts[1]) : 1
const day = parts.length > 2 ? Number(parts[2]) : 1
if (!year || !month || !day) {
return null
}
const date = new Date(year, month - 1, day)
if (isNaN(date.getTime())) {
return null
}
return date
},
getInfo() {
if (this.id) {
$.post('/platform/retired/list/userInfo', {userId: this.id}).then(res => {
this.$set(this, "formData", this.normalizeUserData(res.data || {}))
})
} else {
this.$set(this, "formData", {})
}
},
normalizeUserData(user) {
const data = Object.assign({}, user)
// 兼容视图查询和实体查询返回字段大小写不一致的情况,保证查看页字段能正常回显。
const fieldMap = {
idcard: "idCard",
idcardtype: "idCardType",
unitid: "unitId",
unitname: "unitName",
unionid: "unionId",
unionname: "unionName",
uniongroupid: "unionGroupId",
uniongroupname: "unionGroupName",
joinpartydate: "joinPartyDate",
nativeplace: "nativePlace",
academicdegree: "academicDegree",
professionaltitle: "professionalTitle",
arrivalatschooldate: "arrivalAtSchoolDate",
teachingtime: "teachingTime",
userstate: "userState",
persontype: "personType",
retiredate: "retireDate",
membertime: "memberTime",
personaldata: "personalData",
campusname: "campusName",
threeunitname: "threeUnitName",
retiredgroupname: "retiredGroupName",
retiredpartybranchname: "retiredPartyBranchName"
}
Object.keys(fieldMap).forEach(key => {
if ((data[fieldMap[key]] === undefined || data[fieldMap[key]] === null) && user[key] !== undefined) {
this.$set(data, fieldMap[key], user[key])
}
})
if (typeof data.families === "string") {
try {
this.$set(data, "families", JSON.parse(data.families))
} catch (e) {
this.$set(data, "families", [])
}
} else if (!data.families) {
this.$set(data, "families", [])
}
return data
}
},
watch: {
id: {
handler: function (val) {
this.getInfo()
},
immediate: true
}
},
created() {
}
}
</script>
<style scoped>
</style>
@@ -0,0 +1,101 @@
<template>
<div class="v-tree-layout">
<div class="shrink" :style="'left:' + (showTree ? 'calc(20% - 15px)' : '0')" @click="shrink">
<i v-if="!showTree" class="el-icon-arrow-right"></i>
<i v-else class="el-icon-arrow-left"></i>
</div>
<div class="v-tree-layout-left" :style="{width: showTree ? '20%' : '0'}">
<slot name="tree"></slot>
</div>
<div class="v-tree-layout-right" :style="{width: showTree ? '80%' : '100%'}">
<slot></slot>
</div>
</div>
</template>
<script>
module.exports = {
props: {},
data() {
return {
showTree: true
}
},
methods: {
shrink() {
this.showTree = !this.showTree
}
}
}
</script>
<style>
.shrink {
position: absolute;
height: 30px;
width: 30px;
display: flex;
justify-content: center;
align-items: center;
border-radius: 15px;
background-color: white;
z-index: 99;
top: 0;
bottom: 0;
margin: auto;
box-shadow: 0 0 5px rgb(200, 200, 200);
cursor: pointer;
transition: all 500ms;
}
.shrink:hover {
box-shadow: 0 0 10px rgb(150, 150, 150);
}
.v-tree-layout-left::-webkit-scrollbar {
width: 4px;
height: 10px;
}
.v-tree-layout-left::-webkit-scrollbar-thumb {
border-radius: 10px;
background: #dcdcdc;
}
.v-tree-layout-left::-webkit-scrollbar-track {
-webkit-box-shadow: inset 0 0 5px transparent;
border-radius: 10px;
background: transparent;
}
.v-tree-layout {
width: 100%;
height: 100%;
position: relative;
display: flex;
flex-direction: row;
}
.v-tree-layout-left {
width: 20%;
}
.v-tree-layout-left,
.v-tree-layout-left .el-tree {
background-color: #ffffff;
}
.v-tree-layout-right {
width: 80%;
border-left: 1px solid rgb(240, 240, 240);
}
.v-tree-layout-left,
.v-tree-layout-right {
height: 100%;
box-sizing: border-box;
overflow: auto;
transition: all 500ms;
position: relative;
}
</style>
@@ -3,6 +3,12 @@
<el-form-item label="审批意见" prop="tf_opinion" :rules="[{required:true,message:'必填',trigger:['change','blur']}]">
<user-opinion-textarea v-model="formData.tf_opinion"></user-opinion-textarea>
</el-form-item>
<el-form-item label="是否需要副主席审核" prop="select">
<el-radio-group v-model="formData.select" size="small">
<el-radio label="need" border>是</el-radio>
<el-radio label="notNeeded" border>否</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
<snaker-flow-task-form-action @task-action="handleTaskAction" @cancel="handleCancel"></snaker-flow-task-form-action>
</div>
@@ -18,8 +24,16 @@
},
}
},
mounted() {
this.$set(this.formData, "select", "")
},
methods: {
handleTaskAction(val) {
// 同意流转时必须带流程分支变量 select,供 HDBX 条件线判断是否进入副主席审核。
if (val.submitType === 1 && !this.formData.select) {
this.$message.warning("请选择是否需要副主席审核")
return
}
this.$refs.formRef.validate(valid => {
if (valid) {
console.log(val)
@@ -127,6 +127,12 @@ layout("/layouts/platform.html"){
:rules="[{required:true,message:'必填',trigger:['change','blur']}]">
<pc-signature v-model="formData.tf_userSign"></pc-signature>
</el-form-item>
<el-form-item label="是否需要副主席审核" prop="select">
<el-radio-group v-model="formData.select" size="small">
<el-radio label="need" border>是</el-radio>
<el-radio label="notNeeded" border>否</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
<el-row type="flex" justify="end">
<el-button @click="$refs.guava.index()" size="small">取消</el-button>
@@ -194,11 +200,17 @@ layout("/layouts/platform.html"){
processTaskId: row.taskId,
taskName: row.curTaskName
}
this.$set(this.formData, "select", "")
this.$refs.infoRef.onOpen(row)
})
},
handleTaskAction(val) {
// 同意流转时必须带流程分支变量 select,供 HDBX 条件线判断是否进入副主席审核。
if (val === 1 && !this.formData.select) {
this.$message.warning("请选择是否需要副主席审核")
return
}
this.$refs.formRef.validate((valid) => {
if (valid) {
this.$confirm("您确定要提交吗?", "提示", {
@@ -51,7 +51,9 @@ layout("/layouts/platform.html"){
</el-select>
</el-form-item>
</el-descriptions-item>
<el-descriptions-item></el-descriptions-item>
<el-descriptions-item label="职称">
{{formData.professionalTitle}}
</el-descriptions-item>
<el-descriptions-item label="子女姓名">
<el-form-item prop="childrenName"
@@ -132,8 +134,7 @@ layout("/layouts/platform.html"){
</el-form-item>
</el-descriptions-item>
<el-descriptions-item label="户口簿照片" :span="2">
<el-form-item prop="huKouFiles"
:rules="[{required:true,message:'必填',trigger:['change','blur']}]">
<el-form-item prop="huKouFiles">
<file-upload :upload_number="10" :value.sync="formData.huKouFiles"
upload_result_type="url"
upload_text="请上传户口首页、户主页、父母和子女页"
@@ -337,22 +338,38 @@ layout("/layouts/platform.html"){
return resp.data
}
},
async ensureUserLoaded() {
if (this.$store.state.user && this.$store.state.user.id) {
return
}
const resp = await $.get("/platform/sys/user/getLogonUser")
if (resp.code === 0) {
window.sessionStorage.setItem("user", JSON.stringify(resp.data))
this.$store.commit("setUser", resp.data)
}
},
init() {
if (this.bizId) {
this.findOne(this.bizId).then(async data => {
this.formData = data
if (!this.formData.professionalTitle) {
this.$set(this.formData, "professionalTitle", (this.$store.state.user && this.$store.state.user.professionalTitle) || "")
}
})
} else {
const {id, username, loginname, mobile, union, unit} = this.$store.state.user
const user = this.$store.state.user || {}
const unit = user.unit || {}
const union = user.union || {}
this.formData = {
userId: id,
userName: username,
loginName: loginname,
unitName: unit.name,
unitId: unit.id,
unionName: union.name,
unionId: union.id,
mobile: mobile,
userId: user.id,
userName: user.username,
loginName: user.loginname,
professionalTitle: user.professionalTitle || "",
unitName: unit.name || "",
unitId: unit.id || "",
unionName: union.name || "",
unionId: union.id || "",
mobile: user.mobile,
}
}
@@ -360,6 +377,7 @@ layout("/layouts/platform.html"){
}
},
async created() {
await this.ensureUserLoaded()
this.init()
this.getEnrollmentRegistrationPlan()
@@ -9,6 +9,7 @@ const ENROLLMENT_REGISTRATION_INFO = {
<el-descriptions :column="3" border>
<el-descriptions-item label="监护人(教工)姓名">{{viewData.userName}}</el-descriptions-item>
<el-descriptions-item label="工号">{{viewData.loginName}}</el-descriptions-item>
<el-descriptions-item label="职称">{{viewData.professionalTitle}}</el-descriptions-item>
<el-descriptions-item label="手机号码">{{viewData.mobile}}</el-descriptions-item>
<el-descriptions-item label="所在单位">{{viewData.unitName}}</el-descriptions-item>
<el-descriptions-item label="监护人与学生关系">
@@ -41,14 +41,32 @@ layout("/layouts/platform.html"){
<el-option v-for="item in unitOptions" :label="item.name" :value="item.id"></el-option>
</el-select>
</search-item>
<search-item label="职称">
<el-select clearable filterable placeholder="请选择职称"
style="width: 100%;"
v-model="pageForm.professionalTitle">
<el-option :label="item.name" :value="item.code"
v-for="item in dict.type.TECHNICAL_TITLE_LEVEL"></el-option>
</el-select>
</search-item>
<search-item label="是否解决">
<el-select clearable placeholder="请选择是否解决"
style="width: 100%;"
v-model="pageForm.solved">
<el-option label="已解决" :value="true"></el-option>
<el-option label="未解决" :value="false"></el-option>
</el-select>
</search-item>
</search>
</el-card>
<el-card shadow="never">
<table-tool label="申请列表">
<el-button @click="doExportExcel" size="mini" type="primary">导出汇总名单
<el-button @click="doBatchSetSolved" size="small" type="primary">一键设置已解决
</el-button>
<el-button @click="doExportZip" size="mini" type="primary">导出附件zip
<el-button @click="doExportExcel" icon="el-icon-download" size="small" type="primary">导出汇总名单
</el-button>
<el-button @click="doExportZip" icon="el-icon-download" size="small" type="primary">导出附件zip
</el-button>
</table-tool>
<el-table :data="tableData" @sort-change="pageOrder" style="width: 100%">
@@ -58,6 +76,7 @@ layout("/layouts/platform.html"){
<el-table-column prop="loginName" label="工号"></el-table-column>
<el-table-column prop="unionName" label="所属工会"></el-table-column>
<el-table-column prop="unitName" label="所属单位"></el-table-column>
<el-table-column prop="professionalTitle" label="职称"></el-table-column>
<el-table-column prop="mobile" label="手机号码"></el-table-column>
<el-table-column prop="registrationType" label="登记类型">
<template v-slot="{row}">
@@ -66,9 +85,20 @@ layout("/layouts/platform.html"){
</template>
</el-table-column>
<el-table-column prop="applyTime" label="填报时间"></el-table-column>
<el-table-column prop="solved" label="是否解决">
<template v-slot="{row}">
<el-tag :type="isSolved(row) ? 'success' : 'info'">
{{isSolved(row) ? '已解决' : '未解决'}}
</el-tag>
</template>
</el-table-column>
<el-table-column align="center" fixed="right" header-align="center" label="操作"
width="300px">
<template scope="{row}">
<el-button @click="doUpdateSolved(row, !isSolved(row))" size="mini"
:type="isSolved(row) ? 'success' : 'info'">
{{isSolved(row) ? '已解决' : '未解决'}}
</el-button>
<el-button @click="openView(row)" size="mini" type="primary">查看</el-button>
<el-button @click="doDelete(row)" size="mini" type="danger"
v-if="$auth.hasRoleOr(['SYSADMIN'])">删除
@@ -91,7 +121,7 @@ layout("/layouts/platform.html"){
new Vue({
el: "#app",
store,
dicts: ["ENROLLMENT_REGISTRATION_TYPE", "ENROLLMENT_REGISTRATION_CHILD_RELATIONSHIP", "ENROLLMENT_REGISTRATION_CHILDREN_PLAN_SCHOOL"],
dicts: ["ENROLLMENT_REGISTRATION_TYPE", "ENROLLMENT_REGISTRATION_CHILD_RELATIONSHIP", "ENROLLMENT_REGISTRATION_CHILDREN_PLAN_SCHOOL", "TECHNICAL_TITLE_LEVEL"],
mixins: [initTableMixins],
data() {
return {
@@ -134,6 +164,44 @@ layout("/layouts/platform.html"){
data: JSON.stringify(this.pageForm)
})
},
isSolved(row) {
return row.solved === true || row.solved === 1 || row.solved === "1"
},
// 切换单条记录解决状态,历史空值在页面上按未解决处理。
doUpdateSolved(row, solved) {
this.$confirm("确定要切换解决状态吗?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
}).then(() => {
this.$axios.post("/platform/enrollmentRegistration/summary/doUpdateSolved", {
id: row.id,
solved: solved
}).then((resp) => {
if (resp.code === 0) {
this.$set(row, "solved", solved)
this.$message.success("操作成功")
}
})
})
},
// 按当前筛选条件批量设置,后端会更新符合条件的全部记录。
doBatchSetSolved() {
this.$confirm("确定将当前筛选条件下的数据全部设置为已解决吗?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
}).then(() => {
this.$axios.post("/platform/enrollmentRegistration/summary/doBatchSetSolved", {
data: JSON.stringify(this.pageForm)
}).then((resp) => {
if (resp.code === 0) {
this.$message.success(resp.msg || "操作成功")
this.doSearch()
}
})
})
},
openView(row) {
this.$refs.guava.view(() => {
this.showApprovalForm = false
@@ -39,10 +39,10 @@ layout("/layouts/platform.html"){
<dict-select v-model="pageForm.personTypes" placeholder="请选择教职工类别" @change="doSearch"
code="USER_PERSON_TYPE" multiple collapse-tags></dict-select>
</search-item>
<search-item label="编制类别">
<dict-select v-model="pageForm.preparedBys" placeholder="请选择编制类别" @change="doSearch"
code="USER_PREPARED_BY_TYPE" multiple collapse-tags></dict-select>
</search-item>
<!-- <search-item label="编制类别">-->
<!-- <dict-select v-model="pageForm.preparedBys" placeholder="请选择编制类别" @change="doSearch"-->
<!-- code="USER_PREPARED_BY_TYPE" multiple collapse-tags></dict-select>-->
<!-- </search-item>-->
</search>
</el-card>
<el-card shadow="never">
@@ -106,7 +106,7 @@ layout("/layouts/platform.html"){
{ label: "出生年月", prop: "birthday" },
{ label: "在职状态", prop: "userState", sortable: true },
{ label: "教职工类别", prop: "personType", sortable: true },
{ label: '编制类别', prop: 'preparedBy', sortable: true },
// { label: '编制类别', prop: 'preparedBy', sortable: true },
{ label: "所属工会", prop: "unionName" },
{ label: "所属单位", prop: "unitName" },
],
@@ -163,7 +163,7 @@ layout("/layouts/platform.html"){
const pageForm = clone(this.pageForm)
pageForm.userStates = JSON.stringify(this.pageForm.userStates)
pageForm.personTypes = JSON.stringify(this.pageForm.personTypes)
pageForm.preparedBys = JSON.stringify(this.pageForm.preparedBys)
// pageForm.preparedBys = JSON.stringify(this.pageForm.preparedBys)
this.$axios.post('/platform/fundMember/batchJoin/pageData', pageForm)
.then(res => {
this.tableData = res.data.list
@@ -26,12 +26,7 @@ layout("/layouts/platform.html"){
<el-table-column type="index" width="50px" label="序号" :index="indexMethod"></el-table-column>
<el-table-column prop="loginName" label="工号"></el-table-column>
<el-table-column prop="userName" label="姓名"></el-table-column>
<el-table-column prop="isJoin" label="申请类型">
<template slot-scope="{row}">
<el-tag v-if="row.isJoin" size="small">加入</el-tag>
<el-tag v-else size="small" type="danger">退出</el-tag>
</template>
</el-table-column>
<el-table-column prop="aidFundMemberType" label="会员类别"></el-table-column>
<el-table-column prop="submitTime" label="申请时间"></el-table-column>
<el-table-column prop="curTaskName" label="当前节点"></el-table-column>
<el-table-column prop="instanceState" label="流程状态">
@@ -1,62 +1,61 @@
const fundMemberInfo = {
template: /*language=HTML*/ `
<div>
<div class="process-title">
申请信息
<el-link type="primary" @click="openChart">点击查看流程图</el-link>
</div>
<el-descriptions :column="2" border>
<el-descriptions-item label="姓名">{{viewData.userName}}</el-descriptions-item>
<el-descriptions-item label="工号">{{viewData.loginName}}</el-descriptions-item>
<el-descriptions-item label="性别">{{viewData.sex}}</el-descriptions-item>
<el-descriptions-item label="出生年月">{{viewData.birthday}}</el-descriptions-item>
<el-descriptions-item label="身份证号码">{{viewData.idCard}}</el-descriptions-item>
<el-descriptions-item label="参加工作时间">{{viewData.joinWorkTime}}</el-descriptions-item>
<el-descriptions-item label="退休时间">{{viewData.retireTime}}</el-descriptions-item>
<el-descriptions-item label="家庭住址">{{viewData.address}}</el-descriptions-item>
<el-descriptions-item label="手机电话">{{viewData.mobile}}</el-descriptions-item>
<el-descriptions-item label="住宅号码">{{viewData.homePhone}}</el-descriptions-item>
<el-descriptions-item label="照片">
<img :src="viewData.avatar" alt="" style="width: 120px;height: 150px" v-if="viewData.avatar">
</el-descriptions-item>
</el-descriptions>
<template v-for="task in doneTasks">
<div class="mt10">
<div class="process-title">{{ task.displayName }}</div>
<el-descriptions border class="flow-task-form" :column="3" :key="task.id"
v-if="task.ext.isFirstTaskNode">
<el-descriptions-item label="申请用户">{{ task.ext.initiatorName
}}({{task.ext.initiatorAccount}})
</el-descriptions-item>
<el-descriptions-item label="申请时间">{{ task.finishTime }}</el-descriptions-item>
<el-descriptions-item label="办理结果">
<dict-tag :options="dict.type.PROCESS_TASK_SUBMIT_TYPE"
:value="task.ext.submitType"></dict-tag>
</el-descriptions-item>
</el-descriptions>
<el-descriptions border class="flow-task-form" :column="3" :key="task.id" v-else>
<el-descriptions-item label="办理用户">{{ task.taskFormData.userName
}}({{task.taskFormData.loginName}})
</el-descriptions-item>
<el-descriptions-item label="办理时间">{{ task.finishTime }}</el-descriptions-item>
<el-descriptions-item label="办理结果">
<dict-tag :options="dict.type.PROCESS_TASK_SUBMIT_TYPE"
:value="task.ext.submitType"></dict-tag>
</el-descriptions-item>
<el-descriptions-item label="办理意见" v-if="!task.ext.isFirstTaskNode">{{
task.taskFormData.opinion }}
</el-descriptions-item>
</el-descriptions>
</div>
</template>
<slot></slot>
<snaker-chart ref="snakerChartRef"></snaker-chart>
</div>
`,
template: /*language=HTML*/ [
'<div>',
' <div class="process-title">',
' 申请信息',
' <el-link type="primary" @click="openChart" v-if="row && row.instanceId">点击查看流程图</el-link>',
' </div>',
' <el-descriptions :column="2" border>',
' <el-descriptions-item label="姓名">{{ viewData.userName || \'-\' }}</el-descriptions-item>',
' <el-descriptions-item label="工号">{{ viewData.loginName || \'-\' }}</el-descriptions-item>',
' <el-descriptions-item label="出生年月">{{ viewData.birthday || \'-\' }}</el-descriptions-item>',
' <el-descriptions-item label="性别">{{ viewData.sex || \'-\' }}</el-descriptions-item>',
' <el-descriptions-item label="籍贯">{{ viewData.nativePlace || \'-\' }}</el-descriptions-item>',
' <el-descriptions-item label="家庭住址">{{ viewData.address || \'-\' }}</el-descriptions-item>',
' <el-descriptions-item label="健康状况">{{ viewData.healthStatus || \'-\' }}</el-descriptions-item>',
' <el-descriptions-item label="学历">{{ viewData.education || \'-\' }}</el-descriptions-item>',
' <el-descriptions-item label="联系电话">{{ viewData.mobile || \'-\' }}</el-descriptions-item>',
' <el-descriptions-item label="入职时间">{{ viewData.joinWorkTime || \'-\' }}</el-descriptions-item>',
' <el-descriptions-item label="身份证号码" :span="2">{{ viewData.idCard || \'-\' }}</el-descriptions-item>',
' <el-descriptions-item label="会员类别">{{ viewData.aidFundMemberType || \'-\' }}</el-descriptions-item>',
' <el-descriptions-item label="退休时间">{{ viewData.retireTime || \'-\' }}</el-descriptions-item>',
' <el-descriptions-item label="家庭主要成员" :span="2">',
' <el-table :data="viewData.families || []" border size="small">',
' <el-table-column label="序号" width="80" align="center">',
' <template slot-scope="scope">{{ scope.row.sequence || scope.$index + 1 }}</template>',
' </el-table-column>',
' <el-table-column prop="name" label="姓名" min-width="160"></el-table-column>',
' <el-table-column prop="age" label="年龄" width="120"></el-table-column>',
' <el-table-column prop="relation" label="与本人关系" min-width="180"></el-table-column>',
' </el-table>',
' </el-descriptions-item>',
' <el-descriptions-item label="备注" :span="2">{{ viewData.remark || \'-\' }}</el-descriptions-item>',
' </el-descriptions>',
'',
' <template v-for="task in doneTasks">',
' <div class="mt10">',
' <div class="process-title">{{ task.displayName }}</div>',
' <el-descriptions border class="flow-task-form" :column="3" :key="task.id" v-if="task.ext.isFirstTaskNode">',
' <el-descriptions-item label="申请用户">{{ task.ext.initiatorName }}({{ task.ext.initiatorAccount }})</el-descriptions-item>',
' <el-descriptions-item label="申请时间">{{ task.finishTime }}</el-descriptions-item>',
' <el-descriptions-item label="办理结果">',
' <dict-tag :options="dict.type.PROCESS_TASK_SUBMIT_TYPE" :value="task.ext.submitType"></dict-tag>',
' </el-descriptions-item>',
' </el-descriptions>',
' <el-descriptions border class="flow-task-form" :column="3" :key="task.id" v-else>',
' <el-descriptions-item label="办理用户">{{ task.taskFormData.userName }}({{ task.taskFormData.loginName }})</el-descriptions-item>',
' <el-descriptions-item label="办理时间">{{ task.finishTime }}</el-descriptions-item>',
' <el-descriptions-item label="办理结果">',
' <dict-tag :options="dict.type.PROCESS_TASK_SUBMIT_TYPE" :value="task.ext.submitType"></dict-tag>',
' </el-descriptions-item>',
' <el-descriptions-item label="办理意见" v-if="!task.ext.isFirstTaskNode">{{ task.taskFormData.opinion }}</el-descriptions-item>',
' </el-descriptions>',
' </div>',
' </template>',
' <slot></slot>',
' <snaker-chart ref="snakerChartRef"></snaker-chart>',
'</div>'
].join(''),
store,
dicts: ["PROCESS_TASK_SUBMIT_TYPE"],
data(){
@@ -72,21 +71,29 @@ const fundMemberInfo = {
onOpen(row) {
this.row = row
this.visible = true
this.viewData = Object.assign({}, row || {})
this.doneTasks = []
this.getInfo()
this.getDoneTasks()
},
// 获取申请信息
getInfo() {
if (!this.row || !this.row.id) {
return
}
this.$axios.post('/platform/fundMember/common/info', {id: this.row.id}).then((res) => {
if (res.code === 0) {
this.viewData = res.data
this.viewData = Object.assign({}, this.viewData, res.data || {})
}
})
},
// 获取已办任务审批记录
getDoneTasks() {
if (!this.row || !this.row.instanceId) {
return
}
this.$axios.post("/flow/common/doneTasks", {bizId: this.row.id}).then((res) => {
if (res.code === 0) {
this.doneTasks = res.data
@@ -96,6 +103,10 @@ const fundMemberInfo = {
// 查看流程图
openChart() {
if (!this.row || !this.row.instanceId) {
this.$message.warning("暂无流程信息")
return
}
this.$refs.snakerChartRef.onOpenFull(this.row.instanceProcessDefineId, this.row.instanceId)
}
}
@@ -17,12 +17,7 @@ layout("/layouts/platform.html"){
<el-table-column type="index" width="50px" label="序号" :index="indexMethod"></el-table-column>
<el-table-column prop="loginName" label="工号"></el-table-column>
<el-table-column prop="userName" label="姓名"></el-table-column>
<el-table-column prop="isJoin" label="申请类型">
<template slot-scope="{row}">
<el-tag v-if="row.isJoin" size="small">加入</el-tag>
<el-tag v-else size="small" type="danger">退出</el-tag>
</template>
</el-table-column>
<el-table-column prop="aidFundMemberType" label="会员类别"></el-table-column>
<el-table-column prop="submitTime" label="申请时间"></el-table-column>
<el-table-column prop="taskName" label="当前节点"></el-table-column>
<el-table-column prop="instanceState" label="流程状态">
@@ -34,7 +29,6 @@ layout("/layouts/platform.html"){
<el-table-column label="操作" fixed="right" width="300px">
<template slot-scope="{row}">
<el-button @click="openView(row)" size="mini" type="primary">查看</el-button>
<el-button @click="exportDocx(row.id)" size="mini" type="primary">导出</el-button>
<el-button v-if="row.taskKey === 'startTask' || !row.instanceId" @click="onEdit(row)"
size="mini" type="primary">
编辑
@@ -42,9 +36,10 @@ layout("/layouts/platform.html"){
<el-button v-if="row.canRevoke" @click="onRevoke(row)" size="mini" type="danger">撤回
</el-button>
<!-- v-if="row.taskKey === 'startTask' || !row.instanceId"-->
<el-button @click="onDelete(row.id)"
<el-button v-if="row.taskKey === 'startTask' || !row.instanceId" @click="onDelete(row.id)"
size="mini" type="danger">删除
</el-button>
<el-button @click="exportDocx(row.id)" size="mini" type="primary">导出</el-button>
</template>
</el-table-column>
</el-table>
@@ -39,14 +39,14 @@ layout("/layouts/platform.html"){
<dict-select v-model="pageForm.userStates" placeholder="请选择在职状态" @change="doSearch"
code="USER_STATE" multiple collapse-tags></dict-select>
</search-item>
<search-item label="教职工类别">
<dict-select v-model="pageForm.personTypes" placeholder="请选择教职工类别" @change="doSearch"
code="USER_PERSON_TYPE" multiple collapse-tags></dict-select>
</search-item>
<search-item label="编制类别">
<dict-select v-model="pageForm.preparedBys" placeholder="请选择编制类别" @change="doSearch"
code="USER_PREPARED_BY_TYPE" multiple collapse-tags></dict-select>
<search-item label="会员类别">
<dict-select v-model="pageForm.aidFundMemberTypes" placeholder="请选择会员类别" @change="doSearch"
code="USER_AID_FUND_MEMBER_TYPE" multiple collapse-tags></dict-select>
</search-item>
<!-- <search-item label="编制类别">-->
<!-- <dict-select v-model="pageForm.preparedBys" placeholder="请选择编制类别" @change="doSearch"-->
<!-- code="USER_PREPARED_BY_TYPE" multiple collapse-tags></dict-select>-->
<!-- </search-item>-->
</search>
</el-card>
<el-card shadow="never">
@@ -78,6 +78,11 @@ layout("/layouts/platform.html"){
<el-tag v-if="row.isJoin" size="small">加入</el-tag>
<el-tag v-else size="small" type="danger">退出</el-tag>
</template>
<template v-else-if="column.prop === 'aidFundMemberType'" scope="{row}">
<dict-select v-model="row.aidFundMemberType" placeholder="请选择会员类别"
code="USER_AID_FUND_MEMBER_TYPE" style="width: 100%"
@change="onAidFundMemberTypeChange(row)"></dict-select>
</template>
</el-table-column>
<el-table-column label="操作" fixed="right" width="200px">
<template slot-scope="{row}">
@@ -113,8 +118,8 @@ layout("/layouts/platform.html"){
{ label: "性别", prop: "sex" },
{ label: "出生年月", prop: "birthday" },
{ label: "在职状态", prop: "userState", sortable: true },
{ label: "教职工类别", prop: "personType", sortable: true },
{ label: '编制类别', prop: 'preparedBy', sortable: true },
{ label: "会员类别", prop: "aidFundMemberType", sortable: true },
// { label: '编制类别', prop: 'preparedBy', sortable: true },
{ label: "所属工会", prop: "unionName" },
{ label: "所属单位", prop: "unitName" },
{ label: "加入时间", prop: "joinTime" },
@@ -151,6 +156,22 @@ layout("/layouts/platform.html"){
})
},
// 修改会员类别
onAidFundMemberTypeChange(row) {
this.$axios.post('/platform/fundMember/query/updateAidFundMemberType', {
loginName: row.loginName,
aidFundMemberType: row.aidFundMemberType
}).then((res) => {
if (res.code === 0) {
this.$message.success('修改成功')
} else {
this.pageData()
}
}).catch(() => {
this.pageData()
})
},
// 备份会员
backup() {
// 此处需要选择年份
@@ -200,8 +221,8 @@ layout("/layouts/platform.html"){
pageData() {
const pageForm = clone(this.pageForm)
pageForm.userStates = JSON.stringify(this.pageForm.userStates)
pageForm.personTypes = JSON.stringify(this.pageForm.personTypes)
pageForm.preparedBys = JSON.stringify(this.pageForm.preparedBys)
pageForm.aidFundMemberTypes = JSON.stringify(this.pageForm.aidFundMemberTypes)
// pageForm.preparedBys = JSON.stringify(this.pageForm.preparedBys)
this.$axios.post('/platform/fundMember/query/pageData', pageForm)
.then(res => {
this.tableData = res.data.list
@@ -37,12 +37,7 @@ layout("/layouts/platform.html"){
<el-table-column prop="loginName" label="工号"></el-table-column>
<el-table-column prop="userName" label="姓名"></el-table-column>
<el-table-column prop="unionName" label="所属工会"></el-table-column>
<el-table-column prop="isJoin" label="申请类型">
<template slot-scope="{row}">
<el-tag v-if="row.isJoin" size="small">加入</el-tag>
<el-tag v-else size="small" type="danger">退出</el-tag>
</template>
</el-table-column>
<el-table-column prop="aidFundMemberType" label="会员类别"></el-table-column>
<el-table-column prop="submitTime" label="申请时间"></el-table-column>
<el-table-column prop="curTaskName" label="当前节点"></el-table-column>
<el-table-column prop="instanceState" label="流程状态">
@@ -4,7 +4,8 @@ layout("/layouts/platform.html"){
<div id="app" v-cloak>
<el-card shadow="never">
<snaker-start slot="header" label="基金会员入会申请" define_key="FUND_MEMBER"></snaker-start>
<snaker-start slot="header" label="基金会员入会申请" define_key="JJHY"></snaker-start>
<el-alert v-if="isFundMember" title="您已是基金会员,无需重复申请。" type="warning" :closable="false" show-icon class="mb20"></el-alert>
<el-form :model="formData" ref="formRef" :rules="formRules" label-width="120px">
<el-row :gutter="20">
<el-col :span="12">
@@ -43,8 +44,8 @@ layout("/layouts/platform.html"){
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="身份证号码" prop="idCard">
<el-input v-model="formData.idCard" placeholder="请输入身份证号码"></el-input>
<el-form-item label="籍贯" prop="nativePlace">
<el-input v-model="formData.nativePlace" placeholder="请输入籍贯"></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
@@ -56,16 +57,51 @@ layout("/layouts/platform.html"){
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="参加工作时间" prop="joinWorkTime">
<el-form-item label="健康状况" prop="healthStatus">
<el-input v-model="formData.healthStatus" maxlength="200" placeholder="请输入健康状况"></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="学历" prop="education">
<dict-select v-model="formData.education" placeholder="请选择学历" code="USER_EDUCATION" style="width: 100%"></dict-select>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="联系电话" prop="mobile">
<el-input v-model="formData.mobile" placeholder="请输入联系电话" maxlength="11"></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="入职时间" prop="joinWorkTime">
<el-date-picker
v-model="formData.joinWorkTime"
type="date"
value-format="yyyy-MM-dd"
placeholder="选择参加工作时间"
placeholder="选择入职时间"
style="width: 100%"
></el-date-picker>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="身份证号码" prop="idCard">
<el-input v-model="formData.idCard" placeholder="请输入身份证号码"></el-input>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="会员类别" prop="aidFundMemberType">
<dict-select v-model="formData.aidFundMemberType" placeholder="请选择会员类别"
code="USER_AID_FUND_MEMBER_TYPE" style="width: 100%"></dict-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="退休时间" prop="retireTime">
<el-date-picker
@@ -79,19 +115,54 @@ layout("/layouts/platform.html"){
</el-col>
</el-row>
<!--
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="手机号码" prop="mobile">
<el-input v-model="formData.mobile" placeholder="请输入手机号码" maxlength="11"></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="住宅号码" prop="homePhone">
<el-input v-model="formData.homePhone" placeholder="请输入住宅电话" maxlength="20"></el-input>
</el-form-item>
</el-col>
</el-row>
-->
<el-form-item label="家庭主要成员" prop="families" required>
<el-table :data="formData.families" border size="small">
<el-table-column label="序号" width="80" align="center">
<template slot-scope="scope">
{{ scope.row.sequence }}
</template>
</el-table-column>
<el-table-column label="姓名" min-width="160">
<template slot-scope="scope">
<el-input v-model="scope.row.name" maxlength="50" placeholder="请输入姓名"></el-input>
</template>
</el-table-column>
<el-table-column label="年龄" width="160">
<template slot-scope="scope">
<el-input-number v-model="scope.row.age" :min="0" :max="150" :precision="0" controls-position="right" style="width: 100%"></el-input-number>
</template>
</el-table-column>
<el-table-column label="与本人关系" min-width="180">
<template slot-scope="scope">
<el-input v-model="scope.row.relation" maxlength="50" placeholder="请输入与本人关系"></el-input>
</template>
</el-table-column>
<el-table-column width="150" align="center">
<template slot="header">
<el-button type="primary" size="mini" @click="addFamily">添加成员</el-button>
</template>
<template slot-scope="scope">
<el-button type="danger" size="mini" icon="el-icon-delete" @click="removeFamily(scope.$index)">删除</el-button>
</template>
</el-table-column>
</el-table>
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="formData.remark" type="textarea" :rows="3" maxlength="500" show-word-limit placeholder="请输入备注"></el-input>
</el-form-item>
<!--
<el-form-item label="照 片">
<file-upload
style="--upload-width: 150px;--upload-height:176px"
@@ -102,6 +173,7 @@ layout("/layouts/platform.html"){
upload_result_type="url"
></file-upload>
</el-form-item>
-->
<!-- <el-form-item>-->
<!-- <el-checkbox v-model="isAgree">-->
@@ -112,8 +184,8 @@ layout("/layouts/platform.html"){
</el-form>
<el-row type="flex" justify="end" class="mt20">
<!-- <el-button type="primary" plain @click="onSave">保存</el-button>-->
<el-button type="primary" @click="onSubmit" v-if="!taskId">提交申请</el-button>
<el-button type="primary" @click="onFinishTask" v-else>提交申请</el-button>
<el-button type="primary" @click="onSubmit" v-if="!taskId && !isFundMember">提交申请</el-button>
<el-button type="primary" @click="onFinishTask" v-if="taskId && !isFundMember">提交申请</el-button>
</el-row>
</el-card>
</div>
@@ -126,7 +198,14 @@ layout("/layouts/platform.html"){
return {
bizId: GetQueryString("bizId"),
taskId: GetQueryString("taskId"),
formData: {},
isFundMember: false,
formData: {
families: [],
nativePlace: "",
education: "",
healthStatus: "",
remark: ""
},
formRules: {
userName: [{required: true, message: '请输入姓名', trigger: 'blur'}],
loginName: [{required: true, message: '请输入工资号', trigger: 'blur'}],
@@ -134,14 +213,18 @@ layout("/layouts/platform.html"){
sex: [{required: true, message: '请选择性别', trigger: 'blur'}],
idCard: [{required: true, message: '请输入身份证号码', trigger: 'blur'},
{
pattern: /^[1-9]\d{5}(18|19|20)\d{2}((0[1-9])|(1[0-2]))([0-2][1-9]|3[0-1])\d{3}[Xx\d]$/,
pattern: /^[1-9]\d{5}(18|19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}[Xx\d]$/,
message: '请输入正确的身份证号码',
trigger: 'blur'
}
],
address: [{required: true, message: '请输入家庭住址', trigger: 'blur'}],
joinWorkTime: [{required: true, message: '请选择参加工作时间', trigger: 'blur'}],
nativePlace: [{required: true, message: '请输入籍贯', trigger: 'blur'}],
healthStatus: [{required: true, message: '请输入健康状况', trigger: 'blur'}],
education: [{required: true, message: '请选择学历', trigger: 'change'}],
joinWorkTime: [{required: true, message: '请选择入职时间', trigger: 'blur'}],
retireTime: [{required: false, message: '请选择退休时间', trigger: 'blur'}],
aidFundMemberType: [{required: true, message: '请选择会员类别', trigger: 'change'}],
mobile: [{required: true, message: '请输入手机号码', trigger: 'blur'},
{
pattern: /^1[3456789]\d{9}$/,
@@ -162,6 +245,63 @@ layout("/layouts/platform.html"){
}
},
methods: {
// 确保家庭成员字段始终为可响应式数组,兼容旧申请数据。
ensureFamilies() {
if (!Array.isArray(this.formData.families)) {
this.$set(this.formData, "families", [])
}
},
// 按当前行顺序重置家庭成员序号,避免删除成员后出现断号。
resetFamilySequence() {
this.ensureFamilies()
this.formData.families.forEach((family, index) => {
this.$set(family, "sequence", index + 1)
})
},
// 添加一名家庭成员并自动设置序号。
addFamily() {
this.ensureFamilies()
this.formData.families.push({
sequence: this.formData.families.length + 1,
name: "",
age: null,
relation: ""
})
},
// 删除指定家庭成员后重排序号。
removeFamily(index) {
this.ensureFamilies()
this.formData.families.splice(index, 1)
this.resetFamilySequence()
},
// 提交前校验家庭主要成员的必填信息。
validateFamilies() {
this.resetFamilySequence()
if (this.formData.families.length === 0) {
this.$message.error("请至少添加一名家庭主要成员")
return false
}
for (const family of this.formData.families) {
if (!family.name || !String(family.name).trim()) {
this.$message.error("请填写第" + family.sequence + "位家庭成员的姓名")
return false
}
if (family.age === null || family.age === undefined || family.age === "") {
this.$message.error("请填写第" + family.sequence + "位家庭成员的年龄")
return false
}
if (!family.relation || !String(family.relation).trim()) {
this.$message.error("请填写第" + family.sequence + "位家庭成员与本人的关系")
return false
}
}
return true
},
// 保存
// onSave() {
// this.$confirm("您确定保存吗?", "提示", {
@@ -182,6 +322,7 @@ layout("/layouts/platform.html"){
onSubmit() {
this.$refs.formRef.validate(valid => {
if (!valid) return
if (!this.validateFamilies()) return
// if (!this.isAgree) {
// this.$message.error("请先阅读并同意《南昌大学教职工医疗互助“爱心”基金管理办法》")
// return
@@ -204,19 +345,22 @@ layout("/layouts/platform.html"){
},
onFinishTask() {
this.$confirm("您确定要提交吗?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
}).then(() => {
this.$axios.post('/platform/fundMember/write/submitAgain', {
data: JSON.stringify(this.formData),
taskId: GetQueryString("taskId")
}).then(res => {
if (res.code === 0) {
this.$message.success("提交成功")
commonUtil.pjaxPush('/platform/fundMember/mine')
}
this.$refs.formRef.validate(valid => {
if (!valid || !this.validateFamilies()) return
this.$confirm("您确定要提交吗?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
}).then(() => {
this.$axios.post('/platform/fundMember/write/submitAgain', {
data: JSON.stringify(this.formData),
taskId: GetQueryString("taskId")
}).then(res => {
if (res.code === 0) {
this.$message.success("提交成功")
commonUtil.pjaxPush('/platform/fundMember/mine')
}
})
})
})
},
@@ -228,10 +372,11 @@ layout("/layouts/platform.html"){
this.$axios.post("/platform/fundMember/write/info", {id: this.bizId}).then((res) => {
if (res.code === 0) {
this.formData = res.data
this.resetFamilySequence()
}
})
} else {
const {username, loginname, sex, birthday, mobile, union, userState, idCard} = this.$store.state.user
const {username, loginname, sex, birthday, mobile, union, userState, idCard, education} = this.$store.state.user
this.formData = {
userName: username,
loginName: loginname,
@@ -241,7 +386,12 @@ layout("/layouts/platform.html"){
unionId: union?.id,
unionName: union?.name,
userState: userState,
idCard: idCard
idCard: idCard,
nativePlace: "",
education: education || "",
healthStatus: "",
remark: "",
families: []
}
}
},
@@ -249,6 +399,11 @@ layout("/layouts/platform.html"){
},
created() {
this.init()
this.$axios.post('/platform/fundMember/write/memberStatus').then((res) => {
if (res.code === 0) {
this.$set(this, "isFundMember", Boolean(res.data))
}
})
if(!this.bizId){
this.$axios.post('/platform/fundMember/write/validateApply')
}
@@ -58,6 +58,17 @@ const basicForm = {
</div>
</el-form-item>
<el-form-item label="封面" prop="cover">
<file-upload
:upload_number="1"
:value.sync="formData.cover"
accept=".jpg,.jpeg,.png"
upload_mode="image"
upload_result_category="interval"
upload_result_type="url"
></file-upload>
</el-form-item>
<template v-if="formData.category==='QUIZ'">
<el-form-item label="出题模式" prop="mode">
<span slot="label">
@@ -171,17 +182,6 @@ const basicForm = {
</el-radio-group>
</el-form-item>
<!-- <el-form-item label="封面" prop="cover">-->
<!-- <file-upload-->
<!-- :upload_number="1"-->
<!-- :value.sync="formData.cover"-->
<!-- accept=".jpg,.jpeg,.png"-->
<!-- complete_result-->
<!-- upload_mode="image"-->
<!-- upload_result_category="interval"-->
<!-- ></file-upload>-->
<!-- </el-form-item>-->
</template>
</el-form>
</div>
@@ -204,7 +204,8 @@ const basicForm = {
return {
dialogVisible: false,
formData: {
category: ''
category: '',
cover: ''
},
formRules: {
category: [{ required: true, message: "请选择类型", trigger: "change" }],
@@ -228,7 +229,9 @@ const basicForm = {
}
})
} else {
this.formData = {}
this.formData = {
cover: ''
}
}
},
onSubmit() {
@@ -31,11 +31,27 @@ layout("/layouts/platform.html"){
</el-table-column>
<el-table-column label="开始时间" prop="startTime" sortable></el-table-column>
<el-table-column label="结束时间" prop="endTime" sortable></el-table-column>
<el-table-column label="操作" fixed="right" width="450px">
<el-table-column label="状态" width="100">
<template scope="{row}">
<el-tag v-if="row.enabled !== false" type="success">开启</el-tag>
<el-tag v-else type="info">关闭</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" fixed="right" width="520px">
<template scope="{row}">
<el-button size="mini" type="primary" @click="openQrCode(row)">二维码</el-button>
<el-button size="mini" type="primary" @click="openEdit(row)">编辑</el-button>
<el-button size="mini" type="primary" @click="openSubject(row.id)">题目设置</el-button>
<el-button
v-if="row.enabled === false"
size="mini"
type="success"
@click="updateEnabled(row, true)">开启</el-button>
<el-button
v-else
size="mini"
type="warning"
@click="updateEnabled(row, false)">关闭</el-button>
<el-button size="mini" type="danger" @click="onDelete(row.id)">删除</el-button>
</template>
</el-table-column>
@@ -94,6 +110,26 @@ layout("/layouts/platform.html"){
})
},
// 开启或关闭活动,关闭后移动端首页不展示,直达手机端问卷也不能答题
updateEnabled(row, enabled) {
const actionText = enabled ? "开启" : "关闭"
this.$confirm("您确认" + actionText + "该活动吗?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
}).then(() => {
$.post("/platform/qsv/activity/updateEnabled", {
id: row.id,
enabled
}).then((res) => {
if (res.code === 0) {
this.$message.success(res.msg)
this.pageData()
}
})
})
},
// 删除
onDelete(id) {
this.$confirm("您确认删除吗, 是否继续?", "提示", {
@@ -8,6 +8,20 @@ const setting = {
<el-form-item label="详情" prop="description">
<text-editor v-model="formData.description"></text-editor>
</el-form-item>
<el-form-item label="补充填写" prop="fillRequired">
<el-switch
v-model="formData.fillRequired"
active-text="需要填写"
inactive-text="无需填写">
</el-switch>
</el-form-item>
<el-form-item label="填写提示" prop="fillPlaceholder" v-if="formData.fillRequired">
<el-input
maxlength="255"
placeholder="例如:请填写原因"
v-model="formData.fillPlaceholder">
</el-input>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="visible = false">取 消</el-button>
@@ -26,7 +40,11 @@ const setting = {
methods:{
onOpen(option,ext){
console.log(option)
this.formData = { ...option }
this.formData = {
...option,
fillRequired: !!option.fillRequired,
fillPlaceholder: option.fillPlaceholder || ""
}
this.ext = ext
this.visible = true
},
@@ -12,14 +12,21 @@ const subjectForm = {
<transition-group>
<div v-for="(subject, subjectIndex) in subjects" :key="subject.id" class="subject-item">
<div class="subject-header">
<div style="display: flex; align-items: center;">
<div class="subject-title-row">
<i class="el-icon-rank drag-handler"></i>
<span style="font-weight: bold; margin-right: 10px;">第 {{subjectIndex + 1}} 题</span>
<el-input
class="subject-title-input"
v-model="subject.title"
placeholder="请输入题目"
style="flex: 1">
placeholder="请输入题目">
</el-input>
<el-button
class="subject-visible-btn"
type="primary"
icon="el-icon-setting"
@click="openSubjectVisibleSetting(subjectIndex, subject)">
隐显逻辑
</el-button>
</div>
<div class="subject-meta">
@@ -214,6 +221,87 @@ const subjectForm = {
<setting ref="settingRef" @confirm="onSettingConfirm"></setting>
<txt-import ref="txtImportRef" @confirm="onTxtImportConfirm"></txt-import>
<bank ref="bankRef" @confirm="onBankConfirm"></bank>
<el-dialog
title="设置题目隐显逻辑"
:visible.sync="subjectVisibleSetting.visible"
append-to-body
width="520px">
<el-form :model="subjectVisibleSetting.formData" label-width="110px">
<el-form-item label="启用逻辑">
<el-switch
v-model="subjectVisibleSetting.formData.enabled"
active-text="启用"
inactive-text="关闭">
</el-switch>
</el-form-item>
<template v-if="subjectVisibleSetting.formData.enabled">
<el-form-item label="处理方式">
<el-radio-group v-model="subjectVisibleSetting.formData.action">
<el-radio label="show">选中后显示本题</el-radio>
<el-radio label="hide">选中后隐藏本题</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="条件关系">
<el-radio-group v-model="subjectVisibleSetting.formData.logic">
<el-radio label="AND">并且</el-radio>
<el-radio label="OR">或者</el-radio>
</el-radio-group>
</el-form-item>
<div
v-for="(condition, conditionIndex) in subjectVisibleSetting.formData.conditions"
:key="conditionIndex"
class="subject-visible-condition">
<el-form-item :label="'条件' + (conditionIndex + 1)">
<div style="display: flex; gap: 8px;">
<el-select
v-model="condition.subjectId"
placeholder="请选择触发题目"
style="flex: 1;"
@change="subjectVisibleRuleSubjectChange(condition)">
<el-option
v-for="item in subjectVisibleRuleSubjects"
:key="item.id"
:label="item.title || '未填写题目'"
:value="item.id">
</el-option>
</el-select>
<el-select
v-model="condition.optionId"
placeholder="请选择触发选项"
style="flex: 1;">
<el-option
v-for="item in getSubjectVisibleRuleOptions(condition.subjectId)"
:key="item.id"
:label="item.text || '未填写选项'"
:value="item.id">
</el-option>
</el-select>
<el-button
type="danger"
icon="el-icon-delete"
size="mini"
@click="removeSubjectVisibleRuleCondition(conditionIndex)">
</el-button>
</div>
</el-form-item>
</div>
<el-form-item label="">
<el-button
type="primary"
icon="el-icon-plus"
size="small"
@click="addSubjectVisibleRuleCondition">
添加条件
</el-button>
</el-form-item>
</template>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="subjectVisibleSetting.visible = false">取 消</el-button>
<el-button type="primary" @click="onSubjectVisibleSettingConfirm">确 定</el-button>
</div>
</el-dialog>
</div>
`,
components: {
@@ -230,7 +318,32 @@ const subjectForm = {
subjects: [],
previewDialogVisible: false,
activity: {},
formRules: {}
formRules: {},
subjectVisibleSetting: {
visible: false,
subjectIndex: null,
formData: {
enabled: false,
action: "show",
logic: "AND",
conditions: [
{
subjectId: "",
optionId: ""
}
]
}
}
}
},
computed: {
subjectVisibleRuleSubjects() {
if (this.subjectVisibleSetting.subjectIndex === null) {
return []
}
return this.subjects.filter((item, index) => {
return index < this.subjectVisibleSetting.subjectIndex && item.type !== "text"
})
}
},
methods: {
@@ -351,13 +464,113 @@ const subjectForm = {
//选项设置确认
onSettingConfirm({option, ext}) {
this.subjects[ext.subjectIndex].options[ext.optionIndex] = option
this.$set(this.subjects[ext.subjectIndex].options, ext.optionIndex, option)
console.log(this.subjects[ext.subjectIndex].options[ext.optionIndex])
// this.$set(this.subjects[ext.subjectIndex].options[ext.optionIndex], "isCorrect", option.isCorrect)
// this.$set(this.subjects[ext.subjectIndex].options[ext.optionIndex], "hint", option.hint)
},
//打开题目隐显逻辑设置,用于配置当前题目受前置选择题的哪个选项控制显示或隐藏
openSubjectVisibleSetting(subjectIndex, subject) {
const conditions = this.buildSubjectVisibleConditions(subject)
this.subjectVisibleSetting.subjectIndex = subjectIndex
this.subjectVisibleSetting.formData = {
enabled: !!subject.visibleRuleEnabled,
action: subject.visibleRuleAction || "show",
logic: subject.visibleRuleLogic || "AND",
conditions
}
this.subjectVisibleSetting.visible = true
},
//构建题目隐显逻辑条件,已有条件按实际数量回显;未配置时默认提供一条空条件
buildSubjectVisibleConditions(subject) {
let visibleRuleConditions = subject.visibleRuleConditions || []
if (typeof visibleRuleConditions === "string") {
try {
visibleRuleConditions = JSON.parse(visibleRuleConditions)
} catch (e) {
visibleRuleConditions = []
}
}
const conditions = visibleRuleConditions || []
const result = conditions.map((condition) => {
return {
subjectId: condition.subjectId || "",
optionId: condition.optionId || ""
}
})
if (result.length === 0) {
result.push({
subjectId: "",
optionId: ""
})
}
return result
},
//根据触发题目获取可选项,用于每条隐显条件选择触发选项
getSubjectVisibleRuleOptions(subjectId) {
const subject = this.subjects.find((item) => {
return item.id === subjectId
})
if (!subject || !subject.options) {
return []
}
return subject.options
},
//切换触发题目后清空当前条件的旧选项,避免保留其他题目的选项导致隐显条件错误
subjectVisibleRuleSubjectChange(condition) {
condition.optionId = ""
},
//添加一条隐显逻辑条件,多个条件通过条件关系字段统一按并且或或者计算
addSubjectVisibleRuleCondition() {
this.subjectVisibleSetting.formData.conditions.push({
subjectId: "",
optionId: ""
})
},
//删除一条隐显逻辑条件,至少保留一条条件用于继续配置
removeSubjectVisibleRuleCondition(conditionIndex) {
if (this.subjectVisibleSetting.formData.conditions.length === 1) {
this.$message.warning("至少保留一条条件")
return
}
this.subjectVisibleSetting.formData.conditions.splice(conditionIndex, 1)
},
//保存题目隐显逻辑到当前题目对象,后续保存题目时随 subjects 一起提交
onSubjectVisibleSettingConfirm() {
const subjectIndex = this.subjectVisibleSetting.subjectIndex
if (subjectIndex === null) {
return
}
const formData = this.subjectVisibleSetting.formData
if (formData.enabled) {
for (let i = 0; i < formData.conditions.length; i++) {
const condition = formData.conditions[i]
if (!condition.subjectId || !condition.optionId) {
this.$message.warning("请选择条件" + (i + 1) + "的触发题目和触发选项")
return
}
}
}
this.$set(this.subjects[subjectIndex], "visibleRuleEnabled", formData.enabled)
this.$set(this.subjects[subjectIndex], "visibleRuleAction", formData.enabled ? formData.action : "show")
this.$set(this.subjects[subjectIndex], "visibleRuleLogic", formData.enabled ? formData.logic : "AND")
this.$set(this.subjects[subjectIndex], "visibleRuleConditions", formData.enabled ? formData.conditions.map((condition) => {
return {
subjectId: condition.subjectId,
optionId: condition.optionId
}
}) : [])
this.subjectVisibleSetting.visible = false
},
//从文本导入
openTxtImport() {
this.$refs.txtImportRef.visible = true;
@@ -449,6 +662,21 @@ const subjectForm = {
margin-bottom: 15px;
}
/deep/ .subject-title-row {
display: flex;
align-items: center;
}
/deep/ .subject-title-input {
flex: 1;
}
/deep/ .subject-visible-btn {
height: 40px;
margin-left: 8px;
flex-shrink: 0;
}
/deep/ .subject-toolbar {
display: flex;
justify-content: space-between;
@@ -5,7 +5,7 @@ const answer = {
<el-row type="flex" justify="end" class="mb10">
<el-button type="primary" size="small" icon="el-icon-download" @click="exportUserAnswerXlsx">导出xlsx</el-button>
</el-row>
<el-table :data="tableData">
<el-table :data="tableData" v-loading="tableLoading">
<el-table-column label="序号" type="index" width="70"></el-table-column>
<el-table-column v-for="item in tableColumns" :key="item.prop" :label="item.label" :prop="item.prop"></el-table-column>
<el-table-column label="操作" fixed="right" width="100px">
@@ -30,17 +30,24 @@ const answer = {
onOpen(activityId) {
this.activityId = activityId
this.dialogVisible = true
this.tableColumns = []
this.tableData = []
this.pageForm.pageNumber = 1
this.pageForm.totalCount = 0
this.pageData()
},
pageData() {
this.pageForm.activityId = this.activityId
this.tableLoading = true
$.post("/platform/qsv/survey/userAnswer", this.pageForm).then((res) => {
if (res.code === 0) {
this.tableColumns = res.data.tableColumns
this.tableData = res.data.tableData.list
this.pageForm.totalCount = res.data.tableData.totalCount
}
}).always(() => {
this.tableLoading = false
})
},
@@ -53,7 +60,7 @@ const answer = {
$.post("/platform/qsv/survey/deleteUserAnswer", { id }).then((res) => {
if (res.code === 0) {
this.$message.success(res.msg)
this.list()
this.pageData()
}
})
})
@@ -2,6 +2,42 @@
layout("/layouts/platform.html"){
#-->
<style>
.survey-report-dialog {
margin-top: 40px !important;
height: calc(100vh - 80px);
display: flex;
flex-direction: column;
box-sizing: border-box;
}
.survey-report-dialog .el-dialog__header {
flex: 0 0 auto;
}
.survey-report-dialog .el-dialog__body {
flex: 1;
min-height: 0;
overflow: hidden;
padding-top: 10px;
display: flex;
flex-direction: column;
box-sizing: border-box;
}
.survey-report-toolbar {
flex: 0 0 auto;
}
.survey-report-body {
flex: 1;
min-height: 0;
overflow-y: auto;
padding-right: 8px;
box-sizing: border-box;
}
</style>
<div id="app" v-cloak>
<guava>
<el-card shadow="never">
@@ -1,41 +1,44 @@
const report = {
/*language=HTML*/
template: `
<el-dialog title="分析报告" :visible.sync="dialogVisible" width="50%">
<el-row type="flex" justify="end" class="mb10">
<el-button type="primary" size="small" icon="el-icon-download" @click="exportReportXlsx">导出xlsx</el-button>
</el-row>
<div v-for="(subject,subjectIndex) in subjects" class="subject">
<div>
<div class="title">
第{{subjectIndex+1}}题: {{subject.title}}
<span class="type" v-if="subject.type==='text'">[填空题]</span>
<span class="type" v-else-if="subject.type==='radio'">[单选题]</span>
<span class="type" v-else-if="subject.type==='text'">[多选题]</span>
</div>
<el-dialog title="分析报告" :visible.sync="dialogVisible" width="50%" custom-class="survey-report-dialog">
<div class="survey-report-toolbar">
<el-row type="flex" justify="end" class="mb10">
<el-button type="primary" size="small" icon="el-icon-download" @click="exportReportXlsx">导出xlsx</el-button>
</el-row>
</div>
<div class="survey-report-body">
<div v-for="(subject,subjectIndex) in subjects" class="subject">
<div>
<div class="title">
第{{subjectIndex+1}}题: {{subject.title}}
<span class="type" v-if="subject.type==='text'">[填空题]</span>
<span class="type" v-else-if="subject.type==='radio'">[单选题]</span>
<span class="type" v-else-if="subject.type==='checkbox'">[多选题]</span>
</div>
<div v-if="subject.type==='text'">
{{subject.texts.join('、')}}
</div>
<div v-if="subject.type==='text'">
<el-button type="text" @click="openTextDetail(subject)">查看填写内容</el-button>
</div>
<div v-else-if="subject.type==='radio' || subject.type==='checkbox'">
<el-table :data="subject.options" size="small">
<el-table-column label="选项" prop="text"></el-table-column>
<el-table-column label="小计" prop="selectCount" width="100"></el-table-column>
<el-table-column label="比例" width="500">
<template slot-scope="scope">
<el-progress
:percentage="Math.round(scope.row.selectCount / subject.selectTotal * 100)"></el-progress>
</template>
</el-table-column>
<el-table-column label="详情" width="100px">
<template slot-scope="{row}">
<el-button type="text" @click="openDetail(subject.id,row.id)">详情</el-button>
</template>
</el-table-column>
</el-table>
<div v-else-if="subject.type==='radio' || subject.type==='checkbox'">
<el-table :data="subject.options" size="small">
<el-table-column label="选项" prop="text"></el-table-column>
<el-table-column label="小计" prop="selectCount" width="100"></el-table-column>
<el-table-column label="比例" width="500">
<template slot-scope="scope">
<el-progress
:percentage="Math.round(scope.row.selectCount / subject.selectTotal * 100)"></el-progress>
</template>
</el-table-column>
<el-table-column label="详情" width="100px">
<template slot-scope="{row}">
<el-button type="text" @click="openDetail(subject.id,row.id)">详情</el-button>
</template>
</el-table-column>
</el-table>
</div>
</div>
</div>
</div>
@@ -45,9 +48,22 @@ const report = {
<el-table-column label="工号" prop="loginName"></el-table-column>
<el-table-column label="所属工会" prop="unionName"></el-table-column>
<el-table-column label="所属单位" prop="unitName"></el-table-column>
<el-table-column label="补充理由" prop="fillContent" show-overflow-tooltip></el-table-column>
<el-table-column label="选择时间" prop="attemptDate"></el-table-column>
</el-table>
</el-dialog>
<el-dialog :title="textSubject.title || '填写内容'" :visible.sync="textVisible" width="50%" append-to-body>
<el-table :data="textAnswers" size="small">
<el-table-column label="序号" type="index" width="70"></el-table-column>
<el-table-column label="姓名" prop="userName" width="100"></el-table-column>
<el-table-column label="工号" prop="loginName" width="120"></el-table-column>
<el-table-column label="所属工会" prop="unionName" width="140"></el-table-column>
<el-table-column label="所属单位" prop="unitName" width="180" show-overflow-tooltip></el-table-column>
<el-table-column label="填写内容" prop="text" show-overflow-tooltip></el-table-column>
<el-table-column label="提交时间" prop="submitTime" width="160"></el-table-column>
</el-table>
</el-dialog>
</el-dialog>
`,
@@ -59,7 +75,10 @@ const report = {
report: null,
subjects: [],
optionVisible: false,
optionUsers: []
optionUsers: [],
textVisible: false,
textSubject: {},
textAnswers: []
}
},
methods: {
@@ -73,8 +92,6 @@ const report = {
})
},
openDetail(subjectId, optionId) {
console.log(subjectId)
console.log(optionId)
$.post("/platform/qsv/survey/selectOptionUsers", {
activityId: this.activityId,
subjectId,
@@ -87,6 +104,13 @@ const report = {
}
})
},
openTextDetail(subject) {
this.textSubject = subject
this.textAnswers = subject.textAnswers || (subject.texts || []).map((text) => {
return { text }
})
this.textVisible = true
},
exportReportXlsx(){
this.$downLoad("/platform/qsv/survey/exportReportXlsx", { activityId: this.activityId })
}
@@ -77,7 +77,7 @@ layout("/layouts/platform.html"){
</template>
</el-table-column>
<el-table-column prop="taskName" label="当前节点" width="120"></el-table-column>
<el-table-column label="操作" fixed="right" width="200">
<el-table-column label="操作" fixed="right" width="250">
<template slot-scope="{row}">
<el-button size="mini" type="primary" @click="onOpen(row)">查看</el-button>
<el-button size="mini" type="primary" v-if="canEdit(row)" @click="onEdit(row)">修改</el-button>
@@ -196,9 +196,10 @@ layout("/layouts/platform.html"){
<span v-else>-</span>
</template>
</el-table-column>
<el-table-column label="操作" fixed="right" width="90">
<el-table-column label="操作" fixed="right" width="150">
<template slot-scope="{row}">
<el-button size="mini" type="primary" @click="onOpen(row)">查看</el-button>
<el-button v-if="canDelete(row)" size="mini" type="danger" @click="onDelete(row)">删除</el-button>
</template>
</el-table-column>
</el-table>
@@ -257,6 +258,24 @@ layout("/layouts/platform.html"){
this.$refs.infoRef.onOpen(row)
})
},
canDelete(row) {
return this.$auth.hasRole('SYSADMIN') && row.state === '已办结' && Number(row.instanceState) === 20
},
onDelete(row) {
this.$confirm("您确定要删除吗?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
}).then(() => {
this.$axios.post("/platform/appeal/query/delete", {id: row.id}).then((res) => {
if (res.code === 0) {
this.$message.success(res.msg)
this.pageData()
this.loadStatistics()
}
})
})
},
doSearch() {
this.pageNumber = 1
this.pageData()
@@ -56,10 +56,11 @@ layout("/layouts/platform.html"){
<el-tag :type="row.isReply ? 'success' : 'warning'" size="small">{{ row.isReply ? '已回复' : '待回复' }}</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" fixed="right" width="160px">
<el-table-column label="操作" fixed="right" width="240px">
<template slot-scope="{row}">
<el-button @click="openView(row)" size="mini" type="primary">查看</el-button>
<el-button v-if="!row.isReply" @click="openReply(row)" size="mini" type="success">回复</el-button>
<el-button @click="handleDelete(row)" size="mini" type="danger">删除</el-button>
</template>
</el-table-column>
</el-table>
@@ -187,6 +188,23 @@ layout("/layouts/platform.html"){
})
})
},
handleDelete(row) {
this.$confirm('确认删除该信件吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.$axios.post('/platform/chairmanMailbox/query/delete', {
id: row.id
}).then((res) => {
if (res.code === 0) {
this.$message.success('删除成功')
this.loadStats()
this.pageData()
}
})
})
},
loadStats() {
this.$axios.post('/platform/chairmanMailbox/query/getStats').then((res) => {
if (res.code === 0) {
@@ -103,7 +103,7 @@ layout("/layouts/platform.html"){
{ prop: "userName", label: "姓名", sortable: true },
{ prop: "userState", label: "在职状态", sortable: true },
{ prop: "personType", label: "教职工类别", sortable: true },
{ prop: "preparedBy", label: "编制类别", sortable: true },
<!-- { prop: "preparedBy", label: "编制类别", sortable: true }, -->
{ prop: "unionName", label: "所属工会", sortable: true },
{ prop: "unitName", label: "所属单位", sortable: true },
{ prop: "sign", label: "签字" },

Some files were not shown because too many files have changed in this diff Show More