同步通讯录信息

This commit is contained in:
dzq 2025-03-31 09:41:07 +08:00
parent 3558d69759
commit 6f0e0587ab
47 changed files with 1430 additions and 76 deletions
.vscode
agileboot-admin/src
agileboot-api/src/main/java/com/agileboot/api/controller
agileboot-domain/src/main
agileboot-infrastructure/src/main/java/com/agileboot/infrastructure/mybatisplus
doc
sql

3
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,3 @@
{
"java.jdt.ls.vmargs": "-XX:+UseParallelGC -XX:GCTimeRatio=4 -XX:AdaptiveSizePolicyWeight=90 -Dsun.zip.disableMemoryMapping=true -Xmx2G -Xms100m -Xlog:disable"
}

View File

@ -1,12 +1,18 @@
package com.agileboot.admin.controller.common;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONUtil;
import com.agileboot.common.config.AgileBootConfig;
import com.agileboot.common.constant.WeixinConstants;
import com.agileboot.common.core.dto.ResponseDTO;
import com.agileboot.common.exception.ApiException;
import com.agileboot.common.exception.error.ErrorCode;
import com.agileboot.common.exception.error.ErrorCode.Business;
import com.agileboot.domain.common.dto.CurrentLoginUserDTO;
import com.agileboot.domain.common.dto.TokenDTO;
import com.agileboot.domain.qywx.accessToken.AccessTokenApplicationService;
import com.agileboot.domain.qywx.accessToken.db.QyAccessTokenEntity;
import com.agileboot.domain.system.menu.MenuApplicationService;
import com.agileboot.domain.system.menu.dto.RouterDTO;
import com.agileboot.domain.system.user.UserApplicationService;
@ -23,12 +29,18 @@ import com.agileboot.infrastructure.annotations.ratelimit.RateLimitKey;
import com.agileboot.admin.customize.service.login.LoginService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestClientException;
/**
* 首页
@ -37,6 +49,7 @@ import org.springframework.web.bind.annotation.RestController;
*/
@Tag(name = "登录API", description = "登录相关接口")
@RestController
@Slf4j
@RequiredArgsConstructor
public class LoginController {
@ -48,6 +61,8 @@ public class LoginController {
private final AgileBootConfig agileBootConfig;
private final AccessTokenApplicationService accessTokenApplicationService;
/**
* 访问首页提示语
*/
@ -137,4 +152,27 @@ public class LoginController {
return ResponseDTO.fail(new ApiException(Business.COMMON_UNSUPPORTED_OPERATION));
}
@GetMapping("/getQyUserinfo")
public ResponseDTO<String> getQyUserinfo(String corpid, String code) {
try {
QyAccessTokenEntity qyAccessToken = accessTokenApplicationService.getByAppid("QWTONG_YS_WXSHOP", corpid);
String url = String.format(
"https://qyapi.weixin.qq.com/cgi-bin/auth/getuserinfo?access_token=%s&code=%s",
qyAccessToken.getAccessToken(), code);
String response = HttpUtil.get(url);
log.info("微信getuserinfo接口返回: {}", response);
Map<String, Object> result = JSONUtil.toBean(response, HashMap.class);
if (result.containsKey("errcode") && !result.get("errcode").equals(0)) {
log.error("微信接口返回错误: {}", result);
return ResponseDTO.fail(new ApiException(ErrorCode.Client.COMMON_REQUEST_PARAMETERS_INVALID, "无效的code参数"));
}
return ResponseDTO.ok(String.valueOf(result.get("userid")));
} catch (RestClientException e) {
log.error("获取openid失败", e);
return ResponseDTO.fail(new ApiException(ErrorCode.Client.COMMON_REQUEST_PARAMETERS_INVALID, "微信服务调用失败"));
}
}
}

View File

@ -7,6 +7,8 @@ import com.agileboot.common.core.dto.ResponseDTO;
import com.agileboot.common.core.page.PageDTO;
import com.agileboot.common.enums.common.BusinessTypeEnum;
import com.agileboot.domain.common.command.BulkOperationCommand;
import com.agileboot.domain.qywx.authCorpInfo.AuthCorpInfoApplicationService;
import com.agileboot.domain.qywx.authCorpInfo.db.QyAuthCorpInfoEntity;
import com.agileboot.domain.qywx.department.DepartmentApplicationService;
import com.agileboot.domain.qywx.department.command.AddDepartmentCommand;
import com.agileboot.domain.qywx.department.command.UpdateDepartmentCommand;
@ -15,13 +17,17 @@ import com.agileboot.domain.qywx.department.dto.QyDepartmentDTO;
import com.agileboot.domain.qywx.department.query.SearchQyDepartmentQuery;
import com.agileboot.domain.qywx.template.command.UpdateTemplateCommand;
import com.agileboot.domain.qywx.template.db.QyTemplateEntity;
import com.agileboot.domain.system.dept.dto.DeptDTO;
import com.agileboot.domain.system.dept.query.DeptQuery;
import io.swagger.v3.oas.annotations.Operation;
import java.util.List;
import java.util.stream.Collectors;
import javax.validation.constraints.NotNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
@ -42,6 +48,7 @@ public class QyDepartmentController extends BaseController {
private final DepartmentApplicationService departmentApplicationService;
private final QywxScheduleJob qywxScheduleJob;
private final AuthCorpInfoApplicationService authCorpInfoApplicationService;
@Operation(summary = "部门列表")
@GetMapping
@ -79,11 +86,47 @@ public class QyDepartmentController extends BaseController {
@GetMapping("/syncDepartmentInfo")
public String syncDepartmentInfo(@RequestParam String appid) {
try {
qywxScheduleJob.syncDepartmentInfo(appid);
List<QyAuthCorpInfoEntity> authCorpInfoList = authCorpInfoApplicationService.getByAppid(appid);
if (null == authCorpInfoList || authCorpInfoList.isEmpty()) {
return "appid not exist";
}
for (QyAuthCorpInfoEntity authCorpInfo : authCorpInfoList) {
qywxScheduleJob.syncDepartmentInfo(appid, authCorpInfo);
}
return "success";
} catch (Exception e) {
log.error("syncDepartmentInfo error", e);
return e.getLocalizedMessage();
}
}
/**
* 用户管理获取部门列表
*/
@Operation(summary = "部门列表")
@GetMapping("/depts")
public ResponseDTO<List<DeptDTO>> depts(@RequestParam String corpid) {
List<QyDepartmentEntity> qyDepartmentEntityList = departmentApplicationService.getDepartmentList();
if (StringUtils.isNoneBlank(corpid)) {
qyDepartmentEntityList = qyDepartmentEntityList.stream()
.filter(item -> StringUtils.equals(item.getCorpid(), corpid))
.collect(Collectors.toList());
}
List<DeptDTO> deptList = qyDepartmentEntityList.stream().map(entity -> {
DeptDTO dto = new DeptDTO(null); // 使用null初始化后续手动设置字段
dto.setId(entity.getId().longValue());
dto.setParentId(Long.parseLong(entity.getParentid()));
dto.setDeptName(entity.getName());
dto.setOrderNum(0);
dto.setLeaderName("");
dto.setPhone("");
dto.setEmail("");
dto.setStatus(1);
dto.setStatusStr("");
dto.setCreateTime(entity.getCreateTime());
// 其他字段根据业务需求补充
return dto;
}).collect(Collectors.toList());
return ResponseDTO.ok(deptList);
}
}

View File

@ -1,14 +1,15 @@
package com.agileboot.admin.controller.qywx;
import com.agileboot.admin.customize.aop.accessLog.AccessLog;
import com.agileboot.admin.customize.service.QywxScheduleJob;
import com.agileboot.common.core.base.BaseController;
import com.agileboot.common.core.dto.ResponseDTO;
import com.agileboot.common.core.page.PageDTO;
import com.agileboot.common.enums.common.BusinessTypeEnum;
import com.agileboot.domain.common.command.BulkOperationCommand;
import com.agileboot.domain.qywx.user.QyUserApplicationService;
import com.agileboot.domain.qywx.user.command.AddUserCommand;
import com.agileboot.domain.qywx.user.command.UpdateUserCommand;
import com.agileboot.domain.qywx.user.command.AddQyUserCommand;
import com.agileboot.domain.qywx.user.command.UpdateQyUserCommand;
import com.agileboot.domain.qywx.user.db.QyUserEntity;
import com.agileboot.domain.qywx.user.dto.QyUserDTO;
import com.agileboot.domain.qywx.user.query.SearchQyUserQuery;
@ -33,6 +34,7 @@ import org.springframework.web.bind.annotation.RestController;
public class QyUserController extends BaseController {
private final QyUserApplicationService qyUserApplicationService;
private final QywxScheduleJob qywxScheduleJob;
@Operation(summary = "用户列表")
@GetMapping
@ -44,7 +46,7 @@ public class QyUserController extends BaseController {
@Operation(summary = "新增用户")
@AccessLog(title = "用户管理", businessType = BusinessTypeEnum.ADD)
@PostMapping
public ResponseDTO<Void> add(@Validated @RequestBody AddUserCommand command) {
public ResponseDTO<Void> add(@Validated @RequestBody AddQyUserCommand command) {
qyUserApplicationService.addUser(command);
return ResponseDTO.ok();
}
@ -52,7 +54,7 @@ public class QyUserController extends BaseController {
@Operation(summary = "修改用户")
@AccessLog(title = "用户管理", businessType = BusinessTypeEnum.MODIFY)
@PutMapping("/{id}")
public ResponseDTO<Void> edit(@PathVariable Integer id, @Validated @RequestBody UpdateUserCommand command) {
public ResponseDTO<Void> edit(@PathVariable Integer id, @Validated @RequestBody UpdateQyUserCommand command) {
command.setId(id);
qyUserApplicationService.updateUser(command);
return ResponseDTO.ok();
@ -65,4 +67,18 @@ public class QyUserController extends BaseController {
qyUserApplicationService.deleteUser(new BulkOperationCommand<>(ids));
return ResponseDTO.ok();
}
@Operation(summary = "同步用户信息")
@PostMapping("/sync")
public ResponseDTO<Void> syncUserInfo() {
qywxScheduleJob.syncUserInfoTask();
return ResponseDTO.ok();
}
@Operation(summary = "同步创建系统用户信息")
@PostMapping("/syncSysUser")
public ResponseDTO<Void> syncSysUser() {
qywxScheduleJob.syncUserBindings();
return ResponseDTO.ok();
}
}

View File

@ -137,7 +137,7 @@ public class SecurityConfig {
.antMatchers("/login", "/register", "/getConfig", "/captchaImage", "/api/**", "/file/**").anonymous()
.antMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js",
"/profile/**").permitAll()
.antMatchers("/qywx/**", "/test/**").permitAll()
.antMatchers("/qywx/**", "/test/**", "/getQyUserinfo").permitAll()
// TODO this is danger.
.antMatchers("/swagger-ui.html").anonymous()
.antMatchers("/swagger-resources/**").anonymous()

View File

@ -9,6 +9,7 @@ import com.agileboot.domain.qywx.api.QywxApiUtil;
import com.agileboot.domain.qywx.api.response.DepartmentInfoResponse;
import com.agileboot.domain.qywx.api.response.DepartmentInfoResponse.Department;
import com.agileboot.domain.qywx.api.response.DepartmentListResponse;
import com.agileboot.domain.qywx.api.response.UserListResponse;
import com.agileboot.domain.qywx.authCorpInfo.AuthCorpInfoApplicationService;
import com.agileboot.domain.qywx.authCorpInfo.db.QyAuthCorpInfoEntity;
import com.agileboot.domain.qywx.department.DepartmentApplicationService;
@ -22,8 +23,22 @@ import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;
import com.agileboot.domain.qywx.user.QyUserApplicationService;
import com.agileboot.domain.qywx.user.command.AddQyUserCommand;
import com.agileboot.domain.qywx.user.command.UpdateQyUserCommand;
import com.agileboot.domain.qywx.user.db.QyUserEntity;
import com.agileboot.domain.qywx.userQySys.SysUserQyUserApplicationService;
import com.agileboot.domain.qywx.userQySys.command.AddSysUserQyUserCommand;
import com.agileboot.domain.qywx.userQySys.db.SysUserQyUserEntity;
import com.agileboot.domain.system.user.UserApplicationService;
import com.agileboot.domain.system.user.command.AddUserCommand;
import com.agileboot.domain.system.user.model.UserModel;
import com.agileboot.domain.system.user.model.UserModelFactory;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
@ -39,17 +54,20 @@ public class QywxScheduleJob {
private final AuthCorpInfoApplicationService authCorpInfoApplicationService;
private final AccessTokenApplicationService accessTokenApplicationService;
private final DepartmentApplicationService departmentApplicationService;
private final QyUserApplicationService qyUserApplicationService;
private final SysUserQyUserApplicationService sysUserQyUserApplicationService;
private final UserModelFactory userModelFactory;
private static final String appid = "QYTONG_YS_WXSHOP";
// private static final String appid = "QYTONG_YS_WXSHOP";
private static final String appid2 = "QWTONG_YS_WXSHOP";
@Scheduled(cron = "0 10 * * * *")
public void getSuiteAccessTokenTask() {
try {
/*try {
getSuiteAccessToken(appid);
} catch (Exception e) {
log.error("getSuiteAccessTokenTask appid: " + appid, e);
}
}*/
try {
getSuiteAccessToken(appid2);
@ -81,11 +99,11 @@ public class QywxScheduleJob {
@Scheduled(cron = "0 20 * * * *")
public void getAccessTokenTask() {
try {
/*try {
getAccessToken(appid);
} catch (Exception e) {
log.error("getAccessTokenTask error appid: " + appid, e);
}
}*/
try {
getAccessToken(appid2);
@ -98,12 +116,18 @@ public class QywxScheduleJob {
log.info("getAccessToken Current Thread : {}, Fixed Rate Task : The time is now {}",
Thread.currentThread().getName(), DateUtil.formatTime(new Date()));
try {
QyAuthCorpInfoEntity authCorpInfo = authCorpInfoApplicationService.getByAppid(appid);
if (null == authCorpInfo) {
List<QyAuthCorpInfoEntity> authCorpInfoList = authCorpInfoApplicationService.getByAppid(appid);
if (null == authCorpInfoList || authCorpInfoList.isEmpty()) {
return;
}
accessTokenApplicationService.getAccessToken(authCorpInfo.getCorpid(), authCorpInfo.getPermanentCode(), appid);
try {
for (QyAuthCorpInfoEntity authCorpInfo : authCorpInfoList) {
accessTokenApplicationService.getAccessToken(authCorpInfo.getCorpid(), authCorpInfo.getPermanentCode(), appid);
}
} catch (Exception e) {
log.error("getAccessToken error", e);
}
} catch (Exception e) {
log.error("getAccessToken error", e);
}
@ -111,31 +135,42 @@ public class QywxScheduleJob {
@Scheduled(cron = "0 30 * * * *")
public void syncDepartmentInfoTask() {
try {
syncDepartmentInfo(appid);
/*try {
List<QyAuthCorpInfoEntity> authCorpInfoList = authCorpInfoApplicationService.getByAppid(appid);
if (null == authCorpInfoList || authCorpInfoList.isEmpty()) {
return;
}
for (QyAuthCorpInfoEntity authCorpInfo : authCorpInfoList) {
syncDepartmentInfo(appid, authCorpInfo);
}
} catch (Exception e) {
log.error("syncDepartmentInfoTask error appid: " + appid, e);
}
}*/
try {
syncDepartmentInfo(appid2);
List<QyAuthCorpInfoEntity> authCorpInfoList = authCorpInfoApplicationService.getByAppid(appid2);
if (null == authCorpInfoList || authCorpInfoList.isEmpty()) {
return;
}
for (QyAuthCorpInfoEntity authCorpInfo : authCorpInfoList) {
syncDepartmentInfo(appid2, authCorpInfo);
}
} catch (Exception e) {
log.error("syncDepartmentInfoTask error appid: " + appid2, e);
}
}
public void syncDepartmentInfo(String appid) {
public void syncDepartmentInfo(String appid, QyAuthCorpInfoEntity authCorpInfo) {
log.info("syncDepartmentInfo Current Thread : {}, Fixed Rate Task : The time is now {}",
Thread.currentThread().getName(), DateUtil.formatTime(new Date()));
try {
QyAuthCorpInfoEntity authCorpInfo = authCorpInfoApplicationService.getByAppid(appid);
if (authCorpInfo == null) {
return;
}
QyAccessTokenEntity accessToken = accessTokenApplicationService.getByAppid(appid);
QyAccessTokenEntity accessToken = accessTokenApplicationService.getByAppid(appid, authCorpInfo.getCorpid());
if (null == accessToken || StringUtils.isBlank(accessToken.getAccessToken())) {
log.error("syncDepartmentInfo accessToken is null");
return;
@ -164,6 +199,10 @@ public class QywxScheduleJob {
if (null == departmentEntities) {
departmentEntities = new ArrayList<>();
}
departmentEntities = departmentEntities.stream()
.filter(d -> Objects.equals(d.getCorpid(), authCorpInfo.getCorpid()))
.collect(Collectors.toList());
// 转换为Map结构
Map<String, QyDepartmentEntity> localDeptMap = departmentEntities.stream()
.collect(Collectors.toMap(QyDepartmentEntity::getDepartmentId, Function.identity()));
@ -202,6 +241,7 @@ public class QywxScheduleJob {
List<AddDepartmentCommand> newDeptList = toAdd.stream()
.map(d -> {
AddDepartmentCommand command = new AddDepartmentCommand();
command.setCorpid(authCorpInfo.getCorpid());
command.setDepartmentId(String.valueOf(d.getId()));
command.setName(d.getName());
command.setNameEn(d.getName_en());
@ -254,5 +294,292 @@ public class QywxScheduleJob {
log.error("syncDepartmentInfo error", e);
}
}
@Scheduled(cron = "0 40 * * * *")
public void syncUserInfoTask() {
/*try {
List<QyAuthCorpInfoEntity> authCorpInfoList = authCorpInfoApplicationService.getByAppid(appid);
if (null == authCorpInfoList || authCorpInfoList.isEmpty()) {
return;
}
for (QyAuthCorpInfoEntity authCorpInfo : authCorpInfoList) {
syncUserInfo(appid, authCorpInfo);
}
} catch (Exception e) {
log.error("syncUserInfoTask error appid: " + appid, e);
}*/
try {
List<QyAuthCorpInfoEntity> authCorpInfoList = authCorpInfoApplicationService.getByAppid(appid2);
if (null == authCorpInfoList || authCorpInfoList.isEmpty()) {
return;
}
for (QyAuthCorpInfoEntity authCorpInfo : authCorpInfoList) {
syncUserInfo(appid2, authCorpInfo);
}
} catch (Exception e) {
log.error("syncUserInfoTask error appid: " + appid2, e);
}
}
public void syncUserInfo(String appid, QyAuthCorpInfoEntity authCorpInfo) {
log.info("syncUserInfo Current Thread : {}, Fixed Rate Task : The time is now {}",
Thread.currentThread().getName(), DateUtil.formatTime(new Date()));
try {
if (authCorpInfo == null) {
return;
}
QyAccessTokenEntity accessToken = accessTokenApplicationService.getByAppid(appid, authCorpInfo.getCorpid());
if (null == accessToken || StringUtils.isBlank(accessToken.getAccessToken())) {
log.error("syncUserInfo accessToken is null");
return;
}
List<QyDepartmentEntity> departmentList = departmentApplicationService.getDepartmentList();
if (null == departmentList || departmentList.isEmpty()) {
return;
}
departmentList = departmentList.stream()
.filter(d -> Objects.equals(d.getCorpid(), authCorpInfo.getCorpid()))
.collect(Collectors.toList());
List<QyUserEntity> qyUserList = qyUserApplicationService.selectAll();
if (null == qyUserList) {
qyUserList = new ArrayList<>();
}
qyUserList = qyUserList.stream()
.filter(u -> u.getCorpid().equals(authCorpInfo.getCorpid()))
.collect(Collectors.toList());
for (QyDepartmentEntity department : departmentList) {
// 获取部门用户列表
UserListResponse userList = QywxApiUtil.getUserList(accessToken.getAccessToken(), department.getDepartmentId());
if (null == userList) {
log.error("获取部门用户列表失败: null");
continue;
}
if (null != userList.getErrcode() && userList.getErrcode() != 0) {
log.error("获取部门用户列表失败: {}", userList.getErrmsg());
continue;
}
log.info("获取部门用户列表成功: {}", JSONUtil.toJsonStr(userList));
List<UserListResponse.UserInfo> wxUsers = userList.getUserlist();
if (wxUsers == null || wxUsers.isEmpty()) {
continue;
}
Map<String, UserListResponse.UserInfo> wxUserMap = wxUsers.stream()
.collect(Collectors.toMap(UserListResponse.UserInfo::getUserid, Function.identity()));
List<QyUserEntity> localUsers = qyUserList.stream()
.filter(u -> u.getAppid().equals(appid))
.collect(Collectors.toList());
Map<String, QyUserEntity> localUserMap = localUsers.stream()
.collect(Collectors.toMap(QyUserEntity::getUserid, Function.identity()));
// 识别需要新增的用户
List<UserListResponse.UserInfo> toAdd = wxUsers.stream()
.filter(wxUser -> !localUserMap.containsKey(wxUser.getUserid()))
.collect(Collectors.toList());
log.info("syncUserInfo toAdd: {}", JSONUtil.toJsonStr(toAdd));
// 识别需要删除的用户
List<QyUserEntity> toRemove = localUsers.stream()
.filter(localUser -> !wxUserMap.containsKey(localUser.getUserid()))
.collect(Collectors.toList());
log.info("syncUserInfo toRemove: {}", JSONUtil.toJsonStr(toRemove));
// 识别需要更新的用户
List<UpdateQyUserCommand> toUpdate = localUsers.stream()
.filter(localUser -> wxUserMap.containsKey(localUser.getUserid()))
.filter(localUser -> {
UserListResponse.UserInfo wxUser = wxUserMap.get(localUser.getUserid());
return !Objects.equals(localUser.getName(), wxUser.getName())
|| !Objects.equals(localUser.getDepartment(), StringUtils.join(wxUser.getDepartment(), ","))
|| !Objects.equals(localUser.getPosition(), wxUser.getPosition())
|| !Objects.equals(localUser.getUserOrder(), StringUtils.join(wxUser.getOrder(), ","))
|| !Objects.equals(localUser.getMobile(), wxUser.getMobile())
|| !Objects.equals(localUser.getGender(), String.valueOf(wxUser.getGender()))
|| !Objects.equals(localUser.getEmail(), wxUser.getEmail())
|| !Objects.equals(localUser.getBizMail(), wxUser.getBiz_mail())
|| !Objects.equals(localUser.getDirectLeader(), StringUtils.join(wxUser.getDirect_leader(), ","))
|| !Objects.equals(localUser.getIsLeaderInDept(), StringUtils.join(wxUser.getIs_leader_in_dept(), ","))
|| !Objects.equals(localUser.getTelephone(), wxUser.getTelephone())
|| !Objects.equals(localUser.getAlias(), wxUser.getAlias())
|| !Objects.equals(localUser.getAddress(), wxUser.getAddress())
|| !Objects.equals(localUser.getMainDepartment(), String.valueOf(wxUser.getMain_department()))
|| !Objects.equals(localUser.getAvatar(), wxUser.getAvatar())
|| !Objects.equals(localUser.getThumbAvatar(), wxUser.getThumb_avatar())
|| !Objects.equals(localUser.getStatus(), String.valueOf(wxUser.getStatus()))
|| !Objects.equals(localUser.getQrCode(), wxUser.getQr_code())
|| !Objects.equals(localUser.getExternalPosition(), wxUser.getExternal_position());
})
.peek(localUser -> {
UserListResponse.UserInfo wxUser = wxUserMap.get(localUser.getUserid());
localUser.setName(wxUser.getName());
localUser.setDepartment(StringUtils.join(wxUser.getDepartment(), ","));
localUser.setUserOrder(StringUtils.join(wxUser.getOrder(), ","));
localUser.setPosition(wxUser.getPosition());
localUser.setMobile(wxUser.getMobile());
localUser.setGender(String.valueOf(wxUser.getGender()));
localUser.setEmail(wxUser.getEmail());
localUser.setBizMail(wxUser.getBiz_mail());
localUser.setDirectLeader(StringUtils.join(wxUser.getDirect_leader(), ","));
localUser.setIsLeaderInDept(StringUtils.join(wxUser.getIs_leader_in_dept(), ","));
localUser.setTelephone(wxUser.getTelephone());
localUser.setAlias(wxUser.getAlias());
localUser.setAddress(wxUser.getAddress());
localUser.setMainDepartment(String.valueOf(wxUser.getMain_department()));
localUser.setAvatar(wxUser.getAvatar());
localUser.setThumbAvatar(wxUser.getThumb_avatar());
localUser.setStatus(String.valueOf(wxUser.getStatus()));
localUser.setQrCode(wxUser.getQr_code());
localUser.setExternalPosition(wxUser.getExternal_position());
localUser.setUpdateTime(new Date());
})
.map(localUser -> {
UpdateQyUserCommand command = new UpdateQyUserCommand();
BeanUtils.copyProperties(localUser, command);
return command;
})
.collect(Collectors.toList());
log.info("syncUserInfo toUpdate: {}", JSONUtil.toJsonStr(toUpdate));
// 新增用户
if (!toAdd.isEmpty()) {
toAdd.forEach(wxUser -> {
AddQyUserCommand newUser = new AddQyUserCommand();
newUser.setUserid(wxUser.getUserid());
newUser.setOpenUserid(wxUser.getOpen_userid());
newUser.setName(wxUser.getName());
newUser.setDepartment(StringUtils.join(wxUser.getDepartment(), ","));
newUser.setUserOrder(StringUtils.join(wxUser.getOrder(), ","));
newUser.setPosition(wxUser.getPosition());
newUser.setMobile(wxUser.getMobile());
newUser.setGender(String.valueOf(wxUser.getGender()));
newUser.setEmail(wxUser.getEmail());
newUser.setBizMail(wxUser.getBiz_mail());
newUser.setDirectLeader(StringUtils.join(wxUser.getDirect_leader(), ","));
newUser.setIsLeaderInDept(StringUtils.join(wxUser.getIs_leader_in_dept(), ","));
newUser.setTelephone(wxUser.getTelephone());
newUser.setAlias(wxUser.getAlias());
newUser.setAddress(wxUser.getAddress());
newUser.setMainDepartment(String.valueOf(wxUser.getMain_department()));
newUser.setAvatar(wxUser.getAvatar());
newUser.setThumbAvatar(wxUser.getThumb_avatar());
newUser.setStatus(String.valueOf(wxUser.getStatus()));
newUser.setQrCode(wxUser.getQr_code());
newUser.setExternalPosition(wxUser.getExternal_position());
newUser.setEnable(String.valueOf(1));
newUser.setCorpid(authCorpInfo.getCorpid());
newUser.setAppid(appid);
newUser.setDeleted(false);
newUser.setCreatorId(0L);
newUser.setCreateTime(new Date());
newUser.setUpdaterId(0L);
newUser.setUpdateTime(new Date());
qyUserApplicationService.addUser(newUser);
});
}
// 更新用户
if (!toUpdate.isEmpty()) {
toUpdate.forEach(qyUserApplicationService::updateUser);
}
// 删除用户
if (!toRemove.isEmpty()) {
BulkOperationCommand<Integer> command = new BulkOperationCommand<>(
toRemove.stream().map(QyUserEntity::getId).collect(Collectors.toList())
);
qyUserApplicationService.deleteUser(command);
}
}
} catch (Exception e) {
log.error("syncUserInfo error", e);
}
}
@Scheduled(cron = "0 50 * * * *")
public void syncUserBindingsTask() {
try {
syncUserBindings();
} catch (Exception e) {
log.error("syncUserBindingsTask error appid: " + appid2, e);
}
}
public void syncUserBindings() {
log.info("syncUserBindings Current Thread : {}, Fixed Rate Task : The time is now {}",
Thread.currentThread().getName(), DateUtil.formatTime(new Date()));
try {
List<QyUserEntity> qyUserList = qyUserApplicationService.selectAll();
if (null == qyUserList || qyUserList.isEmpty()) {
log.error("syncUserBindings qyUserList is null");
return;
}
List<SysUserQyUserEntity> sysUserQyUserEntityList = sysUserQyUserApplicationService.getAll();
// 比对绑定关系
List<AddSysUserQyUserCommand> toAdd = new ArrayList<>();
List<Long> toRemove = new ArrayList<>();
// 遍历企业用户匹配系统用户
qyUserList.forEach(qyUser -> {
boolean exists = sysUserQyUserEntityList.stream()
.anyMatch(binding -> binding.getQyUserId().equals(qyUser.getId()));
if (!exists) {
AddSysUserQyUserCommand newBinding = new AddSysUserQyUserCommand();
newBinding.setQyUserId(qyUser.getId());
newBinding.setCreatorId(0L);
newBinding.setCreateTime(new Date());
newBinding.setUpdaterId(0L);
newBinding.setUpdateTime(new Date());
newBinding.setDeleted(false);
toAdd.add(newBinding);
}
});
for (AddSysUserQyUserCommand binding : toAdd) {
AddUserCommand command = new AddUserCommand();
command.setUsername("qywx" + binding.getQyUserId());
command.setNickname(qyUserList.stream()
.filter(u -> u.getId().equals(binding.getQyUserId()))
.findFirst().get().getName());
// 生成随机手机号(1开头) + 后几位是qyUserId
String randomPrefix = "1" + String.format("%08d", (int)(Math.random() * 100000000));
String phoneNumber = randomPrefix.substring(0, 11 - String.valueOf(binding.getQyUserId()).length())
+ binding.getQyUserId();
command.setPhoneNumber(phoneNumber);
command.setEmail(phoneNumber + "@qywx.com");
command.setPassword("123456");
command.setStatus(1);
UserModel userModel = addUser(command);
binding.setSysUserId(userModel.getUserId());
sysUserQyUserApplicationService.addSysUserQyUser(binding);
}
} catch (Exception e) {
log.error("syncUserBindings error", e);
}
}
private UserModel addUser(AddUserCommand command) {
UserModel model = userModelFactory.create();
model.loadAddUserCommand(command);
model.checkUsernameIsUnique();
model.checkPhoneNumberIsUnique();
model.checkEmailIsUnique();
model.checkFieldRelatedEntityExist();
model.resetPassword(command.getPassword());
model.insert();
return model;
}
}

View File

@ -0,0 +1,104 @@
package com.agileboot.admin.customize.service.login;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.StrUtil;
import com.agileboot.common.enums.BasicEnumUtil;
import com.agileboot.common.enums.common.UserStatusEnum;
import com.agileboot.common.exception.ApiException;
import com.agileboot.common.exception.error.ErrorCode;
import com.agileboot.domain.system.menu.db.SysMenuEntity;
import com.agileboot.domain.system.menu.db.SysMenuService;
import com.agileboot.domain.system.role.db.SysRoleEntity;
import com.agileboot.domain.system.role.db.SysRoleService;
import com.agileboot.domain.system.user.db.SysUserEntity;
import com.agileboot.domain.system.user.db.SysUserService;
import com.agileboot.infrastructure.user.web.DataScopeEnum;
import com.agileboot.infrastructure.user.web.RoleInfo;
import com.agileboot.infrastructure.user.web.SystemLoginUser;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.SetUtils;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
@Service
@Slf4j
@RequiredArgsConstructor
public class FakeUserDetailsServiceImpl {
private final SysUserService userService;
private final SysMenuService menuService;
private final SysRoleService roleService;
private final TokenService tokenService;
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
SysUserEntity userEntity = userService.getUserByUserName(username);
if (userEntity == null) {
log.info("登录用户:{} 不存在.", username);
throw new ApiException(ErrorCode.Business.USER_NON_EXIST, username);
}
if (!Objects.equals(UserStatusEnum.NORMAL.getValue(), userEntity.getStatus())) {
log.info("登录用户:{} 已被停用.", username);
throw new ApiException(ErrorCode.Business.USER_IS_DISABLE, username);
}
RoleInfo roleInfo = getRoleInfo(userEntity.getRoleId(), userEntity.getIsAdmin());
SystemLoginUser loginUser = new SystemLoginUser(userEntity.getUserId(), userEntity.getIsAdmin(), userEntity.getUsername(),
userEntity.getPassword(), roleInfo, userEntity.getDeptId());
loginUser.fillLoginInfo();
loginUser.setAutoRefreshCacheTime(loginUser.getLoginInfo().getLoginTime()
+ TimeUnit.MINUTES.toMillis(tokenService.getAutoRefreshTime()));
return loginUser;
}
public RoleInfo getRoleInfo(Long roleId, boolean isAdmin) {
if (roleId == null) {
return RoleInfo.EMPTY_ROLE;
}
if (isAdmin) {
LambdaQueryWrapper<SysMenuEntity> menuQuery = Wrappers.lambdaQuery();
menuQuery.select(SysMenuEntity::getMenuId);
List<SysMenuEntity> allMenus = menuService.list(menuQuery);
Set<Long> allMenuIds = allMenus.stream().map(SysMenuEntity::getMenuId).collect(Collectors.toSet());
return new RoleInfo(RoleInfo.ADMIN_ROLE_ID, RoleInfo.ADMIN_ROLE_KEY, DataScopeEnum.ALL, SetUtils.emptySet(),
RoleInfo.ADMIN_PERMISSIONS, allMenuIds);
}
SysRoleEntity roleEntity = roleService.getById(roleId);
if (roleEntity == null) {
return RoleInfo.EMPTY_ROLE;
}
List<SysMenuEntity> menuList = roleService.getMenuListByRoleId(roleId);
Set<Long> menuIds = menuList.stream().map(SysMenuEntity::getMenuId).collect(Collectors.toSet());
Set<String> permissions = menuList.stream().map(SysMenuEntity::getPermission).collect(Collectors.toSet());
DataScopeEnum dataScopeEnum = BasicEnumUtil.fromValue(DataScopeEnum.class, roleEntity.getDataScope());
Set<Long> deptIdSet = SetUtils.emptySet();
if (StrUtil.isNotEmpty(roleEntity.getDeptIdSet())) {
deptIdSet = StrUtil.split(roleEntity.getDeptIdSet(), ",").stream()
.map(Convert::toLong).collect(Collectors.toSet());
}
return new RoleInfo(roleId, roleEntity.getRoleKey(), dataScopeEnum, deptIdSet, permissions, menuIds);
}
}

View File

@ -10,8 +10,11 @@ import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.extra.servlet.ServletUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONUtil;
import com.agileboot.common.config.AgileBootConfig;
import com.agileboot.common.constant.Constants.Captcha;
import com.agileboot.common.core.dto.ResponseDTO;
import com.agileboot.common.exception.ApiException;
import com.agileboot.common.exception.error.ErrorCode;
import com.agileboot.common.exception.error.ErrorCode.Business;
@ -21,6 +24,9 @@ import com.agileboot.domain.common.cache.GuavaCacheService;
import com.agileboot.domain.common.cache.MapCache;
import com.agileboot.domain.common.cache.RedisCacheService;
import com.agileboot.admin.customize.async.AsyncTaskFactory;
import com.agileboot.domain.qywx.accessToken.AccessTokenApplicationService;
import com.agileboot.domain.qywx.accessToken.db.QyAccessTokenEntity;
import com.agileboot.domain.qywx.userQySys.SysUserQyUserApplicationService;
import com.agileboot.infrastructure.thread.ThreadPoolManager;
import com.agileboot.admin.customize.service.login.dto.CaptchaDTO;
import com.agileboot.admin.customize.service.login.dto.ConfigDTO;
@ -31,6 +37,8 @@ import com.agileboot.common.enums.common.LoginStatusEnum;
import com.agileboot.domain.system.user.db.SysUserEntity;
import com.google.code.kaptcha.Producer;
import java.awt.image.BufferedImage;
import java.util.HashMap;
import java.util.Map;
import javax.annotation.Resource;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@ -40,8 +48,10 @@ import org.springframework.security.authentication.UsernamePasswordAuthenticatio
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import org.springframework.util.FastByteArrayOutputStream;
import org.springframework.web.client.RestClientException;
/**
* 登录校验方法
@ -61,6 +71,12 @@ public class LoginService {
private final AuthenticationManager authenticationManager;
private final AccessTokenApplicationService accessTokenApplicationService;
private final FakeUserDetailsServiceImpl fakeUserDetailsService;
private final SysUserQyUserApplicationService sysUserQyUserApplicationService;
@Resource(name = "captchaProducer")
private Producer captchaProducer;
@ -80,22 +96,33 @@ public class LoginService {
}
// 用户验证
Authentication authentication;
String decryptPassword = decryptPassword(loginCommand.getPassword());
try {
// 该方法会去调用UserDetailsServiceImpl#loadUserByUsername 校验用户名和密码 认证鉴权
authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(
loginCommand.getUsername(), decryptPassword));
} catch (BadCredentialsException e) {
ThreadPoolManager.execute(AsyncTaskFactory.loginInfoTask(loginCommand.getUsername(), LoginStatusEnum.LOGIN_FAIL,
MessageUtils.message("Business.LOGIN_WRONG_USER_PASSWORD")));
throw new ApiException(e, ErrorCode.Business.LOGIN_WRONG_USER_PASSWORD);
} catch (AuthenticationException e) {
ThreadPoolManager.execute(AsyncTaskFactory.loginInfoTask(loginCommand.getUsername(), LoginStatusEnum.LOGIN_FAIL, e.getMessage()));
throw new ApiException(e, ErrorCode.Business.LOGIN_ERROR, e.getMessage());
} catch (Exception e) {
ThreadPoolManager.execute(AsyncTaskFactory.loginInfoTask(loginCommand.getUsername(), LoginStatusEnum.LOGIN_FAIL, e.getMessage()));
throw new ApiException(e, Business.LOGIN_ERROR, e.getMessage());
if (StrUtil.isNotEmpty(loginCommand.getCode())) {
String userid = getQyUserinfo(loginCommand.getCorpid(), loginCommand.getCode());
String username = sysUserQyUserApplicationService.getUsernameByQyUserid(userid);
UserDetails user = fakeUserDetailsService.loadUserByUsername(username);
authentication = new UsernamePasswordAuthenticationToken(
user, null, user.getAuthorities());
} else {
String decryptPassword = decryptPassword(loginCommand.getPassword());
try {
// 该方法会去调用UserDetailsServiceImpl#loadUserByUsername 校验用户名和密码 认证鉴权
authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(
loginCommand.getUsername(), decryptPassword));
} catch (BadCredentialsException e) {
ThreadPoolManager.execute(AsyncTaskFactory.loginInfoTask(loginCommand.getUsername(), LoginStatusEnum.LOGIN_FAIL,
MessageUtils.message("Business.LOGIN_WRONG_USER_PASSWORD")));
throw new ApiException(e, ErrorCode.Business.LOGIN_WRONG_USER_PASSWORD);
} catch (AuthenticationException e) {
ThreadPoolManager.execute(AsyncTaskFactory.loginInfoTask(loginCommand.getUsername(), LoginStatusEnum.LOGIN_FAIL, e.getMessage()));
throw new ApiException(e, ErrorCode.Business.LOGIN_ERROR, e.getMessage());
} catch (Exception e) {
ThreadPoolManager.execute(AsyncTaskFactory.loginInfoTask(loginCommand.getUsername(), LoginStatusEnum.LOGIN_FAIL, e.getMessage()));
throw new ApiException(e, Business.LOGIN_ERROR, e.getMessage());
}
}
// 把当前登录用户 放入上下文中
SecurityContextHolder.getContext().setAuthentication(authentication);
// 这里获取的loginUser是UserDetailsServiceImpl#loadUserByUsername方法返回的LoginUser
@ -219,4 +246,26 @@ public class LoginService {
return Convert.toBool(guavaCache.configCache.get(ConfigKeyEnum.CAPTCHA.getValue()));
}
private String getQyUserinfo(String corpid, String code) {
try {
QyAccessTokenEntity qyAccessToken = accessTokenApplicationService.getByAppid("QWTONG_YS_WXSHOP", corpid);
String url = String.format(
"https://qyapi.weixin.qq.com/cgi-bin/auth/getuserinfo?access_token=%s&code=%s",
qyAccessToken.getAccessToken(), code);
String response = HttpUtil.get(url);
log.info("微信getuserinfo接口返回: {}", response);
Map<String, Object> result = JSONUtil.toBean(response, HashMap.class);
if (result.containsKey("errcode") && !result.get("errcode").equals(0)) {
log.error("微信接口返回错误: {}", result);
throw new ApiException(ErrorCode.Client.COMMON_REQUEST_PARAMETERS_INVALID, "无效的code参数");
}
return String.valueOf(result.get("userid"));
} catch (RestClientException e) {
log.error("获取openid失败", e);
throw new ApiException(ErrorCode.Client.COMMON_REQUEST_PARAMETERS_INVALID, "微信服务调用失败");
}
}
}

View File

@ -30,4 +30,8 @@ public class LoginCommand {
*/
private String captchaCodeKey;
// 企业id
private String corpid;
// 企业授权码
private String code;
}

View File

@ -1,17 +1,29 @@
package com.agileboot.admin.config;
import cn.hutool.json.JSONUtil;
import com.agileboot.admin.AgileBootAdminApplication;
import com.agileboot.admin.customize.service.login.LoginService;
import com.agileboot.common.config.AgileBootConfig;
import com.agileboot.common.constant.Constants.UploadSubDir;
import java.io.File;
import java.util.List;
import javax.annotation.Resource;
import com.agileboot.domain.qywx.accessToken.AccessTokenApplicationService;
import com.agileboot.domain.qywx.accessToken.db.QyAccessTokenEntity;
import com.agileboot.domain.qywx.authCorpInfo.AuthCorpInfoApplicationService;
import com.agileboot.domain.qywx.authCorpInfo.db.QyAuthCorpInfoEntity;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.jupiter.api.Assertions;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.test.context.junit4.SpringRunner;
@Slf4j
@SpringBootTest(classes = AgileBootAdminApplication.class)
@RunWith(SpringRunner.class)
public class AgileBootConfigTest {
@ -19,6 +31,15 @@ public class AgileBootConfigTest {
@Resource
private AgileBootConfig config;
@Autowired
private AccessTokenApplicationService accessTokenApplicationService;
@Autowired
private AuthCorpInfoApplicationService authCorpInfoApplicationService;
@Autowired
private LoginService loginService;
@Test
public void testConfig() {
String fileBaseDir = "D:\\agileboot\\profile";
@ -41,4 +62,22 @@ public class AgileBootConfigTest {
AgileBootConfig.getFileBaseDir() + File.separator + UploadSubDir.UPLOAD_PATH);
}
@Test
public void testAccessToken() {
String appid = "QWTONG_YS_WXSHOP";
List<QyAuthCorpInfoEntity> authCorpInfoList = authCorpInfoApplicationService.getByAppid(appid);
for (QyAuthCorpInfoEntity authCorpInfo : authCorpInfoList) {
QyAccessTokenEntity accessToken = accessTokenApplicationService.getByAppid(appid, authCorpInfo.getCorpid());
log.info(JSONUtil.toJsonStr(accessToken));
}
}
@Test
public void testDecryptPassword() {
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
String res = passwordEncoder.encode("admin123");
Boolean match = passwordEncoder.matches("admin123", "$2a$10$o55UFZAtyWnDpRV6dvQe8.c/MjlFacC49ASj2usNXm9BY74SYI/uG");
log.info(res);
log.info(String.valueOf(match));
}
}

View File

@ -15,6 +15,7 @@ import com.agileboot.domain.qywx.template.command.UpdateTemplateCommand;
import com.agileboot.domain.qywx.template.db.QyTemplateEntity;
import com.agileboot.infrastructure.thread.ThreadPoolManager;
import java.io.StringReader;
import java.util.List;
import javax.annotation.PostConstruct;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
@ -108,13 +109,20 @@ public class QywxController {
@RequestParam String echostr,
@RequestParam String appid) {
try {
QyAuthCorpInfoEntity qyAuthCorpInfo = authCorpInfoApplicationService.getByAppid(appid);
QyTemplateEntity template = templateApplicationService.getByAppid(appid);
String token = template.getToken();
String encodingAesKey = template.getEncodingAESKey();
String corpId = qyAuthCorpInfo.getCorpid();
WXBizMsgCrypt wxCrypt = new WXBizMsgCrypt(token, encodingAesKey, corpId);
return wxCrypt.VerifyURL(msg_signature, timestamp, nonce, echostr);
List<QyAuthCorpInfoEntity> authCorpInfoList = authCorpInfoApplicationService.getByAppid(appid);
for (QyAuthCorpInfoEntity qyAuthCorpInfo : authCorpInfoList) {
try {
QyTemplateEntity template = templateApplicationService.getByAppid(appid);
String token = template.getToken();
String encodingAesKey = template.getEncodingAESKey();
String corpId = qyAuthCorpInfo.getCorpid();
WXBizMsgCrypt wxCrypt = new WXBizMsgCrypt(token, encodingAesKey, corpId);
return wxCrypt.VerifyURL(msg_signature, timestamp, nonce, echostr);
} catch (Exception ignored) {
}
}
log.error("验证失败");
throw new ApiException(ErrorCode.FAILED, "验证失败");
} catch (Exception e) {
log.error("验证失败", e);
throw new ApiException(ErrorCode.FAILED, "验证失败", e);

View File

@ -56,8 +56,8 @@ public class AccessTokenApplicationService {
}
}
public QyAccessTokenEntity getByAppid(String appid) {
return accessTokenService.getByAppid(appid);
public QyAccessTokenEntity getByAppid(String appid, String corpid) {
return accessTokenService.getByAppid(appid, corpid);
}
public void getAccessToken(String corpid, String corpsecret, String appid) {
@ -73,7 +73,7 @@ public class AccessTokenApplicationService {
return;
}
QyAccessTokenEntity accessToken = accessTokenService.getByAppid(appid);
QyAccessTokenEntity accessToken = accessTokenService.getByAppid(appid, corpid);
if (null == accessToken) {
AddAccessTokenCommand command = new AddAccessTokenCommand();
command.setAppid(appid);
@ -82,13 +82,14 @@ public class AccessTokenApplicationService {
command.setCorpid(corpid);
command.setSecret(corpsecret);
command.setGettokenTime(new Date());
command.setAuthState("1");
command.setEnable("1");
addAccessToken(command);
} else {
UpdateAccessTokenCommand command = new UpdateAccessTokenCommand();
command.setId(accessToken.getId());
command.setAccessToken(access_token);
command.setExpiresIn(expires_in);
command.setCorpid(corpid);
command.setSecret(corpsecret);
command.setGettokenTime(new Date());
updateAccessToken(command);

View File

@ -32,6 +32,6 @@ public interface QyAccessTokenMapper extends BaseMapper<QyAccessTokenEntity> {
"ORDER BY gettoken_time DESC")
List<QyAccessTokenEntity> selectAll();
@Select("SELECT * FROM qy_access_token WHERE appid = #{appid} LIMIT 1")
QyAccessTokenEntity selectByAppid(String appid);
@Select("SELECT * FROM qy_access_token WHERE appid = #{appid} AND corpid = #{corpid} LIMIT 1")
QyAccessTokenEntity selectByAppid(@Param("appid")String appid, @Param("corpid")String corpid);
}

View File

@ -19,5 +19,5 @@ public interface QyAccessTokenService extends IService<QyAccessTokenEntity> {
List<QyAccessTokenEntity> selectAll();
QyAccessTokenEntity getByAppid(String appid);
QyAccessTokenEntity getByAppid(String appid, String corpid);
}

View File

@ -34,7 +34,7 @@ public class QyAccessTokenServiceImpl extends ServiceImpl<QyAccessTokenMapper, Q
}
@Override
public QyAccessTokenEntity getByAppid(String appid) {
return baseMapper.selectByAppid(appid);
public QyAccessTokenEntity getByAppid(String appid, String corpid) {
return baseMapper.selectByAppid(appid, corpid);
}
}

View File

@ -2,13 +2,20 @@ package com.agileboot.domain.qywx.api;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONUtil;
import com.agileboot.domain.qywx.api.response.DepartmentInfoResponse;
import com.agileboot.domain.qywx.api.response.DepartmentListResponse;
import com.agileboot.domain.qywx.api.response.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
@Slf4j
public class QywxApiUtil {
/**
* 获取企业微信部门列表简易信息
* @param access_token 企业微信接口调用凭证
* @param id 部门id非必传若为空则获取全量组织架构
* @return 部门列表响应对象
*/
public static DepartmentListResponse getDepartmentSimplelist(String access_token, String id) {
String url = "https://qyapi.weixin.qq.com/cgi-bin/department/simplelist?access_token="+access_token;
if (StringUtils.isNotBlank(id)) {
@ -21,6 +28,12 @@ public class QywxApiUtil {
return JSONUtil.toBean(response, DepartmentListResponse.class);
}
/**
* 获取指定部门详细信息
* @param access_token 企业微信接口调用凭证
* @param id 需要查询的部门ID
* @return 部门详细信息响应对象
*/
public static DepartmentInfoResponse getDepartmentInfo(String access_token, String id) {
String url = "https://qyapi.weixin.qq.com/cgi-bin/department/get?access_token="+access_token+"&id="+id;
@ -29,4 +42,59 @@ public class QywxApiUtil {
return JSONUtil.toBean(response, DepartmentInfoResponse.class);
}
/**
* 获取部门成员列表
* @param access_token 企业微信接口调用凭证
* @param department_id 需要查询的部门ID
* @return 用户列表响应对象
*/
public static UserListResponse getUserList(String access_token, String department_id) {
String url = "https://qyapi.weixin.qq.com/cgi-bin/user/list?access_token=" + access_token
+ "&department_id=" + department_id;
String response = HttpUtil.get(url);
log.info("getUserList response: {}", response);
return JSONUtil.toBean(response, UserListResponse.class);
}
/**
* 将微信openid转换为企业微信userid
* @param access_token 企业微信接口调用凭证
* @param openid 微信openid来自微信授权
* @return 用户ID转换响应对象
*/
public static UserIdResponse convertToUserid(String access_token, String openid) {
String url = "https://qyapi.weixin.qq.com/cgi-bin/user/convert_to_userid?access_token=" + access_token;
// 构建JSON请求参数
String paramJson = JSONUtil.createObj().set("openid", openid).toString();
String response = HttpUtil.post(url, paramJson);
log.info("convertToUserid response: {}", response);
return JSONUtil.toBean(response, UserIdResponse.class);
}
/**
* 将企业微信用户ID转换为openid
*
* @param access_token 企业微信API访问凭证
* @param userid 企业内成员的用户ID
* @return 包含openid的响应对象当userid非法或不存在时会返回错误
*/
public static OpenidResponse convertToOpenid(String access_token, String userid) {
String url = "https://qyapi.weixin.qq.com/cgi-bin/user/convert_to_openid?access_token=" + access_token;
// 构建包含userid和可选agentid的JSON参数
String paramJson = JSONUtil.createObj()
.set("userid", userid)
.toString();
String response = HttpUtil.post(url, paramJson);
log.info("convertToOpenid response: {}", response);
return JSONUtil.toBean(response, OpenidResponse.class);
}
}

View File

@ -0,0 +1,10 @@
package com.agileboot.domain.qywx.api.response;
import lombok.Data;
@Data
public class OpenidResponse {
private Integer errcode;
private String errmsg;
private String openid;
}

View File

@ -0,0 +1,10 @@
package com.agileboot.domain.qywx.api.response;
import lombok.Data;
@Data
public class UserIdResponse {
private Integer errcode;
private String errmsg;
private String userid;
}

View File

@ -0,0 +1,83 @@
package com.agileboot.domain.qywx.api.response;
import lombok.Data;
import java.util.List;
@Data
public class UserListResponse {
private Integer errcode;
private String errmsg;
private List<UserInfo> userlist;
@Data
public static class UserInfo {
private String userid;
private String name;
private List<Integer> department;
private List<Integer> order;
private String position;
private String mobile;
private String gender;
private String email;
private String biz_mail;
private List<Integer> is_leader_in_dept;
private List<String> direct_leader;
private Integer status;
private String telephone;
private String alias;
private String qr_code;
private String open_userid;
private String external_position;
private String address;
private String english_name;
private Integer main_department;
private String avatar;
private String thumb_avatar;
// private ExtAttr extattr;
// private ExternalProfile external_profile;
/*@Data
public static class ExtAttr {
private List<Attr> attrs;
@Data
public static class Attr {
private Integer type;
private String name;
private Text text;
private Web web;
@Data
public static class Text {
private String value;
}
@Data
public static class MiniProgram {
private String appid;
private String pagepath;
private String title;
}
@Data
public static class Web {
private String url;
private String title;
}
}
}
@Data
public static class ExternalProfile {
private String external_corp_name;
private WechatChannels wechat_channels;
private List<ExtAttr.Attr> external_attr;
@Data
public static class WechatChannels {
private String nickname;
private Integer status;
}
}*/
}
}

View File

@ -51,7 +51,7 @@ public class AuthCorpInfoApplicationService {
}
public QyAuthCorpInfoEntity getByAppid(String appid) {
public List<QyAuthCorpInfoEntity> getByAppid(String appid) {
return authCorpInfoService.getByAppid(appid);
}
}

View File

@ -32,6 +32,6 @@ public interface QyAuthCorpInfoMapper extends BaseMapper<QyAuthCorpInfoEntity> {
List<QyAuthCorpInfoEntity> selectAll();
@Select("SELECT * FROM qy_auth_corp_info WHERE appid = #{appid} LIMIT 1")
QyAuthCorpInfoEntity selectByAppid(String appid);
@Select("SELECT * FROM qy_auth_corp_info WHERE appid = #{appid}")
List<QyAuthCorpInfoEntity> selectByAppid(@Param("appid")String appid);
}

View File

@ -19,5 +19,5 @@ public interface QyAuthCorpInfoService extends IService<QyAuthCorpInfoEntity> {
List<QyAuthCorpInfoEntity> selectAll();
QyAuthCorpInfoEntity getByAppid(String appid);
List<QyAuthCorpInfoEntity> getByAppid(String appid);
}

View File

@ -30,7 +30,7 @@ public class QyAuthCorpInfoServiceImpl extends ServiceImpl<QyAuthCorpInfoMapper,
}
@Override
public QyAuthCorpInfoEntity getByAppid(String appid) {
public List<QyAuthCorpInfoEntity> getByAppid(String appid) {
return baseMapper.selectByAppid(appid);
}
}

View File

@ -20,7 +20,7 @@ public class QyDepartmentServiceImpl extends ServiceImpl<QyDepartmentMapper, QyD
@Override
public List<QyDepartmentEntity> selectAll() {
LambdaQueryWrapper<QyDepartmentEntity> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(QyDepartmentEntity::getDeleted, false);
return this.list(wrapper);
}
}

View File

@ -1,9 +1,6 @@
package com.agileboot.domain.qywx.template.db;
import com.agileboot.common.core.page.AbstractPageQuery;
import com.agileboot.domain.qywx.template.db.QyTemplateService;
import com.agileboot.domain.qywx.template.db.QyTemplateEntity;
import com.agileboot.domain.qywx.template.db.QyTemplateMapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;

View File

@ -2,8 +2,8 @@ package com.agileboot.domain.qywx.user;
import com.agileboot.common.core.page.PageDTO;
import com.agileboot.domain.common.command.BulkOperationCommand;
import com.agileboot.domain.qywx.user.command.AddUserCommand;
import com.agileboot.domain.qywx.user.command.UpdateUserCommand;
import com.agileboot.domain.qywx.user.command.AddQyUserCommand;
import com.agileboot.domain.qywx.user.command.UpdateQyUserCommand;
import com.agileboot.domain.qywx.user.db.QyUserEntity;
import com.agileboot.domain.qywx.user.db.QyUserService;
import com.agileboot.domain.qywx.user.dto.QyUserDTO;
@ -30,13 +30,17 @@ public class QyUserApplicationService {
return new PageDTO<>(dtoList, page.getTotal());
}
public void addUser(AddUserCommand command) {
public List<QyUserEntity> selectAll() {
return userService.selectAll();
}
public void addUser(AddQyUserCommand command) {
UserModel model = qyUserModelFactory.create();
model.loadAddCommand(command);
model.insert();
}
public void updateUser(UpdateUserCommand command) {
public void updateUser(UpdateQyUserCommand command) {
UserModel model = qyUserModelFactory.loadById(command.getId());
model.loadUpdateCommand(command);
model.updateById();

View File

@ -6,6 +6,6 @@ import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = true)
@Data
public class AddUserCommand extends QyUserEntity {
public class AddQyUserCommand extends QyUserEntity {
}

View File

@ -7,7 +7,7 @@ import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = true)
@Data
public class UpdateUserCommand extends AddUserCommand {
public class UpdateQyUserCommand extends AddQyUserCommand {
@NotNull
@PositiveOrZero

View File

@ -28,6 +28,7 @@ public class QyUserServiceImpl extends ServiceImpl<QyUserMapper, QyUserEntity> i
public List<QyUserEntity> selectAll() {
LambdaQueryWrapper<QyUserEntity> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(QyUserEntity::getEnable, "1")
.eq(QyUserEntity::getDeleted, false)
.orderByDesc(QyUserEntity::getId);
return this.list(wrapper);

View File

@ -1,8 +1,8 @@
package com.agileboot.domain.qywx.user.model;
import cn.hutool.core.bean.BeanUtil;
import com.agileboot.domain.qywx.user.command.AddUserCommand;
import com.agileboot.domain.qywx.user.command.UpdateUserCommand;
import com.agileboot.domain.qywx.user.command.AddQyUserCommand;
import com.agileboot.domain.qywx.user.command.UpdateQyUserCommand;
import com.agileboot.domain.qywx.user.db.QyUserEntity;
import com.agileboot.domain.qywx.user.db.QyUserService;
import lombok.Data;
@ -25,13 +25,13 @@ public class UserModel extends QyUserEntity {
this.userService = userService;
}
public void loadAddCommand(AddUserCommand command) {
public void loadAddCommand(AddQyUserCommand command) {
if (command != null) {
BeanUtil.copyProperties(command, this, "id");
}
}
public void loadUpdateCommand(UpdateUserCommand command) {
public void loadUpdateCommand(UpdateQyUserCommand command) {
if (command != null) {
loadAddCommand(command);
}

View File

@ -0,0 +1,69 @@
package com.agileboot.domain.qywx.userQySys;
import com.agileboot.common.core.page.PageDTO;
import com.agileboot.domain.common.command.BulkOperationCommand;
import com.agileboot.domain.qywx.userQySys.command.AddSysUserQyUserCommand;
import com.agileboot.domain.qywx.userQySys.command.UpdateSysUserQyUserCommand;
import com.agileboot.domain.qywx.userQySys.db.SysUserQyUserEntity;
import com.agileboot.domain.qywx.userQySys.db.SysUserQyUserService;
import com.agileboot.domain.qywx.userQySys.dto.SysUserQyUserDTO;
import com.agileboot.domain.qywx.userQySys.model.SysUserQyUserModel;
import com.agileboot.domain.qywx.userQySys.model.SysUserQyUserModelFactory;
import com.agileboot.domain.qywx.userQySys.query.SearchSysUserQyUserQuery;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import java.util.List;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@Service
@Slf4j
@RequiredArgsConstructor
public class SysUserQyUserApplicationService {
private final SysUserQyUserService sysUserQyUserService;
private final SysUserQyUserModelFactory sysUserQyUserModelFactory;
public PageDTO<SysUserQyUserDTO> getSysUserQyUserList(SearchSysUserQyUserQuery<SysUserQyUserEntity> query) {
Page<SysUserQyUserEntity> page = sysUserQyUserService.getUserQyUserList(query);
List<SysUserQyUserDTO> dtoList = page.getRecords().stream()
.map(SysUserQyUserDTO::new)
.collect(Collectors.toList());
return new PageDTO<>(dtoList, page.getTotal());
}
public void addSysUserQyUser(AddSysUserQyUserCommand command) {
SysUserQyUserModel model = sysUserQyUserModelFactory.create();
model.loadAddCommand(command);
model.insert();
}
public void updateSysUserQyUser(UpdateSysUserQyUserCommand command) {
SysUserQyUserModel model = sysUserQyUserModelFactory.loadById(command.getId());
model.loadUpdateCommand(command);
model.updateById();
}
public void deleteSysUserQyUser(BulkOperationCommand<Long> command) {
for (Long id : command.getIds()) {
SysUserQyUserModel model = sysUserQyUserModelFactory.loadById(id);
model.deleteById();
}
}
public SysUserQyUserEntity getBySysUserId(Long sysUserId) {
return sysUserQyUserService.getBySysUserId(sysUserId);
}
public SysUserQyUserEntity getByQyUserId(Integer qyUserId) {
return sysUserQyUserService.getByQyUserId(qyUserId);
}
public List<SysUserQyUserEntity> getAll() {
return sysUserQyUserService.selectAll();
}
public String getUsernameByQyUserid(String userid) {
return sysUserQyUserService.getUsernameByQyUserid(userid);
}
}

View File

@ -0,0 +1,11 @@
package com.agileboot.domain.qywx.userQySys.command;
import com.agileboot.domain.qywx.userQySys.db.SysUserQyUserEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = true)
@Data
public class AddSysUserQyUserCommand extends SysUserQyUserEntity {
}

View File

@ -0,0 +1,17 @@
package com.agileboot.domain.qywx.userQySys.command;
import com.agileboot.domain.qywx.userQySys.db.SysUserQyUserEntity;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.PositiveOrZero;
import lombok.Data;
import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = true)
@Data
public class UpdateSysUserQyUserCommand extends AddSysUserQyUserCommand {
@NotNull
@PositiveOrZero
private Long id;
}

View File

@ -0,0 +1,52 @@
package com.agileboot.domain.qywx.userQySys.db;
import com.agileboot.common.core.base.BaseEntity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
/**
* <p>
* 系统用户与企业微信用户关联表
* </p>
*
* @author valarchie
* @since 2025-03-27
*/
@Getter
@Setter
@TableName("sys_user_qy_user")
@ApiModel(value = "SysUserQyUserEntity对象", description = "系统用户与企业微信用户关联表")
public class SysUserQyUserEntity extends BaseEntity<SysUserQyUserEntity> {
private static final long serialVersionUID = 1L;
@ApiModelProperty("关联ID")
@TableId(value = "id", type = IdType.AUTO)
private Long id;
@ApiModelProperty("系统用户ID")
@TableField("sys_user_id")
private Long sysUserId;
@ApiModelProperty("企业微信用户ID")
@TableField("qy_user_id")
private Integer qyUserId;
@ApiModelProperty("备注")
@TableField("remark")
private String remark;
@Override
public Serializable pkVal() {
return this.id;
}
}

View File

@ -0,0 +1,49 @@
package com.agileboot.domain.qywx.userQySys.db;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import java.util.List;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
/**
* <p>
* 系统用户与企业微信用户关联表 Mapper 接口
* </p>
*
* @author valarchie
* @since 2025-03-27
*/
public interface SysUserQyUserMapper extends BaseMapper<SysUserQyUserEntity> {
@Select("SELECT id, sys_user_id, qy_user_id, remark " +
"FROM sys_user_qy_user " +
"${ew.customSqlSegment}")
Page<SysUserQyUserEntity> getUserQyUserList(
Page<SysUserQyUserEntity> page,
@Param(Constants.WRAPPER) Wrapper<SysUserQyUserEntity> queryWrapper
);
@Select("SELECT * " +
"FROM sys_user_qy_user " +
"WHERE sys_user_id = #{sysUserId}")
List<SysUserQyUserEntity> selectBySysUserId(Long sysUserId);
@Select("SELECT * " +
"FROM sys_user_qy_user " +
"WHERE qy_user_id = #{qyUserId}")
List<SysUserQyUserEntity> selectByQyUserId(Integer qyUserId);
@Select("SELECT * FROM sys_user_qy_user WHERE sys_user_id = #{sysUserId} AND qy_user_id = #{qyUserId} LIMIT 1")
SysUserQyUserEntity selectBySysUserIdAndQyUserId(@Param("sysUserId") Long sysUserId, @Param("qyUserId") Integer qyUserId);
@Select("SELECT su.username " +
"FROM sys_user_qy_user suqy " +
"JOIN qy_user qu ON suqy.qy_user_id = qu.id " +
"JOIN sys_user su ON suqy.sys_user_id = su.user_id " +
"WHERE qu.userid = #{userid}")
String selectUsernameByQyUserid(@Param("userid") String userid);
}

View File

@ -0,0 +1,29 @@
package com.agileboot.domain.qywx.userQySys.db;
import com.agileboot.common.core.page.AbstractPageQuery;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import java.util.List;
/**
* <p>
* 系统用户与企业微信用户关联表 服务类
* </p>
*
* @author valarchie
* @since 2025-03-27
*/
public interface SysUserQyUserService extends IService<SysUserQyUserEntity> {
Page<SysUserQyUserEntity> getUserQyUserList(AbstractPageQuery<SysUserQyUserEntity> query);
List<SysUserQyUserEntity> selectAll();
SysUserQyUserEntity getBySysUserId(Long sysUserId);
SysUserQyUserEntity getByQyUserId(Integer qyUserId);
SysUserQyUserEntity getBySysUserIdAndQyUserId(Long sysUserId, Integer qyUserId);
String getUsernameByQyUserid(String userid);
}

View File

@ -0,0 +1,52 @@
package com.agileboot.domain.qywx.userQySys.db;
import com.agileboot.common.core.page.AbstractPageQuery;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* <p>
* 系统用户与企业微信用户关联表 服务实现类
* </p>
*
* @author valarchie
* @since 2025-03-27
*/
@Service
public class SysUserQyUserServiceImpl extends ServiceImpl<SysUserQyUserMapper, SysUserQyUserEntity> implements SysUserQyUserService {
@Override
public Page<SysUserQyUserEntity> getUserQyUserList(AbstractPageQuery<SysUserQyUserEntity> query) {
return this.page(query.toPage(), query.toQueryWrapper());
}
@Override
public List<SysUserQyUserEntity> selectAll() {
LambdaQueryWrapper<SysUserQyUserEntity> wrapper = new LambdaQueryWrapper<>();
return this.list(wrapper);
}
@Override
public SysUserQyUserEntity getBySysUserId(Long sysUserId) {
return baseMapper.selectBySysUserId(sysUserId).stream().findFirst().orElse(null);
}
@Override
public SysUserQyUserEntity getByQyUserId(Integer qyUserId) {
return baseMapper.selectByQyUserId(qyUserId).stream().findFirst().orElse(null);
}
@Override
public SysUserQyUserEntity getBySysUserIdAndQyUserId(Long sysUserId, Integer qyUserId) {
return baseMapper.selectBySysUserIdAndQyUserId(sysUserId, qyUserId);
}
@Override
public String getUsernameByQyUserid(String userid) {
return baseMapper.selectUsernameByQyUserid(userid);
}
}

View File

@ -0,0 +1,33 @@
package com.agileboot.domain.qywx.userQySys.dto;
import cn.hutool.core.bean.BeanUtil;
import com.agileboot.common.annotation.ExcelColumn;
import com.agileboot.common.annotation.ExcelSheet;
import com.agileboot.domain.common.cache.CacheCenter;
import com.agileboot.domain.qywx.userQySys.db.SysUserQyUserEntity;
import com.agileboot.domain.system.user.db.SysUserEntity;
import lombok.Data;
@ExcelSheet(name = "系统用户与企业微信用户关联列表")
@Data
public class SysUserQyUserDTO {
public SysUserQyUserDTO(SysUserQyUserEntity entity) {
if (entity != null) {
BeanUtil.copyProperties(entity, this);
}
}
@ExcelColumn(name = "关联ID")
private Long id;
@ExcelColumn(name = "系统用户ID")
private Long sysUserId;
@ExcelColumn(name = "企业微信用户ID")
private Integer qyUserId;
@ExcelColumn(name = "备注")
private String remark;
}

View File

@ -0,0 +1,39 @@
package com.agileboot.domain.qywx.userQySys.model;
import cn.hutool.core.bean.BeanUtil;
import com.agileboot.domain.qywx.userQySys.command.AddSysUserQyUserCommand;
import com.agileboot.domain.qywx.userQySys.command.UpdateSysUserQyUserCommand;
import com.agileboot.domain.qywx.userQySys.db.SysUserQyUserEntity;
import com.agileboot.domain.qywx.userQySys.db.SysUserQyUserService;
import lombok.Data;
import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = true)
@Data
public class SysUserQyUserModel extends SysUserQyUserEntity {
private SysUserQyUserService sysUserQyUserService;
public SysUserQyUserModel(SysUserQyUserEntity entity, SysUserQyUserService sysUserQyUserService) {
this(sysUserQyUserService);
if (entity != null) {
BeanUtil.copyProperties(entity, this);
}
}
public SysUserQyUserModel(SysUserQyUserService sysUserQyUserService) {
this.sysUserQyUserService = sysUserQyUserService;
}
public void loadAddCommand(AddSysUserQyUserCommand command) {
if (command != null) {
BeanUtil.copyProperties(command, this, "id");
}
}
public void loadUpdateCommand(UpdateSysUserQyUserCommand command) {
if (command != null) {
loadAddCommand(command);
}
}
}

View File

@ -0,0 +1,27 @@
package com.agileboot.domain.qywx.userQySys.model;
import com.agileboot.common.exception.ApiException;
import com.agileboot.common.exception.error.ErrorCode;
import com.agileboot.domain.qywx.userQySys.db.SysUserQyUserEntity;
import com.agileboot.domain.qywx.userQySys.db.SysUserQyUserService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
@Component
@RequiredArgsConstructor
public class SysUserQyUserModelFactory {
private final SysUserQyUserService sysUserQyUserService;
public SysUserQyUserModel loadById(Long id) {
SysUserQyUserEntity entity = sysUserQyUserService.getById(id);
if (entity == null) {
throw new ApiException(ErrorCode.Business.COMMON_OBJECT_NOT_FOUND, id, "系统用户与企业微信用户关联");
}
return new SysUserQyUserModel(entity, sysUserQyUserService);
}
public SysUserQyUserModel create() {
return new SysUserQyUserModel(sysUserQyUserService);
}
}

View File

@ -0,0 +1,34 @@
package com.agileboot.domain.qywx.userQySys.query;
import cn.hutool.core.util.StrUtil;
import com.agileboot.common.core.page.AbstractPageQuery;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import java.util.Date;
import lombok.Data;
import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = true)
@Data
public class SearchSysUserQyUserQuery<T> extends AbstractPageQuery<T> {
private Long sysUserId;
private Integer qyUserId;
private String remark;
private Date startTime;
private Date endTime;
@Override
public QueryWrapper<T> addQueryCondition() {
QueryWrapper<T> queryWrapper = new QueryWrapper<>();
queryWrapper
.eq(sysUserId != null, "sys_user_id", sysUserId)
.eq(qyUserId != null, "qy_user_id", qyUserId)
.like(StrUtil.isNotEmpty(remark), "remark", remark)
.between(startTime != null && endTime != null, "create_time", startTime, endTime);
this.timeRangeColumn = "create_time";
return queryWrapper;
}
}

View File

@ -63,6 +63,13 @@ public class OrderApplicationService {
if (orderModel.getStatus() != 2) {
throw new ApiException(ErrorCode.Client.COMMON_FORBIDDEN_TO_CALL, "订单状态不允许操作");
}
Date payTime = orderModel.getPayTime();
if (payTime == null)
payTime = orderModel.getCreateTime();
// 验证payTime是否在24小时内
if (DateUtil.offsetHour(payTime, 24).before(new Date())) {
throw new ApiException(ErrorCode.Client.COMMON_FORBIDDEN_TO_CALL, "订单支付时间超过24小时不允许操作");
}
ShopOrderGoodsEntity goodsEntity = orderGoodsService.getById(orderGoodsId);
if (null == goodsEntity || !orderId.equals(goodsEntity.getOrderId())) {

View File

@ -5,6 +5,8 @@ import com.agileboot.domain.system.post.db.SysPostEntity;
import com.agileboot.domain.system.role.db.SysRoleEntity;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import java.util.Set;
@ -87,6 +89,4 @@ public interface SysUserService extends IService<SysUserEntity> {
* @return 用户信息集合信息
*/
Page<SearchUserDO> getUserList(AbstractPageQuery<SearchUserDO> query);
}

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.agileboot.domain.qywx.userQySys.db.SysUserQyUserMapper">
</mapper>

View File

@ -61,7 +61,7 @@ public class CodeGenerator {
//生成的类 放在orm子模块下的/target/generated-code目录底下
.module("/agileboot-orm/target/generated-code")
.parentPackage("com.agileboot")
.tableName("cabinet_cell")
.tableName("sys_user_qy_user")
// 决定是否继承基类
.isExtendsFromBaseEntity(true)
.build();

File diff suppressed because one or more lines are too long

17
sql/20250327.sql Normal file
View File

@ -0,0 +1,17 @@
CREATE TABLE `sys_user_qy_user` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '关联ID',
`sys_user_id` bigint NOT NULL COMMENT '系统用户ID',
`qy_user_id` int NOT NULL COMMENT '企业微信用户ID',
`creator_id` bigint NULL DEFAULT '0' COMMENT '创建者ID',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updater_id` bigint NULL DEFAULT '0' COMMENT '更新者ID',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`remark` varchar(512) DEFAULT NULL COMMENT '备注',
`deleted` tinyint(1) NOT NULL DEFAULT 0 COMMENT '删除标志(0存在 1删除)',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_sys_user_qy_user` (`sys_user_id`,`qy_user_id`) COMMENT '防止重复绑定',
KEY `idx_sys_user_id` (`sys_user_id`),
KEY `idx_qy_user_id` (`qy_user_id`),
CONSTRAINT `fk_sys_user_qy_user_sys` FOREIGN KEY (`sys_user_id`) REFERENCES `sys_user` (`user_id`) ON DELETE CASCADE,
CONSTRAINT `fk_sys_user_qy_user_qy` FOREIGN KEY (`qy_user_id`) REFERENCES `qy_user` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='系统用户与企业微信用户关联表';