From 05f72a9a44a444ddfd02c47ac303619bfb5bfe71 Mon Sep 17 00:00:00 2001 From: dzq Date: Wed, 2 Apr 2025 09:32:48 +0800 Subject: [PATCH] =?UTF-8?q?=E7=B3=BB=E7=BB=9F=E7=94=A8=E6=88=B7=E5=85=B3?= =?UTF-8?q?=E8=81=94=E4=BC=81=E4=B8=9A=E5=BE=AE=E4=BF=A1=E7=94=A8=E6=88=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../qywx/QyDepartmentController.java | 14 +++-- .../controller/qywx/QyUserController.java | 20 +++++++ .../customize/service/login/LoginService.java | 28 ++++++--- .../api/controller/PaymentController.java | 57 ++++++++++++++++++- .../controller/common/LoginController.java | 52 ++++++++++++++++- .../api/response/GetBalanceResponse.java | 14 +++++ .../domain/common/dto/QyLoginDTO.java | 13 +++++ .../domain/qywx/api/QywxApiUtil.java | 29 ++++++++++ .../qywx/user/QyUserApplicationService.java | 4 ++ .../domain/qywx/user/db/QyUserEntity.java | 5 ++ .../domain/qywx/user/db/QyUserMapper.java | 11 +++- .../domain/qywx/user/db/QyUserService.java | 1 + .../qywx/user/db/QyUserServiceImpl.java | 5 ++ .../domain/qywx/user/dto/QyUserDTO.java | 8 +++ .../qywx/user/query/SearchQyUserQuery.java | 14 +++-- .../SysUserQyUserApplicationService.java | 5 +- .../userQySys/db/SysUserQyUserMapper.java | 5 +- .../userQySys/db/SysUserQyUserService.java | 3 +- .../db/SysUserQyUserServiceImpl.java | 5 +- .../shop/order/OrderApplicationService.java | 34 +++++++++-- .../order/command/SubmitOrderCommand.java | 4 ++ .../shop/order/dto/CreateOrderResult.java | 1 + .../domain/system/dept/dto/DeptDTO.java | 1 + sql/20250327.sql | 5 +- 24 files changed, 301 insertions(+), 37 deletions(-) create mode 100644 agileboot-api/src/main/java/com/agileboot/api/response/GetBalanceResponse.java create mode 100644 agileboot-domain/src/main/java/com/agileboot/domain/common/dto/QyLoginDTO.java diff --git a/agileboot-admin/src/main/java/com/agileboot/admin/controller/qywx/QyDepartmentController.java b/agileboot-admin/src/main/java/com/agileboot/admin/controller/qywx/QyDepartmentController.java index 1fdd814..a20a4fb 100644 --- a/agileboot-admin/src/main/java/com/agileboot/admin/controller/qywx/QyDepartmentController.java +++ b/agileboot-admin/src/main/java/com/agileboot/admin/controller/qywx/QyDepartmentController.java @@ -107,14 +107,18 @@ public class QyDepartmentController extends BaseController { @GetMapping("/depts") public ResponseDTO> depts(@RequestParam String corpid) { List qyDepartmentEntityList = departmentApplicationService.getDepartmentList(); - if (StringUtils.isNoneBlank(corpid)) { - qyDepartmentEntityList = qyDepartmentEntityList.stream() - .filter(item -> StringUtils.equals(item.getCorpid(), corpid)) - .collect(Collectors.toList()); + if (StringUtils.isBlank(corpid)) { + corpid = "wpZ1ZrEgAA2QTxIRcB4cMtY7hQbTcPAw"; } + // 根据corpid过滤部门列表 + String finalCorpid = corpid; + qyDepartmentEntityList = qyDepartmentEntityList.stream() + .filter(item -> StringUtils.equals(item.getCorpid(), finalCorpid)) + .collect(Collectors.toList()); + List deptList = qyDepartmentEntityList.stream().map(entity -> { DeptDTO dto = new DeptDTO(null); // 使用null初始化,后续手动设置字段 - dto.setId(entity.getId().longValue()); + dto.setId(Long.parseLong(entity.getDepartmentId())); dto.setParentId(Long.parseLong(entity.getParentid())); dto.setDeptName(entity.getName()); dto.setOrderNum(0); diff --git a/agileboot-admin/src/main/java/com/agileboot/admin/controller/qywx/QyUserController.java b/agileboot-admin/src/main/java/com/agileboot/admin/controller/qywx/QyUserController.java index a532178..9bb39f1 100644 --- a/agileboot-admin/src/main/java/com/agileboot/admin/controller/qywx/QyUserController.java +++ b/agileboot-admin/src/main/java/com/agileboot/admin/controller/qywx/QyUserController.java @@ -1,5 +1,6 @@ package com.agileboot.admin.controller.qywx; +import cn.hutool.json.JSONUtil; import com.agileboot.admin.customize.aop.accessLog.AccessLog; import com.agileboot.admin.customize.service.QywxScheduleJob; import com.agileboot.common.core.base.BaseController; @@ -7,6 +8,11 @@ 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.accessToken.AccessTokenApplicationService; +import com.agileboot.domain.qywx.accessToken.db.QyAccessTokenEntity; +import com.agileboot.domain.qywx.api.QywxApiUtil; +import com.agileboot.domain.qywx.api.response.OpenidResponse; +import com.agileboot.domain.qywx.api.response.UserIdResponse; import com.agileboot.domain.qywx.user.QyUserApplicationService; import com.agileboot.domain.qywx.user.command.AddQyUserCommand; import com.agileboot.domain.qywx.user.command.UpdateQyUserCommand; @@ -35,6 +41,7 @@ public class QyUserController extends BaseController { private final QyUserApplicationService qyUserApplicationService; private final QywxScheduleJob qywxScheduleJob; + private final AccessTokenApplicationService accessTokenApplicationService; @Operation(summary = "用户列表") @GetMapping @@ -81,4 +88,17 @@ public class QyUserController extends BaseController { qywxScheduleJob.syncUserBindings(); return ResponseDTO.ok(); } + + @GetMapping("/convertToOpenid") + public ResponseDTO convertToOpenid(String corpid, String userid) { + QyAccessTokenEntity accessToken = accessTokenApplicationService.getByAppid("QWTONG_YS_WXSHOP", corpid); + OpenidResponse response = QywxApiUtil.convertToOpenid(accessToken.getAccessToken(), userid); + return ResponseDTO.ok(JSONUtil.toJsonStr(response)); + } + @GetMapping("/convertToUserid") + public ResponseDTO convertToUserid(String corpid, String openid) { + QyAccessTokenEntity accessToken = accessTokenApplicationService.getByAppid("QWTONG_YS_WXSHOP", corpid); + UserIdResponse response = QywxApiUtil.convertToUserid(accessToken.getAccessToken(), openid); + return ResponseDTO.ok(JSONUtil.toJsonStr(response)); + } } \ No newline at end of file diff --git a/agileboot-admin/src/main/java/com/agileboot/admin/customize/service/login/LoginService.java b/agileboot-admin/src/main/java/com/agileboot/admin/customize/service/login/LoginService.java index fa60657..166d2b6 100644 --- a/agileboot-admin/src/main/java/com/agileboot/admin/customize/service/login/LoginService.java +++ b/agileboot-admin/src/main/java/com/agileboot/admin/customize/service/login/LoginService.java @@ -14,7 +14,6 @@ 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; @@ -94,41 +93,52 @@ public class LoginService { if (isCaptchaOn()) { validateCaptcha(loginCommand.getUsername(), loginCommand.getCaptchaCode(), loginCommand.getCaptchaCodeKey()); } + // 用户验证 Authentication authentication; + // 企业微信扫码登录分支 if (StrUtil.isNotEmpty(loginCommand.getCode())) { - String userid = getQyUserinfo(loginCommand.getCorpid(), loginCommand.getCode()); - String username = sysUserQyUserApplicationService.getUsernameByQyUserid(userid); + // 通过企业微信code获取用户ID + String userid = getQyUserid(loginCommand.getCorpid(), loginCommand.getCode()); + // 根据企业微信用户ID查询系统用户名 + SysUserEntity sysUserEntity = sysUserQyUserApplicationService.getSysUserByQyUserid(userid); - UserDetails user = fakeUserDetailsService.loadUserByUsername(username); + // 加载用户信息并创建认证对象(跳过密码验证) + UserDetails user = fakeUserDetailsService.loadUserByUsername(sysUserEntity.getUsername()); authentication = new UsernamePasswordAuthenticationToken( user, null, user.getAuthorities()); } else { + // 普通账号密码登录分支 String decryptPassword = decryptPassword(loginCommand.getPassword()); try { - // 该方法会去调用UserDetailsServiceImpl#loadUserByUsername 校验用户名和密码 认证鉴权 + // 调用认证管理器进行标准用户名密码认证 + // 内部会调用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 + // 从认证信息中获取系统登录用户对象 SystemLoginUser loginUser = (SystemLoginUser) authentication.getPrincipal(); + // 记录登录成功信息 recordLoginInfo(loginUser); - // 生成token + // 生成JWT令牌并缓存用户信息 return tokenService.createTokenAndPutUserInCache(loginUser); } @@ -246,7 +256,7 @@ public class LoginService { return Convert.toBool(guavaCache.configCache.get(ConfigKeyEnum.CAPTCHA.getValue())); } - private String getQyUserinfo(String corpid, String code) { + private String getQyUserid(String corpid, String code) { try { QyAccessTokenEntity qyAccessToken = accessTokenApplicationService.getByAppid("QWTONG_YS_WXSHOP", corpid); String url = String.format( diff --git a/agileboot-api/src/main/java/com/agileboot/api/controller/PaymentController.java b/agileboot-api/src/main/java/com/agileboot/api/controller/PaymentController.java index df802fd..52dc759 100644 --- a/agileboot-api/src/main/java/com/agileboot/api/controller/PaymentController.java +++ b/agileboot-api/src/main/java/com/agileboot/api/controller/PaymentController.java @@ -3,17 +3,25 @@ import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.net.URLDecoder; import cn.hutool.http.HttpUtil; import cn.hutool.json.JSONUtil; +import com.agileboot.api.response.GetBalanceResponse; 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.Client; import com.agileboot.common.utils.OpenSignUtil; +import com.agileboot.domain.qywx.accessToken.AccessTokenApplicationService; +import com.agileboot.domain.qywx.accessToken.db.QyAccessTokenEntity; +import com.agileboot.domain.qywx.api.QywxApiUtil; +import com.agileboot.domain.qywx.api.response.UserIdResponse; +import com.agileboot.domain.qywx.authCorpInfo.AuthCorpInfoApplicationService; +import com.agileboot.domain.qywx.authCorpInfo.db.QyAuthCorpInfoEntity; +import com.agileboot.domain.qywx.user.QyUserApplicationService; +import com.agileboot.domain.qywx.user.db.QyUserEntity; import com.agileboot.domain.shop.order.OrderApplicationService; import com.agileboot.domain.shop.payment.dto.PaymentCallbackRequest; import java.nio.charset.StandardCharsets; -import java.util.HashMap; -import java.util.Map; +import java.util.*; import javax.servlet.http.HttpServletRequest; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -37,6 +45,9 @@ import org.springframework.web.client.RestTemplate; @RequestMapping("/api/payment") public class PaymentController { private final OrderApplicationService orderApplicationService; + private final AccessTokenApplicationService accessTokenApplicationService; + private final QyUserApplicationService qyUserApplicationService; + private final AuthCorpInfoApplicationService authCorpInfoApplicationService; // 新增回调接口 @PostMapping("/callback") @@ -98,6 +109,48 @@ public class PaymentController { } } + @GetMapping("/getBalance") + public ResponseDTO getBalance(@RequestParam String openid) { + String appid = "QWTONG_YS_WXSHOP"; + List qyAuthCorpInfoEntityList = authCorpInfoApplicationService.getByAppid(appid); + List qyUserEntityList = new ArrayList<>(); + for (QyAuthCorpInfoEntity qyAuthCorpInfoEntity : qyAuthCorpInfoEntityList) { + String corpid = qyAuthCorpInfoEntity.getCorpid(); + QyAccessTokenEntity accessToken = accessTokenApplicationService.getByAppid(appid, corpid); + UserIdResponse userIdResponse = QywxApiUtil.convertToUserid(accessToken.getAccessToken(), openid); + QyUserEntity qyUser = qyUserApplicationService.getUserByUserId(userIdResponse.getUserid(), corpid); + if (qyUser == null) { + continue; + } + qyUserEntityList.add(qyUser); + } + if (qyUserEntityList.isEmpty()) { + return ResponseDTO.fail(new ApiException(ErrorCode.Client.COMMON_REQUEST_PARAMETERS_INVALID, "无效的openid参数")); + } + // 找出余额最大的用户 + QyUserEntity maxBalanceUser = qyUserEntityList.stream() + .filter(user -> user.getBalance() != null) + .max(Comparator.comparing(QyUserEntity::getBalance)) + .orElse(null); + + if (maxBalanceUser == null) { + return ResponseDTO.fail(new ApiException(ErrorCode.Client.COMMON_REQUEST_PARAMETERS_INVALID, "未找到有效余额信息")); + } + + // 创建响应对象(假设GetBalanceResponse包含balance字段) + GetBalanceResponse response = new GetBalanceResponse(maxBalanceUser.getUserid(), maxBalanceUser.getCorpid(), maxBalanceUser.getBalance()); + return ResponseDTO.ok(response); + } + + @GetMapping("/getBalanceByQyUserid") + public ResponseDTO getBalanceByQyUserid(@RequestParam String corpid, @RequestParam String userid) { + QyUserEntity qyUser = qyUserApplicationService.getUserByUserId(userid, corpid); + + // 创建响应对象(假设GetBalanceResponse包含balance字段) + GetBalanceResponse response = new GetBalanceResponse(qyUser.getUserid(), qyUser.getCorpid(), qyUser.getBalance()); + return ResponseDTO.ok(response); + } + private PaymentCallbackRequest parseCallbackRequest(String requestBody) { // 实现将URL参数解析为PaymentCallbackRequest // 示例实现(需要根据实际参数格式调整): diff --git a/agileboot-api/src/main/java/com/agileboot/api/controller/common/LoginController.java b/agileboot-api/src/main/java/com/agileboot/api/controller/common/LoginController.java index 6c0ec6d..506c63b 100644 --- a/agileboot-api/src/main/java/com/agileboot/api/controller/common/LoginController.java +++ b/agileboot-api/src/main/java/com/agileboot/api/controller/common/LoginController.java @@ -1,27 +1,48 @@ package com.agileboot.api.controller.common; import cn.hutool.core.map.MapUtil; +import cn.hutool.json.JSONUtil; import com.agileboot.api.customize.service.JwtTokenService; import com.agileboot.common.core.base.BaseController; import com.agileboot.common.core.dto.ResponseDTO; -import java.util.Map; + +import java.util.List; + +import com.agileboot.common.exception.ApiException; +import com.agileboot.common.exception.error.ErrorCode; +import com.agileboot.domain.common.dto.QyLoginDTO; +import com.agileboot.domain.qywx.accessToken.AccessTokenApplicationService; +import com.agileboot.domain.qywx.accessToken.db.QyAccessTokenEntity; +import com.agileboot.domain.qywx.api.QywxApiUtil; +import com.agileboot.domain.qywx.api.response.OpenidResponse; +import com.agileboot.domain.qywx.userQySys.SysUserQyUserApplicationService; +import com.agileboot.domain.system.menu.MenuApplicationService; +import com.agileboot.domain.system.menu.dto.RouterDTO; +import com.agileboot.domain.system.user.db.SysUserEntity; +import com.agileboot.infrastructure.user.web.SystemLoginUser; import lombok.AllArgsConstructor; -import org.springframework.security.access.AccessDeniedException; +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.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.client.RestClientException; /** * 调度日志操作处理 * * @author ruoyi */ +@Slf4j @RestController @RequestMapping("/common") @AllArgsConstructor public class LoginController extends BaseController { private final JwtTokenService jwtTokenService; + private final AccessTokenApplicationService accessTokenApplicationService; + private final SysUserQyUserApplicationService sysUserQyUserApplicationService; + private final MenuApplicationService menuApplicationService; /** * 访问首页,提示语 @@ -32,7 +53,34 @@ public class LoginController extends BaseController { return ResponseDTO.ok(token); } + @GetMapping("/login/qy") + public ResponseDTO qyLogin(String corpid, String code, String state) { + try { + QyAccessTokenEntity qyAccessToken = accessTokenApplicationService.getByAppid("QWTONG_YS_WXSHOP", corpid); + // 通过企业微信code获取用户ID + String userid = QywxApiUtil.getQyUserid(qyAccessToken.getAccessToken(), code); + // 根据企业微信用户ID查询系统用户名 + SysUserEntity sysUserEntity = sysUserQyUserApplicationService.getSysUserByQyUserid(userid); + SystemLoginUser loginUser = new SystemLoginUser(); + loginUser.setAdmin(false); + loginUser.setUserId(sysUserEntity.getUserId()); + List routerTree = menuApplicationService.getRouterTree(loginUser); + log.info("getRouterTree,userid: {}, routerTree: {}", userid, JSONUtil.toJsonStr(routerTree)); + int isCabinetAdmin = 0; + if (routerTree != null && !routerTree.isEmpty()) { + isCabinetAdmin = routerTree.stream().anyMatch(router -> router.getName().equals("CabinetCell")) + ? 1 : 0; + } + + OpenidResponse openidResponse = QywxApiUtil.convertToOpenid(qyAccessToken.getAccessToken(), userid); + + return ResponseDTO.ok(new QyLoginDTO(userid, openidResponse.getOpenid(), isCabinetAdmin)); + } catch (RestClientException e) { + log.error("qyLogin失败", e); + return ResponseDTO.fail(new ApiException(ErrorCode.Client.COMMON_REQUEST_PARAMETERS_INVALID, "微信服务调用失败")); + } + } } diff --git a/agileboot-api/src/main/java/com/agileboot/api/response/GetBalanceResponse.java b/agileboot-api/src/main/java/com/agileboot/api/response/GetBalanceResponse.java new file mode 100644 index 0000000..059d108 --- /dev/null +++ b/agileboot-api/src/main/java/com/agileboot/api/response/GetBalanceResponse.java @@ -0,0 +1,14 @@ +package com.agileboot.api.response; + +import lombok.AllArgsConstructor; +import lombok.Data; + +import java.math.BigDecimal; + +@Data +@AllArgsConstructor +public class GetBalanceResponse { + private String userid; + private String corpid; + private BigDecimal balance; +} diff --git a/agileboot-domain/src/main/java/com/agileboot/domain/common/dto/QyLoginDTO.java b/agileboot-domain/src/main/java/com/agileboot/domain/common/dto/QyLoginDTO.java new file mode 100644 index 0000000..787aa67 --- /dev/null +++ b/agileboot-domain/src/main/java/com/agileboot/domain/common/dto/QyLoginDTO.java @@ -0,0 +1,13 @@ +package com.agileboot.domain.common.dto; + +import lombok.AllArgsConstructor; +import lombok.Data; + +@Data +@AllArgsConstructor +public class QyLoginDTO { + + private String userid; + private String openid; + private Integer isCabinetAdmin; +} diff --git a/agileboot-domain/src/main/java/com/agileboot/domain/qywx/api/QywxApiUtil.java b/agileboot-domain/src/main/java/com/agileboot/domain/qywx/api/QywxApiUtil.java index a2fbbb0..b251ba4 100644 --- a/agileboot-domain/src/main/java/com/agileboot/domain/qywx/api/QywxApiUtil.java +++ b/agileboot-domain/src/main/java/com/agileboot/domain/qywx/api/QywxApiUtil.java @@ -2,10 +2,17 @@ package com.agileboot.domain.qywx.api; import cn.hutool.http.HttpUtil; import cn.hutool.json.JSONUtil; +import com.agileboot.common.exception.ApiException; +import com.agileboot.common.exception.error.ErrorCode; +import com.agileboot.domain.qywx.accessToken.db.QyAccessTokenEntity; import com.agileboot.domain.qywx.api.response.*; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; +import org.springframework.web.client.RestClientException; + +import java.util.HashMap; +import java.util.Map; @Slf4j public class QywxApiUtil { @@ -96,5 +103,27 @@ public class QywxApiUtil { return JSONUtil.toBean(response, OpenidResponse.class); } + + public static String getQyUserid(String accessToken, String code) { + try { + String url = String.format( + "https://qyapi.weixin.qq.com/cgi-bin/auth/getuserinfo?access_token=%s&code=%s", + accessToken, code); + + String response = HttpUtil.get(url); + log.info("微信getuserinfo接口返回: {}", response); + Map 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, "微信服务调用失败"); + } + } } diff --git a/agileboot-domain/src/main/java/com/agileboot/domain/qywx/user/QyUserApplicationService.java b/agileboot-domain/src/main/java/com/agileboot/domain/qywx/user/QyUserApplicationService.java index 455ae55..9071e0a 100644 --- a/agileboot-domain/src/main/java/com/agileboot/domain/qywx/user/QyUserApplicationService.java +++ b/agileboot-domain/src/main/java/com/agileboot/domain/qywx/user/QyUserApplicationService.java @@ -52,4 +52,8 @@ public class QyUserApplicationService { model.deleteById(); } } + + public QyUserEntity getUserByUserId(String userid, String corpid) { + return userService.getUserByUserId(userid, corpid); + } } \ No newline at end of file diff --git a/agileboot-domain/src/main/java/com/agileboot/domain/qywx/user/db/QyUserEntity.java b/agileboot-domain/src/main/java/com/agileboot/domain/qywx/user/db/QyUserEntity.java index f0f2f71..7ecdfe8 100644 --- a/agileboot-domain/src/main/java/com/agileboot/domain/qywx/user/db/QyUserEntity.java +++ b/agileboot-domain/src/main/java/com/agileboot/domain/qywx/user/db/QyUserEntity.java @@ -6,6 +6,8 @@ import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import java.io.Serializable; +import java.math.BigDecimal; + import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Getter; @@ -142,6 +144,9 @@ public class QyUserEntity extends BaseEntity { @TableField("cid") private Integer cid; + @ApiModelProperty("用户余额") + @TableField("balance") + private BigDecimal balance; @Override public Serializable pkVal() { diff --git a/agileboot-domain/src/main/java/com/agileboot/domain/qywx/user/db/QyUserMapper.java b/agileboot-domain/src/main/java/com/agileboot/domain/qywx/user/db/QyUserMapper.java index c558ac4..450c358 100644 --- a/agileboot-domain/src/main/java/com/agileboot/domain/qywx/user/db/QyUserMapper.java +++ b/agileboot-domain/src/main/java/com/agileboot/domain/qywx/user/db/QyUserMapper.java @@ -9,7 +9,7 @@ import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Select; public interface QyUserMapper extends BaseMapper { - @Select("SELECT id, oper_id, open_userid, userid, name, mobile, department, user_order, position, gender, email, biz_mail, is_leader_in_dept, direct_leader, avatar, thumb_avatar, telephone, alias, extattr, status, qr_code, external_profile, external_position, address, main_department, enable, corpid, appid, cid " + + @Select("SELECT * " + "FROM qy_user " + "${ew.customSqlSegment}") Page getUserList( @@ -22,4 +22,13 @@ public interface QyUserMapper extends BaseMapper { "WHERE enable = '1' " + "ORDER BY oper_id DESC") List selectAll(); + + @Select("SELECT * " + + "FROM qy_user " + + "WHERE userid = #{userid} " + + "AND corpid = #{corpid}" + + "AND enable = '1'" + + "AND deleted = 0") + QyUserEntity selectByUserid(@Param("userid") String userid, @Param("corpid") String corpid); + } diff --git a/agileboot-domain/src/main/java/com/agileboot/domain/qywx/user/db/QyUserService.java b/agileboot-domain/src/main/java/com/agileboot/domain/qywx/user/db/QyUserService.java index 0bbc8f5..54398d7 100644 --- a/agileboot-domain/src/main/java/com/agileboot/domain/qywx/user/db/QyUserService.java +++ b/agileboot-domain/src/main/java/com/agileboot/domain/qywx/user/db/QyUserService.java @@ -19,4 +19,5 @@ public interface QyUserService extends IService { List selectAll(); + QyUserEntity getUserByUserId(String userid, String corpid); } diff --git a/agileboot-domain/src/main/java/com/agileboot/domain/qywx/user/db/QyUserServiceImpl.java b/agileboot-domain/src/main/java/com/agileboot/domain/qywx/user/db/QyUserServiceImpl.java index 8f2d947..2b29820 100644 --- a/agileboot-domain/src/main/java/com/agileboot/domain/qywx/user/db/QyUserServiceImpl.java +++ b/agileboot-domain/src/main/java/com/agileboot/domain/qywx/user/db/QyUserServiceImpl.java @@ -33,4 +33,9 @@ public class QyUserServiceImpl extends ServiceImpl i return this.list(wrapper); } + + @Override + public QyUserEntity getUserByUserId(String userid, String corpid) { + return baseMapper.selectByUserid(userid, corpid); + } } diff --git a/agileboot-domain/src/main/java/com/agileboot/domain/qywx/user/dto/QyUserDTO.java b/agileboot-domain/src/main/java/com/agileboot/domain/qywx/user/dto/QyUserDTO.java index e09d07c..8621556 100644 --- a/agileboot-domain/src/main/java/com/agileboot/domain/qywx/user/dto/QyUserDTO.java +++ b/agileboot-domain/src/main/java/com/agileboot/domain/qywx/user/dto/QyUserDTO.java @@ -7,7 +7,12 @@ import com.agileboot.common.annotation.ExcelSheet; import com.agileboot.domain.common.cache.CacheCenter; import com.agileboot.domain.qywx.user.db.QyUserEntity; import com.agileboot.domain.system.user.db.SysUserEntity; + +import java.math.BigDecimal; import java.util.Date; + +import com.baomidou.mybatisplus.annotation.TableField; +import io.swagger.annotations.ApiModelProperty; import lombok.Data; @ExcelSheet(name = "企微用户信息列表") @@ -96,4 +101,7 @@ public class QyUserDTO { @ExcelColumn(name = "应用ID") private String appid; + + @ExcelColumn(name = "用户余额") + private BigDecimal balance; } \ No newline at end of file diff --git a/agileboot-domain/src/main/java/com/agileboot/domain/qywx/user/query/SearchQyUserQuery.java b/agileboot-domain/src/main/java/com/agileboot/domain/qywx/user/query/SearchQyUserQuery.java index 3590b1d..a45b2ab 100644 --- a/agileboot-domain/src/main/java/com/agileboot/domain/qywx/user/query/SearchQyUserQuery.java +++ b/agileboot-domain/src/main/java/com/agileboot/domain/qywx/user/query/SearchQyUserQuery.java @@ -11,10 +11,12 @@ import lombok.EqualsAndHashCode; @Data public class SearchQyUserQuery extends AbstractPageQuery { + private String corpid; private String userid; private String name; private String mobile; private String department; + private String mainDepartment; private String enable; private Date startTime; private Date endTime; @@ -24,11 +26,13 @@ public class SearchQyUserQuery extends AbstractPageQuery { QueryWrapper queryWrapper = new QueryWrapper<>(); queryWrapper - .eq(StrUtil.isNotEmpty(userid), "userid", userid) - .like(StrUtil.isNotEmpty(name), "name", name) - .like(StrUtil.isNotEmpty(mobile), "mobile", mobile) - .eq(StrUtil.isNotEmpty(department), "department", department) - .eq(StrUtil.isNotEmpty(enable), "enable", enable) + .eq(StrUtil.isNotBlank(corpid), "corpid", corpid) + .eq(StrUtil.isNotBlank(userid), "userid", userid) + .like(StrUtil.isNotBlank(name), "name", name) + .like(StrUtil.isNotBlank(mobile), "mobile", mobile) + .eq(StrUtil.isNotBlank(department), "department", department) + .eq(StrUtil.isNotBlank(mainDepartment), "main_department", mainDepartment) + .eq(StrUtil.isNotBlank(enable), "enable", enable) .between(startTime != null && endTime != null, "create_time", startTime, endTime); this.timeRangeColumn = "create_time"; diff --git a/agileboot-domain/src/main/java/com/agileboot/domain/qywx/userQySys/SysUserQyUserApplicationService.java b/agileboot-domain/src/main/java/com/agileboot/domain/qywx/userQySys/SysUserQyUserApplicationService.java index d1ca427..4fdfad1 100644 --- a/agileboot-domain/src/main/java/com/agileboot/domain/qywx/userQySys/SysUserQyUserApplicationService.java +++ b/agileboot-domain/src/main/java/com/agileboot/domain/qywx/userQySys/SysUserQyUserApplicationService.java @@ -10,6 +10,7 @@ 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.agileboot.domain.system.user.db.SysUserEntity; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import java.util.List; import java.util.stream.Collectors; @@ -63,7 +64,7 @@ public class SysUserQyUserApplicationService { return sysUserQyUserService.selectAll(); } - public String getUsernameByQyUserid(String userid) { - return sysUserQyUserService.getUsernameByQyUserid(userid); + public SysUserEntity getSysUserByQyUserid(String userid) { + return sysUserQyUserService.getSysUserByQyUserid(userid); } } \ No newline at end of file diff --git a/agileboot-domain/src/main/java/com/agileboot/domain/qywx/userQySys/db/SysUserQyUserMapper.java b/agileboot-domain/src/main/java/com/agileboot/domain/qywx/userQySys/db/SysUserQyUserMapper.java index 759b198..f7e1393 100644 --- a/agileboot-domain/src/main/java/com/agileboot/domain/qywx/userQySys/db/SysUserQyUserMapper.java +++ b/agileboot-domain/src/main/java/com/agileboot/domain/qywx/userQySys/db/SysUserQyUserMapper.java @@ -1,5 +1,6 @@ package com.agileboot.domain.qywx.userQySys.db; +import com.agileboot.domain.system.user.db.SysUserEntity; import com.baomidou.mybatisplus.core.conditions.Wrapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.core.toolkit.Constants; @@ -39,11 +40,11 @@ public interface SysUserQyUserMapper extends BaseMapper { @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 " + + @Select("SELECT su.* " + "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); + SysUserEntity selectSysUserByQyUserid(@Param("userid") String userid); } diff --git a/agileboot-domain/src/main/java/com/agileboot/domain/qywx/userQySys/db/SysUserQyUserService.java b/agileboot-domain/src/main/java/com/agileboot/domain/qywx/userQySys/db/SysUserQyUserService.java index 42f89d8..41b1991 100644 --- a/agileboot-domain/src/main/java/com/agileboot/domain/qywx/userQySys/db/SysUserQyUserService.java +++ b/agileboot-domain/src/main/java/com/agileboot/domain/qywx/userQySys/db/SysUserQyUserService.java @@ -1,6 +1,7 @@ package com.agileboot.domain.qywx.userQySys.db; import com.agileboot.common.core.page.AbstractPageQuery; +import com.agileboot.domain.system.user.db.SysUserEntity; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.IService; @@ -25,5 +26,5 @@ public interface SysUserQyUserService extends IService { SysUserQyUserEntity getBySysUserIdAndQyUserId(Long sysUserId, Integer qyUserId); - String getUsernameByQyUserid(String userid); + SysUserEntity getSysUserByQyUserid(String userid); } diff --git a/agileboot-domain/src/main/java/com/agileboot/domain/qywx/userQySys/db/SysUserQyUserServiceImpl.java b/agileboot-domain/src/main/java/com/agileboot/domain/qywx/userQySys/db/SysUserQyUserServiceImpl.java index 449447b..58261bf 100644 --- a/agileboot-domain/src/main/java/com/agileboot/domain/qywx/userQySys/db/SysUserQyUserServiceImpl.java +++ b/agileboot-domain/src/main/java/com/agileboot/domain/qywx/userQySys/db/SysUserQyUserServiceImpl.java @@ -1,6 +1,7 @@ package com.agileboot.domain.qywx.userQySys.db; import com.agileboot.common.core.page.AbstractPageQuery; +import com.agileboot.domain.system.user.db.SysUserEntity; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; @@ -46,7 +47,7 @@ public class SysUserQyUserServiceImpl extends ServiceImpl getOrderList(SearchShopOrderQuery<> query) { Page page = orderService.page(query.toPage(), query.toQueryWrapper()); @@ -122,12 +128,30 @@ public class OrderApplicationService { processOrderGoods(orderModel, goodsList); - // 新增支付接口调用 - WxJsApiPreCreateRequest paymentRequest = buildPaymentRequest(orderModel); - WxJsApiPreCreateResponse paymentResponse = paymentApplicationService.callJsApiPreCreate(paymentRequest); - - return new CreateOrderResult(orderModel.getOrderId(), orderModel.getTotalAmount(), paymentResponse); + if (Objects.equals(command.getPaymentType(), "wechat")) { + // 新增支付接口调用 + WxJsApiPreCreateRequest paymentRequest = buildPaymentRequest(orderModel); + WxJsApiPreCreateResponse paymentResponse = paymentApplicationService.callJsApiPreCreate(paymentRequest); + return new CreateOrderResult(orderModel.getOrderId(), orderModel.getTotalAmount(), paymentResponse, BigDecimal.valueOf(0)); + } else if (Objects.equals(command.getPaymentType(), "balance")) { + QyUserEntity qyUser = userService.getUserByUserId(command.getUserid(), command.getCorpid()); + // 余额不足 + if (qyUser.getBalance().compareTo(orderModel.getTotalAmount()) < 0) { + throw new ApiException(ErrorCode.Client.COMMON_REQUEST_PARAMETERS_INVALID, "余额不足"); + } else { + qyUser.setBalance(qyUser.getBalance().subtract(orderModel.getTotalAmount())); + userService.updateById(qyUser); + } + // 金额转换(元转分)并四舍五入 + BigDecimal amountInFen = orderModel.getTotalAmount() + .multiply(new BigDecimal("100")) + .setScale(0, RoundingMode.HALF_UP); + handlePaymentSuccess(orderModel.getOrderId(), Integer.valueOf(amountInFen.toPlainString()), + "balance-" + orderModel.getOrderId(), DateUtil.formatDateTime(new Date())); + return new CreateOrderResult(orderModel.getOrderId(), orderModel.getTotalAmount(), null, qyUser.getBalance()); + } + throw new ApiException(ErrorCode.Client.COMMON_REQUEST_PARAMETERS_INVALID, "无效的支付类型"); } private WxJsApiPreCreateRequest buildPaymentRequest(OrderModel orderModel) { diff --git a/agileboot-domain/src/main/java/com/agileboot/domain/shop/order/command/SubmitOrderCommand.java b/agileboot-domain/src/main/java/com/agileboot/domain/shop/order/command/SubmitOrderCommand.java index d317001..40cb562 100644 --- a/agileboot-domain/src/main/java/com/agileboot/domain/shop/order/command/SubmitOrderCommand.java +++ b/agileboot-domain/src/main/java/com/agileboot/domain/shop/order/command/SubmitOrderCommand.java @@ -8,6 +8,10 @@ import lombok.Data; @Data public class SubmitOrderCommand { private String openid; + private String userid; + private String corpid; private ShopOrderEntity order; + // 支付类型'wechat' | 'balance' + private String paymentType; private List goodsList; } \ No newline at end of file diff --git a/agileboot-domain/src/main/java/com/agileboot/domain/shop/order/dto/CreateOrderResult.java b/agileboot-domain/src/main/java/com/agileboot/domain/shop/order/dto/CreateOrderResult.java index bbb2bcb..eed53a8 100644 --- a/agileboot-domain/src/main/java/com/agileboot/domain/shop/order/dto/CreateOrderResult.java +++ b/agileboot-domain/src/main/java/com/agileboot/domain/shop/order/dto/CreateOrderResult.java @@ -11,4 +11,5 @@ public class CreateOrderResult { private Long orderId; private BigDecimal totalAmount; private WxJsApiPreCreateResponse paymentInfo; + private BigDecimal newBalance; } \ No newline at end of file diff --git a/agileboot-domain/src/main/java/com/agileboot/domain/system/dept/dto/DeptDTO.java b/agileboot-domain/src/main/java/com/agileboot/domain/system/dept/dto/DeptDTO.java index fd78f43..1f75104 100644 --- a/agileboot-domain/src/main/java/com/agileboot/domain/system/dept/dto/DeptDTO.java +++ b/agileboot-domain/src/main/java/com/agileboot/domain/system/dept/dto/DeptDTO.java @@ -1,5 +1,6 @@ package com.agileboot.domain.system.dept.dto; +import com.agileboot.common.annotation.ExcelColumn; import com.agileboot.common.enums.common.StatusEnum; import com.agileboot.common.enums.BasicEnumUtil; import com.agileboot.domain.system.dept.db.SysDeptEntity; diff --git a/sql/20250327.sql b/sql/20250327.sql index 3f1247f..8faef74 100644 --- a/sql/20250327.sql +++ b/sql/20250327.sql @@ -14,4 +14,7 @@ CREATE TABLE `sys_user_qy_user` ( 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='系统用户与企业微信用户关联表'; \ No newline at end of file +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='系统用户与企业微信用户关联表'; + +ALTER TABLE qy_user +ADD COLUMN `balance` DECIMAL(15,2) NOT NULL DEFAULT 0.00 COMMENT '用户余额(精度与商品价格对齐)'; \ No newline at end of file