From d0cf2cb2c352bdeb146aad2bb57ba7d39cef3bbd Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期二, 03 十一月 2026 17:59:04 +0800
Subject: [PATCH] 完善会员审批及门户配置
---
yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/auth/WxcpMembershipRegistration.java | 7
yudao-module-ums/src/main/java/cn/iocoder/yudao/module/ums/controller/admin/application/vo/UmsApplicationRespVO.java | 5
yudao-module-ums/src/test/java/cn/iocoder/yudao/module/ums/service/member/WxcpInitialPasswordServiceTest.java | 49 +++
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskApprovalValidator.java | 9
yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialUserRespDTO.java | 7
sql/mysql/ums_member_password_setup.sql | 2
yudao-module-research/src/main/java/cn/iocoder/yudao/module/research/dal/mysql/application/ResearchApplicationMemberMapper.java | 7
yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.java | 6
yudao-module-ums/src/main/java/cn/iocoder/yudao/module/ums/service/application/WxcpMembershipRegistrationImpl.java | 106 ++++++++
yudao-module-ums/src/main/java/cn/iocoder/yudao/module/ums/service/application/UmsApplicationServiceImpl.java | 42 +++
yudao-module-ums/src/main/java/cn/iocoder/yudao/module/ums/dal/dataobject/application/UmsApplicationDO.java | 8
yudao-module-ums/src/test/java/cn/iocoder/yudao/module/ums/service/application/WxcpMembershipRegistrationImplTest.java | 52 ++++
yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/auth/WxcpPasswordSetupGrant.java | 21 +
yudao-module-ums/src/main/java/cn/iocoder/yudao/module/ums/controller/admin/application/vo/UmsApplicationSaveReqVO.java | 5
yudao-module-ums/src/main/java/cn/iocoder/yudao/module/ums/controller/admin/member/vo/UmsMemberRespVO.java | 5
yudao-module-ums/src/main/java/cn/iocoder/yudao/module/ums/service/application/UmsApplicationService.java | 5
yudao-module-ums/src/main/java/cn/iocoder/yudao/module/ums/dal/dataobject/member/UmsMemberDO.java | 5
yudao-module-ums/src/main/java/cn/iocoder/yudao/module/ums/controller/admin/application/UmsApplicationController.java | 16 +
sql/mysql/research_application_member_type.sql | 3
yudao-module-ums/src/main/java/cn/iocoder/yudao/module/ums/controller/admin/member/vo/profile/WxcpInitialPasswordReqVO.java | 12
yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImpl.java | 25 +
yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/homebanner/HomeBannerController.java | 40 +++
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java | 3
yudao-module-research/src/main/java/cn/iocoder/yudao/module/research/controller/admin/application/vo/ResearchApplicationSaveReqVO.java | 7
yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/homebanner/HomeBannerDO.java | 13 +
yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/user/dto/AdminUserCreateReqDTO.java | 5
yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/social/SocialUserService.java | 3
yudao-module-ums/src/test/java/cn/iocoder/yudao/module/ums/service/application/UmsApplicationFeeConfirmationTest.java | 33 ++
yudao-module-ums/src/main/java/cn/iocoder/yudao/module/ums/dal/mysql/member/UmsMemberMapper.java | 6
yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/enums/ErrorCodeConstants.java | 1
yudao-module-ums/src/main/java/cn/iocoder/yudao/module/ums/service/member/UmsMemberServiceImpl.java | 55 +++
yudao-module-ums/src/main/java/cn/iocoder/yudao/module/ums/service/application/listener/UmsApplicationStatusListener.java | 13
yudao-module-ums/src/test/java/cn/iocoder/yudao/module/ums/service/application/UmsApplicationApprovalValidatorTest.java | 46 +++
yudao-module-ums/src/main/java/cn/iocoder/yudao/module/ums/service/member/WxcpInitialPasswordService.java | 38 ++
sql/mysql/ums_application_fee_paid.sql | 2
sql/mysql/system_home_banner.sql | 16 +
yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/homebanner/HomeBannerMapper.java | 5
yudao-module-ums/src/main/java/cn/iocoder/yudao/module/ums/controller/admin/member/UmsMemberProfileChangeController.java | 21 +
yudao-module-ums/src/main/java/cn/iocoder/yudao/module/ums/service/member/UmsMemberProfileChangeServiceImpl.java | 3
sql/mysql/ums_application_wxcp_openid.sql | 4
yudao-module-ums/src/main/java/cn/iocoder/yudao/module/ums/enums/ErrorCodeConstants.java | 9
yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/social/SocialUserServiceImpl.java | 30 ++
yudao-module-ums/src/main/java/cn/iocoder/yudao/module/ums/service/application/UmsApplicationApprovalValidator.java | 29 ++
43 files changed, 736 insertions(+), 43 deletions(-)
diff --git a/sql/mysql/research_application_member_type.sql b/sql/mysql/research_application_member_type.sql
new file mode 100644
index 0000000..5e59ae6
--- /dev/null
+++ b/sql/mysql/research_application_member_type.sql
@@ -0,0 +1,3 @@
+-- 璇鹃缁勬垚鍛樼被鍨嬶紱鏃ф垚鍛樹繚鐣欑┖鍊硷紝寰呬笟鍔′汉鍛樻牳瀹炲悗濉啓銆�
+ALTER TABLE research_application_member
+ ADD COLUMN member_type varchar(32) DEFAULT NULL COMMENT '鎴愬憳绫诲瀷锛氭暀甯堛�佸鐢熴�佹牎澶栦汉鍛�';
diff --git a/sql/mysql/system_home_banner.sql b/sql/mysql/system_home_banner.sql
new file mode 100644
index 0000000..cc43ac0
--- /dev/null
+++ b/sql/mysql/system_home_banner.sql
@@ -0,0 +1,16 @@
+CREATE TABLE IF NOT EXISTS `system_home_banner` (
+ `id` bigint NOT NULL AUTO_INCREMENT, `title` varchar(100) NOT NULL, `image_url` varchar(1000) NOT NULL,
+ `sort` int NOT NULL DEFAULT 0, `status` tinyint NOT NULL DEFAULT 0,
+ `creator` varchar(64) DEFAULT '', `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `updater` varchar(64) DEFAULT '', `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+ `deleted` bit(1) NOT NULL DEFAULT b'0', PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='棣栭〉杞挱鍥�';
+-- 鎵ц鍚庤鍦ㄨ鑹茬鐞嗕腑鎺堟潈銆�
+INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted)
+SELECT '棣栭〉杞挱鍥�', 'system:home-banner:query', 2, 90, id, 'home-banner', 'lucide:images', 'system/home-banner/index', 'SystemHomeBanner', 0, b'1', b'1', b'1', '1', NOW(), '1', NOW(), b'0'
+FROM system_menu WHERE parent_id = 0 AND path = '/system' AND deleted = b'0'
+AND NOT EXISTS (SELECT 1 FROM (SELECT * FROM system_menu) m WHERE m.component = 'system/home-banner/index' AND m.deleted = b'0');
+INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted)
+SELECT '杞挱鍥剧淮鎶�', 'system:home-banner:update', 3, 1, id, '', '', '', 0, b'1', b'1', b'1', '1', NOW(), '1', NOW(), b'0'
+FROM system_menu WHERE component = 'system/home-banner/index' AND deleted = b'0'
+AND NOT EXISTS (SELECT 1 FROM (SELECT * FROM system_menu) m WHERE m.permission = 'system:home-banner:update' AND m.deleted = b'0');
diff --git a/sql/mysql/ums_application_fee_paid.sql b/sql/mysql/ums_application_fee_paid.sql
new file mode 100644
index 0000000..cb0b313
--- /dev/null
+++ b/sql/mysql/ums_application_fee_paid.sql
@@ -0,0 +1,2 @@
+-- 鍘嗗彶鐢宠鐨勭即璐圭姸鎬佹湭鐭ワ紝淇濈暀 NULL銆�
+ALTER TABLE ums_application ADD COLUMN fee_paid bit(1) DEFAULT NULL COMMENT '鐢宠鏃舵槸鍚︾即璐�';
diff --git a/sql/mysql/ums_application_wxcp_openid.sql b/sql/mysql/ums_application_wxcp_openid.sql
new file mode 100644
index 0000000..e51e55f
--- /dev/null
+++ b/sql/mysql/ums_application_wxcp_openid.sql
@@ -0,0 +1,4 @@
+-- 鎵爜韬唤涓庣敵璇峰敮涓�鍏宠仈锛涙櫘閫氱敵璇蜂繚鐣� NULL銆�
+ALTER TABLE ums_application
+ ADD COLUMN wxcp_openid varchar(191) DEFAULT NULL COMMENT '浼佷笟寰俊鎵爜韬唤',
+ ADD UNIQUE KEY uk_ums_application_wxcp_openid (wxcp_openid);
diff --git a/sql/mysql/ums_member_password_setup.sql b/sql/mysql/ums_member_password_setup.sql
new file mode 100644
index 0000000..77b3e04
--- /dev/null
+++ b/sql/mysql/ums_member_password_setup.sql
@@ -0,0 +1,2 @@
+-- 浠呮柊鐨勬棤鎵嬫満鍙蜂紒涓氬井淇℃壂鐮佷細鍛橀渶瑕侀娆¤缃瘑鐮併��
+ALTER TABLE ums_member ADD COLUMN password_setup_required bit(1) NOT NULL DEFAULT b'0' COMMENT '鏄惁闇�瑕侀娆¤缃瘑鐮�';
diff --git a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskApprovalValidator.java b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskApprovalValidator.java
new file mode 100644
index 0000000..df5c988
--- /dev/null
+++ b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskApprovalValidator.java
@@ -0,0 +1,9 @@
+package cn.iocoder.yudao.module.bpm.service.task;
+
+import org.flowable.engine.runtime.ProcessInstance;
+import org.flowable.task.api.Task;
+
+/** 涓氬姟妯″潡鍦ㄥ鎵逛换鍔″畬鎴愬墠杩涜鏍¢獙銆� */
+public interface BpmTaskApprovalValidator {
+ void validate(Long userId, Task task, ProcessInstance instance);
+}
diff --git a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java
index b32d85f..9e9fcc2 100644
--- a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java
+++ b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java
@@ -93,6 +93,8 @@
@Resource
private BpmProcessInstanceService processInstanceService;
+ @org.springframework.beans.factory.annotation.Autowired
+ private org.springframework.beans.factory.ObjectProvider<BpmTaskApprovalValidator> approvalValidators;
@Resource
private BpmProcessDefinitionService bpmProcessDefinitionService;
@Resource
@@ -574,6 +576,7 @@
if (instance == null) {
throw exception(PROCESS_INSTANCE_NOT_EXISTS);
}
+ approvalValidators.orderedStream().forEach(validator -> validator.validate(userId, task, instance));
// 1.3 鏍¢獙绛惧悕
BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(task.getProcessDefinitionId());
Boolean signEnable = parseSignEnable(bpmnModel, task.getTaskDefinitionKey());
diff --git a/yudao-module-research/src/main/java/cn/iocoder/yudao/module/research/controller/admin/application/vo/ResearchApplicationSaveReqVO.java b/yudao-module-research/src/main/java/cn/iocoder/yudao/module/research/controller/admin/application/vo/ResearchApplicationSaveReqVO.java
index 806de20..407eab4 100644
--- a/yudao-module-research/src/main/java/cn/iocoder/yudao/module/research/controller/admin/application/vo/ResearchApplicationSaveReqVO.java
+++ b/yudao-module-research/src/main/java/cn/iocoder/yudao/module/research/controller/admin/application/vo/ResearchApplicationSaveReqVO.java
@@ -4,6 +4,8 @@
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.Email;
+import jakarta.validation.constraints.Size;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Pattern;
@@ -47,7 +49,10 @@
@Pattern(regexp = "^1[3-9]\\d{9}$", message = "鑱旂郴鐢佃瘽鏍煎紡涓嶆纭紝璇疯緭鍏ユ纭殑 11 浣嶆墜鏈哄彿")
private String contactPhone;
- @Schema(description = "鑱旂郴閭", example = "zhangsan@example.com")
+ @Schema(description = "鑱旂郴閭", requiredMode = Schema.RequiredMode.REQUIRED, example = "zhangsan@example.com")
+ @NotBlank(message = "鑱旂郴閭涓嶈兘涓虹┖")
+ @Email(message = "鑱旂郴閭鏍煎紡涓嶆纭�")
+ @Size(max = 128, message = "鑱旂郴閭闀垮害涓嶈兘瓒呰繃128涓瓧绗�")
private String contactEmail;
@Schema(description = "璇鹃缁勬垚鍛橈紙鏃х増鏂囨湰瀛楁锛屼粎淇濈暀鍏煎锛涙柊鏁版嵁璇蜂娇鐢� members锛�")
diff --git a/yudao-module-research/src/main/java/cn/iocoder/yudao/module/research/dal/mysql/application/ResearchApplicationMemberMapper.java b/yudao-module-research/src/main/java/cn/iocoder/yudao/module/research/dal/mysql/application/ResearchApplicationMemberMapper.java
index 099c3de..4cd0ce7 100644
--- a/yudao-module-research/src/main/java/cn/iocoder/yudao/module/research/dal/mysql/application/ResearchApplicationMemberMapper.java
+++ b/yudao-module-research/src/main/java/cn/iocoder/yudao/module/research/dal/mysql/application/ResearchApplicationMemberMapper.java
@@ -1,6 +1,7 @@
package cn.iocoder.yudao.module.research.dal.mysql.application;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
+import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.research.dal.dataobject.application.ResearchApplicationMemberDO;
import org.apache.ibatis.annotations.Mapper;
@@ -10,7 +11,11 @@
public interface ResearchApplicationMemberMapper extends BaseMapperX<ResearchApplicationMemberDO> {
default List<ResearchApplicationMemberDO> selectListByApplicationId(Long applicationId) {
- return selectList(ResearchApplicationMemberDO::getApplicationId, applicationId);
+ LambdaQueryWrapperX<ResearchApplicationMemberDO> query = new LambdaQueryWrapperX<>();
+ query.eq(ResearchApplicationMemberDO::getApplicationId, applicationId);
+ query.orderByAsc(ResearchApplicationMemberDO::getSortNo);
+ query.orderByAsc(ResearchApplicationMemberDO::getId);
+ return selectList(query);
}
default int deleteByApplicationId(Long applicationId) {
diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialUserRespDTO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialUserRespDTO.java
index 0492c3b..5df2584 100644
--- a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialUserRespDTO.java
+++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialUserRespDTO.java
@@ -32,4 +32,11 @@
*/
private Long userId;
+ /** 浼佷笟寰俊鎺堟潈鍚庡彲鑾峰彇鐨勬墜鏈哄彿锛屽彲鑳戒负绌恒�� */
+ private String mobile;
+
+ public SocialUserRespDTO(String openid, String nickname, String avatar, Long userId) {
+ this.openid = openid; this.nickname = nickname; this.avatar = avatar; this.userId = userId;
+ }
+
}
diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/user/dto/AdminUserCreateReqDTO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/user/dto/AdminUserCreateReqDTO.java
index 2474d99..490546c 100644
--- a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/user/dto/AdminUserCreateReqDTO.java
+++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/user/dto/AdminUserCreateReqDTO.java
@@ -33,8 +33,7 @@
@Schema(description = "瑙掕壊缂栧彿鏁扮粍", example = "[1, 2]")
private Set<Long> roleIds;
- @Schema(description = "鎵嬫満鍙风爜", requiredMode = Schema.RequiredMode.REQUIRED, example = "15601691300")
- @NotEmpty(message = "鎵嬫満鍙风爜涓嶈兘涓虹┖")
+ @Schema(description = "鎵嬫満鍙风爜锛堟湭鑾峰彇鍒版椂鍙负绌猴級", example = "15601691300")
private String mobile;
@Schema(description = "瀵嗙爜", requiredMode = Schema.RequiredMode.REQUIRED, example = "123456")
@@ -53,4 +52,4 @@
@Schema(description = "澶囨敞", example = "澶囨敞淇℃伅")
private String remark;
-}
\ No newline at end of file
+}
diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.java
index 974585a..5bf1cfc 100644
--- a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.java
+++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.java
@@ -170,7 +170,11 @@
@PermitAll
@Operation(summary = "绀句氦蹇嵎鐧诲綍锛屼娇鐢� code 鎺堟潈鐮�", description = "閫傚悎鏈櫥褰曠殑鐢ㄦ埛锛屼絾鏄ぞ浜よ处鍙峰凡缁戝畾鐢ㄦ埛")
public CommonResult<AuthLoginRespVO> socialQuickLogin(@RequestBody @Valid AuthSocialLoginReqVO reqVO) {
- return success(authService.socialLogin(reqVO));
+ AuthLoginRespVO result = authService.socialLogin(reqVO);
+ if (result == null) {
+ return CommonResult.error(cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.AUTH_WXCP_MEMBERSHIP_PENDING);
+ }
+ return success(result);
}
}
diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/homebanner/HomeBannerController.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/homebanner/HomeBannerController.java
new file mode 100644
index 0000000..249788e
--- /dev/null
+++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/homebanner/HomeBannerController.java
@@ -0,0 +1,40 @@
+package cn.iocoder.yudao.module.system.controller.admin.homebanner;
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
+import cn.iocoder.yudao.module.system.dal.dataobject.homebanner.HomeBannerDO;
+import cn.iocoder.yudao.module.system.dal.mysql.homebanner.HomeBannerMapper;
+import jakarta.annotation.Resource;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.*;
+import lombok.Data;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+import java.util.List;
+import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+@RestController @RequestMapping("/system/home-banner") @Validated
+public class HomeBannerController {
+ @Resource private HomeBannerMapper mapper;
+ private LambdaQueryWrapperX<HomeBannerDO> ordered() {
+ LambdaQueryWrapperX<HomeBannerDO> query = new LambdaQueryWrapperX<>();
+ query.orderByAsc(HomeBannerDO::getSort); query.orderByDesc(HomeBannerDO::getId); return query;
+ }
+ @GetMapping("/list") @PreAuthorize("@ss.hasPermission('system:home-banner:query')")
+ public CommonResult<List<HomeBannerDO>> list() { return success(mapper.selectList(ordered())); }
+ @GetMapping("/enabled-list")
+ public CommonResult<List<HomeBannerDO>> enabledList() { return success(mapper.selectList(ordered().eq(HomeBannerDO::getStatus, 0))); }
+ @PostMapping("/save") @PreAuthorize("@ss.hasPermission('system:home-banner:update')")
+ public CommonResult<Boolean> save(@Valid @RequestBody SaveRequest req) {
+ HomeBannerDO item = new HomeBannerDO(); item.setId(req.getId()); item.setTitle(req.getTitle().trim()); item.setImageUrl(req.getImageUrl().trim()); item.setSort(req.getSort()); item.setStatus(req.getStatus());
+ if (item.getId() == null) mapper.insert(item); else mapper.updateById(item); return success(true);
+ }
+ @DeleteMapping("/delete") @PreAuthorize("@ss.hasPermission('system:home-banner:update')")
+ public CommonResult<Boolean> delete(@RequestParam("id") Long id) { mapper.deleteById(id); return success(true); }
+ @Data public static class SaveRequest {
+ private Long id;
+ @NotBlank @Size(max=100) private String title;
+ @NotBlank @Size(max=1000) private String imageUrl;
+ @NotNull @Min(0) private Integer sort;
+ @NotNull @Min(0) @Max(1) private Integer status;
+ }
+}
diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/homebanner/HomeBannerDO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/homebanner/HomeBannerDO.java
new file mode 100644
index 0000000..1c584c7
--- /dev/null
+++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/homebanner/HomeBannerDO.java
@@ -0,0 +1,13 @@
+package cn.iocoder.yudao.module.system.dal.dataobject.homebanner;
+import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+@Data @EqualsAndHashCode(callSuper = true) @TableName("system_home_banner")
+public class HomeBannerDO extends BaseDO {
+ private Long id;
+ private String title;
+ private String imageUrl;
+ private Integer sort;
+ private Integer status;
+}
diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/homebanner/HomeBannerMapper.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/homebanner/HomeBannerMapper.java
new file mode 100644
index 0000000..2d474be
--- /dev/null
+++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/homebanner/HomeBannerMapper.java
@@ -0,0 +1,5 @@
+package cn.iocoder.yudao.module.system.dal.mysql.homebanner;
+import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
+import cn.iocoder.yudao.module.system.dal.dataobject.homebanner.HomeBannerDO;
+import org.apache.ibatis.annotations.Mapper;
+@Mapper public interface HomeBannerMapper extends BaseMapperX<HomeBannerDO> {}
diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/enums/ErrorCodeConstants.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/enums/ErrorCodeConstants.java
index 5af11a3..42374ee 100644
--- a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/enums/ErrorCodeConstants.java
+++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/enums/ErrorCodeConstants.java
@@ -16,6 +16,7 @@
ErrorCode AUTH_THIRD_LOGIN_NOT_BIND = new ErrorCode(1_002_000_005, "鏈粦瀹氳处鍙凤紝闇�瑕佽繘琛岀粦瀹�");
ErrorCode AUTH_MOBILE_NOT_EXISTS = new ErrorCode(1_002_000_007, "鎵嬫満鍙蜂笉瀛樺湪");
ErrorCode AUTH_REGISTER_CAPTCHA_CODE_ERROR = new ErrorCode(1_002_000_008, "楠岃瘉鐮佷笉姝g‘锛屽師鍥狅細{}");
+ ErrorCode AUTH_WXCP_MEMBERSHIP_PENDING = new ErrorCode(1_002_000_009, "鍏ヤ細鐢宠宸茬櫥璁帮紝璇风瓑寰呯鐞嗗憳纭缂磋垂骞跺畬鎴愬鎵瑰悗鍐嶆鎵爜鐧诲綍");
// ========== 鑿滃崟妯″潡 1-002-001-000 ==========
ErrorCode MENU_NAME_DUPLICATE = new ErrorCode(1_002_001_000, "宸茬粡瀛樺湪璇ュ悕瀛楃殑鑿滃崟");
diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImpl.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImpl.java
index 72de06f..4f21422 100644
--- a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImpl.java
+++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImpl.java
@@ -40,6 +40,7 @@
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
+import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
@@ -101,6 +102,10 @@
private RoleService roleService;
@Resource
private PermissionService permissionService;
+ @Autowired(required = false)
+ private WxcpMembershipRegistration wxcpMembershipRegistration;
+ @Resource
+ private WxcpPasswordSetupGrant wxcpPasswordSetupGrant;
/**
* 楠岃瘉鐮佺殑寮�鍏筹紝榛樿涓� true
@@ -118,6 +123,7 @@
createLoginLog(null, username, logTypeEnum, LoginResultEnum.BAD_CREDENTIALS);
throw exception(AUTH_LOGIN_BAD_CREDENTIALS);
}
+
if (!userService.isPasswordMatch(password, user.getPassword())) {
createLoginLog(user.getId(), username, logTypeEnum, LoginResultEnum.BAD_CREDENTIALS);
throw exception(AUTH_LOGIN_BAD_CREDENTIALS);
@@ -141,6 +147,14 @@
// 濡傛灉 socialType 闈炵┖锛岃鏄庨渶瑕佺粦瀹氱ぞ浜ょ敤鎴�
if (reqVO.getSocialType() != null) {
+ if (WECHAT_ENTERPRISE_TYPE.equals(reqVO.getSocialType())) {
+ SocialUserRespDTO social = socialUserService.getSocialUserByCode(getUserType().getValue(),
+ reqVO.getSocialType(), reqVO.getSocialCode(), reqVO.getSocialState());
+ if (wxcpMembershipRegistration == null || social == null
+ || wxcpMembershipRegistration.hasPendingApplication(social.getOpenid())) {
+ throw exception(AUTH_WXCP_MEMBERSHIP_PENDING);
+ }
+ }
socialUserService.bindSocialUser(new SocialUserBindReqDTO(user.getId(), getUserType().getValue(),
reqVO.getSocialType(), reqVO.getSocialCode(), reqVO.getSocialState()));
}
@@ -228,6 +242,11 @@
AdminUserDO user = bindUserId != null ? userService.getUser(bindUserId) : null;
if (user == null) {
+ if (WECHAT_ENTERPRISE_TYPE.equals(reqVO.getType())) {
+ if (wxcpMembershipRegistration == null) throw new IllegalStateException("浼氬憳娉ㄥ唽鏈嶅姟涓嶅彲鐢�");
+ wxcpMembershipRegistration.startIfAbsent(socialUser.getOpenid(), socialUser.getNickname(), socialUser.getMobile());
+ return null; // 鎻愪氦浜嬪姟鍚庣敱 Controller 杩斿洖寰呭鏍告彁绀猴紝涓嶇鍙戜护鐗�
+ }
// 2.1 棣栨鐧诲綍锛屾垨鍘熺粦瀹氱敤鎴峰凡琚垹闄わ細鑷姩娉ㄥ唽鏂扮敤鎴峰苟锛堟敼锛夌粦瀹�
//
// 鍘熺粦瀹氱敤鎴峰凡鍒犻櫎鏃跺繀椤昏蛋杩欐潯璺細鍚﹀垯璇ヤ紒涓氭垚鍛樹細姘镐箙鍗″湪銆岀敤鎴蜂笉瀛樺湪銆嶁�斺��
@@ -245,8 +264,12 @@
user.getId(), user.getUsername(), reqVO.getType());
}
+ if (CommonStatusEnum.isDisable(user.getStatus())) throw exception(AUTH_LOGIN_USER_DISABLED);
// 3. 鍒涘缓 Token 浠ょ墝锛岃褰曠櫥褰曟棩蹇�
AuthLoginRespVO result = createTokenAfterLoginSuccess(user.getId(), user.getUsername(), LoginLogTypeEnum.LOGIN_SOCIAL);
+ if (WECHAT_ENTERPRISE_TYPE.equals(reqVO.getType())) {
+ wxcpPasswordSetupGrant.issue(result.getAccessToken(), user.getId());
+ }
// 4. 鍙戝竷绀句氦鐧诲綍浜嬩欢锛堜緵鍏朵粬妯″潡鐩戝惉锛屽鍙戦�佷紒涓氬井淇¢�氱煡锛�
eventPublisher.publishEvent(new SocialLoginEvent(
@@ -435,4 +458,4 @@
userService.updateUserPassword(userByMobile.getId(), reqVO.getPassword());
}
-}
\ No newline at end of file
+}
diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/auth/WxcpMembershipRegistration.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/auth/WxcpMembershipRegistration.java
new file mode 100644
index 0000000..407647a
--- /dev/null
+++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/auth/WxcpMembershipRegistration.java
@@ -0,0 +1,7 @@
+package cn.iocoder.yudao.module.system.service.auth;
+
+/** 浼佷笟寰俊棣栨鎵爜鏃跺垱寤哄緟瀹℃牳鐨勫叆浼氱敵璇凤紝鐢变細鍛樻ā鍧楀疄鐜般�� */
+public interface WxcpMembershipRegistration {
+ void startIfAbsent(String openid, String nickname, String mobile);
+ boolean hasPendingApplication(String openid);
+}
diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/auth/WxcpPasswordSetupGrant.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/auth/WxcpPasswordSetupGrant.java
new file mode 100644
index 0000000..f5d782e
--- /dev/null
+++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/auth/WxcpPasswordSetupGrant.java
@@ -0,0 +1,21 @@
+package cn.iocoder.yudao.module.system.service.auth;
+
+import cn.hutool.crypto.digest.DigestUtil;
+import jakarta.annotation.Resource;
+import org.springframework.data.redis.core.StringRedisTemplate;
+import org.springframework.stereotype.Service;
+import java.time.Duration;
+
+/** 缁戝畾鍒版湰娆′紒涓氬井淇℃壂鐮佺櫥褰曚护鐗岀殑鐭湡瀵嗙爜璁剧疆鎺堟潈銆� */
+@Service
+public class WxcpPasswordSetupGrant {
+ @Resource private StringRedisTemplate redis;
+ private String key(String token) { return "wxcp:password-setup:" + DigestUtil.sha256Hex(token); }
+ public void issue(String token, Long userId) {
+ redis.opsForValue().set(key(token), userId.toString(), Duration.ofMinutes(10));
+ }
+ public boolean valid(String token, Long userId) {
+ return token != null && userId != null && userId.toString().equals(redis.opsForValue().get(key(token)));
+ }
+ public void revoke(String token) { redis.delete(key(token)); }
+}
diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/social/SocialUserService.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/social/SocialUserService.java
index f7901da..d1abcb9 100644
--- a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/social/SocialUserService.java
+++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/social/SocialUserService.java
@@ -18,6 +18,9 @@
*/
public interface SocialUserService {
+ /** 瀹℃壒閫氳繃鍚庣粦瀹氬凡璁よ瘉鐨勪紒涓氬井淇¤韩浠斤紱涓嶅緱瑕嗙洊鍏朵粬鐢ㄦ埛鐨勭粦瀹氥�� */
+ void bindApprovedWxcpUser(String openid, Long userId);
+
/**
* 鑾峰緱鎸囧畾鐢ㄦ埛鐨勭ぞ浜ょ敤鎴峰垪琛�
*
diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/social/SocialUserServiceImpl.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/social/SocialUserServiceImpl.java
index 2ee215b..63efddb 100644
--- a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/social/SocialUserServiceImpl.java
+++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/social/SocialUserServiceImpl.java
@@ -47,6 +47,24 @@
private SocialClientService socialClientService;
@Override
+ @Transactional(rollbackFor = Exception.class)
+ public void bindApprovedWxcpUser(String openid, Long userId) {
+ int type = 30;
+ int userType = cn.iocoder.yudao.framework.common.enums.UserTypeEnum.ADMIN.getValue();
+ SocialUserDO socialUser = socialUserMapper.selectByTypeAndOpenid(type, openid);
+ if (socialUser == null) throw new IllegalStateException("浼佷笟寰俊韬唤涓嶅瓨鍦紝璇烽噸鏂版壂鐮�");
+ SocialUserBindDO existing = socialUserBindMapper.selectByUserTypeAndSocialUserId(userType, socialUser.getId());
+ if (existing != null) {
+ if (!existing.getUserId().equals(userId)) throw new IllegalStateException("璇ヤ紒涓氬井淇¤韩浠藉凡缁戝畾鍏朵粬璐﹀彿");
+ return;
+ }
+ if (socialUserBindMapper.selectByUserIdAndUserTypeAndSocialType(userId, userType, type) != null)
+ throw new IllegalStateException("璇ヨ处鍙峰凡缁戝畾鍏朵粬浼佷笟寰俊韬唤");
+ socialUserBindMapper.insert(SocialUserBindDO.builder().userId(userId).userType(userType)
+ .socialUserId(socialUser.getId()).socialType(type).build());
+ }
+
+ @Override
public List<SocialUserDO> getSocialUserList(Long userId, Integer userType) {
// 鑾峰緱缁戝畾
List<SocialUserBindDO> socialUserBinds = socialUserBindMapper.selectListByUserIdAndUserType(userId, userType);
@@ -115,8 +133,18 @@
// 鑾峰緱缁戝畾鐢ㄦ埛
SocialUserBindDO socialUserBind = socialUserBindMapper.selectByUserTypeAndSocialUserId(userType,
socialUser.getId());
- return new SocialUserRespDTO(socialUser.getOpenid(), socialUser.getNickname(), socialUser.getAvatar(),
+ SocialUserRespDTO result = new SocialUserRespDTO(socialUser.getOpenid(), socialUser.getNickname(), socialUser.getAvatar(),
socialUserBind != null ? socialUserBind.getUserId() : null);
+ if (socialType == 30 && cn.hutool.core.util.StrUtil.isNotBlank(socialUser.getRawUserInfo())) {
+ try {
+ cn.hutool.json.JSONObject raw = cn.hutool.json.JSONUtil.parseObj(socialUser.getRawUserInfo());
+ result.setMobile(raw.getStr("mobile"));
+ if (cn.hutool.core.util.StrUtil.isBlank(result.getNickname())) result.setNickname(raw.getStr("name"));
+ } catch (Exception ignored) {
+ // 鏁忔劅淇℃伅涓嶅彲鐢ㄦ椂涓嶅奖鍝嶆壂鐮侊紝鐢宠浣跨敤闅忔満璐﹀彿鏂规銆�
+ }
+ }
+ return result;
}
/**
diff --git a/yudao-module-ums/src/main/java/cn/iocoder/yudao/module/ums/controller/admin/application/UmsApplicationController.java b/yudao-module-ums/src/main/java/cn/iocoder/yudao/module/ums/controller/admin/application/UmsApplicationController.java
index 8cd451a..2d06523 100644
--- a/yudao-module-ums/src/main/java/cn/iocoder/yudao/module/ums/controller/admin/application/UmsApplicationController.java
+++ b/yudao-module-ums/src/main/java/cn/iocoder/yudao/module/ums/controller/admin/application/UmsApplicationController.java
@@ -31,6 +31,7 @@
import java.util.stream.Collectors;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;
@@ -51,6 +52,19 @@
@Resource
private StorageAttachmentApi storageAttachmentApi;
+
+ @GetMapping("/fee-confirm-available")
+ @Operation(summary = "鍒ゆ柇褰撳墠鐢ㄦ埛鑳藉惁纭缂磋垂鐘舵��")
+ public CommonResult<Boolean> canConfirmFee(@RequestParam("id") Long id) {
+ return success(applicationService.canConfirmFee(id, getLoginUserId()));
+ }
+
+ @PutMapping("/confirm-fee")
+ @Operation(summary = "褰撳墠瀹℃壒浜虹‘璁ょ即璐圭姸鎬�")
+ public CommonResult<Boolean> confirmFee(@RequestParam("id") Long id, @RequestParam("feePaid") Boolean feePaid) {
+ applicationService.confirmFee(id, getLoginUserId(), feePaid);
+ return success(true);
+ }
@PostMapping("/create")
@Operation(summary = "鍒涘缓鍏ヤ細鐢宠")
@@ -143,4 +157,4 @@
});
}
-}
\ No newline at end of file
+}
diff --git a/yudao-module-ums/src/main/java/cn/iocoder/yudao/module/ums/controller/admin/application/vo/UmsApplicationRespVO.java b/yudao-module-ums/src/main/java/cn/iocoder/yudao/module/ums/controller/admin/application/vo/UmsApplicationRespVO.java
index c2d0b09..8fd7c18 100644
--- a/yudao-module-ums/src/main/java/cn/iocoder/yudao/module/ums/controller/admin/application/vo/UmsApplicationRespVO.java
+++ b/yudao-module-ums/src/main/java/cn/iocoder/yudao/module/ums/controller/admin/application/vo/UmsApplicationRespVO.java
@@ -47,6 +47,9 @@
@Schema(description = "鐢宠绛夌骇", example = "1")
private String memberGrade;
+ @Schema(description = "鏄惁缂磋垂")
+ private Boolean feePaid;
+
@Schema(description = "鍦板尯缂栫爜", example = "110000")
private String regionCode;
@@ -77,4 +80,4 @@
@Schema(description = "鏄惁鏈夐檮浠�")
private Boolean hasAttachment;
-}
\ No newline at end of file
+}
diff --git a/yudao-module-ums/src/main/java/cn/iocoder/yudao/module/ums/controller/admin/application/vo/UmsApplicationSaveReqVO.java b/yudao-module-ums/src/main/java/cn/iocoder/yudao/module/ums/controller/admin/application/vo/UmsApplicationSaveReqVO.java
index a5d6c57..9b7e0d4 100644
--- a/yudao-module-ums/src/main/java/cn/iocoder/yudao/module/ums/controller/admin/application/vo/UmsApplicationSaveReqVO.java
+++ b/yudao-module-ums/src/main/java/cn/iocoder/yudao/module/ums/controller/admin/application/vo/UmsApplicationSaveReqVO.java
@@ -46,6 +46,9 @@
@Schema(description = "鐢宠绛夌骇", example = "1")
private String memberGrade;
+ @Schema(description = "鏄惁缂磋垂")
+ private Boolean feePaid;
+
@Schema(description = "鍦板尯缂栫爜锛圓rea 鏍戞湯绾э級", example = "110000")
private String regionCode;
@@ -58,4 +61,4 @@
@Schema(description = "闄勪欢BlobId鍒楄〃")
private List<Long> blobIds;
-}
\ No newline at end of file
+}
diff --git a/yudao-module-ums/src/main/java/cn/iocoder/yudao/module/ums/controller/admin/member/UmsMemberProfileChangeController.java b/yudao-module-ums/src/main/java/cn/iocoder/yudao/module/ums/controller/admin/member/UmsMemberProfileChangeController.java
index bd382e6..2f3b70a 100644
--- a/yudao-module-ums/src/main/java/cn/iocoder/yudao/module/ums/controller/admin/member/UmsMemberProfileChangeController.java
+++ b/yudao-module-ums/src/main/java/cn/iocoder/yudao/module/ums/controller/admin/member/UmsMemberProfileChangeController.java
@@ -8,7 +8,14 @@
import cn.iocoder.yudao.module.ums.controller.admin.member.vo.profile.UmsMemberProfileChangeRespVO;
import cn.iocoder.yudao.module.ums.controller.admin.member.vo.profile.UmsMemberProfileChangeSaveReqVO;
import cn.iocoder.yudao.module.ums.dal.dataobject.member.UmsMemberProfileChangeDO;
+import cn.iocoder.yudao.module.ums.dal.dataobject.member.UmsMemberDO;
import cn.iocoder.yudao.module.ums.service.member.UmsMemberProfileChangeService;
+import cn.iocoder.yudao.module.ums.service.member.WxcpInitialPasswordService;
+import cn.iocoder.yudao.module.ums.controller.admin.member.vo.profile.WxcpInitialPasswordReqVO;
+import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
+import cn.iocoder.yudao.framework.security.config.SecurityProperties;
+import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
+import jakarta.servlet.http.HttpServletRequest;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
@@ -19,6 +26,7 @@
import org.springframework.web.bind.annotation.*;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
@Tag(name = "绠$悊鍚庡彴 - 浼氬憳璧勬枡鑷富缁存姢")
@RestController
@@ -28,11 +36,22 @@
@Resource
private UmsMemberProfileChangeService profileChangeService;
+ @Resource private WxcpInitialPasswordService initialPasswordService;
+ @Resource private SecurityProperties securityProperties;
+
+ @PostMapping("/initial-password")
+ @Operation(summary = "浼佷笟寰俊鎵爜浼氬憳棣栨璁剧疆瀵嗙爜")
+ @ApiAccessLog(requestEnable = false)
+ public CommonResult<String> setInitialPassword(@Valid @RequestBody WxcpInitialPasswordReqVO req, HttpServletRequest request) {
+ String token = SecurityFrameworkUtils.obtainAuthorization(request, securityProperties.getTokenHeader(), securityProperties.getTokenParameter());
+ return success(initialPasswordService.setPassword(getLoginUserId(), token, req.getPassword()));
+ }
@GetMapping("/get")
@Operation(summary = "鑾峰緱褰撳墠鐧诲綍浼氬憳鐨勪釜浜鸿祫鏂�")
public CommonResult<UmsMemberRespVO> getMyProfile() {
- return success(BeanUtils.toBean(profileChangeService.getMyProfile(), UmsMemberRespVO.class));
+ UmsMemberDO member = profileChangeService.getMyProfile();
+ return success(member == null ? null : BeanUtils.toBean(member, UmsMemberRespVO.class));
}
@GetMapping("/change/latest")
diff --git a/yudao-module-ums/src/main/java/cn/iocoder/yudao/module/ums/controller/admin/member/vo/UmsMemberRespVO.java b/yudao-module-ums/src/main/java/cn/iocoder/yudao/module/ums/controller/admin/member/vo/UmsMemberRespVO.java
index 7944a0a..2c9203f 100644
--- a/yudao-module-ums/src/main/java/cn/iocoder/yudao/module/ums/controller/admin/member/vo/UmsMemberRespVO.java
+++ b/yudao-module-ums/src/main/java/cn/iocoder/yudao/module/ums/controller/admin/member/vo/UmsMemberRespVO.java
@@ -78,6 +78,9 @@
@Schema(description = "鏄惁缂寸撼浼氳垂", example = "true")
private Boolean feePaid;
+ @Schema(description = "鏄惁闇�瑕侀娆¤缃瘑鐮�")
+ private Boolean passwordSetupRequired;
+
@Schema(description = "缂磋垂骞村害", example = "2026")
private Integer feeYear;
@@ -102,4 +105,4 @@
/** 鏄惁鏈夐檮浠讹紙閫氳褰�/鍒楄〃鐢級 */
private Boolean hasAttachment;
-}
\ No newline at end of file
+}
diff --git a/yudao-module-ums/src/main/java/cn/iocoder/yudao/module/ums/controller/admin/member/vo/profile/WxcpInitialPasswordReqVO.java b/yudao-module-ums/src/main/java/cn/iocoder/yudao/module/ums/controller/admin/member/vo/profile/WxcpInitialPasswordReqVO.java
new file mode 100644
index 0000000..7388e45
--- /dev/null
+++ b/yudao-module-ums/src/main/java/cn/iocoder/yudao/module/ums/controller/admin/member/vo/profile/WxcpInitialPasswordReqVO.java
@@ -0,0 +1,12 @@
+package cn.iocoder.yudao.module.ums.controller.admin.member.vo.profile;
+
+import jakarta.validation.constraints.NotBlank;
+import lombok.Data;
+import org.hibernate.validator.constraints.Length;
+
+@Data
+public class WxcpInitialPasswordReqVO {
+ @NotBlank
+ @Length(min = 8, max = 16, message = "瀵嗙爜闀垮害椤讳负 8 鑷� 16 浣�")
+ private String password;
+}
diff --git a/yudao-module-ums/src/main/java/cn/iocoder/yudao/module/ums/dal/dataobject/application/UmsApplicationDO.java b/yudao-module-ums/src/main/java/cn/iocoder/yudao/module/ums/dal/dataobject/application/UmsApplicationDO.java
index 32f8d5b..1035e9f 100644
--- a/yudao-module-ums/src/main/java/cn/iocoder/yudao/module/ums/dal/dataobject/application/UmsApplicationDO.java
+++ b/yudao-module-ums/src/main/java/cn/iocoder/yudao/module/ums/dal/dataobject/application/UmsApplicationDO.java
@@ -59,6 +59,12 @@
/** 鐢宠绛夌骇锛堟寜 member_type 鍙栦笉鍚屽瓧鍏革級 */
private String memberGrade;
+ /** 鐢宠鏃舵槸鍚︾即璐� */
+ private Boolean feePaid;
+
+ /** 棣栨鎵爜鐢宠瀵瑰簲鐨勪紒涓氬井淇¤韩浠� */
+ private String wxcpOpenid;
+
/** 鍦板尯缂栫爜锛圓rea 鏍戞湯绾э級 */
private String regionCode;
@@ -74,4 +80,4 @@
/** 澶囨敞 */
private String remark;
-}
\ No newline at end of file
+}
diff --git a/yudao-module-ums/src/main/java/cn/iocoder/yudao/module/ums/dal/dataobject/member/UmsMemberDO.java b/yudao-module-ums/src/main/java/cn/iocoder/yudao/module/ums/dal/dataobject/member/UmsMemberDO.java
index 5b7994e..f1421c3 100644
--- a/yudao-module-ums/src/main/java/cn/iocoder/yudao/module/ums/dal/dataobject/member/UmsMemberDO.java
+++ b/yudao-module-ums/src/main/java/cn/iocoder/yudao/module/ums/dal/dataobject/member/UmsMemberDO.java
@@ -86,6 +86,9 @@
/** 鏄惁缂寸撼浼氳垂 */
private Boolean feePaid;
+ /** 鏄惁闇�瑕侀娆¤缃瘑鐮� */
+ private Boolean passwordSetupRequired;
+
/** 缂磋垂骞村害锛堝 2026锛� */
private Integer feeYear;
@@ -98,4 +101,4 @@
/** 澶囨敞 */
private String remark;
-}
\ No newline at end of file
+}
diff --git a/yudao-module-ums/src/main/java/cn/iocoder/yudao/module/ums/dal/mysql/member/UmsMemberMapper.java b/yudao-module-ums/src/main/java/cn/iocoder/yudao/module/ums/dal/mysql/member/UmsMemberMapper.java
index af4b970..efb7b89 100644
--- a/yudao-module-ums/src/main/java/cn/iocoder/yudao/module/ums/dal/mysql/member/UmsMemberMapper.java
+++ b/yudao-module-ums/src/main/java/cn/iocoder/yudao/module/ums/dal/mysql/member/UmsMemberMapper.java
@@ -18,6 +18,10 @@
return selectOne(UmsMemberDO::getUserId, userId);
}
+ default UmsMemberDO selectByApplicationId(Long applicationId) {
+ return selectOne(UmsMemberDO::getApplicationId, applicationId);
+ }
+
default PageResult<UmsMemberDO> selectPage(UmsMemberPageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<UmsMemberDO>()
.likeIfPresent(UmsMemberDO::getNo, reqVO.getNo())
@@ -31,4 +35,4 @@
.orderByDesc(UmsMemberDO::getId));
}
-}
\ No newline at end of file
+}
diff --git a/yudao-module-ums/src/main/java/cn/iocoder/yudao/module/ums/enums/ErrorCodeConstants.java b/yudao-module-ums/src/main/java/cn/iocoder/yudao/module/ums/enums/ErrorCodeConstants.java
index 99deaa6..e23377e 100644
--- a/yudao-module-ums/src/main/java/cn/iocoder/yudao/module/ums/enums/ErrorCodeConstants.java
+++ b/yudao-module-ums/src/main/java/cn/iocoder/yudao/module/ums/enums/ErrorCodeConstants.java
@@ -16,6 +16,10 @@
ErrorCode APPLICATION_DELETE_FAIL_NOT_DRAFT = new ErrorCode(1_030_108_003, "鍏ヤ細鐢宠({})涓嶆槸鑽夌鐘舵�侊紝鏃犳硶鍒犻櫎");
ErrorCode APPLICATION_SUBMIT_FAIL_STATUS = new ErrorCode(1_030_108_004, "鎻愪氦瀹℃壒澶辫触锛屽彧鏈夎崏绋挎垨宸查┏鍥炵姸鎬佺殑鍏ヤ細鐢宠鎵嶈兘鎻愪氦");
ErrorCode APPLICATION_BPM_CATEGORY_NOT_EXISTS = new ErrorCode(1_030_108_005, "鍏ヤ細瀹℃壒鍒嗙被涓嶅瓨鍦紝璇峰厛鍦˙PM妯″潡鍒涘缓鍏ヤ細瀹℃壒娴佺▼鍒嗙被");
+ ErrorCode APPLICATION_FEE_CONFIRM_REQUIRED = new ErrorCode(1_030_108_006, "璇峰厛鏍稿疄骞跺~鍐欐槸鍚︾即绾充細璐癸紝鍐嶆彁浜ゅ鎵�");
+ ErrorCode APPLICATION_WXCP_INFO_REQUIRED = new ErrorCode(1_030_108_007, "璇峰畬鍠勪細鍛樼瓑绾у強鏈夋晥鎵嬫満鍙凤紱浼佷笟寰俊鎵爜鐢宠鍙笉濉墜鏈哄彿");
+ ErrorCode APPLICATION_WXCP_DELETE_FORBIDDEN = new ErrorCode(1_030_108_008, "鎵爜浜х敓鐨勫叆浼氱敵璇蜂笉鍙垹闄わ紝璇峰畬鍠勫悗鎻愪氦瀹℃壒");
+ ErrorCode APPLICATION_FEE_CONFIRM_FORBIDDEN = new ErrorCode(1_030_108_009, "鍙湁褰撳墠瀹℃壒浜哄彲浠ョ‘璁ょ即璐圭姸鎬�");
// ========== 浼氬憳锛�1-030-108-100 ~ 108-109锛� ==========
ErrorCode MEMBER_NOT_EXISTS = new ErrorCode(1_030_108_100, "浼氬憳涓嶅瓨鍦�");
@@ -26,6 +30,9 @@
ErrorCode MEMBER_ARCHIVE_FAIL_STATUS = new ErrorCode(1_030_108_105, "褰掓。澶辫触锛氫細鍛�({})褰撳墠鐘舵�佷负銆攞}銆曪紝鍙湁銆屾甯搞�嶇姸鎬佺殑浼氬憳鎵嶈兘褰掓。鍒版。妗堢鐞嗭紝璇峰厛鍚敤璇ヤ細鍛�");
ErrorCode MEMBER_ALREADY_ARCHIVED = new ErrorCode(1_030_108_106, "閲嶅褰掓。锛氫細鍛�({})宸蹭簬姝ゅ墠褰掓。骞剁敓鎴愭。妗�(缂栧彿={})锛岃鍕块噸澶嶅綊妗o紝濡傞渶鏌ョ湅璇峰墠寰�妗f绠$悊妯″潡");
ErrorCode MEMBER_FEE_YEAR_REQUIRED = new ErrorCode(1_030_108_107, "浼氳垂缂寸撼骞村害涓虹┖锛氬凡鏍囪涓恒�屽凡缂寸撼浼氳垂銆嶏紝璇烽�夋嫨缂磋垂骞村害锛堜緥濡� 2026锛夊悗鍐嶄繚瀛�");
+ ErrorCode MEMBER_WXCP_ROLE_NOT_AVAILABLE = new ErrorCode(1_030_108_120, "浼佷笟寰俊鎵爜鐢ㄦ埛瑙掕壊涓嶅瓨鍦ㄦ垨宸插仠鐢紝璇疯仈绯荤鐞嗗憳閰嶇疆瑙掕壊缂栫爜 wxcp_user");
+ ErrorCode MEMBER_PASSWORD_SETUP_EXPIRED = new ErrorCode(1_030_108_121, "鎵爜鎺堟潈宸茶繃鏈燂紝璇烽噸鏂版壂鐮佺櫥褰曞悗璁剧疆瀵嗙爜");
+ ErrorCode MEMBER_PASSWORD_SETUP_NOT_REQUIRED = new ErrorCode(1_030_108_122, "褰撳墠璐﹀彿鏃犻渶棣栨璁剧疆瀵嗙爜锛岃鍦ㄤ釜浜轰腑蹇冧慨鏀瑰瘑鐮�");
// ========== 浼氬憳璧勬枡鍙樻洿鐢宠锛�1-030-108-108 ~ 108-119锛� ==========
ErrorCode MEMBER_PROFILE_NOT_LINKED = new ErrorCode(1_030_108_108, "褰撳墠鐧诲綍璐﹀彿鏈叧鑱斾細鍛橈紝鏃犳硶缁存姢涓汉璧勬枡锛岃鑱旂郴瀛︿細绠$悊鍛樼粦瀹氫細鍛樿处鍙�");
@@ -35,4 +42,4 @@
ErrorCode MEMBER_PROFILE_CHANGE_REJECT_REASON_REQUIRED = new ErrorCode(1_030_108_112, "椹冲洖璧勬枡鍙樻洿鐢宠鏃跺繀椤诲~鍐欓┏鍥炲師鍥狅紝璇疯ˉ鍏呭師鍥犲悗鍐嶆搷浣�");
ErrorCode MEMBER_PROFILE_CHANGE_EMPTY = new ErrorCode(1_030_108_113, "璧勬枡鏈彂鐢熶换浣曞彉鏇达紝鏃犻渶鎻愪氦瀹℃牳");
-}
\ No newline at end of file
+}
diff --git a/yudao-module-ums/src/main/java/cn/iocoder/yudao/module/ums/service/application/UmsApplicationApprovalValidator.java b/yudao-module-ums/src/main/java/cn/iocoder/yudao/module/ums/service/application/UmsApplicationApprovalValidator.java
new file mode 100644
index 0000000..cb6b284
--- /dev/null
+++ b/yudao-module-ums/src/main/java/cn/iocoder/yudao/module/ums/service/application/UmsApplicationApprovalValidator.java
@@ -0,0 +1,29 @@
+package cn.iocoder.yudao.module.ums.service.application;
+
+import cn.iocoder.yudao.module.bpm.service.task.BpmTaskApprovalValidator;
+import cn.iocoder.yudao.module.ums.dal.dataobject.application.UmsApplicationDO;
+import cn.iocoder.yudao.module.ums.dal.mysql.application.UmsApplicationMapper;
+import jakarta.annotation.Resource;
+import org.flowable.engine.runtime.ProcessInstance;
+import org.flowable.task.api.Task;
+import org.springframework.stereotype.Component;
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.iocoder.yudao.module.ums.enums.ErrorCodeConstants.*;
+
+@Component
+public class UmsApplicationApprovalValidator implements BpmTaskApprovalValidator {
+ @Resource private UmsApplicationMapper mapper;
+
+ @Override
+ public void validate(Long userId, Task task, ProcessInstance instance) {
+ if (!"ums_membership_approve".equals(instance.getProcessDefinitionKey())) return;
+ // 鍙戣捣浜鸿妭鐐圭敱 BPM 鍦ㄦ祦绋嬪惎鍔ㄦ椂鑷姩鎻愪氦锛涚即璐圭敱鍚庣画瀹℃壒浜虹‘璁ゃ��
+ if ("StartUserNode".equals(task.getTaskDefinitionKey())) return;
+ Long id;
+ try { id = Long.valueOf(instance.getBusinessKey()); }
+ catch (Exception e) { throw exception(APPLICATION_NOT_EXISTS); }
+ UmsApplicationDO application = mapper.selectById(id);
+ if (application == null) throw exception(APPLICATION_NOT_EXISTS);
+ if (application.getFeePaid() == null) throw exception(APPLICATION_FEE_CONFIRM_REQUIRED);
+ }
+}
diff --git a/yudao-module-ums/src/main/java/cn/iocoder/yudao/module/ums/service/application/UmsApplicationService.java b/yudao-module-ums/src/main/java/cn/iocoder/yudao/module/ums/service/application/UmsApplicationService.java
index f1208a5..4f75a1e 100644
--- a/yudao-module-ums/src/main/java/cn/iocoder/yudao/module/ums/service/application/UmsApplicationService.java
+++ b/yudao-module-ums/src/main/java/cn/iocoder/yudao/module/ums/service/application/UmsApplicationService.java
@@ -23,10 +23,13 @@
void submitApplication(Long id, String processDefinitionKey);
+ boolean canConfirmFee(Long id, Long userId);
+ void confirmFee(Long id, Long userId, boolean feePaid);
+
List<Map<String, Object>> getApproveProcessDefinitionList();
void updateApplicationAuditStatus(Long id, Integer bpmResult);
void createMemberFromApplicationOnApproved(Long id);
-}
\ No newline at end of file
+}
diff --git a/yudao-module-ums/src/main/java/cn/iocoder/yudao/module/ums/service/application/UmsApplicationServiceImpl.java b/yudao-module-ums/src/main/java/cn/iocoder/yudao/module/ums/service/application/UmsApplicationServiceImpl.java
index 3b005e8..5d17c2e 100644
--- a/yudao-module-ums/src/main/java/cn/iocoder/yudao/module/ums/service/application/UmsApplicationServiceImpl.java
+++ b/yudao-module-ums/src/main/java/cn/iocoder/yudao/module/ums/service/application/UmsApplicationServiceImpl.java
@@ -9,6 +9,8 @@
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO;
import cn.iocoder.yudao.module.bpm.service.definition.BpmCategoryService;
import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService;
+import cn.iocoder.yudao.module.bpm.service.task.BpmTaskService;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import cn.iocoder.yudao.module.system.api.storage.StorageAttachmentApi;
import cn.iocoder.yudao.module.ums.controller.admin.application.vo.UmsApplicationPageReqVO;
import cn.iocoder.yudao.module.ums.controller.admin.application.vo.UmsApplicationSaveReqVO;
@@ -61,6 +63,26 @@
@Resource
private UmsMemberService memberService;
+ @Resource private BpmTaskService bpmTaskService;
+
+ @Override
+ public boolean canConfirmFee(Long id, Long userId) {
+ UmsApplicationDO application = validateApplicationExists(id);
+ return userId != null && UmsApplicationStatusEnum.APPROVING.getStatus().equals(application.getStatus())
+ && StrUtil.isNotBlank(application.getProcessInstanceId())
+ && bpmTaskService.getTodoTask(userId, null, application.getProcessInstanceId()) != null;
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void confirmFee(Long id, Long userId, boolean feePaid) {
+ if (!canConfirmFee(id, userId)) throw exception(APPLICATION_FEE_CONFIRM_FORBIDDEN);
+ int updated = applicationMapper.update(null, new LambdaUpdateWrapper<UmsApplicationDO>()
+ .eq(UmsApplicationDO::getId, id)
+ .eq(UmsApplicationDO::getStatus, UmsApplicationStatusEnum.APPROVING.getStatus())
+ .set(UmsApplicationDO::getFeePaid, feePaid));
+ if (updated != 1) throw exception(APPLICATION_SUBMIT_FAIL_STATUS);
+ }
@Override
@Transactional(rollbackFor = Exception.class)
@@ -102,6 +124,9 @@
@Transactional(rollbackFor = Exception.class)
public void deleteApplication(Long id) {
UmsApplicationDO application = validateApplicationExists(id);
+ if (StrUtil.isNotBlank(application.getWxcpOpenid())) {
+ throw exception(APPLICATION_WXCP_DELETE_FORBIDDEN);
+ }
if (!UmsApplicationStatusEnum.DRAFT.getStatus().equals(application.getStatus())) {
throw exception(APPLICATION_DELETE_FAIL_NOT_DRAFT, application.getNo());
}
@@ -128,6 +153,15 @@
if (!UmsApplicationStatusEnum.DRAFT.getStatus().equals(status)
&& !UmsApplicationStatusEnum.REJECTED.getStatus().equals(status)) {
throw exception(APPLICATION_SUBMIT_FAIL_STATUS);
+ }
+ if (application.getFeePaid() == null) {
+ throw exception(APPLICATION_FEE_CONFIRM_REQUIRED);
+ }
+ if (StrUtil.isBlank(application.getMemberGrade())
+ || (StrUtil.isBlank(application.getContactPhone()) && StrUtil.isBlank(application.getWxcpOpenid()))
+ || (StrUtil.isNotBlank(application.getContactPhone())
+ && !application.getContactPhone().trim().matches("^1[3-9]\\d{9}$"))) {
+ throw exception(APPLICATION_WXCP_INFO_REQUIRED);
}
String processInstanceId = processInstanceApi.createProcessInstance(getLoginUserId(),
@@ -185,8 +219,14 @@
Integer newStatus = convertBpmResultToStatus(bpmResult);
if (newStatus != null) {
+ if (UmsApplicationStatusEnum.APPROVED.getStatus().equals(newStatus) && application.getFeePaid() == null) {
+ throw exception(APPLICATION_FEE_CONFIRM_REQUIRED);
+ }
applicationMapper.updateById(new UmsApplicationDO()
.setId(id).setStatus(newStatus));
+ if (UmsApplicationStatusEnum.APPROVED.getStatus().equals(newStatus)) {
+ memberService.createMemberFromApplication(application);
+ }
}
}
@@ -230,4 +270,4 @@
memberService.createMemberFromApplication(application);
}
-}
\ No newline at end of file
+}
diff --git a/yudao-module-ums/src/main/java/cn/iocoder/yudao/module/ums/service/application/WxcpMembershipRegistrationImpl.java b/yudao-module-ums/src/main/java/cn/iocoder/yudao/module/ums/service/application/WxcpMembershipRegistrationImpl.java
new file mode 100644
index 0000000..81856ea
--- /dev/null
+++ b/yudao-module-ums/src/main/java/cn/iocoder/yudao/module/ums/service/application/WxcpMembershipRegistrationImpl.java
@@ -0,0 +1,106 @@
+package cn.iocoder.yudao.module.ums.service.application;
+
+import cn.hutool.core.util.StrUtil;
+import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import cn.iocoder.yudao.module.system.service.auth.WxcpMembershipRegistration;
+import cn.iocoder.yudao.module.ums.dal.dataobject.application.UmsApplicationDO;
+import cn.iocoder.yudao.module.ums.dal.mysql.application.UmsApplicationMapper;
+import cn.iocoder.yudao.module.ums.dal.redis.UmsNoRedisDAO;
+import cn.iocoder.yudao.module.ums.enums.UmsApplicationStatusEnum;
+import cn.iocoder.yudao.module.bpm.api.task.BpmProcessInstanceApi;
+import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO;
+import cn.iocoder.yudao.module.system.service.user.AdminUserService;
+import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
+import cn.iocoder.yudao.module.system.controller.admin.user.vo.user.UserSaveReqVO;
+import cn.iocoder.yudao.module.system.service.permission.PermissionService;
+import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission;
+import jakarta.annotation.Resource;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import java.util.Collections;
+import java.util.UUID;
+
+@Service
+public class WxcpMembershipRegistrationImpl implements WxcpMembershipRegistration {
+ @Resource private UmsApplicationMapper mapper;
+ @Resource private UmsNoRedisDAO noRedisDAO;
+ @Resource private BpmProcessInstanceApi processInstanceApi;
+ @Resource private AdminUserService userService;
+ @Resource private PermissionService permissionService;
+
+ @Override
+ public boolean hasPendingApplication(String openid) {
+ UmsApplicationDO application = mapper.selectOne(new LambdaQueryWrapperX<UmsApplicationDO>()
+ .eq(UmsApplicationDO::getWxcpOpenid, openid));
+ return application != null && !UmsApplicationStatusEnum.APPROVED.getStatus().equals(application.getStatus());
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ @DataPermission(enable = false)
+ public void startIfAbsent(String openid, String nickname, String mobile) {
+ if (StrUtil.isBlank(openid)) throw new IllegalArgumentException("浼佷笟寰俊韬唤涓虹┖");
+ UmsApplicationDO existing = mapper.selectOne(new LambdaQueryWrapperX<UmsApplicationDO>()
+ .eq(UmsApplicationDO::getWxcpOpenid, openid));
+ if (existing != null) {
+ // 鍏煎鏃х増鏈娆℃壂鐮佷骇鐢熺殑鑽夌锛涘凡鍦ㄥ鎵规垨宸茬粨鏉熺殑鐢宠涓嶉噸澶嶅彂璧枫��
+ if (UmsApplicationStatusEnum.DRAFT.getStatus().equals(existing.getStatus())
+ && StrUtil.isBlank(existing.getProcessInstanceId())) {
+ existing.setStatus(UmsApplicationStatusEnum.APPROVING.getStatus());
+ if (StrUtil.isBlank(existing.getMemberGrade())) existing.setMemberGrade("1");
+ if (StrUtil.isBlank(existing.getContactPhone()) && StrUtil.isNotBlank(mobile)
+ && mobile.trim().matches("^1[3-9]\\d{9}$")) existing.setContactPhone(mobile.trim());
+ int claimed = mapper.update(null, new LambdaUpdateWrapper<UmsApplicationDO>()
+ .eq(UmsApplicationDO::getId, existing.getId())
+ .eq(UmsApplicationDO::getStatus, UmsApplicationStatusEnum.DRAFT.getStatus())
+ .set(UmsApplicationDO::getStatus, UmsApplicationStatusEnum.APPROVING.getStatus())
+ .set(UmsApplicationDO::getMemberGrade, existing.getMemberGrade())
+ .set(UmsApplicationDO::getContactPhone, existing.getContactPhone()));
+ if (claimed == 1) startProcess(existing);
+ }
+ return;
+ }
+ UmsApplicationDO application = new UmsApplicationDO();
+ application.setNo(noRedisDAO.generate(UmsNoRedisDAO.APPLICATION_NO_PREFIX));
+ application.setWxcpOpenid(openid);
+ application.setApplicantName(StrUtil.maxLength(StrUtil.blankToDefault(nickname, "寰呭畬鍠�"), 64));
+ application.setMemberType(1);
+ application.setMemberGrade("1");
+ if (StrUtil.isNotBlank(mobile) && mobile.trim().matches("^1[3-9]\\d{9}$")) {
+ application.setContactPhone(mobile.trim());
+ }
+ application.setStatus(UmsApplicationStatusEnum.APPROVING.getStatus());
+ mapper.insert(application);
+ startProcess(application);
+ }
+
+ private void startProcess(UmsApplicationDO application) {
+ Long initiatorId = getServiceUserId();
+ String processId = processInstanceApi.createProcessInstance(initiatorId,
+ new BpmProcessInstanceCreateReqDTO().setProcessDefinitionKey("ums_membership_approve")
+ .setBusinessKey(String.valueOf(application.getId())));
+ mapper.updateById(new UmsApplicationDO().setId(application.getId()).setProcessInstanceId(processId));
+ }
+
+ private Long getServiceUserId() {
+ String username = "wxcpservice";
+ AdminUserDO existing = userService.getUserByUsername(username);
+ if (existing != null) {
+ if (cn.iocoder.yudao.framework.common.enums.CommonStatusEnum.isDisable(existing.getStatus()))
+ throw new IllegalStateException("鍏ヤ細瀹℃壒鏈嶅姟璐﹀彿宸插仠鐢�");
+ if (!"浠呯敤浜庣郴缁熻嚜鍔ㄥ彂璧峰叆浼氬鎵�".equals(existing.getRemark())
+ || !permissionService.getUserRoleIdListByUserId(existing.getId()).isEmpty())
+ throw new IllegalStateException("鍏ヤ細瀹℃壒鏈嶅姟璐﹀彿閰嶇疆涓嶇鍚堣姹傦紝璇疯仈绯荤鐞嗗憳鏍告煡");
+ return existing.getId();
+ }
+ UserSaveReqVO req = new UserSaveReqVO();
+ req.setUsername(username);
+ req.setNickname("鍏ヤ細瀹℃壒鏈嶅姟");
+ req.setPassword(UUID.randomUUID().toString().replace("-", "").substring(0, 16));
+ req.setPostIds(Collections.emptySet());
+ req.setRoleIds(Collections.emptySet());
+ req.setRemark("浠呯敤浜庣郴缁熻嚜鍔ㄥ彂璧峰叆浼氬鎵�");
+ return userService.createUser(req);
+ }
+}
diff --git a/yudao-module-ums/src/main/java/cn/iocoder/yudao/module/ums/service/application/listener/UmsApplicationStatusListener.java b/yudao-module-ums/src/main/java/cn/iocoder/yudao/module/ums/service/application/listener/UmsApplicationStatusListener.java
index 3d1984c..4d9e2fd 100644
--- a/yudao-module-ums/src/main/java/cn/iocoder/yudao/module/ums/service/application/listener/UmsApplicationStatusListener.java
+++ b/yudao-module-ums/src/main/java/cn/iocoder/yudao/module/ums/service/application/listener/UmsApplicationStatusListener.java
@@ -44,18 +44,9 @@
log.info("[onEvent] 鍏ヤ細鐢宠({}) 瀹℃壒鐘舵�佹洿鏂板畬鎴�", applicationId);
} catch (Exception e) {
log.error("[onEvent] 鏇存柊鍏ヤ細鐢宠瀹℃壒鐘舵�佸け璐ワ紝applicationId={}", applicationId, e);
- return;
+ throw e;
}
- // 2. 瀹℃壒閫氳繃鍚庤嚜鍔ㄥ垱寤轰細鍛�
- if (cn.iocoder.yudao.module.bpm.enums.task.BpmTaskStatusEnum.APPROVE.getStatus().equals(event.getStatus())) {
- try {
- applicationService.createMemberFromApplicationOnApproved(applicationId);
- log.info("[onEvent] 鍏ヤ細鐢宠({}) 瀹℃壒閫氳繃锛屼細鍛樺垱寤哄畬鎴�", applicationId);
- } catch (Exception e) {
- log.error("[onEvent] 鍏ヤ細鐢宠({}) 瀹℃壒閫氳繃鍚庡垱寤轰細鍛樺け璐�", applicationId, e);
- }
- }
}
-}
\ No newline at end of file
+}
diff --git a/yudao-module-ums/src/main/java/cn/iocoder/yudao/module/ums/service/member/UmsMemberProfileChangeServiceImpl.java b/yudao-module-ums/src/main/java/cn/iocoder/yudao/module/ums/service/member/UmsMemberProfileChangeServiceImpl.java
index fb118dc..55e45b4 100644
--- a/yudao-module-ums/src/main/java/cn/iocoder/yudao/module/ums/service/member/UmsMemberProfileChangeServiceImpl.java
+++ b/yudao-module-ums/src/main/java/cn/iocoder/yudao/module/ums/service/member/UmsMemberProfileChangeServiceImpl.java
@@ -50,7 +50,8 @@
@Override
public UmsMemberDO getMyProfile() {
- return getLoginMember();
+ Long userId = getLoginUserId();
+ return userId != null ? memberMapper.selectByUserId(userId) : null;
}
@Override
diff --git a/yudao-module-ums/src/main/java/cn/iocoder/yudao/module/ums/service/member/UmsMemberServiceImpl.java b/yudao-module-ums/src/main/java/cn/iocoder/yudao/module/ums/service/member/UmsMemberServiceImpl.java
index 5b5f9b4..77c45b0 100644
--- a/yudao-module-ums/src/main/java/cn/iocoder/yudao/module/ums/service/member/UmsMemberServiceImpl.java
+++ b/yudao-module-ums/src/main/java/cn/iocoder/yudao/module/ums/service/member/UmsMemberServiceImpl.java
@@ -11,10 +11,15 @@
import cn.iocoder.yudao.module.system.api.storage.dto.StorageBlobRespDTO;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserCreateReqDTO;
+import cn.iocoder.yudao.module.system.service.social.SocialUserService;
+import cn.iocoder.yudao.module.system.service.permission.RoleService;
+import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO;
+import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.module.ums.controller.admin.member.vo.UmsMemberPageReqVO;
import cn.iocoder.yudao.module.ums.controller.admin.member.vo.UmsMemberSaveReqVO;
import cn.iocoder.yudao.module.ums.dal.dataobject.application.UmsApplicationDO;
import cn.iocoder.yudao.module.ums.dal.dataobject.member.UmsMemberDO;
+import cn.iocoder.yudao.module.ums.dal.mysql.application.UmsApplicationMapper;
import cn.iocoder.yudao.module.ums.dal.mysql.member.UmsMemberMapper;
import cn.iocoder.yudao.module.ums.dal.redis.UmsNoRedisDAO;
import jakarta.annotation.Resource;
@@ -27,6 +32,7 @@
import java.util.Collections;
import java.util.List;
import java.util.Set;
+import java.util.UUID;
import java.util.stream.Collectors;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
@@ -43,11 +49,9 @@
private static final Long MEMBER_DEPT_ID = 129L;
/**
- * 鏅�氫細鍛樿鑹� ID锛坰ystem_role銆屾櫘閫氫細鍛樸�嶏級
- *
- * <p>璇ヨ鑹插彧鍒嗛厤銆屾垜鐨勮祫鏂欍�嶈彍鍗曪紝涓嶅寘鍚。妗堢鐞嗙瓑鍚庡彴鑿滃崟锛岀敤浜庡疄鐜版潈闄愰殧绂汇��
+ * 浼佷笟寰俊鎵爜鐢ㄦ埛瑙掕壊缂栫爜锛涙寜缂栫爜鏌ヨ锛岄伩鍏嶄笉鍚岀幆澧冭鑹茬紪鍙蜂笉涓�鑷淬��
*/
- private static final Long MEMBER_ROLE_ID = 9100L;
+ private static final String MEMBER_ROLE_CODE = "wxcp_user";
public static final String MEMBER_RECORD_TYPE = "ums_member";
@@ -65,6 +69,12 @@
@Resource
private UmsMemberMapper memberMapper;
+ @Resource
+ private UmsApplicationMapper applicationMapper;
+ @Resource
+ private SocialUserService socialUserService;
+ @Resource
+ private RoleService roleService;
@Resource
private UmsNoRedisDAO noRedisDAO;
@@ -81,6 +91,8 @@
@Override
@Transactional(rollbackFor = Exception.class)
public UmsMemberDO createMemberFromApplication(UmsApplicationDO application) {
+ UmsMemberDO existing = memberMapper.selectByApplicationId(application.getId());
+ if (existing != null) return existing;
// 1. 鐢熸垚浼氬憳缂栧彿
String no = noRedisDAO.generate(UmsNoRedisDAO.MEMBER_NO_PREFIX);
if (memberMapper.selectByNo(no) != null) {
@@ -89,6 +101,9 @@
// 2. 鍒涘缓绯荤粺鐢ㄦ埛璐﹀彿
Long userId = createSystemUser(application);
+ if (StrUtil.isNotBlank(application.getWxcpOpenid())) {
+ socialUserService.bindApprovedWxcpUser(application.getWxcpOpenid(), userId);
+ }
// 3. 鍒涘缓浼氬憳璁板綍
UmsMemberDO member = UmsMemberDO.builder()
@@ -97,6 +112,8 @@
.applicationId(application.getId())
.memberType(application.getMemberType())
.memberGrade(application.getMemberGrade())
+ .feePaid(application.getFeePaid())
+ .passwordSetupRequired(StrUtil.isBlank(application.getContactPhone()))
.name(application.getApplicantName())
.contactPhone(application.getContactPhone())
.contactEmail(application.getContactEmail())
@@ -142,7 +159,7 @@
@Override
@Transactional(rollbackFor = Exception.class)
public void updateMember(UmsMemberSaveReqVO updateReqVO) {
- validateMemberExists(updateReqVO.getId());
+ UmsMemberDO existingMember = validateMemberExists(updateReqVO.getId());
validateMemberGrade(updateReqVO.getMemberType(), updateReqVO.getMemberGrade());
validateFeePaid(updateReqVO);
@@ -154,6 +171,10 @@
UmsMemberDO updateObj = BeanUtils.toBean(updateReqVO, UmsMemberDO.class);
memberMapper.updateById(updateObj);
+ if (existingMember.getApplicationId() != null && updateReqVO.getFeePaid() != null) {
+ applicationMapper.updateById(new UmsApplicationDO()
+ .setId(existingMember.getApplicationId()).setFeePaid(updateReqVO.getFeePaid()));
+ }
if (clearFeeYear) {
memberMapper.update(null, new LambdaUpdateWrapper<UmsMemberDO>()
.eq(UmsMemberDO::getId, updateReqVO.getId())
@@ -224,9 +245,19 @@
private Long createSystemUser(UmsApplicationDO application) {
// 鎵嬫満鍙峰彲浣滀负鐢ㄦ埛鍚�/瀵嗙爜锛堝弬鑰� HRM 鍛樺伐妯″紡锛�
- String mobile = StrUtil.blankToDefault(application.getContactPhone(), "unknown");
- String username = mobile;
- String nickname = application.getApplicantName();
+ String mobile = StrUtil.trim(application.getContactPhone());
+ if ((StrUtil.isBlank(mobile) && StrUtil.isBlank(application.getWxcpOpenid()))
+ || (StrUtil.isNotBlank(mobile) && !mobile.matches("^1[3-9]\\d{9}$"))) {
+ throw exception(APPLICATION_WXCP_INFO_REQUIRED);
+ }
+ RoleDO role = roleService.getRoleByCode(MEMBER_ROLE_CODE);
+ if (role == null || CommonStatusEnum.isDisable(role.getStatus())) {
+ throw exception(MEMBER_WXCP_ROLE_NOT_AVAILABLE);
+ }
+ boolean hasMobile = StrUtil.isNotBlank(mobile);
+ String username = hasMobile ? mobile : "wx" + UUID.randomUUID().toString().replace("-", "").substring(0, 16);
+ String password = hasMobile ? mobile : UUID.randomUUID().toString().replace("-", "").substring(0, 16);
+ String nickname = StrUtil.maxLength(application.getApplicantName(), 30);
try {
return adminUserApi.createUser(new AdminUserCreateReqDTO()
@@ -234,9 +265,9 @@
.setNickname(nickname)
.setDeptId(MEMBER_DEPT_ID)
.setPostIds(Collections.emptySet())
- .setRoleIds(Collections.singleton(MEMBER_ROLE_ID))
- .setMobile(mobile)
- .setPassword(mobile) // 榛樿瀵嗙爜涓烘墜鏈哄彿
+ .setRoleIds(Collections.singleton(role.getId()))
+ .setMobile(hasMobile ? mobile : null)
+ .setPassword(password)
.setEmail(application.getContactEmail())
.setRemark("鐢卞叆浼氱敵璇疯嚜鍔ㄥ垱寤猴細" + application.getNo()));
} catch (Exception e) {
@@ -291,4 +322,4 @@
return archive != null ? archive.getNo() : null;
}
-}
\ No newline at end of file
+}
diff --git a/yudao-module-ums/src/main/java/cn/iocoder/yudao/module/ums/service/member/WxcpInitialPasswordService.java b/yudao-module-ums/src/main/java/cn/iocoder/yudao/module/ums/service/member/WxcpInitialPasswordService.java
new file mode 100644
index 0000000..baddee1
--- /dev/null
+++ b/yudao-module-ums/src/main/java/cn/iocoder/yudao/module/ums/service/member/WxcpInitialPasswordService.java
@@ -0,0 +1,38 @@
+package cn.iocoder.yudao.module.ums.service.member;
+
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import cn.iocoder.yudao.module.system.service.auth.WxcpPasswordSetupGrant;
+import cn.iocoder.yudao.module.system.service.user.AdminUserService;
+import cn.iocoder.yudao.module.ums.dal.dataobject.member.UmsMemberDO;
+import cn.iocoder.yudao.module.ums.dal.mysql.member.UmsMemberMapper;
+import jakarta.annotation.Resource;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.iocoder.yudao.module.ums.enums.ErrorCodeConstants.*;
+
+@Service
+public class WxcpInitialPasswordService {
+ @Resource private UmsMemberMapper memberMapper;
+ @Resource private AdminUserService userService;
+ @Resource private WxcpPasswordSetupGrant grant;
+
+ @Transactional(rollbackFor = Exception.class)
+ public String setPassword(Long userId, String token, String password) {
+ if (!grant.valid(token, userId)) throw exception(MEMBER_PASSWORD_SETUP_EXPIRED);
+ UmsMemberDO member = memberMapper.selectByUserId(userId);
+ if (member == null || !Boolean.TRUE.equals(member.getPasswordSetupRequired())) {
+ throw exception(MEMBER_PASSWORD_SETUP_NOT_REQUIRED);
+ }
+ int changed = memberMapper.update(null, new LambdaUpdateWrapper<UmsMemberDO>()
+ .eq(UmsMemberDO::getId, member.getId())
+ .eq(UmsMemberDO::getUserId, userId)
+ .eq(UmsMemberDO::getPasswordSetupRequired, true)
+ .set(UmsMemberDO::getPasswordSetupRequired, false));
+ if (changed != 1) throw exception(MEMBER_PASSWORD_SETUP_NOT_REQUIRED);
+ userService.updateUserPassword(userId, password);
+ String username = userService.getUser(userId).getUsername();
+ grant.revoke(token);
+ return username;
+ }
+}
diff --git a/yudao-module-ums/src/test/java/cn/iocoder/yudao/module/ums/service/application/UmsApplicationApprovalValidatorTest.java b/yudao-module-ums/src/test/java/cn/iocoder/yudao/module/ums/service/application/UmsApplicationApprovalValidatorTest.java
new file mode 100644
index 0000000..27406e1
--- /dev/null
+++ b/yudao-module-ums/src/test/java/cn/iocoder/yudao/module/ums/service/application/UmsApplicationApprovalValidatorTest.java
@@ -0,0 +1,46 @@
+package cn.iocoder.yudao.module.ums.service.application;
+
+import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
+import cn.iocoder.yudao.module.ums.dal.dataobject.application.UmsApplicationDO;
+import cn.iocoder.yudao.module.ums.dal.mysql.application.UmsApplicationMapper;
+import org.flowable.engine.runtime.ProcessInstance;
+import org.flowable.task.api.Task;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.Mockito.*;
+
+class UmsApplicationApprovalValidatorTest extends BaseMockitoUnitTest {
+ @InjectMocks private UmsApplicationApprovalValidator validator;
+ @Mock private UmsApplicationMapper mapper;
+ @Mock private ProcessInstance instance;
+ @Mock private Task task;
+
+ @Test void requiresConfirmedFeeBeforeApproval() {
+ when(instance.getProcessDefinitionKey()).thenReturn("ums_membership_approve");
+ when(instance.getBusinessKey()).thenReturn("12");
+ when(mapper.selectById(12L)).thenReturn(new UmsApplicationDO());
+ assertThrows(RuntimeException.class, () -> validator.validate(1L, task, instance));
+ }
+
+ @Test void permitsExplicitUnpaid() {
+ when(instance.getProcessDefinitionKey()).thenReturn("ums_membership_approve");
+ when(instance.getBusinessKey()).thenReturn("12");
+ when(mapper.selectById(12L)).thenReturn(new UmsApplicationDO().setFeePaid(false));
+ assertDoesNotThrow(() -> validator.validate(1L, task, instance));
+ }
+
+ @Test void ignoresOtherProcesses() {
+ when(instance.getProcessDefinitionKey()).thenReturn("other");
+ validator.validate(1L, task, instance);
+ verifyNoInteractions(mapper);
+ }
+
+ @Test void permitsAutomaticSubmissionOfStarterNode() {
+ when(instance.getProcessDefinitionKey()).thenReturn("ums_membership_approve");
+ when(task.getTaskDefinitionKey()).thenReturn("StartUserNode");
+ validator.validate(1L, task, instance);
+ verifyNoInteractions(mapper);
+ }
+}
diff --git a/yudao-module-ums/src/test/java/cn/iocoder/yudao/module/ums/service/application/UmsApplicationFeeConfirmationTest.java b/yudao-module-ums/src/test/java/cn/iocoder/yudao/module/ums/service/application/UmsApplicationFeeConfirmationTest.java
new file mode 100644
index 0000000..3e78a78
--- /dev/null
+++ b/yudao-module-ums/src/test/java/cn/iocoder/yudao/module/ums/service/application/UmsApplicationFeeConfirmationTest.java
@@ -0,0 +1,33 @@
+package cn.iocoder.yudao.module.ums.service.application;
+
+import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
+import cn.iocoder.yudao.module.bpm.service.task.BpmTaskService;
+import cn.iocoder.yudao.module.ums.dal.dataobject.application.UmsApplicationDO;
+import cn.iocoder.yudao.module.ums.dal.mysql.application.UmsApplicationMapper;
+import cn.iocoder.yudao.module.ums.enums.UmsApplicationStatusEnum;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.Mockito.*;
+
+class UmsApplicationFeeConfirmationTest extends BaseMockitoUnitTest {
+ @InjectMocks private UmsApplicationServiceImpl service;
+ @Mock private UmsApplicationMapper mapper;
+ @Mock private BpmTaskService taskService;
+
+ @Test void nonAssigneeCannotConfirm() {
+ when(mapper.selectById(1L)).thenReturn(new UmsApplicationDO()
+ .setStatus(UmsApplicationStatusEnum.APPROVING.getStatus()).setProcessInstanceId("p1"));
+ when(taskService.getTodoTask(9L, null, "p1")).thenReturn(null);
+ assertThrows(RuntimeException.class, () -> service.confirmFee(1L, 9L, true));
+ verify(mapper, never()).update(any(), any());
+ }
+
+ @Test void completedApplicationCannotBeChanged() {
+ when(mapper.selectById(1L)).thenReturn(new UmsApplicationDO()
+ .setStatus(UmsApplicationStatusEnum.APPROVED.getStatus()).setProcessInstanceId("p1"));
+ assertFalse(service.canConfirmFee(1L, 9L));
+ verifyNoInteractions(taskService);
+ }
+}
diff --git a/yudao-module-ums/src/test/java/cn/iocoder/yudao/module/ums/service/application/WxcpMembershipRegistrationImplTest.java b/yudao-module-ums/src/test/java/cn/iocoder/yudao/module/ums/service/application/WxcpMembershipRegistrationImplTest.java
new file mode 100644
index 0000000..38b6bc7
--- /dev/null
+++ b/yudao-module-ums/src/test/java/cn/iocoder/yudao/module/ums/service/application/WxcpMembershipRegistrationImplTest.java
@@ -0,0 +1,52 @@
+package cn.iocoder.yudao.module.ums.service.application;
+
+import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
+import cn.iocoder.yudao.module.ums.dal.dataobject.application.UmsApplicationDO;
+import cn.iocoder.yudao.module.ums.dal.mysql.application.UmsApplicationMapper;
+import cn.iocoder.yudao.module.ums.dal.redis.UmsNoRedisDAO;
+import cn.iocoder.yudao.module.ums.enums.UmsApplicationStatusEnum;
+import cn.iocoder.yudao.module.bpm.api.task.BpmProcessInstanceApi;
+import cn.iocoder.yudao.module.system.service.user.AdminUserService;
+import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
+import cn.iocoder.yudao.module.system.service.permission.PermissionService;
+import com.baomidou.mybatisplus.core.conditions.Wrapper;
+import org.junit.jupiter.api.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.ArgumentMatchers.*;
+import static org.mockito.Mockito.*;
+
+class WxcpMembershipRegistrationImplTest extends BaseMockitoUnitTest {
+ @InjectMocks private WxcpMembershipRegistrationImpl service;
+ @Mock private UmsApplicationMapper mapper;
+ @Mock private UmsNoRedisDAO noRedisDAO;
+ @Mock private BpmProcessInstanceApi processInstanceApi;
+ @Mock private AdminUserService userService;
+ @Mock private PermissionService permissionService;
+
+ @Test
+ void firstScanStartsApprovalWithUnconfirmedFee() {
+ when(noRedisDAO.generate(anyString())).thenReturn("SQ001");
+ when(userService.getUserByUsername("wxcpservice")).thenReturn(new AdminUserDO().setId(5L).setStatus(0).setRemark("浠呯敤浜庣郴缁熻嚜鍔ㄥ彂璧峰叆浼氬鎵�"));
+ when(processInstanceApi.createProcessInstance(eq(5L), any())).thenReturn("process-1");
+ service.startIfAbsent("openid-1", "浼氬憳", "13800138000");
+ ArgumentCaptor<UmsApplicationDO> captor = ArgumentCaptor.forClass(UmsApplicationDO.class);
+ verify(mapper).insert(captor.capture());
+ UmsApplicationDO draft = captor.getValue();
+ assertEquals("openid-1", draft.getWxcpOpenid());
+ assertEquals(UmsApplicationStatusEnum.APPROVING.getStatus(), draft.getStatus());
+ assertNull(draft.getFeePaid());
+ assertEquals("13800138000", draft.getContactPhone());
+ verify(processInstanceApi).createProcessInstance(eq(5L), any());
+ }
+
+ @Test
+ void repeatScanDoesNotCreateAnotherDraft() {
+ when(mapper.selectOne(any(Wrapper.class))).thenReturn(new UmsApplicationDO());
+ service.startIfAbsent("openid-1", "浼氬憳", null);
+ verify(mapper, never()).insert(any(UmsApplicationDO.class));
+ verifyNoInteractions(processInstanceApi);
+ }
+}
diff --git a/yudao-module-ums/src/test/java/cn/iocoder/yudao/module/ums/service/member/WxcpInitialPasswordServiceTest.java b/yudao-module-ums/src/test/java/cn/iocoder/yudao/module/ums/service/member/WxcpInitialPasswordServiceTest.java
new file mode 100644
index 0000000..0768342
--- /dev/null
+++ b/yudao-module-ums/src/test/java/cn/iocoder/yudao/module/ums/service/member/WxcpInitialPasswordServiceTest.java
@@ -0,0 +1,49 @@
+package cn.iocoder.yudao.module.ums.service.member;
+
+import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
+import cn.iocoder.yudao.module.system.service.auth.WxcpPasswordSetupGrant;
+import cn.iocoder.yudao.module.system.service.user.AdminUserService;
+import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
+import cn.iocoder.yudao.module.ums.dal.dataobject.member.UmsMemberDO;
+import cn.iocoder.yudao.module.ums.dal.mysql.member.UmsMemberMapper;
+import com.baomidou.mybatisplus.core.conditions.Wrapper;
+import com.baomidou.mybatisplus.core.MybatisConfiguration;
+import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
+import org.apache.ibatis.builder.MapperBuilderAssistant;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.ArgumentMatchers.*;
+import static org.mockito.Mockito.*;
+
+class WxcpInitialPasswordServiceTest extends BaseMockitoUnitTest {
+ @InjectMocks private WxcpInitialPasswordService service;
+ @Mock private UmsMemberMapper memberMapper;
+ @Mock private AdminUserService userService;
+ @Mock private WxcpPasswordSetupGrant grant;
+
+ @Test void rejectsSessionWithoutWxcpGrant() {
+ when(grant.valid("token", 1L)).thenReturn(false);
+ assertThrows(RuntimeException.class, () -> service.setPassword(1L, "token", "password1"));
+ verifyNoInteractions(memberMapper, userService);
+ }
+
+ @Test void rejectsAccountWithoutPendingSetup() {
+ when(grant.valid("token", 1L)).thenReturn(true);
+ when(memberMapper.selectByUserId(1L)).thenReturn(new UmsMemberDO().setPasswordSetupRequired(false));
+ assertThrows(RuntimeException.class, () -> service.setPassword(1L, "token", "password1"));
+ verifyNoInteractions(userService);
+ }
+
+ @Test void setsPasswordOnceAndReturnsOwnUsername() {
+ TableInfoHelper.initTableInfo(new MapperBuilderAssistant(new MybatisConfiguration(), ""), UmsMemberDO.class);
+ when(grant.valid("token", 1L)).thenReturn(true);
+ when(memberMapper.selectByUserId(1L)).thenReturn(new UmsMemberDO().setId(2L).setPasswordSetupRequired(true));
+ when(memberMapper.update(isNull(), any(Wrapper.class))).thenReturn(1);
+ when(userService.getUser(1L)).thenReturn(new AdminUserDO().setUsername("wx_example"));
+ assertEquals("wx_example", service.setPassword(1L, "token", "password1"));
+ verify(userService).updateUserPassword(1L, "password1");
+ verify(grant).revoke("token");
+ }
+}
--
Gitblit v1.9.3