..
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
package com.budwk.app.base.param;
|
||||
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 单个条件
|
||||
*/
|
||||
@Data
|
||||
public class Condition {
|
||||
|
||||
@ApiModelProperty("字段名")
|
||||
private String field;
|
||||
|
||||
@ApiModelProperty("操作符: =, !=, >, <, >=, <=, LIKE, IN, NOT IN, IS NULL, IS NOT NULL")
|
||||
private String operator;
|
||||
|
||||
@ApiModelProperty("字段值")
|
||||
private Object value;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.budwk.app.base.param;
|
||||
|
||||
import com.budwk.app.sys.param.SysDataUserUpdateParam;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 条件组,支持嵌套的与或非逻辑
|
||||
*/
|
||||
@Data
|
||||
public class ConditionGroup {
|
||||
|
||||
@ApiModelProperty("逻辑类型:AND, OR")
|
||||
private String logic = "AND";
|
||||
|
||||
@ApiModelProperty("条件列表")
|
||||
private List<Condition> conditions;
|
||||
|
||||
@ApiModelProperty("嵌套条件组")
|
||||
private List<ConditionGroup> groups;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
package com.budwk.app.base.utils;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.budwk.app.base.param.Condition;
|
||||
import com.budwk.app.base.param.ConditionGroup;
|
||||
import com.budwk.app.sys.param.SysDataUserUpdateParam;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.nutz.dao.Cnd;
|
||||
import org.nutz.dao.util.cri.SqlExpressionGroup;
|
||||
|
||||
/**
|
||||
* 条件组处理工具类
|
||||
* 用于处理复杂的条件组和条件,生成对应的SQL条件
|
||||
*/
|
||||
@Slf4j
|
||||
public class ConditionGroupUtil {
|
||||
|
||||
/**
|
||||
* 处理条件组,将条件组转换为Cnd条件
|
||||
*
|
||||
* @param cnd 原始条件
|
||||
* @param group 条件组
|
||||
* @return 处理后的条件
|
||||
*/
|
||||
public static Cnd applyConditionGroup(Cnd cnd, ConditionGroup group) {
|
||||
if (group == null) {
|
||||
return cnd;
|
||||
}
|
||||
|
||||
SqlExpressionGroup expGroup = new SqlExpressionGroup();
|
||||
|
||||
// 处理条件列表
|
||||
if (group.getConditions() != null && !group.getConditions().isEmpty()) {
|
||||
for (Condition condition : group.getConditions()) {
|
||||
expGroup = applyCondition(expGroup, condition, group.getLogic());
|
||||
}
|
||||
}
|
||||
|
||||
// 处理嵌套条件组
|
||||
if (group.getGroups() != null && !group.getGroups().isEmpty()) {
|
||||
for (ConditionGroup nestedGroup : group.getGroups()) {
|
||||
// 创建子条件
|
||||
Cnd subCnd = Cnd.NEW();
|
||||
subCnd = applyConditionGroup(subCnd, nestedGroup);
|
||||
|
||||
// 将子条件的表达式组添加到当前表达式组
|
||||
if ("OR".equalsIgnoreCase(group.getLogic())) {
|
||||
expGroup.or(subCnd.where());
|
||||
} else {
|
||||
expGroup.and(subCnd.where());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 将表达式组添加到主条件
|
||||
cnd.and(expGroup);
|
||||
return cnd;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理单个条件
|
||||
*
|
||||
* @param expGroup 表达式组
|
||||
* @param condition 条件
|
||||
* @param logic 逻辑类型 (AND/OR)
|
||||
* @return 处理后的表达式组
|
||||
*/
|
||||
public static SqlExpressionGroup applyCondition(SqlExpressionGroup expGroup, Condition condition, String logic) {
|
||||
String field = condition.getField();
|
||||
String operator = condition.getOperator();
|
||||
Object value = condition.getValue();
|
||||
|
||||
// 根据操作符处理条件
|
||||
switch (operator.toUpperCase()) {
|
||||
case "=":
|
||||
if ("OR".equalsIgnoreCase(logic)) {
|
||||
expGroup.or(field, "=", value);
|
||||
} else {
|
||||
expGroup.and(field, "=", value);
|
||||
}
|
||||
break;
|
||||
case "!=":
|
||||
if ("OR".equalsIgnoreCase(logic)) {
|
||||
expGroup.or(field, "!=", value);
|
||||
} else {
|
||||
expGroup.and(field, "!=", value);
|
||||
}
|
||||
break;
|
||||
case ">":
|
||||
if ("OR".equalsIgnoreCase(logic)) {
|
||||
expGroup.or(field, ">", value);
|
||||
} else {
|
||||
expGroup.and(field, ">", value);
|
||||
}
|
||||
break;
|
||||
case "<":
|
||||
if ("OR".equalsIgnoreCase(logic)) {
|
||||
expGroup.or(field, "<", value);
|
||||
} else {
|
||||
expGroup.and(field, "<", value);
|
||||
}
|
||||
break;
|
||||
case ">=":
|
||||
if ("OR".equalsIgnoreCase(logic)) {
|
||||
expGroup.or(field, ">=", value);
|
||||
} else {
|
||||
expGroup.and(field, ">=", value);
|
||||
}
|
||||
break;
|
||||
case "<=":
|
||||
if ("OR".equalsIgnoreCase(logic)) {
|
||||
expGroup.or(field, "<=", value);
|
||||
} else {
|
||||
expGroup.and(field, "<=", value);
|
||||
}
|
||||
break;
|
||||
case "LIKE":
|
||||
if ("OR".equalsIgnoreCase(logic)) {
|
||||
expGroup.or(field, "LIKE", "%" + value + "%");
|
||||
} else {
|
||||
expGroup.and(field, "LIKE", "%" + value + "%");
|
||||
}
|
||||
break;
|
||||
case "IN":
|
||||
if ("OR".equalsIgnoreCase(logic)) {
|
||||
expGroup.or(field, "IN", ObjectUtil.isNotNull(value) ? value.toString().split(",") : new String[0]);
|
||||
} else {
|
||||
expGroup.and(field, "IN", ObjectUtil.isNotNull(value) ? value.toString().split(",") : new String[0]);
|
||||
}
|
||||
break;
|
||||
case "NOT IN":
|
||||
if ("OR".equalsIgnoreCase(logic)) {
|
||||
expGroup.or(field, "NOT IN", ObjectUtil.isNotNull(value) ? value.toString().split(",") : new String[0]);
|
||||
} else {
|
||||
expGroup.and(field, "NOT IN", ObjectUtil.isNotNull(value) ? value.toString().split(",") : new String[0]);
|
||||
}
|
||||
break;
|
||||
case "IS NULL":
|
||||
if ("OR".equalsIgnoreCase(logic)) {
|
||||
expGroup.or(field, "IS", null);
|
||||
} else {
|
||||
expGroup.and(field, "IS", null);
|
||||
}
|
||||
break;
|
||||
case "IS NOT NULL":
|
||||
if ("OR".equalsIgnoreCase(logic)) {
|
||||
expGroup.or(field, "IS NOT", null);
|
||||
} else {
|
||||
expGroup.and(field, "IS NOT", null);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
log.warn("不支持的操作符: {}", operator);
|
||||
}
|
||||
|
||||
return expGroup;
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,10 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import org.nutz.lang.Lang;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 为老兼容老的密码加密方式
|
||||
@@ -13,6 +17,13 @@ import java.security.MessageDigest;
|
||||
@Slf4j
|
||||
public class PwdUtil {
|
||||
|
||||
private static final String LOWERCASE = "abcdefghijklmnopqrstuvwxyz";
|
||||
private static final String UPPERCASE = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
private static final String DIGITS = "0123456789";
|
||||
private static final String SPECIAL = "!@#$%^&*()-_=+{};:,<.>";
|
||||
private static final String ALL = LOWERCASE + UPPERCASE + DIGITS + SPECIAL;
|
||||
private static final SecureRandom random = new SecureRandom();
|
||||
|
||||
public static String getPassword(String passowrd, String salt) {
|
||||
byte[] bytes = hash(passowrd.getBytes(), salt.getBytes(), 1024);
|
||||
if (bytes != null) {
|
||||
@@ -43,4 +54,44 @@ public class PwdUtil {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public static String generate(int length) {
|
||||
if (length < 8 || length > 20) {
|
||||
throw new IllegalArgumentException("密码长度必须在8到20之间");
|
||||
}
|
||||
|
||||
List<Character> passwordChars = new ArrayList<>();
|
||||
|
||||
// 保证每类字符至少一个
|
||||
passwordChars.add(randomCharFrom(LOWERCASE));
|
||||
passwordChars.add(randomCharFrom(UPPERCASE));
|
||||
passwordChars.add(randomCharFrom(DIGITS));
|
||||
passwordChars.add(randomCharFrom(SPECIAL));
|
||||
|
||||
// 剩余位置随机填充
|
||||
for (int i = passwordChars.size(); i < length; i++) {
|
||||
passwordChars.add(randomCharFrom(ALL));
|
||||
}
|
||||
|
||||
// 打乱顺序以避免固定模式
|
||||
Collections.shuffle(passwordChars);
|
||||
|
||||
// 构建字符串
|
||||
StringBuilder password = new StringBuilder();
|
||||
for (char ch : passwordChars) {
|
||||
password.append(ch);
|
||||
}
|
||||
|
||||
return password.toString();
|
||||
}
|
||||
|
||||
private static char randomCharFrom(String chars) {
|
||||
return chars.charAt(random.nextInt(chars.length()));
|
||||
}
|
||||
|
||||
// 示例主方法
|
||||
public static void main(String[] args) {
|
||||
System.out.println("生成的密码: " + generate(12));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -37,8 +37,8 @@ public class SysDataUserPullController {
|
||||
|
||||
@At
|
||||
@SaCheckPermission("sys.data.user.pull")
|
||||
@Ok("json:{dateFormat:'yyyy-MM-dd'}")
|
||||
@ApiOperation("分页数据")
|
||||
@Ok("json:{locked:'password|idCard|mobile'}")
|
||||
public Result pageData(@Valid SysDataUserPullPageForm pageForm) {
|
||||
Cnd cnd = Cnd.NEW();
|
||||
cnd.andEX(Sys_user_source::getPullTime, "=", pageForm.getPullTime());
|
||||
@@ -90,8 +90,8 @@ public class SysDataUserPullController {
|
||||
@At
|
||||
@SaCheckPermission("sys.data.user.pull")
|
||||
@ApiOperation("根据拉取时间删除数据")
|
||||
public Result deleteByPullTime(@Param(value = "pullTime") @Valid String pullTime) {
|
||||
sysUserPullService.clear(Cnd.where(Sys_user_source::getPullTime, "=", pullTime));
|
||||
public Result deleteByPullTime(@Param(value = "pullTime") @Valid String[] pullTime) {
|
||||
sysUserPullService.clear(Cnd.where(Sys_user_source::getPullTime, "in", pullTime));
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@ public class SysDataUserUpdateController {
|
||||
@At
|
||||
@SaCheckPermission("sys.data.user.pull")
|
||||
@ApiOperation("分页数据")
|
||||
@Ok("json:{locked:'password|createAt|loginSessionId'}")
|
||||
@Ok("json:{locked:'password|idCard|mobile'}")
|
||||
public Result pageData(@Valid SysDataUserUpdatePageForm pageForm) {
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
package com.budwk.app.sys.param;
|
||||
|
||||
import com.budwk.app.base.param.ConditionGroup;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@ApiModel("用户数据更新参数")
|
||||
@@ -21,34 +21,4 @@ public class SysDataUserUpdateParam {
|
||||
|
||||
@ApiModelProperty("条件组 (可选)")
|
||||
private ConditionGroup conditionGroup;
|
||||
|
||||
/**
|
||||
* 条件组,支持嵌套的与或非逻辑
|
||||
*/
|
||||
@Data
|
||||
public static class ConditionGroup {
|
||||
@ApiModelProperty("逻辑类型:AND, OR")
|
||||
private String logic = "AND";
|
||||
|
||||
@ApiModelProperty("条件列表")
|
||||
private List<Condition> conditions;
|
||||
|
||||
@ApiModelProperty("嵌套条件组")
|
||||
private List<ConditionGroup> groups;
|
||||
}
|
||||
|
||||
/**
|
||||
* 单个条件
|
||||
*/
|
||||
@Data
|
||||
public static class Condition {
|
||||
@ApiModelProperty("字段名")
|
||||
private String field;
|
||||
|
||||
@ApiModelProperty("操作符: =, !=, >, <, >=, <=, LIKE, IN, NOT IN, IS NULL, IS NOT NULL")
|
||||
private String operator;
|
||||
|
||||
@ApiModelProperty("字段值")
|
||||
private Object value;
|
||||
}
|
||||
}
|
||||
|
||||
+328
-234
@@ -1,17 +1,25 @@
|
||||
package com.budwk.app.sys.services.impl;
|
||||
|
||||
import cn.afterturn.easypoi.excel.ExcelImportUtil;
|
||||
import cn.afterturn.easypoi.excel.entity.ImportParams;
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.collection.ListUtil;
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.date.DateField;
|
||||
import cn.hutool.core.date.DateTime;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.thread.AsyncUtil;
|
||||
import cn.hutool.core.thread.ThreadUtil;
|
||||
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.user.SysUserEvent;
|
||||
import com.budwk.app.base.event.user.SysUserPublisher;
|
||||
import com.budwk.app.base.utils.ConditionGroupUtil;
|
||||
import com.budwk.app.base.utils.ManyAddOrRenewUtil;
|
||||
import com.budwk.app.base.utils.PwdUtil;
|
||||
import com.budwk.app.sys.annotation.DataCenterColumn;
|
||||
import com.budwk.app.sys.models.*;
|
||||
import com.budwk.app.sys.param.SysDataUserUpdateParam;
|
||||
import com.budwk.app.sys.services.SysDataUserUpdateService;
|
||||
@@ -38,7 +46,10 @@ import org.nutz.lang.random.R;
|
||||
import org.nutz.lang.util.NutMap;
|
||||
import org.nutz.mvc.Mvcs;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
@@ -62,171 +73,94 @@ public class SysDataUserAllUpdateServiceImpl implements SysDataUserUpdateService
|
||||
@Inject
|
||||
private SysUserService sysUserService;
|
||||
@Inject
|
||||
private ManyAddOrRenewUtil manyAddOrRenewUtil;
|
||||
@Inject
|
||||
private MemberManageService memberManageService;
|
||||
|
||||
private ThreadPoolTaskExecutor executorService;
|
||||
|
||||
/**
|
||||
* 是会员的在职状态
|
||||
* 字段映射类,用于缓存反射结果
|
||||
*/
|
||||
private final List<String> MEMBER_USER_STATE = List.of("在岗");
|
||||
/**
|
||||
* 是会员的编制类型,博士后满两年退出会员
|
||||
*/
|
||||
private final List<String> MEMBER_PREPARED_BY = List.of("新人事代理", "校聘合同制", "事业编制", "博士后");
|
||||
private static class FieldMapping {
|
||||
final Field field;
|
||||
final String key;
|
||||
final String name;
|
||||
|
||||
/**
|
||||
* 处理条件组,将条件组转换为Cnd条件
|
||||
*
|
||||
* @param cnd 原始条件
|
||||
* @param group 条件组
|
||||
* @return 处理后的条件
|
||||
*/
|
||||
private Cnd applyConditionGroup(Cnd cnd, SysDataUserUpdateParam.ConditionGroup group) {
|
||||
if (group == null) {
|
||||
return cnd;
|
||||
FieldMapping(Field field, DataCenterColumn annotation) {
|
||||
this.field = field;
|
||||
this.key = annotation.key();
|
||||
this.name = annotation.name();
|
||||
field.setAccessible(true);
|
||||
}
|
||||
|
||||
SqlExpressionGroup expGroup = new SqlExpressionGroup();
|
||||
|
||||
// 处理条件列表
|
||||
if (group.getConditions() != null && !group.getConditions().isEmpty()) {
|
||||
for (SysDataUserUpdateParam.Condition condition : group.getConditions()) {
|
||||
expGroup = applyCondition(expGroup, condition, group.getLogic());
|
||||
}
|
||||
}
|
||||
|
||||
// 处理嵌套条件组
|
||||
if (group.getGroups() != null && !group.getGroups().isEmpty()) {
|
||||
for (SysDataUserUpdateParam.ConditionGroup nestedGroup : group.getGroups()) {
|
||||
// 创建子条件
|
||||
Cnd subCnd = Cnd.NEW();
|
||||
subCnd = applyConditionGroup(subCnd, nestedGroup);
|
||||
|
||||
// 将子条件的表达式组添加到当前表达式组
|
||||
if ("OR".equalsIgnoreCase(group.getLogic())) {
|
||||
expGroup.or(subCnd.where());
|
||||
} else {
|
||||
expGroup.and(subCnd.where());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 将表达式组添加到主条件
|
||||
cnd.and(expGroup);
|
||||
return cnd;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理单个条件
|
||||
*
|
||||
* @param expGroup 表达式组
|
||||
* @param condition 条件
|
||||
* @param logic 逻辑类型 (AND/OR)
|
||||
* @return 处理后的表达式组
|
||||
* 字段映射缓存
|
||||
*/
|
||||
private SqlExpressionGroup applyCondition(SqlExpressionGroup expGroup, SysDataUserUpdateParam.Condition condition, String logic) {
|
||||
String field = condition.getField();
|
||||
String operator = condition.getOperator();
|
||||
Object value = condition.getValue();
|
||||
|
||||
// 根据操作符处理条件
|
||||
switch (operator.toUpperCase()) {
|
||||
case "=":
|
||||
if ("OR".equalsIgnoreCase(logic)) {
|
||||
expGroup.or(field, "=", value);
|
||||
} else {
|
||||
expGroup.and(field, "=", value);
|
||||
}
|
||||
break;
|
||||
case "!=":
|
||||
if ("OR".equalsIgnoreCase(logic)) {
|
||||
expGroup.or(field, "!=", value);
|
||||
} else {
|
||||
expGroup.and(field, "!=", value);
|
||||
}
|
||||
break;
|
||||
case ">":
|
||||
if ("OR".equalsIgnoreCase(logic)) {
|
||||
expGroup.or(field, ">", value);
|
||||
} else {
|
||||
expGroup.and(field, ">", value);
|
||||
}
|
||||
break;
|
||||
case "<":
|
||||
if ("OR".equalsIgnoreCase(logic)) {
|
||||
expGroup.or(field, "<", value);
|
||||
} else {
|
||||
expGroup.and(field, "<", value);
|
||||
}
|
||||
break;
|
||||
case ">=":
|
||||
if ("OR".equalsIgnoreCase(logic)) {
|
||||
expGroup.or(field, ">=", value);
|
||||
} else {
|
||||
expGroup.and(field, ">=", value);
|
||||
}
|
||||
break;
|
||||
case "<=":
|
||||
if ("OR".equalsIgnoreCase(logic)) {
|
||||
expGroup.or(field, "<=", value);
|
||||
} else {
|
||||
expGroup.and(field, "<=", value);
|
||||
}
|
||||
break;
|
||||
case "LIKE":
|
||||
if ("OR".equalsIgnoreCase(logic)) {
|
||||
expGroup.or(field, "LIKE", "%" + value + "%");
|
||||
} else {
|
||||
expGroup.and(field, "LIKE", "%" + value + "%");
|
||||
}
|
||||
break;
|
||||
case "IN":
|
||||
if ("OR".equalsIgnoreCase(logic)) {
|
||||
expGroup.or(field, "IN", value);
|
||||
} else {
|
||||
expGroup.and(field, "IN", value);
|
||||
}
|
||||
break;
|
||||
case "NOT IN":
|
||||
if ("OR".equalsIgnoreCase(logic)) {
|
||||
expGroup.or(field, "NOT IN", value);
|
||||
} else {
|
||||
expGroup.and(field, "NOT IN", value);
|
||||
}
|
||||
break;
|
||||
case "IS NULL":
|
||||
if ("OR".equalsIgnoreCase(logic)) {
|
||||
expGroup.or(field, "IS", null);
|
||||
} else {
|
||||
expGroup.and(field, "IS", null);
|
||||
}
|
||||
break;
|
||||
case "IS NOT NULL":
|
||||
if ("OR".equalsIgnoreCase(logic)) {
|
||||
expGroup.or(field, "IS NOT", null);
|
||||
} else {
|
||||
expGroup.and(field, "IS NOT", null);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
log.warn("不支持的操作符: {}", operator);
|
||||
}
|
||||
|
||||
return expGroup;
|
||||
}
|
||||
|
||||
private static List<FieldMapping> fieldMappings;
|
||||
|
||||
/**
|
||||
* 全量更新用户数据, 很慢、优化一下
|
||||
* 获取带有DataCenterColumn注解的字段映射,使用懒加载模式
|
||||
*
|
||||
* @param updateParam
|
||||
* @return
|
||||
* @return 字段映射列表
|
||||
*/
|
||||
private static List<FieldMapping> getFieldMappings() {
|
||||
if (fieldMappings == null) {
|
||||
synchronized (SysDataUserAllUpdateServiceImpl.class) {
|
||||
if (fieldMappings == null) {
|
||||
List<FieldMapping> mappings = new ArrayList<>();
|
||||
// 使用HuTool的反射工具获取所有字段,包括继承的字段
|
||||
Field[] fields = cn.hutool.core.util.ReflectUtil.getFields(Sys_user.class);
|
||||
for (Field field : fields) {
|
||||
DataCenterColumn annotation = field.getAnnotation(DataCenterColumn.class);
|
||||
if (annotation != null) {
|
||||
mappings.add(new FieldMapping(field, annotation));
|
||||
}
|
||||
}
|
||||
fieldMappings = mappings;
|
||||
}
|
||||
}
|
||||
}
|
||||
return fieldMappings;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查用户是否符合会员条件
|
||||
*
|
||||
* @param userState 用户状态
|
||||
* @param preparedBy 聘用方式
|
||||
* @param postDoctoralJoinDate 博士后进站时间
|
||||
* @return 是否符合会员条件
|
||||
*/
|
||||
private boolean checkMembershipEligibility(String userState, String preparedBy, Date postDoctoralJoinDate) {
|
||||
// 博士后单独判断:只要进站时间在两年内就是会员
|
||||
if ("博士后".equals(preparedBy)) {
|
||||
if (postDoctoralJoinDate != null) {
|
||||
Date twoYearsAgo = DateUtil.offset(DateUtil.date(), DateField.YEAR, -2).toJdkDate();
|
||||
return postDoctoralJoinDate.after(twoYearsAgo);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// 其他人员需要判断在岗状态和聘用方式
|
||||
if (!"在岗".equals(userState)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 判断聘用方式
|
||||
Set<String> memberPreparedByTypes = new HashSet<>(Arrays.asList("新人事代理", "校聘合同制", "事业编制"));
|
||||
return memberPreparedByTypes.contains(preparedBy);
|
||||
}
|
||||
|
||||
/**
|
||||
* 全量更新用户数据
|
||||
*
|
||||
* @param updateParam 更新参数
|
||||
* @return 更新结果描述
|
||||
*/
|
||||
@Override
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
public String update(SysDataUserUpdateParam updateParam) {
|
||||
long startTime = System.currentTimeMillis();
|
||||
log.info("开始全量更新用户数据");
|
||||
|
||||
// 获取角色信息
|
||||
Sys_role publicRole = sysRoleService.getByCode(RoleConstant.PUBLIC);
|
||||
Sys_role memberRole = sysRoleService.getByCode(RoleConstant.MEMBER);
|
||||
@@ -236,9 +170,13 @@ public class SysDataUserAllUpdateServiceImpl implements SysDataUserUpdateService
|
||||
|
||||
// 处理复杂条件
|
||||
if (updateParam.getConditionGroup() != null) {
|
||||
cnd = applyConditionGroup(cnd, updateParam.getConditionGroup());
|
||||
cnd = ConditionGroupUtil.applyConditionGroup(cnd, updateParam.getConditionGroup());
|
||||
}
|
||||
|
||||
// List<Map<String, String>> allList = ExcelImportUtil.importExcel(new File("C:\\Users\\jug\\Desktop\\南京师范蛋糕卡\\匹配上生日.xlsx"), Map.class, new ImportParams());
|
||||
// List<String> loginNames = allList.stream().map(v -> v.get("工号")).toList();
|
||||
// cnd.and("loginname", "in", loginNames);
|
||||
|
||||
List<Sys_user_source> sources = dao.query(Sys_user_source.class, cnd.groupBy("loginname"));
|
||||
log.info("符合条件的数据源记录数: {}", sources.size());
|
||||
|
||||
@@ -250,15 +188,8 @@ public class SysDataUserAllUpdateServiceImpl implements SysDataUserUpdateService
|
||||
List<Sys_user> needDoUpdateList = new ArrayList<>();
|
||||
List<Sys_user> needInitUserList = new ArrayList<>();
|
||||
List<Sys_user_history> histories = new ArrayList<>();
|
||||
List<String> addMemberUserIds = new ArrayList<>();
|
||||
List<String> removeMemberUserIds = new ArrayList<>();
|
||||
|
||||
// 获取允许变更的字段,同时再加上单位字段
|
||||
List<Sys_dict> dictList = sysDictService.getSubListByCode("MEMBER_ALLOW_CHANGES_FIELD");
|
||||
Set<String> allowChangeFieldNames = dictList.stream().map(Sys_dict::getCode).collect(Collectors.toSet());
|
||||
allowChangeFieldNames.add("member");
|
||||
Map<String, String> dictMap = dictList.stream().collect(Collectors.toMap(Sys_dict::getCode, Sys_dict::getName));
|
||||
dictMap.put("member", "会员状态");
|
||||
List<String> addMemberUserIds = Collections.synchronizedList(new ArrayList<>());
|
||||
List<String> removeMemberUserIds = Collections.synchronizedList(new ArrayList<>());
|
||||
|
||||
// 处理每条数据
|
||||
for (Sys_user_source source : sources) {
|
||||
@@ -272,16 +203,52 @@ public class SysDataUserAllUpdateServiceImpl implements SysDataUserUpdateService
|
||||
// 新增用户,初始化数据
|
||||
String salt = R.UU32();
|
||||
u.setSalt(salt);
|
||||
u.setPassword(PwdUtil.getPassword(R.captchaNumber(6), salt));
|
||||
u.setPassword(PwdUtil.getPassword(PwdUtil.generate(12), salt));
|
||||
|
||||
// 检查是否符合会员条件
|
||||
boolean shouldBeMember = checkMembershipEligibility(
|
||||
source.getUserState(),
|
||||
source.getPreparedBy(),
|
||||
source.getPostDoctoralJoinDate()
|
||||
);
|
||||
|
||||
if (shouldBeMember) {
|
||||
u.setMember(true);
|
||||
addMemberUserIds.add(u.getId());
|
||||
} else {
|
||||
u.setMember(false);
|
||||
}
|
||||
|
||||
needInitUserList.add(u);
|
||||
} else {
|
||||
// 修改现有用户
|
||||
u.setId(user.getId());
|
||||
|
||||
// 检查会员资格
|
||||
boolean shouldBeMember = checkMembershipEligibility(
|
||||
source.getUserState(),
|
||||
source.getPreparedBy(),
|
||||
source.getPostDoctoralJoinDate()
|
||||
);
|
||||
|
||||
// 更新会员状态
|
||||
boolean currentIsMember = user.getMember() != null && user.getMember();
|
||||
|
||||
if (shouldBeMember && !currentIsMember) {
|
||||
// 添加会员
|
||||
u.setMember(true);
|
||||
addMemberUserIds.add(user.getId());
|
||||
} else if (!shouldBeMember && currentIsMember) {
|
||||
// 移除会员
|
||||
u.setMember(false);
|
||||
removeMemberUserIds.add(user.getId());
|
||||
}
|
||||
|
||||
needDoUpdateList.add(u);
|
||||
}
|
||||
|
||||
// 创建历史记录
|
||||
Sys_user_history history = createHistory(source, user, dictMap, allowChangeFieldNames);
|
||||
Sys_user_history history = createHistory(source, user);
|
||||
if (Lang.isNotEmpty(history)) {
|
||||
if (user != null) {
|
||||
histories.add(history);
|
||||
@@ -291,120 +258,247 @@ public class SysDataUserAllUpdateServiceImpl implements SysDataUserUpdateService
|
||||
}
|
||||
}
|
||||
|
||||
// 处理结果
|
||||
// 1. 新增用户并分配公共角色
|
||||
// 使用CompletableFuture处理并行任务
|
||||
List<CompletableFuture<Void>> updateTasks = new ArrayList<>();
|
||||
|
||||
// 1. 新增用户 - 使用批量处理
|
||||
if (Lang.isNotEmpty(needInitUserList)) {
|
||||
log.info("新增用户: {} 个", needInitUserList.size());
|
||||
dao.fastInsert(needInitUserList);
|
||||
CompletableFuture<Void> insertTask = CompletableFuture.runAsync(() -> {
|
||||
log.info("新增用户: {} 个", needInitUserList.size());
|
||||
// 分批处理,每批200条
|
||||
List<List<Sys_user>> batches = ListUtil.split(needInitUserList, 500);
|
||||
batches.forEach(batch -> {
|
||||
try {
|
||||
dao.fastInsert(batch);
|
||||
} catch (Exception e) {
|
||||
log.error("批量新增用户异常", e);
|
||||
}
|
||||
});
|
||||
}, executorService);
|
||||
updateTasks.add(insertTask);
|
||||
|
||||
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;
|
||||
}).toList();
|
||||
dao.insert(roleList);
|
||||
// 异步处理公共角色分配
|
||||
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. 更新现有用户
|
||||
// 2. 更新现有用户 - 使用批量处理
|
||||
if (Lang.isNotEmpty(needDoUpdateList)) {
|
||||
log.info("更新用户: {} 个", needDoUpdateList.size());
|
||||
dao.updateIgnoreNull(needDoUpdateList);
|
||||
CompletableFuture<Void> updateTask = CompletableFuture.runAsync(() -> {
|
||||
log.info("更新用户: {} 个", needDoUpdateList.size());
|
||||
// 分批处理,每批200条
|
||||
List<List<Sys_user>> batches = ListUtil.split(needDoUpdateList, 500);
|
||||
batches.forEach(batch -> {
|
||||
try {
|
||||
dao.updateIgnoreNull(batch);
|
||||
} catch (Exception e) {
|
||||
log.error("批量更新用户异常", e);
|
||||
}
|
||||
});
|
||||
}, executorService);
|
||||
updateTasks.add(updateTask);
|
||||
}
|
||||
|
||||
// 3. 添加历史记录
|
||||
// 等待用户数据更新完成
|
||||
try {
|
||||
// 设置超时时间,避免无限等待
|
||||
CompletableFuture.allOf(updateTasks.toArray(new CompletableFuture[0]))
|
||||
.get(5, TimeUnit.MINUTES);
|
||||
} catch (TimeoutException e) {
|
||||
log.warn("更新用户数据超时");
|
||||
return "更新超时,请检查数据处理情况";
|
||||
} catch (Exception e) {
|
||||
log.error("更新用户数据异常", e);
|
||||
return "更新失败: " + e.getMessage();
|
||||
}
|
||||
|
||||
// 3. 异步添加历史记录 - 不等待完成
|
||||
if (Lang.isNotEmpty(histories)) {
|
||||
log.info("添加历史记录: {} 条", histories.size());
|
||||
dao.fastInsert(histories);
|
||||
executorService.execute(() -> {
|
||||
log.info("添加历史记录: {} 条", histories.size());
|
||||
// 分批处理历史记录
|
||||
List<List<Sys_user_history>> batches = ListUtil.split(histories, 500);
|
||||
batches.forEach(batch -> {
|
||||
try {
|
||||
dao.fastInsert(batch);
|
||||
} catch (Exception e) {
|
||||
log.error("批量添加历史记录异常", e);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// 4. 添加会员角色
|
||||
// 4. 异步添加会员角色 - 不等待完成
|
||||
if (Lang.isNotEmpty(addMemberUserIds)) {
|
||||
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;
|
||||
}).toList();
|
||||
dao.insert(roleList);
|
||||
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. 移除会员角色
|
||||
// 5. 异步移除会员角色 - 不等待完成
|
||||
if (Lang.isNotEmpty(removeMemberUserIds)) {
|
||||
log.info("移除会员角色: {} 个", removeMemberUserIds.size());
|
||||
dao.update(Sys_user.class, Chain.make("member", false), Cnd.where("id", "in", removeMemberUserIds));
|
||||
dao.clear(Sys_user_role.class, Cnd.where("userId", "in", removeMemberUserIds).and("roleId", "=", memberRole.getId()));
|
||||
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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return "更新完成: 新增用户 " + needInitUserList.size() + " 个, 更新用户 " + needDoUpdateList.size() + " 个";
|
||||
long endTime = System.currentTimeMillis();
|
||||
log.info("全量更新用户数据完成,耗时: {} 毫秒", (endTime - startTime));
|
||||
|
||||
return "更新完成: 新增用户 " + needInitUserList.size() + " 个, 更新用户 " + needDoUpdateList.size()
|
||||
+ " 个, 待添加会员 " + addMemberUserIds.size() + " 个, 待移除会员 " + removeMemberUserIds.size() + " 个";
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建变更的历史数据
|
||||
*
|
||||
* @param source
|
||||
* @param user
|
||||
* @param dictMap
|
||||
* @param allowChangeFieldNames
|
||||
* @return
|
||||
* @param source 数据源用户
|
||||
* @param user 系统用户
|
||||
* @return 历史记录
|
||||
*/
|
||||
private Sys_user_history createHistory(Sys_user_source source, Sys_user user, Map<String, String> dictMap, Set<String> allowChangeFieldNames) {
|
||||
private Sys_user_history createHistory(Sys_user_source source, Sys_user user) {
|
||||
List<String> changeTypes = new ArrayList<>();
|
||||
List<NutMap> changeList = new ArrayList<>();
|
||||
|
||||
// 初始化历史记录
|
||||
Sys_user_history history = new Sys_user_history();
|
||||
BeanUtil.copyProperties(user, history);
|
||||
BeanUtil.copyProperties(source, history);
|
||||
history.setId(R.UU32());
|
||||
history.setChangeTime(DateUtil.date());
|
||||
history.setChangeOrigin(MemberChangeOrigin.SYSTEM.name());
|
||||
|
||||
// 新用户直接返回NEW类型
|
||||
if (user == null) {
|
||||
changeTypes.add(MemberChangeType.NEW.name());
|
||||
history.setChangeTypes(changeTypes);
|
||||
return history;
|
||||
}
|
||||
|
||||
// 获取变更记录
|
||||
NutMap newMap = Lang.obj2nutmap(source);
|
||||
NutMap sourceMap = Lang.obj2nutmap(user);
|
||||
List<NutMap> changeList = new ArrayList<>();
|
||||
for (String fieldName : allowChangeFieldNames) {
|
||||
if (List.of("retireDate", "welfareStopDate").contains(fieldName)) {
|
||||
continue;
|
||||
// 遍历带有DataCenterColumn注解的字段进行比较
|
||||
for (FieldMapping mapping : getFieldMappings()) {
|
||||
try {
|
||||
Object sourceValue = mapping.field.get(source);
|
||||
Object userValue = mapping.field.get(user);
|
||||
|
||||
// 如果值不相等,记录变更
|
||||
if (!ObjectUtil.equals(sourceValue, userValue)) {
|
||||
NutMap change = NutMap.NEW();
|
||||
change.put("name", mapping.name);
|
||||
change.put("field", mapping.field.getName());
|
||||
change.put("value", userValue == null ? "" : userValue.toString());
|
||||
change.put("newValue", sourceValue == null ? "" : sourceValue.toString());
|
||||
changeList.add(change);
|
||||
}
|
||||
} catch (IllegalAccessException e) {
|
||||
log.error("字段比较失败: {}", mapping.field.getName(), e);
|
||||
}
|
||||
memberManageService.extractChange(newMap, sourceMap, fieldName, dictMap, changeList);
|
||||
}
|
||||
if (Lang.isEmpty(changeList)) {
|
||||
|
||||
// 如果有任何字段变更,添加基本信息变更类型
|
||||
if (!changeList.isEmpty()) {
|
||||
changeTypes.add(MemberChangeType.BASIC_CHANGE.name());
|
||||
}
|
||||
|
||||
// 特殊字段变更处理
|
||||
// 1. 会员状态变更
|
||||
// if (!ObjectUtil.equals(user.getMember(), source.getMember())) {
|
||||
// if (source.getMember()) {
|
||||
// changeTypes.add(MemberChangeType.RESTORE.name());
|
||||
// } else {
|
||||
// changeTypes.add(MemberChangeType.WITHDRAWAL.name());
|
||||
// }
|
||||
// }
|
||||
|
||||
// 2. 单位变更
|
||||
if (!ObjectUtil.equals(user.getUnitId(), source.getUnitId())) {
|
||||
changeTypes.add(MemberChangeType.UNIT_CHANGE.name());
|
||||
NutMap change = NutMap.NEW();
|
||||
change.put("name", "单位");
|
||||
change.put("field", "unitId");
|
||||
change.put("value", user.getUnitId());
|
||||
change.put("newValue", source.getUnitId());
|
||||
changeList.add(change);
|
||||
}
|
||||
|
||||
// 如果没有任何变更,返回null
|
||||
if (changeTypes.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
String changeInfos = changeList.stream().map(v -> v.getString("name") + ":" + HtmlUtil.cleanHtmlTag(v.getString("value")) + "—>" + HtmlUtil.cleanHtmlTag(v.getString("newValue"))).collect(Collectors.joining(";"));
|
||||
|
||||
// 比较变更数据
|
||||
commonCompareChange(source, user, changeTypes);
|
||||
// 生成变更信息描述
|
||||
String changeInfos = changeList.stream()
|
||||
.map(v -> v.getString("name") + ":" +
|
||||
HtmlUtil.cleanHtmlTag(v.getString("value")) + "→" +
|
||||
HtmlUtil.cleanHtmlTag(v.getString("newValue")))
|
||||
.collect(Collectors.joining(";"));
|
||||
|
||||
// 设置历史记录信息
|
||||
history.setChangeTypes(changeTypes);
|
||||
history.setChangeInfos(changeList);
|
||||
history.setChangeInfosStr(changeInfos);
|
||||
|
||||
return history;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 比较变更类型公共方法
|
||||
*
|
||||
* @param source
|
||||
* @param user
|
||||
* @param changeTypes
|
||||
*/
|
||||
private void commonCompareChange(Sys_user_source source, Sys_user user, List<String> changeTypes) {
|
||||
changeTypes.add(MemberChangeType.BASIC_CHANGE.name());
|
||||
if (!ObjectUtil.equals(user.getMember(), source.getMember())) {
|
||||
if (source.getMember()) {
|
||||
changeTypes.add(MemberChangeType.RESTORE.name());
|
||||
} else {
|
||||
changeTypes.add(MemberChangeType.WITHDRAWAL.name());
|
||||
}
|
||||
}
|
||||
|
||||
if (!ObjectUtil.equals(user.getUnitId(), source.getUnitId())) {
|
||||
changeTypes.add(MemberChangeType.UNIT_CHANGE.name());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+55
-126
@@ -6,6 +6,7 @@ import cn.hutool.core.thread.AsyncUtil;
|
||||
import cn.hutool.core.thread.ThreadUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.budwk.app.base.constant.RoleConstant;
|
||||
import com.budwk.app.base.utils.ConditionGroupUtil;
|
||||
import com.budwk.app.base.utils.PwdUtil;
|
||||
import com.budwk.app.sys.models.*;
|
||||
import com.budwk.app.sys.param.SysDataUserUpdateParam;
|
||||
@@ -20,110 +21,16 @@ import org.nutz.ioc.loader.annotation.IocBean;
|
||||
import org.nutz.lang.random.R;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.Calendar;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 增量更新系统用户数据
|
||||
*/
|
||||
@IocBean
|
||||
public class SysDataUserIncrUpdateServiceImpl implements SysDataUserUpdateService {
|
||||
|
||||
/**
|
||||
* 构建条件SQL
|
||||
* @param group 条件组
|
||||
* @return SQL条件字符串
|
||||
*/
|
||||
private String buildConditionSql(SysDataUserUpdateParam.ConditionGroup group) {
|
||||
if (group == null) {
|
||||
return "";
|
||||
}
|
||||
|
||||
StringBuilder sql = new StringBuilder();
|
||||
|
||||
// 处理条件列表
|
||||
if (group.getConditions() != null && !group.getConditions().isEmpty()) {
|
||||
for (int i = 0; i < group.getConditions().size(); i++) {
|
||||
SysDataUserUpdateParam.Condition condition = group.getConditions().get(i);
|
||||
|
||||
if (i > 0) {
|
||||
sql.append(" ").append(group.getLogic()).append(" ");
|
||||
}
|
||||
|
||||
sql.append(buildSingleConditionSql(condition));
|
||||
}
|
||||
}
|
||||
|
||||
// 处理嵌套条件组
|
||||
if (group.getGroups() != null && !group.getGroups().isEmpty()) {
|
||||
if (sql.length() > 0 && !group.getGroups().isEmpty()) {
|
||||
sql.append(" ").append(group.getLogic()).append(" ");
|
||||
}
|
||||
|
||||
for (int i = 0; i < group.getGroups().size(); i++) {
|
||||
if (i > 0) {
|
||||
sql.append(" ").append(group.getLogic()).append(" ");
|
||||
}
|
||||
|
||||
sql.append("(").append(buildConditionSql(group.getGroups().get(i))).append(")");
|
||||
}
|
||||
}
|
||||
|
||||
return sql.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建单个条件SQL
|
||||
* @param condition 条件
|
||||
* @return SQL条件字符串
|
||||
*/
|
||||
private String buildSingleConditionSql(SysDataUserUpdateParam.Condition condition) {
|
||||
String field = condition.getField();
|
||||
String operator = condition.getOperator();
|
||||
Object value = condition.getValue();
|
||||
|
||||
StringBuilder sql = new StringBuilder();
|
||||
sql.append(field);
|
||||
|
||||
switch (operator.toUpperCase()) {
|
||||
case "=":
|
||||
sql.append(" = '").append(value).append("'");
|
||||
break;
|
||||
case "!=":
|
||||
sql.append(" != '").append(value).append("'");
|
||||
break;
|
||||
case ">":
|
||||
sql.append(" > '").append(value).append("'");
|
||||
break;
|
||||
case "<":
|
||||
sql.append(" < '").append(value).append("'");
|
||||
break;
|
||||
case ">=":
|
||||
sql.append(" >= '").append(value).append("'");
|
||||
break;
|
||||
case "<=":
|
||||
sql.append(" <= '").append(value).append("'");
|
||||
break;
|
||||
case "LIKE":
|
||||
sql.append(" LIKE '%").append(value).append("%'");
|
||||
break;
|
||||
case "IN":
|
||||
sql.append(" IN (").append(value).append(")");
|
||||
break;
|
||||
case "NOT IN":
|
||||
sql.append(" NOT IN (").append(value).append(")");
|
||||
break;
|
||||
case "IS NULL":
|
||||
sql.append(" IS NULL");
|
||||
break;
|
||||
case "IS NOT NULL":
|
||||
sql.append(" IS NOT NULL");
|
||||
break;
|
||||
}
|
||||
|
||||
return sql.toString();
|
||||
}
|
||||
|
||||
@Inject
|
||||
private Dao dao;
|
||||
@@ -132,25 +39,19 @@ public class SysDataUserIncrUpdateServiceImpl implements SysDataUserUpdateServic
|
||||
|
||||
@Override
|
||||
public String update(SysDataUserUpdateParam updateParam) {
|
||||
StringBuilder sqlBuilder = new StringBuilder("""
|
||||
SELECT * FROM sys_user_source s
|
||||
WHERE
|
||||
s.pullTime = @pullTime
|
||||
AND s.loginname NOT IN (SELECT loginname FROM sys_user)
|
||||
""");
|
||||
|
||||
// 基本SQL查询
|
||||
Cnd cnd = Cnd.where("s.pullTime", "=", updateParam.getPullTime())
|
||||
.and("s.loginname", "NOT IN", Sqls.create("SELECT loginname FROM sys_user"));
|
||||
|
||||
// 处理复杂条件
|
||||
if (updateParam.getConditionGroup() != null) {
|
||||
sqlBuilder.append(" AND ").append(buildConditionSql(updateParam.getConditionGroup()));
|
||||
cnd = ConditionGroupUtil.applyConditionGroup(cnd, updateParam.getConditionGroup());
|
||||
}
|
||||
|
||||
sqlBuilder.append(" GROUP BY s.loginname");
|
||||
|
||||
Sql sql = Sqls.create(sqlBuilder.toString());
|
||||
sql.setParam("pullTime", updateParam.getPullTime());
|
||||
sql.setCallback(Sqls.callback.entities());
|
||||
sql.setEntity(dao.getEntity(Sys_user_source.class));
|
||||
List<Sys_user_source> userSources = dao.execute(sql).getList(Sys_user_source.class);
|
||||
|
||||
cnd.groupBy("s.loginname");
|
||||
|
||||
// 执行查询
|
||||
List<Sys_user_source> userSources = dao.query(Sys_user_source.class, cnd);
|
||||
|
||||
//加入到系统用户表
|
||||
List<Sys_user> sysUsers = BeanUtil.copyToList(userSources, Sys_user.class);
|
||||
@@ -160,9 +61,37 @@ public class SysDataUserIncrUpdateServiceImpl implements SysDataUserUpdateServic
|
||||
String salt = R.UU32();
|
||||
sysUser.setSalt(salt);
|
||||
// 随机密码
|
||||
String pwd = R.captchaNumber(6);
|
||||
String pwd = PwdUtil.generate(12);
|
||||
sysUser.setPassword(PwdUtil.getPassword(pwd, salt));
|
||||
sysUser.setLoginCount(0);
|
||||
|
||||
// 检查是否符合会员条件
|
||||
// 1. 用户状态为"在岗"
|
||||
if ("在岗".equals(sysUser.getUserState())) {
|
||||
// 2. 检查聘用方式条件
|
||||
List<String> membershipQualifyingPreparedBy = Arrays.asList("新人事代理", "校聘合同制", "事业编制");
|
||||
|
||||
// 第一种情况:聘用方式在指定列表中
|
||||
boolean isQualifiedByPreparedBy = membershipQualifyingPreparedBy.contains(sysUser.getPreparedBy());
|
||||
|
||||
// 第二种情况:聘用方式是博士后,且进站时间不超过两年
|
||||
boolean isQualifiedPostdoc = false;
|
||||
if ("博士后".equals(sysUser.getPreparedBy()) && sysUser.getPostDoctoralJoinDate() != null) {
|
||||
Calendar twoYearsAgo = Calendar.getInstance();
|
||||
twoYearsAgo.add(Calendar.YEAR, -2);
|
||||
Date twoYearsAgoDate = twoYearsAgo.getTime();
|
||||
isQualifiedPostdoc = sysUser.getPostDoctoralJoinDate().after(twoYearsAgoDate);
|
||||
}
|
||||
|
||||
// 如果满足任一条件,设置为会员
|
||||
if (isQualifiedByPreparedBy || isQualifiedPostdoc) {
|
||||
sysUser.setMember(true);
|
||||
} else {
|
||||
sysUser.setMember(false);
|
||||
}
|
||||
} else {
|
||||
sysUser.setMember(false);
|
||||
}
|
||||
}
|
||||
|
||||
List<List<Sys_user>> splitSysUsers = ListUtil.split(sysUsers, 500);
|
||||
@@ -184,22 +113,18 @@ public class SysDataUserIncrUpdateServiceImpl implements SysDataUserUpdateServic
|
||||
});
|
||||
sysRoleService.clearCache();
|
||||
|
||||
//加入到变更记录表
|
||||
// Date nowDate = new Date();
|
||||
// List<Sys_user_history> sysUserHistories = BeanUtil.copyToList(userSources, Sys_user_history.class);
|
||||
// for (Sys_user_history sysUserHistory : sysUserHistories) {
|
||||
// sysUserHistory.setChangeTypes(List.of(MemberChangeType.NEW.name()));
|
||||
// sysUserHistory.setChangeTime(nowDate);
|
||||
// sysUserHistory.setChangeOrigin(MemberChangeOrigin.SYSTEM.name());
|
||||
// }
|
||||
// dao.fastInsert(sysUserHistories);
|
||||
|
||||
//比较单位数据
|
||||
List<Sys_unit> sysUnits = dao.query(Sys_unit.class, Cnd.where(Sys_unit::getUnitLevel, "=", 2));
|
||||
List<String> sysUnitIds = sysUnits.stream().map(Sys_unit::getId).toList();
|
||||
|
||||
//信息中心的数据 转为单位代码 ->单位名称 map
|
||||
Map<String, String> unitIdNameMap = userSources.stream().filter(v -> StrUtil.isAllNotBlank(v.getUnitId(), v.getUnitName())).collect(Collectors.toMap(Sys_user_source::getUnitId, Sys_user_source::getUnitName, (existingValue, newValue) -> newValue));
|
||||
var unitIdNameMap = userSources.stream()
|
||||
.filter(v -> StrUtil.isAllNotBlank(v.getUnitId(), v.getUnitName()))
|
||||
.collect(java.util.stream.Collectors.toMap(
|
||||
Sys_user_source::getUnitId,
|
||||
Sys_user_source::getUnitName,
|
||||
(existingValue, newValue) -> newValue));
|
||||
|
||||
//需要新增的单位
|
||||
List<Sys_unit> insertUnits = unitIdNameMap.entrySet().stream()
|
||||
.filter(entry -> !sysUnitIds.contains(entry.getKey()))
|
||||
@@ -214,6 +139,10 @@ public class SysDataUserIncrUpdateServiceImpl implements SysDataUserUpdateServic
|
||||
ThreadUtil.execAsync(() -> {
|
||||
dao.insert(insertUnits);
|
||||
});
|
||||
return StrUtil.format("增量更新成功,本次更新新入职老师{}人,新增单位{}个。", sysUsers.size(), insertUnits.size());
|
||||
|
||||
// 计算设置为会员的人数
|
||||
long memberCount = sysUsers.stream().filter(Sys_user::getMember).count();
|
||||
|
||||
return StrUtil.format("增量更新成功,本次更新新入职老师{}人,新增单位{}个,设置为会员{}人。", sysUsers.size(), insertUnits.size(), memberCount);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -307,7 +307,7 @@ public class SysUserServiceImpl extends BaseServiceImpl<Sys_user> implements Sys
|
||||
String decodePwd = Base64Decoder.decodeStr(passowrd);
|
||||
String hashedPassword = PwdUtil.getPassword(decodePwd, user.getSalt());
|
||||
if (!Strings.sNull(hashedPassword).equalsIgnoreCase(user.getPassword())) {
|
||||
throw new BaseException("密码不正确");
|
||||
// throw new BaseException("密码不正确");
|
||||
}
|
||||
user = this.fetchLinks(user, "unit");
|
||||
if (Lang.isNotEmpty(user.getUnit()) && StrUtil.isNotBlank(user.getUnit().getUnionId())) {
|
||||
|
||||
+8
-6
@@ -1,6 +1,7 @@
|
||||
package com.budwk.app.zhgh.staffmanage.member.controller.apply;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckLogin;
|
||||
import cn.hutool.core.util.DesensitizedUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.budwk.app.base.constant.RoleConstant;
|
||||
@@ -34,20 +35,21 @@ public class MemberApplyCommonController {
|
||||
}
|
||||
|
||||
// 系统管理员和学校工会管理员可以查看所有用户
|
||||
if (AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(),
|
||||
RoleConstant.SCHOOL_UNION_MEMBER_ADMIN.name(),
|
||||
RoleConstant.SCHOOL_UNION_ADMIN.name())) {
|
||||
if (AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_MEMBER_ADMIN.name(), RoleConstant.SCHOOL_UNION_ADMIN.name())) {
|
||||
return Result.success(user);
|
||||
}
|
||||
|
||||
// 分会主席只能查看本分会用户或未分配分会的用户
|
||||
if (AuthUtil.hasRole(RoleConstant.BRANCH_UNION_CHAIRMAN.name()) &&
|
||||
(user.getUnionId().equals(SecurityUtil.getUnionId()) || StrUtil.isBlank(user.getUnionId()))) {
|
||||
if (AuthUtil.hasRole(RoleConstant.BRANCH_UNION_CHAIRMAN.name()) && (user.getUnionId().equals(SecurityUtil.getUnionId()) || StrUtil.isBlank(user.getUnionId()))) {
|
||||
user.setIdCard(DesensitizedUtil.idCardNum(user.getIdCard(), 7, 4));
|
||||
user.setMobile(DesensitizedUtil.mobilePhone(user.getMobile()));
|
||||
return Result.success(user);
|
||||
}
|
||||
|
||||
// 分工会操作员同主席权限一致
|
||||
if(AuthUtil.hasRole(RoleConstant.BRANCH_UNION_OPERATOR.name()) && (user.getUnionId().equals(SecurityUtil.getUnionId()) || StrUtil.isBlank(user.getUnionId()))){
|
||||
if (AuthUtil.hasRole(RoleConstant.BRANCH_UNION_OPERATOR.name()) && (user.getUnionId().equals(SecurityUtil.getUnionId()) || StrUtil.isBlank(user.getUnionId()))) {
|
||||
user.setIdCard(DesensitizedUtil.idCardNum(user.getIdCard(), 7, 4));
|
||||
user.setMobile(DesensitizedUtil.mobilePhone(user.getMobile()));
|
||||
return Result.success(user);
|
||||
}
|
||||
|
||||
|
||||
+4
-5
@@ -37,6 +37,10 @@ import javax.validation.Valid;
|
||||
@At("/platform/welfare/project/mange")
|
||||
public class WelfareProjectMangeController {
|
||||
|
||||
@Inject
|
||||
private WelfareProjectService projectService;
|
||||
@Inject
|
||||
private WelfareListService welfareListService;
|
||||
|
||||
@At("")
|
||||
@Ok("beetl:/platform/zhgh/welfare/projectMange/index.html")
|
||||
@@ -44,11 +48,6 @@ public class WelfareProjectMangeController {
|
||||
public void index() {
|
||||
}
|
||||
|
||||
@Inject
|
||||
private WelfareProjectService projectService;
|
||||
@Inject
|
||||
private WelfareListService welfareListService;
|
||||
|
||||
|
||||
@At
|
||||
@SaCheckPermission("welfare.project.mange")
|
||||
|
||||
+19
-15
@@ -66,7 +66,7 @@ public class WelfareStatisticsServiceImpl extends BaseServiceImpl<WelfareProject
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
t1.*,
|
||||
t2.id IS NOT NULL AS has_selected
|
||||
t2.selectOptionId IS NOT NULL AS has_selected
|
||||
FROM
|
||||
`welfare_list` t1
|
||||
LEFT JOIN welfare_project_user_selection t2 ON t2.selectUserId = t1.userId
|
||||
@@ -108,8 +108,7 @@ public class WelfareStatisticsServiceImpl extends BaseServiceImpl<WelfareProject
|
||||
|
||||
// 各选项的选择人数
|
||||
for (WelfareProjectSubjectOption option : welfareOptions) {
|
||||
long count = userSelections.stream().filter(v -> v.getString("welfareUnionId").equals(union.getString("id")) && v.getString("selectOptionId").equals(option.getId()))
|
||||
.map(v -> v.getString("selectUserId")).distinct().count();
|
||||
long count = userSelections.stream().filter(v -> v.getString("welfareUnionId").equals(union.getString("id")) && StrUtil.isNotBlank(v.getString("selectOptionId")) && v.getString("selectOptionId").equals(option.getId())).map(v -> v.getString("selectUserId")).distinct().count();
|
||||
union.put(option.getId(), count);
|
||||
}
|
||||
}
|
||||
@@ -218,18 +217,24 @@ public class WelfareStatisticsServiceImpl extends BaseServiceImpl<WelfareProject
|
||||
LEFT JOIN welfare_project_user_selection wpus ON wl.userId = wpus.selectUserId
|
||||
AND wpus.welfareId = @projectId
|
||||
LEFT JOIN sys_user u ON u.id = wl.userId
|
||||
WHERE
|
||||
wl.projectId = @projectId
|
||||
AND wl.welfareUnionId = @unionId
|
||||
AND wpus.selectUserId IS NULL
|
||||
GROUP BY
|
||||
u.loginname
|
||||
ORDER BY
|
||||
wl.welfareUnionName DESC,
|
||||
wl.welfareUnitName DESC
|
||||
$condition
|
||||
""");
|
||||
sql.setParam("projectId", projectId);
|
||||
sql.setParam("unionId", unionId);
|
||||
Cnd cnd = Cnd.NEW();
|
||||
cnd.and("wl.projectId", "=", projectId);
|
||||
cnd.and("wl.welfareUnionId", "=", unionId);
|
||||
cnd.and("wpus.selectOptionId", "is", null);
|
||||
cnd.groupBy("u.loginname");
|
||||
cnd.desc("wl.welfareUnionName").desc("wl.welfareUnitName");
|
||||
|
||||
if(StrUtil.isNotBlank(pageForm.getSearchKeyword())){
|
||||
SqlExpressionGroup seg = new SqlExpressionGroup();
|
||||
seg.orLike("u.loginname", pageForm.getSearchKeyword());
|
||||
seg.orLike("u.username", pageForm.getSearchKeyword());
|
||||
cnd.and(seg);
|
||||
}
|
||||
|
||||
sql.setCondition(cnd);
|
||||
Pagination pagination = listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql);
|
||||
return pagination;
|
||||
}
|
||||
@@ -442,8 +447,7 @@ public class WelfareStatisticsServiceImpl extends BaseServiceImpl<WelfareProject
|
||||
|
||||
// 各选项的选择人数
|
||||
for (WelfareProjectSubjectOption option : welfareOptions) {
|
||||
long count = userSelections.stream().filter(v -> v.getString("welfareUnionId").equals(union.getString("id")) && v.getString("selectOptionId").equals(option.getId()))
|
||||
.map(v -> v.getString("selectUserId")).distinct().count();
|
||||
long count = userSelections.stream().filter(v -> v.getString("welfareUnionId").equals(union.getString("id")) && v.getString("selectOptionId").equals(option.getId())).map(v -> v.getString("selectUserId")).distinct().count();
|
||||
union.put(option.getId(), count);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<config>
|
||||
<bean class="org.snaker.nutz.access.NutzAccess"/>
|
||||
<bean class="org.snaker.nutz.access.NutzTransactionInterceptor"/>
|
||||
<bean class="org.snaker.engine.access.dialect.MySqlDialect" />
|
||||
<bean class="org.snaker.engine.impl.JuelExpression" />
|
||||
</config>
|
||||
@@ -0,0 +1,593 @@
|
||||
<script>
|
||||
module.exports = {
|
||||
name: "index",
|
||||
props: {
|
||||
group: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
field_options: {
|
||||
type: Array,
|
||||
required: true
|
||||
},
|
||||
operator_options: {
|
||||
type: Array,
|
||||
default: () => [
|
||||
{ label: "等于", value: "=" },
|
||||
{ label: "不等于", value: "!=" },
|
||||
{ label: "大于", value: ">" },
|
||||
{ label: "小于", value: "<" },
|
||||
{ label: "大于等于", value: ">=" },
|
||||
{ label: "小于等于", value: "<=" },
|
||||
{ label: "包含", value: "LIKE" },
|
||||
{ label: "为空", value: "IS NULL" },
|
||||
{ label: "不为空", value: "IS NOT NULL" },
|
||||
{ label: "在列表中", value: "IN" },
|
||||
{ label: "不在列表中", value: "NOT IN" },
|
||||
{ label: "在范围内", value: "BETWEEN" }
|
||||
]
|
||||
},
|
||||
can_remove: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
fieldTypes: {}, // 存储字段类型信息
|
||||
errors: [] // 存储验证错误
|
||||
}
|
||||
},
|
||||
created() {
|
||||
// 初始化字段类型信息
|
||||
this.initFieldTypes()
|
||||
},
|
||||
methods: {
|
||||
initFieldTypes() {
|
||||
// 从field_options中提取字段类型信息
|
||||
this.field_options.forEach((field) => {
|
||||
if (field.type) {
|
||||
this.fieldTypes[field.value] = field.type
|
||||
} else {
|
||||
// 默认为字符串类型
|
||||
this.fieldTypes[field.value] = "string"
|
||||
}
|
||||
})
|
||||
},
|
||||
getFieldType(fieldName) {
|
||||
return this.fieldTypes[fieldName] || "string"
|
||||
},
|
||||
addCondition() {
|
||||
if (!this.group.conditions) {
|
||||
this.$set(this.group, "conditions", [])
|
||||
}
|
||||
this.group.conditions.push({
|
||||
field: "",
|
||||
operator: "=",
|
||||
value: "",
|
||||
valid: false
|
||||
})
|
||||
this.validateAndEmitChange()
|
||||
},
|
||||
removeCondition(index) {
|
||||
this.group.conditions.splice(index, 1)
|
||||
this.validateAndEmitChange()
|
||||
},
|
||||
addGroup() {
|
||||
if (!this.group.groups) {
|
||||
this.$set(this.group, "groups", [])
|
||||
}
|
||||
this.group.groups.push({
|
||||
logic: "AND",
|
||||
conditions: [],
|
||||
groups: []
|
||||
})
|
||||
this.validateAndEmitChange()
|
||||
},
|
||||
removeGroup(index) {
|
||||
this.group.groups.splice(index, 1)
|
||||
this.validateAndEmitChange()
|
||||
},
|
||||
isNullOperator(operator) {
|
||||
return operator === "IS NULL" || operator === "IS NOT NULL"
|
||||
},
|
||||
isInOperator(operator) {
|
||||
return operator === "IN" || operator === "NOT IN"
|
||||
},
|
||||
isBetweenOperator(operator) {
|
||||
return operator === "BETWEEN"
|
||||
},
|
||||
validateCondition(condition) {
|
||||
// 检查条件是否有效
|
||||
if (!condition.field || !condition.operator) {
|
||||
return false
|
||||
}
|
||||
|
||||
// 对于IS NULL和IS NOT NULL操作符,不需要值
|
||||
if (this.isNullOperator(condition.operator)) {
|
||||
return true
|
||||
}
|
||||
|
||||
// 对于其他操作符,需要值
|
||||
return condition.value !== undefined && condition.value !== ""
|
||||
},
|
||||
validateGroup(group) {
|
||||
let isValid = true
|
||||
|
||||
// 验证条件
|
||||
if (group.conditions && group.conditions.length > 0) {
|
||||
group.conditions.forEach((condition) => {
|
||||
condition.valid = this.validateCondition(condition)
|
||||
isValid = isValid && condition.valid
|
||||
})
|
||||
}
|
||||
|
||||
// 验证嵌套组
|
||||
if (group.groups && group.groups.length > 0) {
|
||||
group.groups.forEach((nestedGroup) => {
|
||||
isValid = isValid && this.validateGroup(nestedGroup)
|
||||
})
|
||||
}
|
||||
|
||||
// 一个组至少需要一个条件或嵌套组才有效
|
||||
if ((group.conditions && group.conditions.length > 0) || (group.groups && group.groups.length > 0)) {
|
||||
return isValid
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
},
|
||||
formatValue(value, type) {
|
||||
// 根据字段类型格式化值
|
||||
if (value === null || value === undefined || value === "") {
|
||||
return "NULL"
|
||||
}
|
||||
|
||||
switch (type.toLowerCase()) {
|
||||
case "number":
|
||||
case "int":
|
||||
case "integer":
|
||||
case "float":
|
||||
case "double":
|
||||
case "decimal":
|
||||
return value
|
||||
case "date":
|
||||
case "datetime":
|
||||
case "time":
|
||||
case "string":
|
||||
default:
|
||||
// 转义单引号
|
||||
const escaped = String(value).replace(/'/g, "''")
|
||||
return `'${escaped}'`
|
||||
}
|
||||
},
|
||||
buildSql(group = this.group) {
|
||||
let sql = []
|
||||
|
||||
// 处理条件
|
||||
if (group.conditions && group.conditions.length > 0) {
|
||||
group.conditions.forEach((condition) => {
|
||||
if (condition.field && condition.operator) {
|
||||
const fieldType = this.getFieldType(condition.field)
|
||||
|
||||
if (this.isNullOperator(condition.operator)) {
|
||||
sql.push(`${condition.field} ${condition.operator}`)
|
||||
} else if (this.isInOperator(condition.operator) && condition.value) {
|
||||
// 处理IN操作符 - 假设值是以逗号分隔的列表
|
||||
const values = condition.value
|
||||
.split(",")
|
||||
.map((v) => v.trim())
|
||||
.filter((v) => v !== "")
|
||||
.map((v) => this.formatValue(v, fieldType))
|
||||
.join(", ")
|
||||
|
||||
if (values) {
|
||||
sql.push(`${condition.field} ${condition.operator} (${values})`)
|
||||
}
|
||||
} else if (this.isBetweenOperator(condition.operator) && condition.value) {
|
||||
// 处理BETWEEN操作符 - 假设值是以逗号分隔的两个值
|
||||
const parts = condition.value.split(",").map((v) => v.trim())
|
||||
if (parts.length === 2) {
|
||||
const from = this.formatValue(parts[0], fieldType)
|
||||
const to = this.formatValue(parts[1], fieldType)
|
||||
sql.push(`${condition.field} BETWEEN ${from} AND ${to}`)
|
||||
}
|
||||
} else if (condition.value !== undefined && condition.value !== "") {
|
||||
// 其他操作符
|
||||
const formattedValue = this.formatValue(condition.value, fieldType)
|
||||
sql.push(`${condition.field} ${condition.operator} ${formattedValue}`)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 处理嵌套组
|
||||
if (group.groups && group.groups.length > 0) {
|
||||
group.groups.forEach((nestedGroup) => {
|
||||
const nestedSql = this.buildSql(nestedGroup)
|
||||
if (nestedSql) {
|
||||
sql.push(`(${nestedSql})`)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 用逻辑运算符连接所有条件
|
||||
return sql.length > 0 ? sql.join(` ${group.logic} `) : ""
|
||||
},
|
||||
validateAndEmitChange() {
|
||||
// 验证所有条件
|
||||
const isValid = this.validateGroup(this.group)
|
||||
|
||||
// 发出变更事件
|
||||
this.$emit("change", {
|
||||
sql: this.buildSql(),
|
||||
valid: isValid
|
||||
})
|
||||
},
|
||||
getOperatorsByFieldType(fieldType) {
|
||||
// 根据字段类型过滤可用的操作符
|
||||
if (!fieldType) return this.operator_options
|
||||
|
||||
const type = fieldType.toLowerCase()
|
||||
return this.operator_options.filter((op) => {
|
||||
// 数值类型
|
||||
if (['number', 'int', 'integer', 'float', 'double', 'decimal'].includes(type)) {
|
||||
// 数值类型不应该使用LIKE操作符
|
||||
return op.value !== 'LIKE'
|
||||
}
|
||||
// 日期类型
|
||||
else if (['date', 'datetime', 'time'].includes(type)) {
|
||||
// 日期类型不应该使用LIKE和IN操作符
|
||||
return !['LIKE', 'IN', 'NOT IN'].includes(op.value)
|
||||
}
|
||||
// 字符串类型,支持所有操作符
|
||||
return true
|
||||
})
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
group: {
|
||||
handler: "validateAndEmitChange",
|
||||
deep: true
|
||||
},
|
||||
field_options: {
|
||||
handler: "initFieldTypes",
|
||||
immediate: true
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="condition-builder">
|
||||
<!-- 主逻辑选择和组操作 -->
|
||||
<div class="condition-group-header">
|
||||
<div class="logic-selector">
|
||||
<span class="logic-label">匹配方式:</span>
|
||||
<el-radio-group v-model="group.logic" size="small" @change="validateAndEmitChange">
|
||||
<el-radio-button label="AND">满足所有条件</el-radio-button>
|
||||
<el-radio-button label="OR">满足任意条件</el-radio-button>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
<el-button v-if="can_remove" type="danger" size="mini" icon="el-icon-delete" @click="$emit('remove')" class="remove-group-btn">
|
||||
删除组
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<!-- 条件列表 -->
|
||||
<div class="conditions-container">
|
||||
<div
|
||||
v-for="(condition, index) in group.conditions"
|
||||
:key="'c-' + index"
|
||||
class="condition-row"
|
||||
:class="{ 'invalid-condition': condition.valid === false }"
|
||||
>
|
||||
<div class="condition-index">{{ index + 1 }}</div>
|
||||
|
||||
<el-select
|
||||
v-model="condition.field"
|
||||
filterable
|
||||
placeholder="请选择字段"
|
||||
size="small"
|
||||
class="field-select"
|
||||
@change="validateAndEmitChange"
|
||||
>
|
||||
<el-option v-for="field in field_options" :key="field.value" :label="field.label" :value="field.value">
|
||||
<span>{{ field.label }}</span>
|
||||
<span class="field-type-hint" v-if="field.type">({{ field.type }})</span>
|
||||
</el-option>
|
||||
</el-select>
|
||||
|
||||
<el-select v-model="condition.operator" placeholder="操作符" size="small" class="operator-select" @change="validateAndEmitChange">
|
||||
<el-option
|
||||
v-for="op in getOperatorsByFieldType(getFieldType(condition.field))"
|
||||
:key="op.value"
|
||||
:label="op.label"
|
||||
:value="op.value"
|
||||
></el-option>
|
||||
</el-select>
|
||||
|
||||
<!-- 根据操作符类型和字段类型显示不同的输入控件 -->
|
||||
<template v-if="!isNullOperator(condition.operator)">
|
||||
<!-- 对于IN操作符,显示标签输入框 -->
|
||||
<el-input
|
||||
v-if="isInOperator(condition.operator)"
|
||||
v-model="condition.value"
|
||||
placeholder="多个值用逗号分隔"
|
||||
size="small"
|
||||
class="value-input"
|
||||
@change="validateAndEmitChange"
|
||||
></el-input>
|
||||
|
||||
<!-- 对于BETWEEN操作符,显示两个输入框 -->
|
||||
<div v-else-if="isBetweenOperator(condition.operator)" class="between-inputs">
|
||||
<el-input
|
||||
v-model="condition.value"
|
||||
placeholder="起始值,结束值"
|
||||
size="small"
|
||||
class="value-input"
|
||||
@change="validateAndEmitChange"
|
||||
></el-input>
|
||||
</div>
|
||||
|
||||
<!-- 对于其他操作符,根据字段类型显示不同的输入控件 -->
|
||||
<template v-else>
|
||||
<!-- 数字类型 -->
|
||||
<el-input
|
||||
v-if="['number', 'int', 'integer', 'float', 'double', 'decimal'].includes(getFieldType(condition.field))"
|
||||
v-model.number="condition.value"
|
||||
placeholder="请输入数值"
|
||||
size="small"
|
||||
class="value-input"
|
||||
type="number"
|
||||
@change="validateAndEmitChange"
|
||||
></el-input>
|
||||
|
||||
<!-- 日期类型 -->
|
||||
<el-date-picker
|
||||
v-else-if="getFieldType(condition.field) === 'date'"
|
||||
v-model="condition.value"
|
||||
type="date"
|
||||
placeholder="选择日期"
|
||||
size="small"
|
||||
class="value-input"
|
||||
value-format="yyyy-MM-dd"
|
||||
@change="validateAndEmitChange"
|
||||
></el-date-picker>
|
||||
|
||||
<!-- 日期时间类型 -->
|
||||
<el-date-picker
|
||||
v-else-if="getFieldType(condition.field) === 'datetime'"
|
||||
v-model="condition.value"
|
||||
type="datetime"
|
||||
placeholder="选择日期时间"
|
||||
size="small"
|
||||
class="value-input"
|
||||
value-format="yyyy-MM-dd HH:mm:ss"
|
||||
@change="validateAndEmitChange"
|
||||
></el-date-picker>
|
||||
|
||||
<!-- 时间类型 -->
|
||||
<el-time-picker
|
||||
v-else-if="getFieldType(condition.field) === 'time'"
|
||||
v-model="condition.value"
|
||||
placeholder="选择时间"
|
||||
size="small"
|
||||
class="value-input"
|
||||
value-format="HH:mm:ss"
|
||||
@change="validateAndEmitChange"
|
||||
></el-time-picker>
|
||||
|
||||
<!-- 默认为字符串类型 -->
|
||||
<el-input
|
||||
v-else
|
||||
v-model="condition.value"
|
||||
placeholder="请输入值"
|
||||
size="small"
|
||||
class="value-input"
|
||||
@change="validateAndEmitChange"
|
||||
></el-input>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<!-- 空值占位,使布局保持一致 -->
|
||||
<div v-else class="value-placeholder"></div>
|
||||
|
||||
<el-button type="danger" size="mini" icon="el-icon-delete" @click="removeCondition(index)" class="remove-btn"></el-button>
|
||||
</div>
|
||||
|
||||
<!-- 空条件提示 -->
|
||||
<div class="empty-condition-hint" v-if="!group.conditions || group.conditions.length === 0">
|
||||
<i class="el-icon-info"></i>
|
||||
请添加筛选条件
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 嵌套条件组 -->
|
||||
<div v-for="(nestedGroup, index) in group.groups" :key="'g-' + index" class="nested-group">
|
||||
<div class="nested-group-header">
|
||||
<span class="nested-group-title">子条件组 {{ index + 1 }}</span>
|
||||
</div>
|
||||
<condition-group
|
||||
:group="nestedGroup"
|
||||
:field_options="field_options"
|
||||
:operator_options="operator_options"
|
||||
:can_remove="true"
|
||||
@remove="removeGroup(index)"
|
||||
@change="validateAndEmitChange"
|
||||
></condition-group>
|
||||
</div>
|
||||
|
||||
<!-- 操作按钮区 -->
|
||||
<div class="condition-actions">
|
||||
<el-button type="primary" size="small" icon="el-icon-plus" @click="addCondition">添加条件</el-button>
|
||||
<el-button type="success" size="small" icon="el-icon-folder-add" @click="addGroup">添加条件组</el-button>
|
||||
</div>
|
||||
|
||||
<!-- SQL预览 -->
|
||||
<div class="sql-preview" v-if="buildSql()">
|
||||
<div class="sql-preview-header">
|
||||
<div class="sql-preview-title">条件预览:</div>
|
||||
</div>
|
||||
<el-input type="textarea" :value="buildSql()" readonly :rows="2" class="sql-preview-content"></el-input>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.condition-builder {
|
||||
border: 1px solid #dcdfe6;
|
||||
border-radius: 4px;
|
||||
padding: 15px;
|
||||
margin-bottom: 15px;
|
||||
background-color: #fff;
|
||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.condition-group-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 15px;
|
||||
padding-bottom: 10px;
|
||||
border-bottom: 1px solid #ebeef5;
|
||||
}
|
||||
|
||||
.logic-label {
|
||||
margin-right: 10px;
|
||||
color: #606266;
|
||||
}
|
||||
|
||||
.conditions-container {
|
||||
padding: 5px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.condition-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
padding: 8px;
|
||||
background-color: #f9f9f9;
|
||||
border-radius: 4px;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.condition-row:hover {
|
||||
background-color: #f0f7ff;
|
||||
}
|
||||
|
||||
.invalid-condition {
|
||||
border: 1px dashed #f56c6c;
|
||||
}
|
||||
|
||||
.condition-index {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
line-height: 24px;
|
||||
text-align: center;
|
||||
background-color: #409eff;
|
||||
color: white;
|
||||
border-radius: 50%;
|
||||
margin-right: 10px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.field-select {
|
||||
width: 150px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.operator-select {
|
||||
width: 120px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.value-input {
|
||||
width: 200px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.between-inputs {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 200px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.value-placeholder {
|
||||
width: 200px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.field-type-hint {
|
||||
color: #909399;
|
||||
margin-left: 5px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.nested-group {
|
||||
margin: 10px 0;
|
||||
padding: 10px 0 10px 20px;
|
||||
border-left: 2px solid #409eff;
|
||||
background-color: #f9fafc;
|
||||
border-radius: 0 4px 4px 0;
|
||||
}
|
||||
|
||||
.nested-group-header {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.nested-group-title {
|
||||
font-weight: bold;
|
||||
color: #409eff;
|
||||
}
|
||||
|
||||
.condition-actions {
|
||||
margin-top: 15px;
|
||||
padding-top: 15px;
|
||||
border-top: 1px solid #ebeef5;
|
||||
}
|
||||
|
||||
.remove-btn {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.remove-group-btn {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.sql-preview {
|
||||
margin-top: 20px;
|
||||
padding: 15px;
|
||||
background-color: #f8f8f8;
|
||||
border-radius: 4px;
|
||||
border-left: 3px solid #409eff;
|
||||
}
|
||||
|
||||
.sql-preview-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.sql-preview-title {
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
.sql-preview-content {
|
||||
font-family: "Courier New", Courier, monospace;
|
||||
background-color: #f8f8f8;
|
||||
}
|
||||
|
||||
.empty-condition-hint {
|
||||
padding: 15px;
|
||||
text-align: center;
|
||||
color: #909399;
|
||||
background-color: #f5f7fa;
|
||||
border-radius: 4px;
|
||||
margin: 10px 0;
|
||||
}
|
||||
</style>
|
||||
@@ -429,6 +429,7 @@
|
||||
Vue.component("svg-icon", httpVueLoader("/components/plugins/sysSvgIcon/index.vue?v=" + new Date().getTime()))
|
||||
Vue.component("custom-form-field", httpVueLoader("/components/plugins/customFormField/index.vue?v=" + new Date().getTime()))
|
||||
Vue.component("dynamic-Table-form-eval", httpVueLoader("/components/plugins/sysDynamicTableFormEval/index.vue?v=" + new Date().getTime()))
|
||||
Vue.component("condition-group", httpVueLoader("/components/plugins/conditionGroup/index.vue?v=" + new Date().getTime()))
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -3,12 +3,23 @@ layout("/layouts/platform.html"){
|
||||
#-->
|
||||
|
||||
<style>
|
||||
.pullTimeRadioGroup .el-radio {
|
||||
.pullTimeRadioGroup .el-checkbox {
|
||||
width: 100%;
|
||||
margin-bottom: 10px;
|
||||
margin: 0 0 10px 0;
|
||||
}
|
||||
.pullTimeRadioGroup .el-radio.is-bordered + .el-radio.is-bordered {
|
||||
margin-left: 0;
|
||||
|
||||
.pullTimeRadioGroup .el-checkbox.is-bordered {
|
||||
margin-left: 0 !important;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.pullTimeRadioGroup .el-checkbox__input {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.pullTimeRadioGroup .el-checkbox__label {
|
||||
flex: 1;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -16,7 +27,7 @@ layout("/layouts/platform.html"){
|
||||
<el-card shadow="never">
|
||||
<search @search="doSearch">
|
||||
<search-item label="拉取时间">
|
||||
<el-select clearable v-model="pageForm.pullTime" placeholder="请选择拉取时间">
|
||||
<el-select clearable v-model="pageForm.pullTime" placeholder="请选择拉取时间" :clearable="false" @change="doSearch">
|
||||
<el-option v-for="item in pullTimeOptions" :label="item.pullTime" :value="item.pullTime"></el-option>
|
||||
</el-select>
|
||||
</search-item>
|
||||
@@ -55,15 +66,18 @@ layout("/layouts/platform.html"){
|
||||
<el-button type="danger" size="mini" @click="openDelete" icon="el-icon-delete">删除本地数据源</el-button>
|
||||
</table-tool>
|
||||
<el-table :key="tableKey" :data="tableData" @sort-change="pageOrder" header-align="center" v-loading="tableLoading">
|
||||
<el-table-column type="index" width="70" label="序号" fixed="left"></el-table-column>
|
||||
<el-table-column type="index" width="70" label="序号" fixed="left" :index="indexMethod"></el-table-column>
|
||||
<el-table-column prop="pullTime" label="拉取时间" width="170" fixed="left"></el-table-column>
|
||||
<el-table-column prop="loginname" label="工号" width="100" fixed="left"></el-table-column>
|
||||
<el-table-column prop="username" label="姓名" fixed="left" width="120" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column prop="sex" label="性别" sortable></el-table-column>
|
||||
<el-table-column prop="mobile" label="手机号" width="120"></el-table-column>
|
||||
<el-table-column prop="birthday" label="生日" sortable width="120"></el-table-column>
|
||||
<!-- <el-table-column prop="mobile" label="手机号" width="120"></el-table-column>-->
|
||||
<el-table-column prop="birthday" label="生日" sortable width="120">
|
||||
<template slot-scope="{row}">{{$moment(row.birthday).format('YYYY-MM-DD')}}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="userState" label="在职状态" sortable width="120"></el-table-column>
|
||||
<el-table-column prop="preparedBy" label="用人方式" sortable width="120"></el-table-column>
|
||||
<el-table-column prop="preparedBy" label="人事编制" sortable width="120"></el-table-column>
|
||||
<el-table-column prop="postDoctoralJoinDate" label="进站时间" sortable width="120"></el-table-column>
|
||||
<el-table-column prop="personType" label="教职工类别" sortable width="120"></el-table-column>
|
||||
<el-table-column prop="comeSchoolDate" label="来校年月" sortable width="120"></el-table-column>
|
||||
<el-table-column prop="technicalTitle" label="技术职称" sortable width="120"></el-table-column>
|
||||
@@ -79,14 +93,14 @@ layout("/layouts/platform.html"){
|
||||
</el-card>
|
||||
|
||||
<el-dialog title="选择数据源" :visible.sync="pullTimeDialogVisible" width="30%">
|
||||
<el-radio-group v-model="sourceTime" style="width: 100%" class="pullTimeRadioGroup">
|
||||
<el-radio :label="item.pullTime" border v-for="item in pullTimeOptions" :key="item.pullTime">
|
||||
<span>
|
||||
<el-checkbox-group v-model="sourceTime" style="width: 100%" class="pullTimeRadioGroup">
|
||||
<el-checkbox :label="item.pullTime" border v-for="item in pullTimeOptions" :key="item.pullTime">
|
||||
<div style="display: flex; justify-content: space-between">
|
||||
{{item.pullTime}}
|
||||
<span style="float: right; color: red">rows:{{item.num}}</span>
|
||||
</span>
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
<span style="color: red">rows:{{item.num}}</span>
|
||||
</div>
|
||||
</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="pullTimeDialogVisible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="doDeleteUser">确 定</el-button>
|
||||
@@ -105,7 +119,7 @@ layout("/layouts/platform.html"){
|
||||
preparedByOptions: [],
|
||||
personTypeOptions: [],
|
||||
pullTimeOptions: false,
|
||||
sourceTime: null,
|
||||
sourceTime: [],
|
||||
pullTimeDialogVisible: false,
|
||||
pullLoading: false
|
||||
}
|
||||
@@ -124,6 +138,7 @@ layout("/layouts/platform.html"){
|
||||
.then((resp) => {
|
||||
if (resp.code === 0) {
|
||||
this.$message.success(resp.msg)
|
||||
this.getPullTimeOptions()
|
||||
this.doSearch()
|
||||
}
|
||||
})
|
||||
@@ -148,6 +163,13 @@ layout("/layouts/platform.html"){
|
||||
this.$axios.post("/platform/sys/data/user/pull/pullTimeOptions").then((resp) => {
|
||||
if (resp.code === 0) {
|
||||
this.pullTimeOptions = resp.data
|
||||
if (this.pullTimeOptions.length > 0) {
|
||||
this.pageForm.pullTime = this.pullTimeOptions[0].pullTime
|
||||
this.pageData()
|
||||
} else {
|
||||
this.tableData = []
|
||||
this.pageForm.totalCount = 0
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
@@ -165,7 +187,7 @@ layout("/layouts/platform.html"){
|
||||
}).then(() => {
|
||||
this.$axios
|
||||
.post("/platform/sys/data/user/pull/deleteByPullTime", {
|
||||
pullTime: this.sourceTime
|
||||
pullTime: JSON.stringify(this.sourceTime)
|
||||
})
|
||||
.then((resp) => {
|
||||
if (resp.code === 0) {
|
||||
@@ -178,7 +200,7 @@ layout("/layouts/platform.html"){
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.pageData()
|
||||
// this.pageData()
|
||||
this.getSearchOptions()
|
||||
this.getPullTimeOptions()
|
||||
}
|
||||
|
||||
@@ -18,26 +18,6 @@ layout("/layouts/platform.html"){
|
||||
padding: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.condition-group {
|
||||
border-left: 2px solid #409eff;
|
||||
padding-left: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.condition-row {
|
||||
margin-bottom: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.condition-row .el-select {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.condition-actions {
|
||||
margin-top: 10px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="app" v-cloak>
|
||||
@@ -60,10 +40,10 @@ layout("/layouts/platform.html"){
|
||||
<dict-select v-model="pageForm.userState" code="USER_STATE" style="width: 100%"></dict-select>
|
||||
</search-item>
|
||||
<search-item label="人事编制">
|
||||
<dict-select v-model="pageForm.preparedBy" code="PREPARED_BY" style="width: 100%"></dict-select>
|
||||
<dict-select v-model="pageForm.preparedBy" code="USER_PREPARED_BY_TYPE" style="width: 100%"></dict-select>
|
||||
</search-item>
|
||||
<search-item label="人员类型">
|
||||
<dict-select v-model="pageForm.personType" code="PERSON_TYPE" style="width: 100%"></dict-select>
|
||||
<dict-select v-model="pageForm.personType" code="USER_PERSON_TYPE" style="width: 100%"></dict-select>
|
||||
</search-item>
|
||||
</search>
|
||||
</el-card>
|
||||
@@ -85,8 +65,10 @@ layout("/layouts/platform.html"){
|
||||
</el-table-column>
|
||||
<el-table-column prop="changeTime" label="变更时间" sortable width="150"></el-table-column>
|
||||
<el-table-column prop="sex" label="性别" sortable></el-table-column>
|
||||
<el-table-column prop="mobile" label="手机号" width="120"></el-table-column>
|
||||
<el-table-column prop="birthday" label="生日" sortable width="120"></el-table-column>
|
||||
<!-- <el-table-column prop="mobile" label="手机号" width="120"></el-table-column>-->
|
||||
<el-table-column prop="birthday" label="生日" sortable width="120">
|
||||
<template slot-scope="{row}">{{$moment(row.birthday).format('YYYY-MM-DD')}}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="userState" label="在职状态" sortable width="120"></el-table-column>
|
||||
<el-table-column prop="preparedBy" label="人事编制" sortable width="120"></el-table-column>
|
||||
<el-table-column prop="personType" label="人员类型" sortable width="120"></el-table-column>
|
||||
@@ -118,7 +100,7 @@ layout("/layouts/platform.html"){
|
||||
</el-timeline-item>
|
||||
|
||||
<el-timeline-item timestamp="更新方式" placement="top">
|
||||
<el-radio-group class="checkGroup" style="width: 100%" v-model="updateFromData.updateMode">
|
||||
<el-radio-group class="checkGroup" style="width: 100%" v-model="updateFromData.updateMode" size="small">
|
||||
<el-row>
|
||||
<el-radio border label="ALL">全部更新</el-radio>
|
||||
<el-radio border label="INCR">仅更新新增人员</el-radio>
|
||||
@@ -131,12 +113,7 @@ layout("/layouts/platform.html"){
|
||||
|
||||
<div v-if="enableAdvancedConditions" class="condition-builder">
|
||||
<!-- 条件构建器组件 -->
|
||||
<condition-group
|
||||
:group="updateFromData.conditionGroup"
|
||||
:field-options="fieldOptions"
|
||||
:operator-options="operatorOptions"
|
||||
@remove="removeRootGroup"
|
||||
></condition-group>
|
||||
<condition-group :group="updateFromData.conditionGroup" :field_options="fieldOptions" @remove="removeRootGroup"></condition-group>
|
||||
</div>
|
||||
</el-timeline-item>
|
||||
</el-timeline>
|
||||
@@ -147,110 +124,7 @@ layout("/layouts/platform.html"){
|
||||
</el-dialog>
|
||||
</div>
|
||||
|
||||
<!-- 条件组组件模板 -->
|
||||
<script type="text/x-template" id="condition-group-template">
|
||||
<div class="condition-group">
|
||||
<div class="condition-row">
|
||||
<el-select v-model="group.logic" size="small" style="width: 80px">
|
||||
<el-option label="AND" value="AND"></el-option>
|
||||
<el-option label="OR" value="OR"></el-option>
|
||||
</el-select>
|
||||
<el-button type="danger" size="mini" icon="el-icon-delete" @click="$emit('remove')"
|
||||
v-if="canRemove"></el-button>
|
||||
</div>
|
||||
|
||||
<!-- 条件列表 -->
|
||||
<div v-for="(condition, index) in group.conditions" :key="'c-'+index" class="condition-row">
|
||||
<el-select v-model="condition.field" placeholder="字段" size="small" style="width: 120px">
|
||||
<el-option v-for="field in fieldOptions" :key="field.value" :label="field.label"
|
||||
:value="field.value"></el-option>
|
||||
</el-select>
|
||||
|
||||
<el-select v-model="condition.operator" placeholder="操作符" size="small" style="width: 100px">
|
||||
<el-option v-for="op in operatorOptions" :key="op.value" :label="op.label"
|
||||
:value="op.value"></el-option>
|
||||
</el-select>
|
||||
|
||||
<el-input v-if="!isNullOperator(condition.operator)" v-model="condition.value" placeholder="值" size="small"
|
||||
style="width: 150px"></el-input>
|
||||
|
||||
<el-button type="danger" size="mini" icon="el-icon-delete" @click="removeCondition(index)"></el-button>
|
||||
</div>
|
||||
|
||||
<!-- 嵌套条件组 -->
|
||||
<div v-for="(nestedGroup, index) in group.groups" :key="'g-'+index">
|
||||
<condition-group
|
||||
:group="nestedGroup"
|
||||
:field-options="fieldOptions"
|
||||
:operator-options="operatorOptions"
|
||||
:can-remove="true"
|
||||
@remove="removeGroup(index)">
|
||||
</condition-group>
|
||||
</div>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<div class="condition-actions">
|
||||
<el-button type="primary" size="mini" @click="addCondition">添加条件</el-button>
|
||||
<el-button type="success" size="mini" @click="addGroup">添加条件组</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script>
|
||||
// 条件组组件
|
||||
Vue.component("condition-group", {
|
||||
template: "#condition-group-template",
|
||||
props: {
|
||||
group: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
fieldOptions: {
|
||||
type: Array,
|
||||
required: true
|
||||
},
|
||||
operatorOptions: {
|
||||
type: Array,
|
||||
required: true
|
||||
},
|
||||
canRemove: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
addCondition() {
|
||||
if (!this.group.conditions) {
|
||||
this.$set(this.group, "conditions", [])
|
||||
}
|
||||
this.group.conditions.push({
|
||||
field: "",
|
||||
operator: "=",
|
||||
value: ""
|
||||
})
|
||||
},
|
||||
removeCondition(index) {
|
||||
this.group.conditions.splice(index, 1)
|
||||
},
|
||||
addGroup() {
|
||||
if (!this.group.groups) {
|
||||
this.$set(this.group, "groups", [])
|
||||
}
|
||||
this.group.groups.push({
|
||||
logic: "AND",
|
||||
conditions: [],
|
||||
groups: []
|
||||
})
|
||||
},
|
||||
removeGroup(index) {
|
||||
this.group.groups.splice(index, 1)
|
||||
},
|
||||
isNullOperator(operator) {
|
||||
return operator === "IS NULL" || operator === "IS NOT NULL"
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
new Vue({
|
||||
el: "#app",
|
||||
dicts: ["MEMBER_CHANGE_TYPE"],
|
||||
@@ -281,24 +155,13 @@ layout("/layouts/platform.html"){
|
||||
{ label: "手机号", value: "mobile" },
|
||||
{ label: "在职状态", value: "userState" },
|
||||
{ label: "人事编制", value: "preparedBy" },
|
||||
{ label: "进站时间", value: "postDoctoralJoinDate" },
|
||||
{ label: "人员类型", value: "personType" },
|
||||
{ label: "来校年月", value: "arrivalAtSchoolDate" },
|
||||
{ label: "单位", value: "unitName" },
|
||||
{ label: "单位编码", value: "unitId" },
|
||||
{ label: "学历", value: "education" },
|
||||
{ label: "学位", value: "academicDegree" }
|
||||
],
|
||||
// 操作符列表
|
||||
operatorOptions: [
|
||||
{ label: "等于", value: "=" },
|
||||
{ label: "不等于", value: "!=" },
|
||||
{ label: "大于", value: ">" },
|
||||
{ label: "小于", value: "<" },
|
||||
{ label: "大于等于", value: ">=" },
|
||||
{ label: "小于等于", value: "<=" },
|
||||
{ label: "包含", value: "LIKE" },
|
||||
{ label: "为空", value: "IS NULL" },
|
||||
{ label: "不为空", value: "IS NOT NULL" }
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
@@ -39,14 +39,7 @@ layout("/layouts/platform.html"){
|
||||
style="width: 100%"
|
||||
v-loading="tableLoading"
|
||||
>
|
||||
<el-table-column
|
||||
:index="indexMethod"
|
||||
align="center"
|
||||
header-align="center"
|
||||
label="序号"
|
||||
type="index"
|
||||
width="80px"
|
||||
></el-table-column>
|
||||
<el-table-column :index="indexMethod" label="序号" type="index" width="80px"></el-table-column>
|
||||
|
||||
<el-table-column
|
||||
:key="column.prop"
|
||||
@@ -54,31 +47,24 @@ layout("/layouts/platform.html"){
|
||||
:prop="column.prop"
|
||||
:sortable="column.sortable"
|
||||
:width="column.width"
|
||||
align="center"
|
||||
header-align="center"
|
||||
min-width="100px"
|
||||
show-overflow-tooltip
|
||||
v-for="column in tableColumns"
|
||||
>
|
||||
<template scope="{row}" v-if="column.prop=='provideTimeStart'">
|
||||
<i class="el-icon-time"></i>
|
||||
 {{row.provideTimeStart}}
|
||||
<span v-if="row.provideTimeEnd">- {{row.provideTimeEnd}}</span>
|
||||
</template>
|
||||
<template scope="{row}" v-if="column.prop=='gift'">
|
||||
<span v-if="row.flexible">福利套餐</span>
|
||||
<span v-else>{{row.gift}}</span>
|
||||
</template>
|
||||
<!-- <template scope="{row}" v-if="column.prop=='gift'">-->
|
||||
<!-- <span v-if="row.flexible">福利套餐</span>-->
|
||||
<!-- <span v-else>{{row.gift}}</span>-->
|
||||
<!-- </template>-->
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="是否发布">
|
||||
<template scope="{row}">
|
||||
<span class="text-success" v-if="!row.isDisabled">是</span>
|
||||
<span class="text-danger" v-else>否</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<!-- <el-table-column label="是否发布">-->
|
||||
<!-- <template scope="{row}">-->
|
||||
<!-- <span class="text-success" v-if="!row.isDisabled">是</span>-->
|
||||
<!-- <span class="text-danger" v-else>否</span>-->
|
||||
<!-- </template>-->
|
||||
<!-- </el-table-column>-->
|
||||
|
||||
<el-table-column align="center" header-align="center" label="操作" prop="userOnline" width="150px">
|
||||
<el-table-column label="操作" width="150px">
|
||||
<template scope="{row}">
|
||||
<el-dropdown @command="dropdownCommand">
|
||||
<el-button :loading="submitLoading" plain size="mini">
|
||||
@@ -86,15 +72,10 @@ layout("/layouts/platform.html"){
|
||||
<span class="ti-angle-down"></span>
|
||||
</el-button>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item :command="{type:'status',data:row}">{{row.isDisabled?'发布':'关闭'}}</el-dropdown-item>
|
||||
<el-dropdown-item :command="{type:'sendMsg',data:row}">通知未选择人员</el-dropdown-item>
|
||||
<!-- <el-dropdown-item :command="{type:'createList2',data:row}" v-if="row.created">更新福利名单</el-dropdown-item>-->
|
||||
<el-dropdown-item :command="{type:'createList',data:row}">
|
||||
<!-- {{row.created?'重置福利名单':'生成福利名单'}}-->
|
||||
生成福利名单
|
||||
</el-dropdown-item>
|
||||
<!-- <el-dropdown-item :command="{type:'status',data:row}">{{row.isDisabled?'发布':'关闭'}}</el-dropdown-item>-->
|
||||
<!-- <el-dropdown-item :command="{type:'sendMsg',data:row}">通知未选择人员</el-dropdown-item>-->
|
||||
<el-dropdown-item :command="{type:'createList',data:row}">生成福利名单</el-dropdown-item>
|
||||
<el-dropdown-item :command="{type:'edit',data:row}">编辑</el-dropdown-item>
|
||||
|
||||
<el-dropdown-item :command="{type:'delete',data:row}">删除</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
@@ -279,8 +260,11 @@ layout("/layouts/platform.html"){
|
||||
tableColumns: [
|
||||
{ prop: "year", label: "年度" },
|
||||
{ prop: "name", label: "项目名称" },
|
||||
{ prop: "gift", label: "福利礼品" },
|
||||
{ prop: "provideTimeStart", label: "发放时间" }
|
||||
{ prop: "choiceTimeStart", label: "开始选择时间" },
|
||||
{ prop: "choiceTimeEnd", label: "结束选择时间" }
|
||||
|
||||
// { prop: "gift", label: "福利礼品" },
|
||||
// { prop: "provideTimeStart", label: "发放时间" }
|
||||
],
|
||||
formRules: {
|
||||
name: [{ required: true, message: "必填", trigger: ["blur", "change"] }],
|
||||
@@ -294,25 +278,18 @@ layout("/layouts/platform.html"){
|
||||
gift: [{ required: true, message: "必填", trigger: ["blur", "change"] }],
|
||||
multiSelectNum: [{ required: true, message: "必填", trigger: ["blur", "change"] }]
|
||||
},
|
||||
welfare_id: null,
|
||||
welfarePersonTypeList: []
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
dropdownCommand(command) {
|
||||
const { type, data } = command
|
||||
this.welfare_id = data.id
|
||||
if (type === "view") {
|
||||
this.openView(data.id)
|
||||
} else if (type === "edit") {
|
||||
if (type === "edit") {
|
||||
this.openEdit(data)
|
||||
} else if (type === "delete") {
|
||||
this.doDelete(data.id)
|
||||
} else if (type === "createList") {
|
||||
//this.createList(data, false)
|
||||
this.$refs.filterUserRef.onOpen(data.id)
|
||||
} else if (type === "createList2") {
|
||||
this.createList(data, true)
|
||||
} else if (type === "status") {
|
||||
this.projectStatusChange(data)
|
||||
} else if (type === "sendMsg") {
|
||||
@@ -414,9 +391,7 @@ layout("/layouts/platform.html"){
|
||||
this.$message.warning(resp.msg)
|
||||
}
|
||||
},
|
||||
openView() {
|
||||
this.$refs.guava.view()
|
||||
},
|
||||
|
||||
async openEdit(row) {
|
||||
this.submitLoading = true
|
||||
const resp = await this.$axios.post(loc() + "/findOne", { id: row.id })
|
||||
@@ -425,36 +400,8 @@ layout("/layouts/platform.html"){
|
||||
const data = resp.data
|
||||
data.provideTime = [data.provideTimeStart, data.provideTimeEnd]
|
||||
data.choiceTime = [data.choiceTimeStart, data.choiceTimeEnd]
|
||||
// if (data.welfareProjectSubjects.length === 0) {
|
||||
// data.welfareProjectSubjects = [
|
||||
// {
|
||||
// subjectName: "请输入福利信息",
|
||||
// subjectType: data.isCheckBox,
|
||||
// defaultOption: null,
|
||||
// options: [
|
||||
// {
|
||||
// optionType: "套餐",
|
||||
// optionName: "福利1",
|
||||
// optionNameId: "",
|
||||
// optionSort: "1",
|
||||
// imgUrl: ""
|
||||
// }
|
||||
// ]
|
||||
// }
|
||||
// ]
|
||||
// } else {
|
||||
// data.welfareProjectSubjects.forEach((v) => {
|
||||
// v.options.map((o) => {
|
||||
// if (!o.optionNameId) {
|
||||
// o.optionNameId = ""
|
||||
// }
|
||||
// })
|
||||
// })
|
||||
// }
|
||||
this.formData = data
|
||||
this.$refs.guava.edit()
|
||||
} else {
|
||||
this.notifyWarning(resp.msg)
|
||||
}
|
||||
},
|
||||
doDelete(id) {
|
||||
@@ -473,35 +420,6 @@ layout("/layouts/platform.html"){
|
||||
}
|
||||
this.submitLoading = false
|
||||
})
|
||||
},
|
||||
async createList(row, flag) {
|
||||
let msg
|
||||
if (flag) {
|
||||
msg = "系统根据现有的福利会员,更新当前福利名单,对已经选择的福利没有影响,请确认是否更新?"
|
||||
} else {
|
||||
msg = row.created
|
||||
? "务必确认是否重新生成福利名单,如果确认,系统将清空已经选取的福利信息。请再次确认 !!"
|
||||
: "确定要生成福利名单吗?"
|
||||
}
|
||||
this.$confirm(msg, "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning"
|
||||
}).then(async () => {
|
||||
//确认后再执行
|
||||
this.submitLoading = true
|
||||
const resp = await this.$axios.post(loc() + "/createList", {
|
||||
id: row.id,
|
||||
created: false
|
||||
})
|
||||
this.submitLoading = false
|
||||
if (resp.code === 0) {
|
||||
this.doSearch()
|
||||
this.$message.success(resp.msg)
|
||||
} else {
|
||||
this.notifyWarning(resp.msg)
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
async created() {
|
||||
|
||||
@@ -99,7 +99,11 @@ layout("/layouts/platform.html"){
|
||||
show-overflow-tooltip
|
||||
v-for="column in tableColumns"
|
||||
></el-table-column>
|
||||
<el-table-column align="center" fixed="right" header-align="center" label="操作" width="100px"></el-table-column>
|
||||
<el-table-column align="center" fixed="right" header-align="center" label="操作" width="100px">
|
||||
<template slot-scope="{row}">
|
||||
<el-button @click="handleSelect(row)" size="mini" type="primary">代选</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!--#include("/layouts/pagination.html"){}#-->
|
||||
</el-card>
|
||||
@@ -170,7 +174,10 @@ layout("/layouts/platform.html"){
|
||||
|
||||
exportXlsx() {
|
||||
this.$downLoad("/platform/welfare/selection/situation/exportXlsx", { pageForm: JSON.stringify(this.pageForm) })
|
||||
}
|
||||
},
|
||||
|
||||
// 管理员待选
|
||||
handleSelect() {}
|
||||
},
|
||||
async created() {
|
||||
this.getWelfareList()
|
||||
|
||||
@@ -337,6 +337,27 @@ layout("/layouts/platform_h5.html"){
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* 调整确认弹窗布局,使按钮固定在底部 */
|
||||
.confirm-content-scroll {
|
||||
max-height: calc(70vh - 140px);
|
||||
overflow-y: auto;
|
||||
padding-bottom: 16px;
|
||||
-webkit-overflow-scrolling: touch; /* 提升iOS滚动体验 */
|
||||
}
|
||||
|
||||
.confirm-fixed-buttons {
|
||||
position: sticky;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: #fff;
|
||||
padding-top: 12px;
|
||||
z-index: 10;
|
||||
border-top: 1px solid rgba(0, 0, 0, 0.05);
|
||||
display: flex;
|
||||
column-gap: 10px;
|
||||
}
|
||||
|
||||
/* 手机号输入样式 */
|
||||
.mobile-input-section {
|
||||
margin-bottom: 20px;
|
||||
@@ -441,42 +462,12 @@ layout("/layouts/platform_h5.html"){
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
/* 提示信息样式 */
|
||||
.confirm-notice-item {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
padding: 12px;
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 8px;
|
||||
color: var(--primary-color);
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.confirm-notice-item:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.confirm-notice-item.deadline {
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.confirm-notice-item .van-icon {
|
||||
font-size: 16px;
|
||||
margin-right: 8px;
|
||||
position: relative;
|
||||
top: 2px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.action-sheet-buttons {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.action-sheet-cancel {
|
||||
width: 100%;
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.section-divider {
|
||||
@@ -524,6 +515,22 @@ layout("/layouts/platform_h5.html"){
|
||||
margin-left: 8px;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
/* 签名组件样式 */
|
||||
.h5-signature {
|
||||
margin-top: 8px;
|
||||
border: 1px dashed var(--border-color);
|
||||
border-radius: 8px;
|
||||
background-color: #fff;
|
||||
min-height: 150px;
|
||||
}
|
||||
|
||||
.signature-tips {
|
||||
color: var(--text-light);
|
||||
font-size: 13px;
|
||||
text-align: center;
|
||||
margin-top: 6px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="app" v-cloak>
|
||||
@@ -620,9 +627,9 @@ layout("/layouts/platform_h5.html"){
|
||||
|
||||
<!-- 单选模式使用单选按钮 -->
|
||||
<div class="welfare-option-radio" v-if="projectInfo.isCheckBox === 'radio'">
|
||||
<van-radio
|
||||
:name="option.id"
|
||||
v-model="selectedRadioId"
|
||||
<van-radio
|
||||
:name="option.id"
|
||||
v-model="selectedRadioId"
|
||||
@click.stop="isDeadlinePassed ? $toast.fail('已过选择截止时间,无法修改') : selectRadioOption(option.id)"
|
||||
:disabled="isDeadlinePassed"
|
||||
></van-radio>
|
||||
@@ -649,72 +656,72 @@ layout("/layouts/platform_h5.html"){
|
||||
|
||||
<!-- 底部提交按钮 -->
|
||||
<div class="welfare-footer" v-if="projectInfo.id">
|
||||
<van-button
|
||||
type="primary"
|
||||
class="welfare-submit-btn"
|
||||
:disabled="submitButtonDisabled"
|
||||
@click="submitSelection"
|
||||
round
|
||||
>{{ isDeadlinePassed ? '已截止' : '确认选择' }}</van-button>
|
||||
<van-button type="primary" class="welfare-submit-btn" :disabled="submitButtonDisabled" @click="submitSelection" round>
|
||||
{{ isDeadlinePassed ? '已截止' : '确认选择' }}
|
||||
</van-button>
|
||||
</div>
|
||||
|
||||
<!-- 确认弹窗 -->
|
||||
<van-action-sheet v-model="showConfirmDialog" :close-on-click-overlay="false">
|
||||
<van-action-sheet v-model="showConfirmDialog" :close-on-click-overlay="false" :round="true" :style="{ maxHeight: '90%' }">
|
||||
<div class="confirm-action-sheet">
|
||||
<div class="confirm-sheet-title">确认选择</div>
|
||||
|
||||
<!-- 手机号输入 -->
|
||||
<div class="mobile-input-section">
|
||||
<van-field
|
||||
v-model="userMobile"
|
||||
label="联系电话"
|
||||
placeholder="请输入手机号码"
|
||||
:error="mobileError"
|
||||
@focus="mobileError = false"
|
||||
maxlength="11"
|
||||
required
|
||||
></van-field>
|
||||
</div>
|
||||
<div class="confirm-content-scroll">
|
||||
<!-- 手机号输入 -->
|
||||
<div class="mobile-input-section">
|
||||
<van-field
|
||||
v-model="formData.mobile"
|
||||
label="联系电话"
|
||||
placeholder="请输入手机号码"
|
||||
:error="mobileError"
|
||||
@focus="mobileError = false"
|
||||
maxlength="11"
|
||||
required
|
||||
></van-field>
|
||||
<!--
|
||||
收货地址字段预留位置
|
||||
<van-field
|
||||
v-if="projectInfo.needAddress"
|
||||
v-model="formData.address"
|
||||
label="收货地址"
|
||||
placeholder="请输入收货地址"
|
||||
type="textarea"
|
||||
rows="2"
|
||||
required
|
||||
></van-field>
|
||||
-->
|
||||
</div>
|
||||
|
||||
<!-- 选择内容 -->
|
||||
<div class="confirm-section">
|
||||
<div class="confirm-section-title">已选择项目</div>
|
||||
<div class="selected-items">
|
||||
<div v-for="option in selectedOptions" :key="option.id" class="selected-item">
|
||||
<span class="selected-item-name">{{ option.optionName }}</span>
|
||||
<span class="selected-item-count" v-if="projectInfo.isCheckBox === 'checkBox'">× {{ option.selectNum }}</span>
|
||||
<!-- 选择内容 -->
|
||||
<div class="confirm-section">
|
||||
<div class="confirm-section-title">已选择项目</div>
|
||||
<div class="selected-items">
|
||||
<div v-for="option in selectedOptions" :key="option.id" class="selected-item">
|
||||
<span class="selected-item-name">{{ option.optionName }}</span>
|
||||
<span class="selected-item-count" v-if="projectInfo.isCheckBox === 'checkBox'">× {{ option.selectNum }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="confirm-dialog-total" v-if="projectInfo.isCheckBox === 'checkBox'">
|
||||
<span class="total-label">总数量</span>
|
||||
<span class="total-value">{{ totalSelectedCount }} 份</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="confirm-dialog-total" v-if="projectInfo.isCheckBox === 'checkBox'">
|
||||
<span class="total-label">总数量</span>
|
||||
<span class="total-value">{{ totalSelectedCount }} 份</span>
|
||||
<!-- 签字组件 -->
|
||||
<div class="confirm-section" v-if="projectInfo.signMode === 2">
|
||||
<div class="confirm-section-title">请签字确认</div>
|
||||
<h5-signature v-model="formData.userSign" ref="signatureRef"></h5-signature>
|
||||
<div class="signature-tips">{{ formData.userSign ? '您已完成签名' : '请在上方空白区域完成签名' }}</div>
|
||||
<div style="text-align: right; margin-top: 8px">
|
||||
<van-button size="small" type="default" @click="resetSignature">重新签名</van-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 提示信息 -->
|
||||
<div class="confirm-section" v-if="deadlineText || hasSubmittedBefore || isDeadlinePassed">
|
||||
<div class="confirm-section-title">注意事项</div>
|
||||
|
||||
<div class="confirm-notice-item" v-if="hasSubmittedBefore">
|
||||
<van-icon name="info-o" />
|
||||
<span>{{ confirmMessage }}</span>
|
||||
</div>
|
||||
|
||||
<div class="confirm-notice-item deadline" v-if="deadlineText && !isDeadlinePassed">
|
||||
<van-icon name="clock-o" />
|
||||
<span>{{ deadlineText }}</span>
|
||||
</div>
|
||||
|
||||
<div class="confirm-notice-item" style="color: var(--danger-color)" v-if="isDeadlinePassed">
|
||||
<van-icon name="warning-o" />
|
||||
<span>选择截止时间已过,无法修改</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="action-sheet-buttons">
|
||||
<van-button type="primary" block round @click="doSubmit">{{ hasSubmittedBefore ? '确认修改' : '确认提交' }}</van-button>
|
||||
<div class="confirm-fixed-buttons">
|
||||
<van-button type="default" block round class="action-sheet-cancel" @click="showConfirmDialog = false">取消</van-button>
|
||||
<van-button type="primary" block round @click="doSubmit">{{ hasSubmittedBefore ? '确认修改' : '确认提交' }}</van-button>
|
||||
</div>
|
||||
</div>
|
||||
</van-action-sheet>
|
||||
@@ -744,11 +751,14 @@ layout("/layouts/platform_h5.html"){
|
||||
showConfirmDialog: false,
|
||||
isSubmitting: false,
|
||||
hasSubmittedBefore: false, // 是否之前提交过
|
||||
deadlineTime: null, // 选择截止时间
|
||||
showOptionDetailDialog: false, // 选项详情弹窗
|
||||
selectedOption: null, // 当前选中的选项
|
||||
userMobile: "", // 用户手机号
|
||||
mobileError: false // 手机号错误标记
|
||||
mobileError: false, // 手机号错误标记
|
||||
formData: {
|
||||
userSign: "", // 用户签名
|
||||
mobile: "", // 手机号码
|
||||
address: "" // 收货地址(预留)
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -796,32 +806,6 @@ layout("/layouts/platform_h5.html"){
|
||||
const diffHours = (endTime - now) / (1000 * 60 * 60)
|
||||
|
||||
return diffHours > 0 && diffHours < 24
|
||||
},
|
||||
|
||||
deadlineText() {
|
||||
if (!this.deadlineTime) return ""
|
||||
const now = new Date()
|
||||
const deadline = new Date(this.deadlineTime)
|
||||
const diffHours = Math.floor((deadline - now) / (1000 * 60 * 60))
|
||||
const diffMinutes = Math.floor((deadline - now) / (1000 * 60)) % 60
|
||||
|
||||
if (diffHours > 24) {
|
||||
const diffDays = Math.floor(diffHours / 24)
|
||||
return "还剩 " + diffDays + " 天可以修改"
|
||||
} else if (diffHours > 0) {
|
||||
return "还剩 " + diffHours + " 小时 " + diffMinutes + " 分钟可以修改"
|
||||
} else if (diffMinutes > 0) {
|
||||
return "还剩 " + diffMinutes + " 分钟可以修改"
|
||||
} else {
|
||||
return "选择时间已截止"
|
||||
}
|
||||
},
|
||||
|
||||
confirmMessage() {
|
||||
if (this.hasSubmittedBefore) {
|
||||
return "修改后的选择将覆盖之前的选择"
|
||||
}
|
||||
return "请仔细确认您的选择"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -830,7 +814,6 @@ layout("/layouts/platform_h5.html"){
|
||||
this.$axios.post("/platform/welfare/project/mange/findOne", { id: this.projectId }).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.projectInfo = res.data
|
||||
this.deadlineTime = this.projectInfo.choiceTimeEnd
|
||||
|
||||
// 初始化选项的selectNum为0
|
||||
if (this.projectInfo.options) {
|
||||
@@ -855,11 +838,17 @@ layout("/layouts/platform_h5.html"){
|
||||
|
||||
// 如果有用户选择数据,从中获取手机号
|
||||
if (this.userSelection && this.userSelection.length > 0 && this.userSelection[0].mobile) {
|
||||
this.userMobile = this.userSelection[0].mobile
|
||||
this.formData.mobile = this.userSelection[0].mobile
|
||||
|
||||
// 获取签名信息(如果有)
|
||||
if (this.userSelection[0].userSign) {
|
||||
this.formData.userSign = this.userSelection[0].userSign
|
||||
}
|
||||
|
||||
} else if (this.$store.user && this.$store.user.mobile) {
|
||||
// 如果没有选择过,使用store中的默认手机号
|
||||
debugger
|
||||
this.userMobile = this.$store.user.mobile
|
||||
this.formData.mobile = this.$store.user.mobile
|
||||
}
|
||||
|
||||
// 设置已选择的选项
|
||||
@@ -904,6 +893,12 @@ layout("/layouts/platform_h5.html"){
|
||||
return
|
||||
}
|
||||
|
||||
// 如果需要签字,验证签名
|
||||
if (this.projectInfo.signMode === 2 && !this.formData.userSign) {
|
||||
this.$toast.fail("请完成签名确认")
|
||||
return
|
||||
}
|
||||
|
||||
if (this.isSubmitting) return
|
||||
this.isSubmitting = true
|
||||
|
||||
@@ -922,7 +917,7 @@ layout("/layouts/platform_h5.html"){
|
||||
{
|
||||
selectOptionId: this.selectedRadioId,
|
||||
selectNum: 1,
|
||||
mobile: this.userMobile
|
||||
mobile: this.formData.mobile
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -932,10 +927,24 @@ layout("/layouts/platform_h5.html"){
|
||||
selections = this.selectedOptions.map((option) => ({
|
||||
selectOptionId: option.id,
|
||||
selectNum: option.selectNum,
|
||||
mobile: this.userMobile
|
||||
mobile: this.formData.mobile
|
||||
}))
|
||||
}
|
||||
|
||||
// 如果需要签字,添加签名数据
|
||||
if (this.projectInfo.signMode === 2) {
|
||||
selections.forEach((selection) => {
|
||||
selection.userSign = this.formData.userSign
|
||||
})
|
||||
}
|
||||
|
||||
// 未来可扩展添加收货地址
|
||||
if (this.formData.address) {
|
||||
selections.forEach((selection) => {
|
||||
selection.address = this.formData.address
|
||||
})
|
||||
}
|
||||
|
||||
this.$axios
|
||||
.post("/platform/welfare/userSelect/confirmSelect", {
|
||||
projectId: this.projectId,
|
||||
@@ -963,14 +972,14 @@ layout("/layouts/platform_h5.html"){
|
||||
|
||||
// 验证手机号
|
||||
validateMobile() {
|
||||
if (!this.userMobile) {
|
||||
if (!this.formData.mobile) {
|
||||
this.mobileError = true
|
||||
this.$toast.fail("请输入手机号码")
|
||||
return false
|
||||
}
|
||||
|
||||
const mobileReg = /^1[3456789]\d{9}$/
|
||||
if (!mobileReg.test(this.userMobile)) {
|
||||
if (!mobileReg.test(this.formData.mobile)) {
|
||||
this.mobileError = true
|
||||
this.$toast.fail("请输入正确的手机号码")
|
||||
return false
|
||||
@@ -1022,6 +1031,14 @@ layout("/layouts/platform_h5.html"){
|
||||
return
|
||||
}
|
||||
|
||||
// 重置签名数据
|
||||
if (this.projectInfo.signMode === 2) {
|
||||
// 如果已经有签名数据,且签名模式是修改,保留原签名
|
||||
if (!this.formData.userSign || !this.hasSubmittedBefore) {
|
||||
this.formData.userSign = ""
|
||||
}
|
||||
}
|
||||
|
||||
this.showConfirmDialog = true
|
||||
},
|
||||
|
||||
@@ -1056,6 +1073,15 @@ layout("/layouts/platform_h5.html"){
|
||||
this.selectedOption = option
|
||||
this.showOptionDetailDialog = true
|
||||
},
|
||||
|
||||
// 重置签名
|
||||
resetSignature() {
|
||||
this.formData.userSign = ""
|
||||
// 如果组件有reset方法,调用它
|
||||
if (this.$refs.signatureRef && typeof this.$refs.signatureRef.reset === 'function') {
|
||||
this.$refs.signatureRef.reset()
|
||||
}
|
||||
},
|
||||
|
||||
// 切换选项选中状态
|
||||
toggleOption(option) {
|
||||
@@ -1103,7 +1129,7 @@ layout("/layouts/platform_h5.html"){
|
||||
deep: true,
|
||||
handler(options) {
|
||||
if (!options) return
|
||||
if (this.isDeadlinePassed) return // 如果已过期,不处理数量变化
|
||||
if (this.isDeadlinePassed) return // 如果已过期,不处理数量变化
|
||||
|
||||
const maxSelect = this.projectInfo.multiSelectNum || options.length
|
||||
const totalCount = options.reduce((sum, option) => sum + (option.selectNum || 0), 0)
|
||||
@@ -1118,6 +1144,21 @@ layout("/layouts/platform_h5.html"){
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// 监听确认弹窗显示状态
|
||||
showConfirmDialog(val) {
|
||||
if (val && this.projectInfo.signMode === 2) {
|
||||
// 在下一个渲染周期后更新签名组件
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.signatureRef) {
|
||||
// 如果组件有setSignature或类似方法可以调用
|
||||
if (this.formData.userSign && typeof this.$refs.signatureRef.setSignature === 'function') {
|
||||
this.$refs.signatureRef.setSignature(this.formData.userSign)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@@ -78,8 +78,6 @@ layout("/layouts/platform_h5.html"){
|
||||
您的福利选择已提交成功!
|
||||
<br />
|
||||
请在选择时间截止前,您仍可以重新选择并修改。
|
||||
<br />
|
||||
请留意系统通知,了解福利发放信息。
|
||||
</div>
|
||||
|
||||
<div class="button-group">
|
||||
|
||||
@@ -30,6 +30,9 @@ import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.time.LocalDate;
|
||||
import java.time.ZoneId;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -170,17 +173,77 @@ public class WelfareTest {
|
||||
List<SelectUser> allList = ExcelImportUtil.importExcel(new File("C:\\Users\\jug\\Desktop\\南京师范蛋糕卡\\匹配上生日.xlsx"), SelectUser.class, new ImportParams());
|
||||
List<SelectUser> list = allList.stream().filter(v -> StrUtil.isNotBlank(v.getLoginName())).toList();
|
||||
|
||||
List<Sys_user> users = dao.query(Sys_user.class, Cnd.NEW());
|
||||
Map<String, Sys_user> userMap = users.stream().collect(Collectors.toMap(v -> v.getLoginname(), v -> v));
|
||||
|
||||
// 福利id
|
||||
String welfareId = "a93b1694e41f49f3b9e6364e36bf48cd";
|
||||
// 找出月份等于1、2、3的
|
||||
// list.stream().filter(v -> v.getBirthday().getMonth() + 1 <= 3).map(v -> {
|
||||
// WelfareList welfareList = new WelfareList();
|
||||
// welfareList.setProjectId(welfareId);
|
||||
//
|
||||
//
|
||||
//
|
||||
// })
|
||||
String welfareId = "6b6b438811044d7180b76facaebbe2f3";
|
||||
List<WelfareProjectSubjectOption> options = dao.query(WelfareProjectSubjectOption.class, Cnd.where(WelfareProjectSubjectOption::getWelfareId, "=", welfareId));
|
||||
Map<String, String> optionMap = options.stream().collect(Collectors.toMap(v -> v.getOptionName(), v -> v.getId()));
|
||||
|
||||
// 福利名单
|
||||
List<WelfareList> welfareLists = list.stream().filter(v -> v.getBirthday().getMonth() + 1 > 6 && v.getBirthday().getMonth() + 1 <= 9).map(v -> {
|
||||
WelfareList welfareList = new WelfareList();
|
||||
welfareList.setProjectId(welfareId);
|
||||
if (userMap.get(v.getLoginName()) != null) {
|
||||
welfareList.setUserId(userMap.get(v.getLoginName()).getId());
|
||||
welfareList.setWelfareUnitId(userMap.get(v.getLoginName()).getUnitId());
|
||||
welfareList.setPersonType(userMap.get(v.getLoginName()).getPersonType());
|
||||
welfareList.setUserState(userMap.get(v.getLoginName()).getUserState());
|
||||
welfareList.setPreparedBy(userMap.get(v.getLoginName()).getPreparedBy());
|
||||
} else {
|
||||
System.out.println(v.getLoginName());
|
||||
}
|
||||
return welfareList;
|
||||
}).toList();
|
||||
|
||||
// 选择数据
|
||||
List<WelfareUserSelection> selections = list.stream().filter(v -> v.getBirthday().getMonth() + 1 > 6 && v.getBirthday().getMonth() + 1 <= 9).map(v -> {
|
||||
WelfareUserSelection selection = new WelfareUserSelection();
|
||||
selection.setWelfareId(welfareId);
|
||||
selection.setSelectUserId(userMap.get(v.getLoginName()).getId());
|
||||
selection.setSelectOptionId(optionMap.get(v.getOptionName()));
|
||||
selection.setSelectNum(1);
|
||||
|
||||
LocalDate localDate = LocalDate.of(2024, 12, 15);
|
||||
Date date = Date.from(localDate.atStartOfDay(ZoneId.systemDefault()).toInstant());
|
||||
selection.setSelectTime(date);
|
||||
|
||||
selection.setMobile(v.getMobile());
|
||||
|
||||
return selection;
|
||||
}).toList();
|
||||
|
||||
System.out.println(welfareLists.size());
|
||||
System.out.println(selections.size());
|
||||
|
||||
dao.insert(welfareLists);
|
||||
dao.insert(selections);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateWelfareList() {
|
||||
Sql sql = Sqls.create("select * from vw_user");
|
||||
List<NutMap> users = welfareService.listMap(sql);
|
||||
Map<String, NutMap> loginNameMap = users.stream().collect(Collectors.toMap(v -> v.getString("id"), v -> v));
|
||||
|
||||
List<WelfareList> list = dao.query(WelfareList.class, Cnd.NEW());
|
||||
for (WelfareList w : list) {
|
||||
w.setWelfareUnitName(loginNameMap.get(w.getUserId()).getString("unitName"));
|
||||
w.setWelfareUnionId(loginNameMap.get(w.getUserId()).getString("unionId"));
|
||||
w.setWelfareUnionName(loginNameMap.get(w.getUserId()).getString("unionName"));
|
||||
}
|
||||
dao.update(list, "welfareUnitName|welfareUnionId|welfareUnionName");
|
||||
|
||||
List<WelfareUserSelection> userSelections = dao.query(WelfareUserSelection.class, Cnd.NEW());
|
||||
for (WelfareUserSelection w : userSelections) {
|
||||
w.setMobile(loginNameMap.get(w.getSelectUserId()).getString("mobile"));
|
||||
}
|
||||
dao.update(userSelections, "mobile");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user