Merge remote-tracking branch 'origin/main'
This commit is contained in:
@@ -230,7 +230,7 @@ public class FlowTodoCenterController {
|
||||
dao.execute(todoSql);
|
||||
int todoCount = todoSql.getInt();
|
||||
|
||||
// 查询我的已办任务
|
||||
// 已办统计排除发起节点 startTask,与已办列表保持一致,避免将本人发起流程计入已办数量。
|
||||
Sql doneSql = Sqls.create("""
|
||||
SELECT
|
||||
count(1)
|
||||
@@ -240,6 +240,7 @@ public class FlowTodoCenterController {
|
||||
WHERE
|
||||
ta.actorId = @userId
|
||||
AND t.taskState = 20
|
||||
AND t.taskName != 'startTask'
|
||||
""");
|
||||
doneSql.setParam("userId", SecurityUtil.getUserId());
|
||||
doneSql.setCallback(Sqls.callback.integer());
|
||||
|
||||
@@ -69,17 +69,6 @@ public class SysDataUserPullController {
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
@At
|
||||
@SaCheckPermission("sys.data.user.pull")
|
||||
@ApiOperation("同步教职工手机号")
|
||||
public Result syncTeacherMobile() {
|
||||
try {
|
||||
return Result.success(sysUserPullService.syncTeacherMobile());
|
||||
} catch (Exception e) {
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@At
|
||||
@SaCheckPermission("sys.data.user.pull")
|
||||
@ApiOperation("删除用户数据")
|
||||
|
||||
@@ -17,6 +17,8 @@ import com.budwk.app.sys.services.*;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.nutz.aop.interceptor.ioc.TransAop;
|
||||
import org.nutz.ioc.aop.Aop;
|
||||
import org.nutz.dao.Cnd;
|
||||
import org.nutz.dao.Sqls;
|
||||
import org.nutz.dao.sql.Sql;
|
||||
@@ -47,6 +49,8 @@ public class SysDataUserUpdateController {
|
||||
@Inject
|
||||
private SysUserService sysUserService;
|
||||
@Inject
|
||||
private SysUserMobileSyncService sysUserMobileSyncService;
|
||||
@Inject
|
||||
private SysDataUserPullService sysDataUserPullService;
|
||||
@Inject
|
||||
private SysDataUnitPullService sysDataUnitPullService;
|
||||
@@ -62,7 +66,8 @@ public class SysDataUserUpdateController {
|
||||
@At
|
||||
@SaCheckPermission("sys.data.user.pull")
|
||||
@ApiOperation("分页数据")
|
||||
@Ok("json:{locked:'password|idCard|mobile'}")
|
||||
// 手机号用于列表核对拉取结果;密码和身份证号仍不返回。
|
||||
@Ok("json:{locked:'password|idCard'}")
|
||||
public Result pageData(@Valid SysDataUserUpdatePageForm pageForm) {
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
@@ -136,4 +141,46 @@ public class SysDataUserUpdateController {
|
||||
sysDataDictPullService.pullDataDict();
|
||||
return Result.success();
|
||||
}
|
||||
/**
|
||||
* 预览:scope传ALL/MEMBER/FILTER,mode传FILL/REFRESH,pageForm传列表筛选条件。
|
||||
* 返回NutMap,scopeCount/existingCount/cooldownCount/total分别为范围、已有号、冷却及待请求工号数。
|
||||
*/
|
||||
@At
|
||||
@SaCheckPermission("sys.data.user.update")
|
||||
public Result mobilePreview(String scope, String mode, SysDataUserUpdatePageForm pageForm) {
|
||||
return Result.success(sysUserMobileSyncService.preview(scope, mode, pageForm));
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动:scope/mode及pageForm同预览;action传NEW/RESUME/RETRY,sourceTaskId仅继续或重试时传原任务编号。
|
||||
* 返回NutMap进度:taskId任务编号、status状态及total/processed/updated/empty/failed/skipped各项人数。
|
||||
*/
|
||||
@At
|
||||
@SaCheckPermission("sys.data.user.update")
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
@SLog(tag = "手机号拉取", msg = "启动手机号后台拉取任务")
|
||||
public Result mobileStart(String scope, String mode, SysDataUserUpdatePageForm pageForm, String sourceTaskId, String action) {
|
||||
return Result.success(sysUserMobileSyncService.start(scope, mode, pageForm, sourceTaskId, action));
|
||||
}
|
||||
|
||||
/**
|
||||
* 进度:taskId可为空(最近任务)或传启动接口返回的编号;返回与启动一致的NutMap,不返回手机号与工号队列。
|
||||
*/
|
||||
@At
|
||||
@SaCheckPermission("sys.data.user.update")
|
||||
public Result mobileStatus(String taskId) {
|
||||
return Result.success(sysUserMobileSyncService.status(taskId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止:taskId必须传当前任务编号;返回NutMap最新进度,当前请求结束后停止且保留已完成结果。
|
||||
*/
|
||||
@At
|
||||
@SaCheckPermission("sys.data.user.update")
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
@SLog(tag = "手机号拉取", msg = "停止手机号后台拉取任务")
|
||||
public Result mobileStop(String taskId) {
|
||||
return Result.success(sysUserMobileSyncService.stop(taskId));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -53,4 +53,20 @@ public interface SysDataUserPullService extends BaseService<Sys_user_source> {
|
||||
* @return 同步过程统计信息,包含工号手机号映射
|
||||
*/
|
||||
NutMap syncTeacherMobileByJobNos(List<String> jobNos);
|
||||
/**
|
||||
* 按单个工号读取数据中心手机号,不修改数据库;空字符串表示接口明确没有号码。
|
||||
* @param jobNo 非空教职工工号
|
||||
* @return 已校验的手机号;接口异常、工号不匹配或号码格式异常时抛出异常
|
||||
*/
|
||||
String fetchTeacherMobile(String jobNo);
|
||||
|
||||
/**
|
||||
* 保存单人工号的有效号码,使用请求前快照避免覆盖期间的人工修改。
|
||||
* @param jobNo 数据中心查询工号
|
||||
* @param mobile 已校验的非空手机号
|
||||
* @param originalMobiles 请求前的用户ID到原手机号映射
|
||||
* @param fillOnly true仅补空号,false核对更新已有号码
|
||||
* @return 实际更新的系统用户行数
|
||||
*/
|
||||
int saveTeacherMobile(String jobNo, String mobile, Map<String, String> originalMobiles, boolean fillOnly);
|
||||
}
|
||||
|
||||
@@ -438,6 +438,101 @@ public class SysDataUserPullServiceImpl extends BaseServiceImpl<Sys_user_source>
|
||||
return listMap(sql);
|
||||
}
|
||||
|
||||
/**
|
||||
* 后台任务逐工号读取号码:每个分页请求均设置超时,且不输出号码或接口原文。
|
||||
* 分页间等待一秒,避免单人工号多页响应绕过任务限速。
|
||||
*/
|
||||
@Override
|
||||
public String fetchTeacherMobile(String jobNo) {
|
||||
DataCenterProperties.Credential credential = dataCenterProperties.credential(TEACHER_MOBILE_CONFIG_KEY);
|
||||
Set<String> numbers = new LinkedHashSet<>();
|
||||
int pages = 1;
|
||||
for (int page = 1; page <= pages; page++) {
|
||||
if (page > 1) {
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
throw new BaseException("手机号拉取已中断");
|
||||
}
|
||||
}
|
||||
String body;
|
||||
try (cn.hutool.http.HttpResponse response = HttpUtil.createPost(credential.getUrl())
|
||||
.header(Header.CONTENT_TYPE, "application/json")
|
||||
.header("X-H3C-TOKEN", credential.getToken())
|
||||
.timeout(10000)
|
||||
.body(JSONUtil.toJsonStr(buildTeacherMobileRequestBody(jobNo, page, TEACHER_MOBILE_PAGE_SIZE)))
|
||||
.execute()) {
|
||||
if (response.getStatus() != 200) {
|
||||
throw new BaseException("手机号接口HTTP异常,请稍后重试");
|
||||
}
|
||||
body = response.body();
|
||||
}
|
||||
pages = collectTeacherMobilePage(body, jobNo, numbers);
|
||||
}
|
||||
return numbers.isEmpty() ? "" : numbers.iterator().next();
|
||||
}
|
||||
|
||||
/** 校验单工号响应结构、页数与号码一致性;只有明确的空records或空号码才视为无号码。 */
|
||||
private int collectTeacherMobilePage(String body, String jobNo, Set<String> numbers) {
|
||||
JSONObject response = JSONUtil.parseObj(body);
|
||||
if (!"0".equals(response.getStr("code"))) {
|
||||
throw new BaseException("手机号接口未成功返回,请检查接口授权或稍后重试");
|
||||
}
|
||||
JSONObject data = response.getJSONObject("data");
|
||||
JSONObject pagination = data == null ? response : data;
|
||||
int pages = Math.max(pagination.getInt("pages", 1),
|
||||
(int) Math.ceil(pagination.getInt("total", 0) / (double) TEACHER_MOBILE_PAGE_SIZE));
|
||||
// 单工号异常大分页通常意味着过滤条件失效,禁止继续当作全量接口拉取。
|
||||
if (pages > 5) {
|
||||
throw new BaseException("单工号返回数据量异常");
|
||||
}
|
||||
JSONArray records = response.getJSONArray("records");
|
||||
if (records == null && data != null) records = data.getJSONArray("records");
|
||||
if (records == null) {
|
||||
throw new BaseException("手机号接口缺少records字段");
|
||||
}
|
||||
for (Object record : records) {
|
||||
JSONObject row = JSONUtil.parseObj(record);
|
||||
if (!jobNo.equals(StrUtil.trimToEmpty(row.getStr("gh")))) {
|
||||
throw new BaseException("手机号接口返回工号不匹配");
|
||||
}
|
||||
String mobile = StrUtil.trimToEmpty(row.getStr("sjh"));
|
||||
if (mobile.startsWith("+86")) mobile = mobile.substring(3);
|
||||
if (mobile.startsWith("0086")) mobile = mobile.substring(4);
|
||||
if (StrUtil.isNotBlank(mobile)) {
|
||||
if (!mobile.matches("1[3-9][0-9]{9}")) {
|
||||
throw new BaseException("接口手机号格式异常");
|
||||
}
|
||||
numbers.add(mobile);
|
||||
}
|
||||
}
|
||||
if (numbers.size() > 1) {
|
||||
throw new BaseException("同一工号返回多个不同手机号,需人工核实");
|
||||
}
|
||||
return pages;
|
||||
}
|
||||
|
||||
/**
|
||||
* 仅在原手机号未被他人修改时落库;空返回值不清空原号,补齐模式不覆盖已有号码。
|
||||
*/
|
||||
@Override
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
public int saveTeacherMobile(String jobNo, String mobile, Map<String, String> originalMobiles, boolean fillOnly) {
|
||||
if (StrUtil.isBlank(mobile) || !mobile.matches("1[3-9][0-9]{9}")) return 0;
|
||||
int updated = 0;
|
||||
for (Map.Entry<String, String> entry : originalMobiles.entrySet()) {
|
||||
String oldMobile = entry.getValue();
|
||||
if (Objects.equals(oldMobile, mobile) || (fillOnly && StrUtil.isNotBlank(oldMobile))) continue;
|
||||
Cnd cnd = Cnd.where(Sys_user::getId, "=", entry.getKey())
|
||||
.and(Sys_user::getLoginname, "=", jobNo);
|
||||
if (oldMobile == null) cnd.and("mobile", "IS", null);
|
||||
else cnd.and("mobile", "=", oldMobile);
|
||||
updated += dao().update(Sys_user.class, Chain.make("mobile", mobile), cnd);
|
||||
}
|
||||
return updated;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从数据中心分页同步教职工手机号,并按工号更新 sys_user.mobile。
|
||||
*/
|
||||
|
||||
-29
@@ -5,11 +5,8 @@ import cn.dev33.satoken.annotation.SaMode;
|
||||
import cn.hutool.core.util.DesensitizedUtil;
|
||||
import com.alibaba.excel.EasyExcel;
|
||||
import com.budwk.app.base.annotation.SLog;
|
||||
import com.budwk.app.base.constant.RoleConstant;
|
||||
import com.budwk.app.base.page.Pagination;
|
||||
import com.budwk.app.base.result.Result;
|
||||
import com.budwk.app.sys.services.SysDataUserPullService;
|
||||
import com.budwk.app.web.commons.auth.utils.AuthUtil;
|
||||
import com.budwk.app.zhgh.staffmanage.member.models.MemberHistory;
|
||||
import com.budwk.app.zhgh.staffmanage.member.param.pageform.MemberInfoPageForm;
|
||||
import com.budwk.app.zhgh.staffmanage.member.service.MemberInfoService;
|
||||
@@ -51,9 +48,6 @@ public class MemberInfoGroupController {
|
||||
@Inject
|
||||
private MemberInfoService memberInfoService;
|
||||
|
||||
@Inject
|
||||
private SysDataUserPullService sysUserPullService;
|
||||
|
||||
@At("")
|
||||
@Ok("beetl:/platform/zhgh/staffmanage/member/info/group/index.html")
|
||||
@SaCheckPermission("member.info.group")
|
||||
@@ -86,29 +80,6 @@ public class MemberInfoGroupController {
|
||||
return Result.success(memberInfoService.dao().count(MemberHistory.class, Cnd.where("year", "=", year)));
|
||||
}
|
||||
|
||||
@At
|
||||
@SaCheckPermission("member.info.group")
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
@SLog(tag = "会员档案管理", msg = "获取会员手机号")
|
||||
public Result syncMobile() {
|
||||
try {
|
||||
if (!AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_ADMIN.name())) {
|
||||
return Result.error("当前用户无权获取会员手机号");
|
||||
}
|
||||
List<String> jobNos = memberInfoService.queryRandomBlankMobileMemberJobNos(500);
|
||||
if (jobNos.isEmpty()) {
|
||||
return Result.error("当前没有手机号为空的会员");
|
||||
}
|
||||
NutMap result = sysUserPullService.syncTeacherMobileByJobNos(jobNos);
|
||||
result.put("sampleCount", jobNos.size());
|
||||
return Result.success(result);
|
||||
} catch (Exception e) {
|
||||
log.error("会员手机号获取失败", e);
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@At
|
||||
@SaCheckPermission("member.info.group")
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
|
||||
Reference in New Issue
Block a user