puzhibing
4 天以前 a0e03b646f2c444b0f09687d24e9995112b99017
ruoyi-service/ruoyi-other/src/test/java/com/ruoyi/other/service/impl/TGroupCompanyServiceImplTest.java
@@ -29,6 +29,7 @@
import org.springframework.test.util.ReflectionTestUtils;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
@@ -48,6 +49,9 @@
    /** 编辑公司用例共享的远程账号客户端 mock,供 verify 使用 */
    private SysUserClient editUserClient;
    /** 新增公司用例共享的远程账号客户端 mock,供 verify 使用 */
    private SysUserClient addUserClient;
    @BeforeAll
    static void initTableInfo() {
@@ -247,7 +251,8 @@
    }
    /**
     * 添加/编辑企业弹窗不再配置账户模式:未传账户模式时按共享模式(1)建档
     * 添加/编辑企业弹窗不再配置账户模式:未传账户模式时按共享模式(1)建档;
     * 登录账号由系统自动生成(忽略前端传入值),并随返回体回显
     */
    @Test
    void addCompanyDefaultsAccountModeToSharedWhenAbsent() {
@@ -257,11 +262,16 @@
        company.setName("测试公司");
        TGroupCompanyServiceImpl service = buildAddCompanyService();
        assertEquals(200, code(service.addCompany(company)));
        AjaxResult result = service.addCompany(company);
        assertEquals(200, code(result));
        ArgumentCaptor<TGroupCompany> captor = ArgumentCaptor.forClass(TGroupCompany.class);
        Mockito.verify(service).save(captor.capture());
        assertEquals(1, captor.getValue().getAccountMode());
        // 账号不可由前端指定:传入值被系统生成值覆盖
        assertTrue(captor.getValue().getAccount().matches("admin\\d{4}"));
        assertTrue(!captor.getValue().getAccount().equals("company01"));
        assertEquals(captor.getValue().getAccount(), String.valueOf(result.get("data")));
    }
    /**
@@ -280,6 +290,70 @@
        ArgumentCaptor<TGroupCompany> captor = ArgumentCaptor.forClass(TGroupCompany.class);
        Mockito.verify(service).save(captor.capture());
        assertEquals(2, captor.getValue().getAccountMode());
        // 账号仍为系统生成值,而非前端传入值
        assertTrue(captor.getValue().getAccount().matches("admin\\d{4}"));
        assertTrue(!captor.getValue().getAccount().equals("company02"));
    }
    /**
     * 新增公司:登录账号自动生成 admin+4位随机数字,初始密码按登录口径 HMAC-MD5 传输,
     * 生成账号随返回体回显
     */
    @Test
    void addCompanyGeneratesAccountAndDefaultPassword() {
        TGroupCompany company = new TGroupCompany();
        company.setName("测试公司");
        TGroupCompanyServiceImpl service = buildAddCompanyService();
        AjaxResult result = service.addCompany(company);
        assertEquals(200, code(result));
        assertTrue(company.getAccount().matches("admin\\d{4}"));
        assertEquals(company.getAccount(), String.valueOf(result.get("data")));
        ArgumentCaptor<SysUser> userCaptor = ArgumentCaptor.forClass(SysUser.class);
        Mockito.verify(addUserClient).addSysUser(userCaptor.capture());
        SysUser created = userCaptor.getValue();
        assertEquals(company.getAccount(), created.getUserName());
        // 初始密码 123456:HMAC-MD5(明文, password),与登录/重置链路口径一致
        assertEquals(hmacMd5Expected("123456", "password"), created.getPassword());
    }
    /**
     * 生成候选账号被 sys_user 占用时换号重试,最终以未占用账号建户
     */
    @Test
    void addCompanyRetriesWhenCandidateAccountOccupied() {
        TGroupCompany company = new TGroupCompany();
        company.setName("测试公司");
        SysUserClient sysUserClient = Mockito.mock(SysUserClient.class);
        SysUser occupied = new SysUser();
        occupied.setUserId(9L);
        // 首个候选账号被占用,第二个候选通过
        Mockito.when(sysUserClient.queryUserByUserName(Mockito.any()))
                .thenReturn(R.ok(occupied), R.ok());
        Mockito.when(sysUserClient.addSysUser(Mockito.any())).thenReturn(R.ok(100L));
        Mockito.when(sysUserClient.getSysUser(Mockito.anyLong())).thenReturn(R.ok());
        SysRoleClient sysRoleClient = Mockito.mock(SysRoleClient.class);
        Mockito.when(sysRoleClient.getRoleIdByRoleKey(Mockito.any())).thenReturn(R.ok(74L));
        TGroupCompanyServiceImpl service = Mockito.spy(new TGroupCompanyServiceImpl());
        Mockito.doReturn(0L).when(service).count(Mockito.any());
        Mockito.doReturn(true).when(service).save(Mockito.any());
        ReflectionTestUtils.setField(service, "sysUserClient", sysUserClient);
        ReflectionTestUtils.setField(service, "sysRoleClient", sysRoleClient);
        AjaxResult result = service.addCompany(company);
        assertEquals(200, code(result));
        assertEquals(company.getAccount(), String.valueOf(result.get("data")));
        // 最终以通过查重的候选账号建户
        ArgumentCaptor<SysUser> userCaptor = ArgumentCaptor.forClass(SysUser.class);
        Mockito.verify(sysUserClient).addSysUser(userCaptor.capture());
        assertEquals(company.getAccount(), userCaptor.getValue().getUserName());
        assertTrue(userCaptor.getValue().getUserName().matches("admin\\d{4}"));
    }
    @Test
@@ -554,10 +628,30 @@
        Mockito.doReturn(true).when(service).save(Mockito.any());
        ReflectionTestUtils.setField(service, "sysUserClient", sysUserClient);
        ReflectionTestUtils.setField(service, "sysRoleClient", sysRoleClient);
        this.addUserClient = sysUserClient;
        return service;
    }
    /**
     * 按登录口径计算 HMAC-MD5(明文, key) 的期望值(小写 Hex),用于断言初始密码传输值
     */
    private String hmacMd5Expected(String text, String key) {
        try {
            javax.crypto.Mac mac = javax.crypto.Mac.getInstance("HmacMD5");
            mac.init(new javax.crypto.spec.SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "HmacMD5"));
            byte[] bytes = mac.doFinal(text.getBytes(StandardCharsets.UTF_8));
            StringBuilder hex = new StringBuilder(bytes.length * 2);
            for (byte b : bytes) {
                hex.append(Character.forDigit((b >> 4) & 0xF, 16));
                hex.append(Character.forDigit(b & 0xF, 16));
            }
            return hex.toString();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    /**
     * 编辑公司修改登录账号:按旧账号定位用户后把 userName 更新为新账号,
     * 不误走补建分支(避免双账号残留)
     */